diff --git a/app/src/androidTest/java/space/hackenslacker/kanbn4droid/app/BoardsFlowTest.kt b/app/src/androidTest/java/space/hackenslacker/kanbn4droid/app/BoardsFlowTest.kt index e762d88..d55b221 100644 --- a/app/src/androidTest/java/space/hackenslacker/kanbn4droid/app/BoardsFlowTest.kt +++ b/app/src/androidTest/java/space/hackenslacker/kanbn4droid/app/BoardsFlowTest.kt @@ -492,6 +492,24 @@ class BoardsFlowTest { .check(matches(isDisplayed())) } + @Test + fun settingsDialogSurvivesActivityRecreate() { + MainActivity.dependencies.apiClientFactory = { + FakeBoardsApiClient( + boards = mutableListOf(BoardSummary("1", "Alpha")), + templates = emptyList(), + ) + } + + val scenario = ActivityScenario.launch(BoardsActivity::class.java) + openSettingsFromDrawer() + + scenario.recreate() + + onView(withId(R.id.settingsFragmentContainer)).check(matches(isDisplayed())) + onView(withId(R.id.settingsSaveAndCloseButton)).check(matches(isDisplayed())) + } + private fun openSettingsFromDrawer() { onView(withId(R.id.boardsDrawerLayout)).perform(DrawerActions.open()) onView(withId(R.id.drawerSettingsButton)).perform(click()) diff --git a/app/src/main/java/space/hackenslacker/kanbn4droid/app/BoardsActivity.kt b/app/src/main/java/space/hackenslacker/kanbn4droid/app/BoardsActivity.kt index 7754a5c..1070186 100644 --- a/app/src/main/java/space/hackenslacker/kanbn4droid/app/BoardsActivity.kt +++ b/app/src/main/java/space/hackenslacker/kanbn4droid/app/BoardsActivity.kt @@ -49,6 +49,15 @@ class BoardsActivity : AppCompatActivity() { private lateinit var apiKeyStore: ApiKeyStore private lateinit var apiClient: KanbnApiClient + internal val sessionStoreForSettingsDialog: SessionStore + get() = sessionStore + + internal val apiKeyStoreForSettingsDialog: ApiKeyStore + get() = apiKeyStore + + internal val apiClientForSettingsDialog: KanbnApiClient + get() = apiClient + private lateinit var swipeRefresh: SwipeRefreshLayout private lateinit var recyclerView: RecyclerView private lateinit var emptyStateText: TextView @@ -413,7 +422,6 @@ class BoardsActivity : AppCompatActivity() { val themeChanged = bundle.getBoolean(SettingsDialogFragment.RESULT_KEY_THEME_CHANGED) viewModel.onSettingsApplied( credentialsChanged = credentialsChanged, - themeChanged = themeChanged, ) } } @@ -423,11 +431,7 @@ class BoardsActivity : AppCompatActivity() { return } SettingsDialogFragment - .newInstance( - sessionStore = sessionStore, - apiClient = apiClient, - apiKeyStore = apiKeyStore, - ) + .newInstance() .show(supportFragmentManager, SettingsDialogFragment.TAG) } diff --git a/app/src/main/java/space/hackenslacker/kanbn4droid/app/boards/BoardsViewModel.kt b/app/src/main/java/space/hackenslacker/kanbn4droid/app/boards/BoardsViewModel.kt index 7b2100f..24810a2 100644 --- a/app/src/main/java/space/hackenslacker/kanbn4droid/app/boards/BoardsViewModel.kt +++ b/app/src/main/java/space/hackenslacker/kanbn4droid/app/boards/BoardsViewModel.kt @@ -129,7 +129,7 @@ class BoardsViewModel( fetchBoards(initial = false, refresh = true) } - fun onSettingsApplied(credentialsChanged: Boolean, themeChanged: Boolean) { + fun onSettingsApplied(credentialsChanged: Boolean) { if (!credentialsChanged) { return } diff --git a/app/src/main/java/space/hackenslacker/kanbn4droid/app/settings/SettingsDialogFragment.kt b/app/src/main/java/space/hackenslacker/kanbn4droid/app/settings/SettingsDialogFragment.kt index 0aa76f5..d4026e5 100644 --- a/app/src/main/java/space/hackenslacker/kanbn4droid/app/settings/SettingsDialogFragment.kt +++ b/app/src/main/java/space/hackenslacker/kanbn4droid/app/settings/SettingsDialogFragment.kt @@ -1,11 +1,13 @@ package space.hackenslacker.kanbn4droid.app.settings import android.app.Dialog +import android.content.Context import android.os.Bundle import android.view.LayoutInflater import android.widget.Button import android.widget.ProgressBar import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate import androidx.core.os.bundleOf import androidx.fragment.app.DialogFragment @@ -13,29 +15,36 @@ import androidx.fragment.app.commitNow import androidx.lifecycle.lifecycleScope import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.coroutines.launch +import space.hackenslacker.kanbn4droid.app.BoardsActivity import space.hackenslacker.kanbn4droid.app.MainActivity import space.hackenslacker.kanbn4droid.app.R import space.hackenslacker.kanbn4droid.app.auth.ApiKeyStore +import space.hackenslacker.kanbn4droid.app.auth.HttpKanbnApiClient import space.hackenslacker.kanbn4droid.app.auth.KanbnApiClient +import space.hackenslacker.kanbn4droid.app.auth.PreferencesApiKeyStore import space.hackenslacker.kanbn4droid.app.auth.SettingsApplyCoordinator import space.hackenslacker.kanbn4droid.app.auth.SettingsApplyResult +import space.hackenslacker.kanbn4droid.app.auth.SessionPreferences import space.hackenslacker.kanbn4droid.app.auth.SessionStore -class SettingsDialogFragment( - internal var sessionStore: SessionStore? = null, - internal var apiClient: KanbnApiClient? = null, - internal var apiKeyStore: ApiKeyStore? = null, - internal var coordinator: SettingsApplyCoordinator? = null, -) : DialogFragment() { +class SettingsDialogFragment : DialogFragment() { + lateinit var sessionStore: SessionStore + private set + + private lateinit var apiClient: KanbnApiClient + private lateinit var apiKeyStore: ApiKeyStore + private var progress: ProgressBar? = null private var saveButton: Button? = null private var errorText: TextView? = null private var controlsEnabled: Boolean = true + override fun onAttach(context: Context) { + super.onAttach(context) + resolveDependencies(context) + } + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val resolvedSessionStore = requireNotNull(sessionStore) { "SessionStore is required" } - val resolvedApiClient = requireNotNull(apiClient) { "KanbnApiClient is required" } - val resolvedApiKeyStore = requireNotNull(apiKeyStore) { "ApiKeyStore is required" } val dialogView = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_settings, null) childFragmentManager.commitNow { @@ -61,25 +70,21 @@ class SettingsDialogFragment( dialog.setOnShowListener { saveButton?.setOnClickListener { - onSaveClicked(resolvedSessionStore, resolvedApiClient, resolvedApiKeyStore) + onSaveClicked() } setApplyInProgress(false) } return dialog } - private fun onSaveClicked( - sessionStore: SessionStore, - apiClient: KanbnApiClient, - apiKeyStore: ApiKeyStore, - ) { + private fun onSaveClicked() { if (!controlsEnabled) { return } - val resolvedCoordinator = coordinator - ?: MainActivity.dependencies.settingsApplyCoordinatorFactory?.invoke( - requireActivity() as androidx.appcompat.app.AppCompatActivity, + val hostActivity = requireActivity() as AppCompatActivity + val resolvedCoordinator = MainActivity.dependencies.settingsApplyCoordinatorFactory?.invoke( + hostActivity, sessionStore, apiClient, apiKeyStore, @@ -177,6 +182,31 @@ class SettingsDialogFragment( AppCompatDelegate.setDefaultNightMode(mode) } + private fun resolveDependencies(context: Context) { + val host = activity as? BoardsActivity + if (host != null) { + sessionStore = host.sessionStoreForSettingsDialog + apiClient = host.apiClientForSettingsDialog + apiKeyStore = host.apiKeyStoreForSettingsDialog + return + } + + val appCompatActivity = activity as? AppCompatActivity + if (appCompatActivity != null) { + sessionStore = MainActivity.dependencies.sessionStoreFactory?.invoke(appCompatActivity) + ?: SessionPreferences(appCompatActivity.applicationContext) + apiClient = MainActivity.dependencies.apiClientFactory?.invoke() + ?: HttpKanbnApiClient() + apiKeyStore = MainActivity.dependencies.apiKeyStoreFactory?.invoke(appCompatActivity) + ?: PreferencesApiKeyStore(appCompatActivity) + return + } + + sessionStore = SessionPreferences(context.applicationContext) + apiClient = HttpKanbnApiClient() + apiKeyStore = PreferencesApiKeyStore(context) + } + companion object { const val TAG: String = "settings_dialog" const val REQUEST_KEY_SETTINGS_APPLIED: String = "settings_applied_result" @@ -185,18 +215,8 @@ class SettingsDialogFragment( private const val SETTINGS_PREFS_TAG: String = "settings_preferences_fragment" - fun newInstance( - sessionStore: SessionStore, - apiClient: KanbnApiClient, - apiKeyStore: ApiKeyStore, - coordinator: SettingsApplyCoordinator? = null, - ): SettingsDialogFragment { - return SettingsDialogFragment( - sessionStore = sessionStore, - apiClient = apiClient, - apiKeyStore = apiKeyStore, - coordinator = coordinator, - ) + fun newInstance(): SettingsDialogFragment { + return SettingsDialogFragment() } } } diff --git a/app/src/test/java/space/hackenslacker/kanbn4droid/app/boards/BoardsViewModelTest.kt b/app/src/test/java/space/hackenslacker/kanbn4droid/app/boards/BoardsViewModelTest.kt index 9513d14..1fa488d 100644 --- a/app/src/test/java/space/hackenslacker/kanbn4droid/app/boards/BoardsViewModelTest.kt +++ b/app/src/test/java/space/hackenslacker/kanbn4droid/app/boards/BoardsViewModelTest.kt @@ -432,7 +432,7 @@ class BoardsViewModelTest { } val viewModel = newViewModel(api) - viewModel.onSettingsApplied(credentialsChanged = true, themeChanged = false) + viewModel.onSettingsApplied(credentialsChanged = true) advanceUntilIdle() assertTrue(api.listBoardsCalls >= 1) @@ -449,7 +449,7 @@ class BoardsViewModelTest { } val viewModel = newViewModel(api) - viewModel.onSettingsApplied(credentialsChanged = false, themeChanged = true) + viewModel.onSettingsApplied(credentialsChanged = false) advanceUntilIdle() assertEquals(0, api.listBoardsCalls) @@ -468,7 +468,7 @@ class BoardsViewModelTest { val viewModel = newViewModel(api, sessionStore = sessionStore) val eventDeferred = async { viewModel.events.first() } - viewModel.onSettingsApplied(credentialsChanged = true, themeChanged = false) + viewModel.onSettingsApplied(credentialsChanged = true) advanceUntilIdle() val event = eventDeferred.await()