From 681fd98b8d4bf4efc1346ab44a1bda5f55db0df3 Mon Sep 17 00:00:00 2001 From: Ghazi Akmal Fauzan <13521058@std.stei.itb.ac.id> Date: Mon, 1 Apr 2024 21:01:23 +0700 Subject: [PATCH] feat: intent gmail for send transaction --- app/src/main/AndroidManifest.xml | 10 ++ .../transaction/TransactionFragment.kt | 153 ++++++++++-------- .../main/res/layout/fragment_transaction.xml | 38 +++++ app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/provider_paths.xml | 6 + 5 files changed, 142 insertions(+), 67 deletions(-) create mode 100644 app/src/main/res/xml/provider_paths.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b178ced..221bdfd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,6 +17,16 @@ android:supportsRtl="true" android:theme="@style/Theme.NerbOS"> + <provider + android:name="androidx.core.content.FileProvider" + android:authorities="${applicationId}.provider" + android:exported="false" + android:grantUriPermissions="true"> + <meta-data + android:name="android.support.FILE_PROVIDER_PATHS" + android:resource="@xml/provider_paths" /> + </provider> + <meta-data android:name="com.google.android.geo.API_KEY" android:value="AIzaSyBdVOQUHGOg7TS74kPCzoEtDDlGGZHpAEs" /> diff --git a/app/src/main/java/com/example/nerbos/fragments/transaction/TransactionFragment.kt b/app/src/main/java/com/example/nerbos/fragments/transaction/TransactionFragment.kt index 4fecdfe..238b866 100644 --- a/app/src/main/java/com/example/nerbos/fragments/transaction/TransactionFragment.kt +++ b/app/src/main/java/com/example/nerbos/fragments/transaction/TransactionFragment.kt @@ -2,18 +2,14 @@ package com.example.nerbos.fragments.transaction import android.Manifest import android.app.Dialog -import android.content.Context -import android.content.Context.STORAGE_SERVICE +import android.content.ActivityNotFoundException import android.content.Intent import android.content.pm.PackageManager +import android.content.pm.ResolveInfo import android.location.Geocoder import android.location.Location import android.os.Bundle -import android.os.Environment -import android.os.storage.StorageManager -import android.provider.SyncStateContract import android.text.TextUtils -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -26,10 +22,8 @@ import android.widget.RadioGroup import android.widget.TextView import android.widget.Toast import androidx.core.app.ActivityCompat -import androidx.core.content.ContextCompat -import androidx.core.content.ContextCompat.getSystemService +import androidx.core.content.FileProvider import androidx.fragment.app.Fragment -import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -37,42 +31,23 @@ import com.example.nerbos.MapsActivity import com.example.nerbos.R import com.example.nerbos.model.Transaction import com.example.nerbos.model.TransactionCategory -import com.example.nerbos.viewmodel.TransactionViewModel import com.example.nerbos.service.Authentication import com.example.nerbos.service.Utils +import com.example.nerbos.viewmodel.TransactionViewModel import com.google.android.gms.location.FusedLocationProviderClient import com.google.android.gms.location.LocationServices -import org.apache.poi.hssf.usermodel.HSSFCell -import org.apache.poi.hssf.usermodel.HSSFRow -import org.apache.poi.hssf.usermodel.HSSFSheet import org.apache.poi.hssf.usermodel.HSSFWorkbook import org.apache.poi.ss.usermodel.Cell import org.apache.poi.ss.usermodel.Row import org.apache.poi.ss.usermodel.Sheet import org.apache.poi.ss.usermodel.Workbook -import org.apache.poi.xssf.usermodel.XSSFSheet import org.apache.poi.xssf.usermodel.XSSFWorkbook import java.io.File -import java.io.FileNotFoundException import java.io.FileOutputStream -import java.io.IOException -import java.io.OutputStream import java.util.Locale -// TODO: Rename parameter arguments, choose names that match -// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER -private const val ARG_PARAM1 = "param1" -private const val ARG_PARAM2 = "param2" - -/** - * A simple [Fragment] subclass. - * Use the [Transaction.newInstance] factory method to - * create an instance of this fragment. - */ class TransactionFragment : Fragment() { - // TODO: Rename and change types of parameters - private var param1: String? = null - private var param2: String? = null + private lateinit var transactionViewModel: TransactionViewModel private lateinit var authentication: Authentication private var currentLocation: Location? = null @@ -82,10 +57,6 @@ class TransactionFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - arguments?.let { - param1 = it.getString(ARG_PARAM1) - param2 = it.getString(ARG_PARAM2) - } fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(requireContext()) geocoder = Geocoder(requireContext(), Locale.getDefault()) } @@ -109,8 +80,9 @@ class TransactionFragment : Fragment() { transactionViewModel = ViewModelProvider(this)[TransactionViewModel::class.java] transactionViewModel.setReadDataUserId(authentication.getNim()) - transactionViewModel.readAllData!!.observe(viewLifecycleOwner, Observer { transactions -> - transactionAdapter.setData(transactions) }) + transactionViewModel.readAllData!!.observe(viewLifecycleOwner) { transactions -> + transactionAdapter.setData(transactions) + } // Add Transaction Button view.findViewById<ImageView>(R.id.addTransactionButton).setOnClickListener { @@ -148,12 +120,44 @@ class TransactionFragment : Fragment() { Toast.makeText(requireContext(), "Transaction Report.xlsx is downloaded", Toast.LENGTH_LONG).show() } + // Send Transaction + val xlsSend = view.findViewById<TextView>(R.id.xlsSend) + val xlsxSend = view.findViewById<TextView>(R.id.xlsxSend) + + view.findViewById<ImageView>(R.id.sendTransactionsButton).setOnClickListener{ + if (xlsSend.visibility == View.VISIBLE){ + xlsSend.visibility = View.GONE + xlsxSend.visibility = View.GONE + } else if(xlsSend.visibility == View.GONE){ + xlsSend.visibility = View.VISIBLE + xlsxSend.visibility = View.VISIBLE + } + } + + xlsSend.setOnClickListener { + val subject = getString(R.string.email_subject) + val message = getString(R.string.email_message) + val workbook: Workbook = createExcelFile(transactionAdapter.getTransactionList(), "xls") + sendWorkbook(authentication.getEmail(), subject, message, workbook, "xls") + xlsSend.visibility = View.GONE + xlsxSend.visibility = View.GONE + } + + xlsxSend.setOnClickListener { + val subject = getString(R.string.email_subject) + val message = getString(R.string.email_message) + val workbook: Workbook = createExcelFile(transactionAdapter.getTransactionList(), "xlsx") + sendWorkbook(authentication.getEmail(), subject, message, workbook, "xlsx") + xlsSend.visibility = View.GONE + xlsxSend.visibility = View.GONE + } + return view } private fun createExcelFile(transactions : List<Transaction>, extension: String) : Workbook{ - var workbook : Workbook - var sheet : Sheet + val workbook : Workbook + val sheet : Sheet if (extension == "xls"){ workbook = HSSFWorkbook() sheet = workbook.createSheet("My Transactions") @@ -195,13 +199,13 @@ class TransactionFragment : Fragment() { private fun saveWorkBook(workbook: Workbook, extension: String) { val appDirectory = requireContext().filesDir - val filepath:File = File(appDirectory , "Transaction Report.$extension") + val filepath = File(appDirectory , "Transaction Report.$extension") try{ if(!filepath.exists()){ filepath.createNewFile() } - val fileOutputStream : FileOutputStream = FileOutputStream(filepath) + val fileOutputStream = FileOutputStream(filepath) workbook.write(fileOutputStream) fileOutputStream.flush() fileOutputStream.close() @@ -211,6 +215,43 @@ class TransactionFragment : Fragment() { } } + private fun sendWorkbook(email: String, subject: String, message: String, workbook: Workbook, extension: String) { + val file = File(requireContext().cacheDir, "Transaction Report.$extension") + + try { + val fileOutputStream = FileOutputStream(file) + workbook.write(fileOutputStream) + fileOutputStream.flush() + fileOutputStream.close() + + val intent = Intent(Intent.ACTION_SEND) + val uri = FileProvider.getUriForFile(requireContext(), requireContext().packageName + ".provider", file) + intent.type = "message/rfc822" + intent.putExtra(Intent.EXTRA_EMAIL, arrayOf(email)) + intent.putExtra(Intent.EXTRA_SUBJECT, subject) + intent.putExtra(Intent.EXTRA_TEXT, message) + intent.putExtra(Intent.EXTRA_STREAM, uri) + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + + // Grant URI permission to the chosen email application + val chooserIntent = Intent.createChooser(intent, "Send Email") + val resInfoList: List<ResolveInfo> = requireContext().packageManager.queryIntentActivities(chooserIntent, PackageManager.MATCH_DEFAULT_ONLY) + for (resolveInfo in resInfoList) { + val packageName = resolveInfo.activityInfo.packageName + requireContext().grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + + try { + startActivity(chooserIntent) + } catch (e: ActivityNotFoundException) { + Toast.makeText(requireContext(), "No email client installed", Toast.LENGTH_SHORT).show() + } + } catch (e: Exception) { + e.printStackTrace() + Toast.makeText(requireContext(), "Failed to send file", Toast.LENGTH_SHORT).show() + } + } + private fun showLocationOnMap(location: String) { val mapIntent = Intent(requireActivity(), MapsActivity::class.java) mapIntent.putExtra("locationName", location) @@ -239,10 +280,8 @@ class TransactionFragment : Fragment() { dialog.findViewById<EditText>(R.id.locationInput).setOnClickListener { // TODO: Buat percabangan kalau ada internet - if (true){ - val etLocation= dialog.findViewById<EditText>(R.id.locationInput) - etLocation.setText(getAddressName()) - } + val etLocation= dialog.findViewById<EditText>(R.id.locationInput) + etLocation.setText(getAddressName()) } dialog.show() @@ -333,7 +372,7 @@ class TransactionFragment : Fragment() { val categoryString : String = radioButton.text.toString() - val userID: Int = authentication.getNim(); + val userID: Int = authentication.getNim() val name : String = view.findViewById<EditText>(R.id.nameInput).text.toString() var category : TransactionCategory = TransactionCategory.INCOME if (categoryString === this.getString(R.string.income)){ @@ -366,7 +405,7 @@ class TransactionFragment : Fragment() { Manifest.permission.ACCESS_COARSE_LOCATION ) != PackageManager.PERMISSION_GRANTED ) { - ActivityCompat.requestPermissions(requireActivity(), arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), permissionCode) + ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), permissionCode) return } fusedLocationProviderClient.lastLocation.addOnSuccessListener (requireActivity()){ @@ -375,6 +414,7 @@ class TransactionFragment : Fragment() { } } + @Suppress("DEPRECATION") private fun getAddressName() : String{ // Hanya set otomatis jika mendapatkan permission, jika tidak input lokasi string setLocation() @@ -390,25 +430,4 @@ class TransactionFragment : Fragment() { private fun inputCheck(name: String, nominal:Float, location:String): Boolean { return !(TextUtils.isEmpty(name) || TextUtils.isEmpty(location) || (nominal<0) ) } - - companion object { - /** - * Use this factory method to create a new instance of - * this fragment using the provided parameters. - * - * @param param1 Parameter 1. - * @param param2 Parameter 2. - * @return A new instance of fragment Transaction. - */ - // TODO: Rename and change types and number of parameters - @JvmStatic - fun newInstance(param1: String, param2: String) = - TransactionFragment().apply { - arguments = Bundle().apply { - putString(ARG_PARAM1, param1) - putString(ARG_PARAM2, param2) - } - } - } - } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_transaction.xml b/app/src/main/res/layout/fragment_transaction.xml index 97a7f7f..10f6f89 100644 --- a/app/src/main/res/layout/fragment_transaction.xml +++ b/app/src/main/res/layout/fragment_transaction.xml @@ -105,5 +105,43 @@ /> </LinearLayout> + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_marginStart="290dp" + android:background="@drawable/round_corner_save_selection" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/sendTransactionsButton" > + + <TextView + android:id="@+id/xlsSend" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/xls" + android:background="@drawable/border_save_selection" + android:textSize="18sp" + android:textColor="@color/base_bg" + android:paddingTop="2dp" + android:paddingStart="30dp" + android:paddingBottom="4dp" + android:paddingEnd="30dp" + android:visibility="gone" + /> + + <TextView + android:id="@+id/xlsxSend" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textSize="18sp" + android:textColor="@color/base_bg" + android:text="@string/xlsx" + android:paddingBottom="3dp" + android:paddingStart="26dp" + android:paddingEnd="30dp" + android:visibility="gone" + /> + </LinearLayout> + </androidx.constraintlayout.widget.ConstraintLayout> </FrameLayout> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6508694..4850983 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -65,5 +65,7 @@ <string name="capture_button">Capture Button</string> <string name="gallery_button">Gallery Button</string> + <string name="email_subject">NerbOS - Transaction Report</string> + <string name="email_message">Here is your transaction report</string> </resources> \ No newline at end of file diff --git a/app/src/main/res/xml/provider_paths.xml b/app/src/main/res/xml/provider_paths.xml new file mode 100644 index 0000000..660be41 --- /dev/null +++ b/app/src/main/res/xml/provider_paths.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<paths> + <cache-path + name="cache" + path="/" /> +</paths> \ No newline at end of file -- GitLab