diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0cfda2f4ae3998d70b8f4d4d7a5ea468c8039890..a60f0275b259d3d716f7f93f449ab8dacabbec76 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,10 +20,13 @@ tools:targetApi="31"> <activity android:name=".MainActivity" - android:exported="false"> + android:exported="false" + android:windowSoftInputMode="adjustResize"> </activity> <activity - android:name=".ui.login.LoginActivity" android:exported="true"> + android:name=".ui.login.LoginActivity" + android:exported="true" + android:windowSoftInputMode="adjustResize"> <intent-filter> <action android:name="android.intent.action.MAIN" /> diff --git a/app/src/main/java/com/example/abe/MainActivity.kt b/app/src/main/java/com/example/abe/MainActivity.kt index e4fcf4b95fba111cae571428aa6be372ce422665..1d489c1e9c4aa5e58d78ef4a4d0d5cc951125b6d 100644 --- a/app/src/main/java/com/example/abe/MainActivity.kt +++ b/app/src/main/java/com/example/abe/MainActivity.kt @@ -8,7 +8,7 @@ import android.content.Intent import android.content.IntentFilter import android.os.Bundle import android.util.Log -import android.widget.Toast +import android.view.View import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity @@ -94,6 +94,11 @@ class MainActivity : AppCompatActivity(), ExportAlertDialogFragment.ExportAlertD setupActionBarWithNavController(navController, appBarConfiguration) navView.setupWithNavController(navController) + navController.addOnDestinationChangedListener { _, destination, _ -> + if (destination.id == R.id.navigation_form_transaction) navView.visibility = View.GONE + else navView.visibility = View.VISIBLE + } + LocalBroadcastManager.getInstance(this).registerReceiver(br, filter) val serviceIntent = Intent(this, AuthService::class.java) @@ -102,7 +107,6 @@ class MainActivity : AppCompatActivity(), ExportAlertDialogFragment.ExportAlertD connectivityObserver = NetworkConnectivityObserver(applicationContext) connectivityObserver.observe().onEach { networkState = it - Log.v("abecekut", "Status is $it") if (it == ConnectivityObserver.NetworkState.UNAVAILABLE || it == ConnectivityObserver.NetworkState.LOST) { runOnUiThread { val builder: AlertDialog.Builder = AlertDialog.Builder(this@MainActivity) diff --git a/app/src/main/java/com/example/abe/domain/GenerateExcelUseCase.kt b/app/src/main/java/com/example/abe/domain/GenerateExcelUseCase.kt index 7764d3cd64172152c8153ee919b8b52ae57aa278..4dde8e65da48c2568ec468f2d3a08e5725200e17 100644 --- a/app/src/main/java/com/example/abe/domain/GenerateExcelUseCase.kt +++ b/app/src/main/java/com/example/abe/domain/GenerateExcelUseCase.kt @@ -33,7 +33,6 @@ class GenerateExcelUseCase( val outputStream = contentResolver.openOutputStream(uri) workbook.write(outputStream) outputStream?.close() - Log.d("ABE-EXPORT", "Finish writing file") } catch (e: Exception) { e.printStackTrace() } diff --git a/app/src/main/java/com/example/abe/services/AuthService.kt b/app/src/main/java/com/example/abe/services/AuthService.kt index c2201ee384d85faa55632fed1156a035349f4b4e..02cf0054244801fb834833dd008b9603de898942 100644 --- a/app/src/main/java/com/example/abe/services/AuthService.kt +++ b/app/src/main/java/com/example/abe/services/AuthService.kt @@ -3,8 +3,10 @@ package com.example.abe.services import android.app.Service import android.content.Context import android.content.Intent +import android.os.Handler import android.os.IBinder +import android.os.Looper import android.util.Log import androidx.core.content.ContentProviderCompat.requireContext import androidx.localbroadcastmanager.content.LocalBroadcastManager @@ -15,20 +17,21 @@ import com.example.abe.data.network.Retrofit class AuthService : Service(), CheckAuthResultCallback { + var isRunning: Boolean = false override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + isRunning = true Thread { - while (true) { - Log.v("Servicecekut", "Service is running...") + while (isRunning) { + Log.d("ABE-SRV", "Service still running") val retrofit = Retrofit() val sharedPref = getSharedPreferences( getString(R.string.preference_file_key), Context.MODE_PRIVATE ) val token = sharedPref.getString("login_token", "").toString() - Log.v("Servicecekut", token) retrofit.checkAuth(token, this) try { - Thread.sleep(30000) + Thread.sleep(10000) } catch (e: InterruptedException) { e.printStackTrace() } @@ -47,4 +50,8 @@ class AuthService : Service(), CheckAuthResultCallback { LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent) } } + + override fun onDestroy() { + isRunning = false + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/abe/ui/form_transaction/FormTransaction.kt b/app/src/main/java/com/example/abe/ui/form_transaction/FormTransaction.kt index d29c659c64b508b2c3badddea6bf84b787757016..688600c39d361493b7765218bfca9e39048a8bf2 100644 --- a/app/src/main/java/com/example/abe/ui/form_transaction/FormTransaction.kt +++ b/app/src/main/java/com/example/abe/ui/form_transaction/FormTransaction.kt @@ -2,6 +2,7 @@ package com.example.abe.ui.form_transaction import android.Manifest import android.annotation.SuppressLint +import android.app.AlertDialog import android.content.Context import android.content.Intent import android.content.pm.PackageManager @@ -11,15 +12,16 @@ import android.location.Location import android.location.LocationManager import android.net.Uri import android.os.Bundle -import android.provider.Settings import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.AdapterView import android.widget.ArrayAdapter +import android.widget.EditText import android.widget.Toast import androidx.activity.addCallback +import androidx.activity.result.contract.ActivityResultContracts import androidx.constraintlayout.widget.ConstraintSet import androidx.core.app.ActivityCompat import androidx.fragment.app.Fragment @@ -30,6 +32,7 @@ import com.example.abe.R import com.example.abe.databinding.FragmentFormTransactionBinding import com.google.android.gms.location.FusedLocationProviderClient import com.google.android.gms.location.LocationServices +import com.google.android.material.textfield.TextInputLayout import java.util.Locale class FormTransaction : Fragment() { @@ -39,7 +42,7 @@ class FormTransaction : Fragment() { private lateinit var fusedLocationProviderClient: FusedLocationProviderClient - private val permissionId = 2 + private val permissionId = 5 private val viewModel: FormTransactionViewModel by viewModels { FormTransactionViewModelFactory((activity?.application as ABEApplication).repository) @@ -48,10 +51,10 @@ class FormTransaction : Fragment() { private lateinit var user: String private var id: Int? = null + private var location: String? = null override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { super.onCreate(savedInstanceState) @@ -92,8 +95,7 @@ class FormTransaction : Fragment() { getLocation() val sharedPref = requireActivity().getSharedPreferences( - getString(R.string.preference_file_key), - Context.MODE_PRIVATE + getString(R.string.preference_file_key), Context.MODE_PRIVATE ) user = sharedPref.getString("user", "").toString() @@ -106,6 +108,7 @@ class FormTransaction : Fragment() { viewModel.setRandomAmount(args.getInt("random_amount")) useNewTrxLayout() } + arguments = null } else { useNewTrxLayout() } @@ -119,7 +122,13 @@ class FormTransaction : Fragment() { ConstraintSet().apply { clone(binding.formTransaction) - connect(R.id.formLocationContainer, ConstraintSet.TOP, R.id.formCategoryContainer, ConstraintSet.BOTTOM, 5) + connect( + R.id.formLocationContainer, + ConstraintSet.TOP, + R.id.formCategoryContainer, + ConstraintSet.BOTTOM, + 5 + ) applyTo(binding.formTransaction) } } @@ -132,34 +141,19 @@ class FormTransaction : Fragment() { private fun titleFocusListener() { binding.formTitleEditText.setOnFocusChangeListener { _, focused -> - if (!focused) { - binding.formTitleContainer.helperText = - if (binding.formTitleEditText.text.toString() - .isEmpty() - ) "Title is required" else null - } + if (!focused) setHelperText(binding.formTitleContainer, binding.formTitleEditText) } } private fun amountFocusListener() { binding.formAmountEditText.setOnFocusChangeListener { _, focused -> - if (!focused) { - binding.formAmountContainer.helperText = - if (binding.formAmountEditText.text.toString() - .isEmpty() - ) "Amount is required" else null - } + if (!focused) setHelperText(binding.formAmountContainer, binding.formAmountEditText) } } private fun categoryFocusListener() { binding.categoryAutocomplete.setOnFocusChangeListener { _, focused -> - if (!focused) { - binding.formCategoryContainer.helperText = - if (binding.categoryAutocomplete.text.toString() - .isEmpty() - ) "Category is required" else null - } + if (!focused) setHelperText(binding.formCategoryContainer, binding.categoryAutocomplete) } } @@ -168,7 +162,8 @@ class FormTransaction : Fragment() { val destinationLatitude = viewModel.latitude.value.toString() val destinationLongitude = viewModel.longitude.value.toString() - val mapUri = Uri.parse("https://maps.google.com/maps?daddr=$destinationLatitude,$destinationLongitude") + val mapUri = + Uri.parse("https://maps.google.com/maps?daddr=$destinationLatitude,$destinationLongitude") val intent = Intent(Intent.ACTION_VIEW, mapUri) startActivity(intent) } @@ -176,21 +171,28 @@ class FormTransaction : Fragment() { private fun locationFocusListener() { binding.formLocationEditText.setOnFocusChangeListener { _, focused -> - if (!focused) { - binding.formLocationContainer.helperText = - if (binding.formLocationEditText.text.toString() - .isEmpty() - ) "Location is required" else null - } + if (!focused) setHelperText(binding.formLocationContainer, binding.formLocationEditText) } } + private fun setHelperText(container: TextInputLayout, editText: EditText) { + container.helperText = + if (editText.text.toString().isEmpty()) "This field is required" else null + } + private fun saveButtonListener() { binding.btnSave.setOnClickListener { + setHelperText(binding.formTitleContainer, binding.formTitleEditText) + setHelperText(binding.formAmountContainer, binding.formAmountEditText) + setHelperText(binding.formCategoryContainer, binding.categoryAutocomplete) + + if (binding.formLocationEditText.text.toString().isEmpty()) { + binding.formLocationEditText.setText(location) + } + if (binding.formTitleEditText.text.toString() .isNotEmpty() && binding.formAmountEditText.text.toString() - .isNotEmpty() && binding.categoryAutocomplete.text.toString() - .isNotEmpty() && binding.formLocationEditText.text.toString().isNotEmpty() + .isNotEmpty() && binding.categoryAutocomplete.text.toString().isNotEmpty() ) { if (id != null) { viewModel.updateTransaction(id!!) @@ -204,8 +206,18 @@ class FormTransaction : Fragment() { private fun deleteButtonListener() { binding.btnDelete.setOnClickListener { - id?.let { viewModel.deleteTransaction(id!!) } - findNavController().navigateUp() + id?.let { + val builder: AlertDialog.Builder = AlertDialog.Builder(requireContext()) + builder.setMessage("Are you sure you want to delete this transaction?") + .setPositiveButton("Delete") { dialog, which -> + viewModel.deleteTransaction(id!!) + findNavController().navigateUp() + }.setNegativeButton("Cancel") { _, _ -> } + + val dialog: AlertDialog = builder.create() + dialog.show() + } + } } @@ -219,12 +231,9 @@ class FormTransaction : Fragment() { private fun checkPermissions(): Boolean { if (ActivityCompat.checkSelfPermission( - requireActivity(), - Manifest.permission.ACCESS_COARSE_LOCATION - ) == PackageManager.PERMISSION_GRANTED && - ActivityCompat.checkSelfPermission( - requireActivity(), - Manifest.permission.ACCESS_FINE_LOCATION + requireActivity(), Manifest.permission.ACCESS_COARSE_LOCATION + ) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission( + requireActivity(), Manifest.permission.ACCESS_FINE_LOCATION ) == PackageManager.PERMISSION_GRANTED ) { return true @@ -232,62 +241,73 @@ class FormTransaction : Fragment() { return false } - private fun requestPermissions() { - ActivityCompat.requestPermissions( - requireActivity(), + private fun askForPermissions() { + requestPermissionsLauncher.launch( arrayOf( - Manifest.permission.ACCESS_COARSE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION - ), - permissionId + Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION + ) ) } - @SuppressLint("MissingSuperCall") - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array<String>, - grantResults: IntArray - ) { - if (requestCode == permissionId) { - if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { - getLocation() - } + private var requestPermissionsLauncher = registerForActivityResult( + ActivityResultContracts.RequestMultiplePermissions() + ) { permissions -> + var granted = false + permissions.entries.forEach { + if (it.value) granted = true + } + if (granted) { + getCurrentLocation() + } else { + val defaultLatitude = -6.892382 + val defaultLongitude = 107.608352 + Toast.makeText(requireActivity(), "Location set to default", Toast.LENGTH_SHORT).show() + setLocation(defaultLatitude, defaultLongitude) } } - @SuppressLint("MissingPermission", "SetTextI18n") private fun getLocation() { if (checkPermissions()) { - if (isLocationEnabled()) { - fusedLocationProviderClient.lastLocation.addOnCompleteListener(requireActivity()) { task -> - val location: Location? = task.result - if (location != null) { - val geocoder = Geocoder(requireContext(), Locale.getDefault()) - val list: MutableList<Address>? = - geocoder.getFromLocation(location.latitude, location.longitude, 1) - Log.v( - "ABECEKUT", - location.latitude.toString() + " " + location.longitude.toString() - ) - binding.viewModel?.latitude?.value = location.latitude - binding.viewModel?.longitude?.value = location.longitude - if (list != null) { - binding.viewModel?.location?.value = (list[0].getAddressLine(0)) - binding.formLocationEditText.setText(list[0].getAddressLine(0)) - } - } else { - Log.v("ABECEKUT", "location not available") - } + getCurrentLocation() + } else { + askForPermissions() + } + } + + private fun setLocation(latitude: Double, longitude: Double) { + val geocoder = Geocoder(requireContext(), Locale.getDefault()) + val list: MutableList<Address>? = geocoder.getFromLocation(latitude, longitude, 1) + viewModel.latitude.value = latitude + viewModel.longitude.value = longitude + if (list != null) { + // TODO: check why this sometimes doesn't update the ui + viewModel.location.value = (list[0].getAddressLine(0)) + location = (list[0].getAddressLine(0)) +// Toast.makeText(requireActivity(), viewModel.location.value, Toast.LENGTH_SHORT) +// .show() + } + } + + @SuppressLint("MissingPermission") + private fun getCurrentLocation() { + val defaultLatitude = -6.892382 + val defaultLongitude = 107.608352 + + if (checkPermissions() && isLocationEnabled()) { + fusedLocationProviderClient.lastLocation.addOnCompleteListener(requireActivity()) { task -> + val location: Location? = task.result + if (location != null) { + setLocation(location.latitude, location.longitude) + } else { + Toast.makeText(requireActivity(), "Location set to default", Toast.LENGTH_SHORT) + .show() + setLocation(defaultLatitude, defaultLongitude) } - } else { - Toast.makeText(requireActivity(), "Please turn on location", Toast.LENGTH_LONG) - .show() - val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS) - startActivity(intent) } } else { - requestPermissions() + Toast.makeText(requireActivity(), "Location set to default", Toast.LENGTH_SHORT).show() + setLocation(defaultLatitude, defaultLongitude) } } + } \ No newline at end of file diff --git a/app/src/main/java/com/example/abe/ui/login/LoginActivity.kt b/app/src/main/java/com/example/abe/ui/login/LoginActivity.kt index efab5113cf8eacf0487ee1069a405484566224da..b1472f0f28cd403dd902636d32d19f82c8f3e6ec 100644 --- a/app/src/main/java/com/example/abe/ui/login/LoginActivity.kt +++ b/app/src/main/java/com/example/abe/ui/login/LoginActivity.kt @@ -68,7 +68,6 @@ class LoginActivity : AppCompatActivity(), LoginResultCallback { connectivityObserver = NetworkConnectivityObserver(applicationContext) connectivityObserver.observe().onEach { - Log.v("abecekut", "Status is $it") networkState = it if (it == ConnectivityObserver.NetworkState.UNAVAILABLE || it == ConnectivityObserver.NetworkState.LOST) { runOnUiThread { diff --git a/app/src/main/res/layout/fragment_form_transaction.xml b/app/src/main/res/layout/fragment_form_transaction.xml index 6bec0c5bd2b7036ce7e296316aa728af398e5ba6..1f190286bd265c2a82573b58e8ce08c6b8535ed6 100644 --- a/app/src/main/res/layout/fragment_form_transaction.xml +++ b/app/src/main/res/layout/fragment_form_transaction.xml @@ -19,6 +19,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginHorizontal="20dp" + android:layout_marginTop="15dp" app:helperTextTextColor="@color/destructive" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" @@ -65,7 +66,7 @@ android:layout_height="wrap_content" android:layout_marginHorizontal="20dp" android:layout_marginTop="5dp" - android:hint="Category" + android:hint="@string/category" app:helperTextTextColor="@color/destructive" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" @@ -91,7 +92,7 @@ android:paddingTop="1dp" android:paddingBottom="1dp" android:layout_marginTop="8dp" - android:text="Open in Map" + android:text="@string/open_in_map" android:textColor="@color/primary" android:background="@android:color/transparent" android:textSize="14sp" @@ -114,9 +115,9 @@ android:id="@+id/formLocationEditText" android:layout_width="match_parent" android:layout_height="match_parent" - android:hint="Location" - android:inputType="text" - android:lines="1" + android:hint="@string/location" + android:inputType="none" + android:lines="2" android:text="@={viewModel.location}" /> </com.google.android.material.textfield.TextInputLayout> @@ -127,7 +128,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginHorizontal="20dp" - android:layout_marginTop="5dp" + android:layout_marginTop="25dp" android:text="@string/save" android:textSize="20sp" app:layout_constraintTop_toBottomOf="@+id/formLocationContainer" diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cb0e255805159c00e1f22d060c974c2ef308f783..a0b2dda42295ac3fb60c37e2c07142703bc0faf6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -11,6 +11,9 @@ <string name="preference_file_key">com.example.ABE.PREFERENCE_FILE_KEY</string> <string name="failed_connect_msg">Failed to connect to the server. Please check your internet connection.</string> <string name="try_again">Try Again</string> + <string name="location">Location</string> + <string name="open_in_map">Open in Map</string> + <string name="category">Category</string> <string-array name="Categories"> <item>Income</item>