diff --git a/app/src/main/java/com/example/util/simpletimetracker/navigation/ActionResolverImpl.kt b/app/src/main/java/com/example/util/simpletimetracker/navigation/ActionResolverImpl.kt index 9269faec..fcc4c769 100644 --- a/app/src/main/java/com/example/util/simpletimetracker/navigation/ActionResolverImpl.kt +++ b/app/src/main/java/com/example/util/simpletimetracker/navigation/ActionResolverImpl.kt @@ -4,6 +4,9 @@ import android.app.Activity import android.content.ActivityNotFoundException import android.content.Intent import android.net.Uri +import androidx.activity.ComponentActivity +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult import com.example.util.simpletimetracker.navigation.params.FileChooserParams import com.example.util.simpletimetracker.navigation.params.OpenMarketParams import com.example.util.simpletimetracker.navigation.params.SendEmailParams @@ -12,7 +15,19 @@ import java.util.Date import java.util.Locale import javax.inject.Inject -class ActionResolverImpl @Inject constructor() : ActionResolver { +class ActionResolverImpl @Inject constructor( + private val resultContainer: ResultContainer, +) : ActionResolver { + + private var createFileResultLauncher: ActivityResultLauncher? = null + private var openFileResultLauncher: ActivityResultLauncher? = null + private var createCsvFileResultLauncher: ActivityResultLauncher? = null + + override fun registerResultListeners(activity: ComponentActivity) { + createFileResultLauncher = activity.registerForActivityResult(RequestCode.REQUEST_CODE_CREATE_FILE) + openFileResultLauncher = activity.registerForActivityResult(RequestCode.REQUEST_CODE_OPEN_FILE) + createCsvFileResultLauncher = activity.registerForActivityResult(RequestCode.REQUEST_CODE_CREATE_CSV_FILE) + } override fun execute(activity: Activity?, action: Action, data: Any?) { when (action) { @@ -33,10 +48,7 @@ class ActionResolverImpl @Inject constructor() : ActionResolver { intent.putExtra(Intent.EXTRA_TITLE, fileName) if (activity?.packageManager?.let(intent::resolveActivity) != null) { - activity.startActivityForResult( - intent, - RequestCode.REQUEST_CODE_CREATE_FILE - ) + createFileResultLauncher?.launch(intent) } else { (data as? FileChooserParams)?.notHandledCallback?.invoke() } @@ -47,10 +59,7 @@ class ActionResolverImpl @Inject constructor() : ActionResolver { intent.type = "application/*" if (activity?.packageManager?.let(intent::resolveActivity) != null) { - activity.startActivityForResult( - intent, - RequestCode.REQUEST_CODE_OPEN_FILE - ) + openFileResultLauncher?.launch(intent) } else { (data as? FileChooserParams)?.notHandledCallback?.invoke() } @@ -66,10 +75,7 @@ class ActionResolverImpl @Inject constructor() : ActionResolver { intent.putExtra(Intent.EXTRA_TITLE, fileName) if (activity?.packageManager?.let(intent::resolveActivity) != null) { - activity.startActivityForResult( - intent, - RequestCode.REQUEST_CODE_CREATE_CSV_FILE - ) + createCsvFileResultLauncher?.launch(intent) } else { (data as? FileChooserParams)?.notHandledCallback?.invoke() } @@ -113,6 +119,15 @@ class ActionResolverImpl @Inject constructor() : ActionResolver { } } + private fun ComponentActivity.registerForActivityResult(key: String): ActivityResultLauncher { + return registerForActivityResult(StartActivityForResult()) { result -> + val intent = result.data + val uri = intent?.data?.toString().takeIf { result.resultCode == Activity.RESULT_OK } + + resultContainer.sendResult(key, uri) + } + } + companion object { private const val MARKET_INTENT = "market://details?id=" private const val MARKET_LINK = "http://play.google.com/store/apps/details?id=" diff --git a/app/src/main/java/com/example/util/simpletimetracker/navigation/RouterImpl.kt b/app/src/main/java/com/example/util/simpletimetracker/navigation/RouterImpl.kt index b2437f5e..c10b788c 100644 --- a/app/src/main/java/com/example/util/simpletimetracker/navigation/RouterImpl.kt +++ b/app/src/main/java/com/example/util/simpletimetracker/navigation/RouterImpl.kt @@ -3,6 +3,7 @@ package com.example.util.simpletimetracker.navigation import android.app.Activity import android.content.Context import android.content.Intent +import androidx.activity.ComponentActivity import androidx.navigation.NavController import androidx.navigation.findNavController import com.example.util.simpletimetracker.ui.MainActivity @@ -15,12 +16,17 @@ class RouterImpl @Inject constructor( private val screenResolver: ScreenResolver, private val actionResolver: ActionResolver, private val notificationResolver: NotificationResolver, - @ApplicationContext private val context: Context + private val resultContainer: ResultContainer, + @ApplicationContext private val context: Context, ) : Router { private var navController: NavController? = null private var activity: Activity? = null + override fun onCreate(activity: ComponentActivity) { + actionResolver.registerResultListeners(activity) + } + override fun bind(activity: Activity) { this.navController = activity.findNavController(R.id.container) this.activity = activity @@ -38,6 +44,14 @@ class RouterImpl @Inject constructor( notificationResolver.show(activity, notification, data, anchor) } + override fun setResultListener(key: String, listener: ResultListener) { + resultContainer.setResultListener(key, listener) + } + + override fun sendResult(key: String, data: Any?) { + resultContainer.sendResult(key, data) + } + override fun back() { navController?.navigateUp() } diff --git a/app/src/main/java/com/example/util/simpletimetracker/ui/MainActivity.kt b/app/src/main/java/com/example/util/simpletimetracker/ui/MainActivity.kt index 667de59a..141565ce 100644 --- a/app/src/main/java/com/example/util/simpletimetracker/ui/MainActivity.kt +++ b/app/src/main/java/com/example/util/simpletimetracker/ui/MainActivity.kt @@ -1,14 +1,11 @@ package com.example.util.simpletimetracker.ui -import android.content.Intent import android.os.Bundle import com.example.util.simpletimetracker.R import com.example.util.simpletimetracker.core.base.BaseActivity -import com.example.util.simpletimetracker.core.extension.getAllFragments import com.example.util.simpletimetracker.core.manager.ThemeManager import com.example.util.simpletimetracker.navigation.Router import dagger.hilt.android.AndroidEntryPoint -import timber.log.Timber import javax.inject.Inject @AndroidEntryPoint @@ -26,18 +23,11 @@ class MainActivity : BaseActivity() { themeManager.setTheme(this) setContentView(R.layout.main_activity) router.bind(this) + router.onCreate(this) } override fun onResume() { super.onResume() router.bind(this) } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - Timber.d("onActivityResult $requestCode $resultCode ${data?.data}") - super.onActivityResult(requestCode, resultCode, data) - getAllFragments().forEach { - it.onActivityResult(requestCode, resultCode, data) - } - } } diff --git a/core/src/main/java/com/example/util/simpletimetracker/core/sharedViewModel/BackupViewModel.kt b/core/src/main/java/com/example/util/simpletimetracker/core/sharedViewModel/BackupViewModel.kt index c8a1eb02..ef57caab 100644 --- a/core/src/main/java/com/example/util/simpletimetracker/core/sharedViewModel/BackupViewModel.kt +++ b/core/src/main/java/com/example/util/simpletimetracker/core/sharedViewModel/BackupViewModel.kt @@ -11,9 +11,14 @@ import com.example.util.simpletimetracker.domain.interactor.CsvExportInteractor import com.example.util.simpletimetracker.domain.model.Range import com.example.util.simpletimetracker.domain.resolver.BackupRepo import com.example.util.simpletimetracker.domain.resolver.CsvRepo +import com.example.util.simpletimetracker.navigation.Action import com.example.util.simpletimetracker.navigation.Notification +import com.example.util.simpletimetracker.navigation.RequestCode import com.example.util.simpletimetracker.navigation.Router +import com.example.util.simpletimetracker.navigation.Screen import com.example.util.simpletimetracker.navigation.params.CsvExportSettingsParams +import com.example.util.simpletimetracker.navigation.params.FileChooserParams +import com.example.util.simpletimetracker.navigation.params.StandardDialogParams import com.example.util.simpletimetracker.navigation.params.ToastParams import kotlinx.coroutines.launch import javax.inject.Inject @@ -28,7 +33,54 @@ class BackupViewModel @Inject constructor( val progressVisibility: LiveData = MutableLiveData(false) private var csvExportSettingsParams: CsvExportSettingsParams? = null - fun onSaveBackup(uriString: String) = viewModelScope.launch { + fun onSaveClick() { + router.setResultListener(RequestCode.REQUEST_CODE_CREATE_FILE) { result -> + if (result is String) onSaveBackup(result) + } + router.execute( + Action.CREATE_FILE, + FileChooserParams(::onFileCreateError) + ) + } + + fun onRestoreClick() { + router.navigate( + Screen.STANDARD_DIALOG, + StandardDialogParams( + tag = ALERT_DIALOG_TAG, + message = resourceRepo.getString(R.string.settings_dialog_message), + btnPositive = resourceRepo.getString(R.string.ok), + btnNegative = resourceRepo.getString(R.string.cancel) + ) + ) + } + + fun onPositiveDialogClick(tag: String?) { + when (tag) { + ALERT_DIALOG_TAG -> { + router.setResultListener(RequestCode.REQUEST_CODE_OPEN_FILE) { result -> + if (result is String) onRestoreBackup(result) + } + router.execute( + Action.OPEN_FILE, + FileChooserParams(::onFileOpenError) + ) + } + } + } + + fun onCsvExportSettingsSelected(data: CsvExportSettingsParams) { + router.setResultListener(RequestCode.REQUEST_CODE_CREATE_CSV_FILE) { result -> + if (result is String) onSaveCsvFile(result) + } + router.execute( + Action.CREATE_CSV_FILE, + FileChooserParams(::onFileCreateError) + ) + csvExportSettingsParams = data + } + + private fun onSaveBackup(uriString: String) = viewModelScope.launch { showProgress(true) val resultCode = backupInteractor.saveBackupFile(uriString) @@ -42,7 +94,7 @@ class BackupViewModel @Inject constructor( showProgress(false) } - fun onRestoreBackup(uriString: String) = viewModelScope.launch { + private fun onRestoreBackup(uriString: String) = viewModelScope.launch { showProgress(true) val resultCode = backupInteractor.restoreBackupFile(uriString) @@ -56,11 +108,7 @@ class BackupViewModel @Inject constructor( showProgress(false) } - fun onCsvExportSettingsSelected(data: CsvExportSettingsParams) { - csvExportSettingsParams = data - } - - fun onSaveCsvFile(uriString: String) = viewModelScope.launch { + private fun onSaveCsvFile(uriString: String) = viewModelScope.launch { showProgress(true) val range = csvExportSettingsParams?.range?.let { @@ -83,6 +131,14 @@ class BackupViewModel @Inject constructor( showProgress(false) } + private fun onFileOpenError() { + showMessage(R.string.settings_file_open_error) + } + + private fun onFileCreateError() { + showMessage(R.string.settings_file_create_error) + } + private fun showMessage(stringResId: Int) { stringResId .let(resourceRepo::getString) @@ -92,4 +148,8 @@ class BackupViewModel @Inject constructor( private fun showProgress(isVisible: Boolean) { (progressVisibility as MutableLiveData).value = isVisible } + + companion object { + private const val ALERT_DIALOG_TAG = "alert_dialog_tag" + } } \ No newline at end of file diff --git a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/view/SettingsFragment.kt b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/view/SettingsFragment.kt index 48dbb29b..77bca1ff 100644 --- a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/view/SettingsFragment.kt +++ b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/view/SettingsFragment.kt @@ -1,8 +1,5 @@ package com.example.util.simpletimetracker.feature_settings.view -import android.app.Activity -import android.content.Intent -import android.net.Uri import android.view.LayoutInflater import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity @@ -13,15 +10,12 @@ import com.example.util.simpletimetracker.core.di.BaseViewModelFactory import com.example.util.simpletimetracker.core.dialog.CsvExportSettingsDialogListener import com.example.util.simpletimetracker.core.dialog.DurationDialogListener import com.example.util.simpletimetracker.core.dialog.StandardDialogListener -import com.example.util.simpletimetracker.feature_views.extension.setOnClick -import com.example.util.simpletimetracker.feature_views.extension.visible import com.example.util.simpletimetracker.core.sharedViewModel.BackupViewModel import com.example.util.simpletimetracker.feature_settings.viewData.CardOrderViewData import com.example.util.simpletimetracker.feature_settings.viewData.FirstDayOfWeekViewData import com.example.util.simpletimetracker.feature_settings.viewModel.SettingsViewModel -import com.example.util.simpletimetracker.navigation.RequestCode.REQUEST_CODE_CREATE_CSV_FILE -import com.example.util.simpletimetracker.navigation.RequestCode.REQUEST_CODE_CREATE_FILE -import com.example.util.simpletimetracker.navigation.RequestCode.REQUEST_CODE_OPEN_FILE +import com.example.util.simpletimetracker.feature_views.extension.setOnClick +import com.example.util.simpletimetracker.feature_views.extension.visible import com.example.util.simpletimetracker.navigation.params.CsvExportSettingsParams import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @@ -71,8 +65,8 @@ class SettingsFragment : layoutSettingsEditCategories.setOnClick(viewModel::onEditCategoriesClick) checkboxSettingsShowRecordTagSelection.setOnClick(viewModel::onShowRecordTagSelectionClicked) tvSettingsArchive.setOnClick(viewModel::onArchiveClick) - layoutSettingsSaveBackup.setOnClick(viewModel::onSaveClick) - layoutSettingsRestoreBackup.setOnClick(viewModel::onRestoreClick) + layoutSettingsSaveBackup.setOnClick(backupViewModel::onSaveClick) + layoutSettingsRestoreBackup.setOnClick(backupViewModel::onRestoreClick) layoutSettingsExportCsv.setOnClick(viewModel::onExportCsvClick) layoutSettingsRate.setOnClick(viewModel::onRateClick) layoutSettingsFeedback.setOnClick(viewModel::onFeedbackClick) @@ -112,32 +106,8 @@ class SettingsFragment : viewModel.onVisible() } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - - if (data != null && resultCode == Activity.RESULT_OK) { - when (requestCode) { - REQUEST_CODE_CREATE_FILE -> { - data.data - ?.let(Uri::toString) - ?.let(backupViewModel::onSaveBackup) - } - REQUEST_CODE_OPEN_FILE -> { - data.data - ?.let(Uri::toString) - ?.let(backupViewModel::onRestoreBackup) - } - REQUEST_CODE_CREATE_CSV_FILE -> { - data.data - ?.let(Uri::toString) - ?.let(backupViewModel::onSaveCsvFile) - } - } - } - } - override fun onPositiveClick(tag: String?, data: Any?) { - viewModel.onPositiveDialogClick(tag) + backupViewModel.onPositiveDialogClick(tag) } override fun onDurationSet(duration: Long, tag: String?) { @@ -150,7 +120,6 @@ class SettingsFragment : override fun onCsvExportSettingsSelected(data: CsvExportSettingsParams) { backupViewModel.onCsvExportSettingsSelected(data) - viewModel.onCsvExportSettingsSelected() } private fun updateCardOrderViewData(viewData: CardOrderViewData) = with(binding) { diff --git a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/SettingsViewModel.kt b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/SettingsViewModel.kt index fb926690..5474b1d2 100644 --- a/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/SettingsViewModel.kt +++ b/features/feature_settings/src/main/java/com/example/util/simpletimetracker/feature_settings/viewModel/SettingsViewModel.kt @@ -20,10 +20,8 @@ import com.example.util.simpletimetracker.navigation.Router import com.example.util.simpletimetracker.navigation.Screen import com.example.util.simpletimetracker.navigation.params.CardOrderDialogParams import com.example.util.simpletimetracker.navigation.params.DurationDialogParams -import com.example.util.simpletimetracker.navigation.params.FileChooserParams import com.example.util.simpletimetracker.navigation.params.OpenMarketParams import com.example.util.simpletimetracker.navigation.params.SendEmailParams -import com.example.util.simpletimetracker.navigation.params.StandardDialogParams import com.example.util.simpletimetracker.navigation.params.ToastParams import kotlinx.coroutines.launch import javax.inject.Inject @@ -164,25 +162,6 @@ class SettingsViewModel @Inject constructor( } } - fun onSaveClick() { - router.execute( - Action.CREATE_FILE, - FileChooserParams(::onFileCreateError) - ) - } - - fun onRestoreClick() { - router.navigate( - Screen.STANDARD_DIALOG, - StandardDialogParams( - tag = ALERT_DIALOG_TAG, - message = resourceRepo.getString(R.string.settings_dialog_message), - btnPositive = resourceRepo.getString(R.string.ok), - btnNegative = resourceRepo.getString(R.string.cancel) - ) - ) - } - fun onExportCsvClick() { router.navigate(Screen.CSV_EXPORT_SETTINGS_DIALOG) } @@ -321,15 +300,6 @@ class SettingsViewModel @Inject constructor( router.navigate(Screen.ARCHIVE) } - fun onPositiveDialogClick(tag: String?) { - when (tag) { - ALERT_DIALOG_TAG -> router.execute( - Action.OPEN_FILE, - FileChooserParams(::onFileOpenError) - ) - } - } - fun onDurationSet(tag: String?, duration: Long) { when (tag) { INACTIVITY_DURATION_DIALOG_TAG -> viewModelScope.launch { @@ -350,13 +320,6 @@ class SettingsViewModel @Inject constructor( } } - fun onCsvExportSettingsSelected() { - router.execute( - Action.CREATE_CSV_FILE, - FileChooserParams(::onFileCreateError) - ) - } - fun onThemeChanged() { (themeChanged as MutableLiveData).value = false } @@ -368,14 +331,6 @@ class SettingsViewModel @Inject constructor( ) } - private fun onFileOpenError() { - showMessage(R.string.settings_file_open_error) - } - - private fun onFileCreateError() { - showMessage(R.string.settings_file_create_error) - } - private fun showMessage(stringResId: Int) { stringResId .let(resourceRepo::getString) @@ -433,7 +388,6 @@ class SettingsViewModel @Inject constructor( } companion object { - private const val ALERT_DIALOG_TAG = "alert_dialog_tag" private const val INACTIVITY_DURATION_DIALOG_TAG = "inactivity_duration_dialog_tag" } } diff --git a/navigation/build.gradle.kts b/navigation/build.gradle.kts index 94d86f75..c6f2b022 100644 --- a/navigation/build.gradle.kts +++ b/navigation/build.gradle.kts @@ -22,6 +22,7 @@ android { dependencies { implementation(project(":domain")) + implementation(Deps.Ktx.fragment) implementation(Deps.Ktx.navigationFragment) implementation(Deps.Ktx.navigationUi) } diff --git a/navigation/src/main/java/com/example/util/simpletimetracker/navigation/ActionResolver.kt b/navigation/src/main/java/com/example/util/simpletimetracker/navigation/ActionResolver.kt index de3d8a22..339ccb30 100644 --- a/navigation/src/main/java/com/example/util/simpletimetracker/navigation/ActionResolver.kt +++ b/navigation/src/main/java/com/example/util/simpletimetracker/navigation/ActionResolver.kt @@ -1,8 +1,11 @@ package com.example.util.simpletimetracker.navigation import android.app.Activity +import androidx.activity.ComponentActivity interface ActionResolver { + fun registerResultListeners(activity: ComponentActivity) + fun execute(activity: Activity?, action: Action, data: Any?) } \ No newline at end of file diff --git a/navigation/src/main/java/com/example/util/simpletimetracker/navigation/RequestCode.kt b/navigation/src/main/java/com/example/util/simpletimetracker/navigation/RequestCode.kt index b62568f0..81da57aa 100644 --- a/navigation/src/main/java/com/example/util/simpletimetracker/navigation/RequestCode.kt +++ b/navigation/src/main/java/com/example/util/simpletimetracker/navigation/RequestCode.kt @@ -2,7 +2,7 @@ package com.example.util.simpletimetracker.navigation object RequestCode { - const val REQUEST_CODE_CREATE_FILE = 1 - const val REQUEST_CODE_OPEN_FILE = 2 - const val REQUEST_CODE_CREATE_CSV_FILE = 3 + const val REQUEST_CODE_CREATE_FILE = "REQUEST_CODE_CREATE_FILE" + const val REQUEST_CODE_OPEN_FILE = "REQUEST_CODE_OPEN_FILE" + const val REQUEST_CODE_CREATE_CSV_FILE = "REQUEST_CODE_CREATE_CSV_FILE" } \ No newline at end of file diff --git a/navigation/src/main/java/com/example/util/simpletimetracker/navigation/ResultContainer.kt b/navigation/src/main/java/com/example/util/simpletimetracker/navigation/ResultContainer.kt new file mode 100644 index 00000000..d825b9e6 --- /dev/null +++ b/navigation/src/main/java/com/example/util/simpletimetracker/navigation/ResultContainer.kt @@ -0,0 +1,17 @@ +package com.example.util.simpletimetracker.navigation + +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ResultContainer @Inject constructor() { + private val listeners: MutableMap = mutableMapOf() + + fun setResultListener(key: String, listener: ResultListener) { + listeners[key] = listener + } + + fun sendResult(key: String, data: Any?) { + listeners.remove(key)?.onResult(data) + } +} \ No newline at end of file diff --git a/navigation/src/main/java/com/example/util/simpletimetracker/navigation/ResultListener.kt b/navigation/src/main/java/com/example/util/simpletimetracker/navigation/ResultListener.kt new file mode 100644 index 00000000..c5e92ff7 --- /dev/null +++ b/navigation/src/main/java/com/example/util/simpletimetracker/navigation/ResultListener.kt @@ -0,0 +1,5 @@ +package com.example.util.simpletimetracker.navigation + +fun interface ResultListener { + fun onResult(data: Any?) +} diff --git a/navigation/src/main/java/com/example/util/simpletimetracker/navigation/Router.kt b/navigation/src/main/java/com/example/util/simpletimetracker/navigation/Router.kt index 2d3b82c2..91c342cf 100644 --- a/navigation/src/main/java/com/example/util/simpletimetracker/navigation/Router.kt +++ b/navigation/src/main/java/com/example/util/simpletimetracker/navigation/Router.kt @@ -2,9 +2,12 @@ package com.example.util.simpletimetracker.navigation import android.app.Activity import android.content.Intent +import androidx.activity.ComponentActivity interface Router { + fun onCreate(activity: ComponentActivity) + fun bind(activity: Activity) fun navigate( @@ -24,6 +27,10 @@ interface Router { anchor: Any? = null // should be a view ) + fun setResultListener(key: String, listener: ResultListener) + + fun sendResult(key: String, data: Any?) + fun back() fun getMainStartIntent(): Intent