From 2a9ad92e55db10fa63fcf0ef2838850d58974cb3 Mon Sep 17 00:00:00 2001 From: Nik Clayton Date: Sun, 11 Jun 2023 15:34:58 +0200 Subject: [PATCH] Make AccountPreferenceDataStore injectable (#3653) This will make tests that need it easier. - Rename from AccountPreferenceHandler - Inject its dependencies - Create an injectable CoroutineScope it can use for launching coroutines - Use it in AccountPreferences --- .../preference/AccountPreferencesFragment.kt | 21 +++------ .../keylesspalace/tusky/di/AppComponent.kt | 1 + .../tusky/di/CoroutineScopeModule.kt | 44 +++++++++++++++++++ ...ndler.kt => AccountPreferenceDataStore.kt} | 16 +++++-- 4 files changed, 64 insertions(+), 18 deletions(-) create mode 100644 app/src/main/java/com/keylesspalace/tusky/di/CoroutineScopeModule.kt rename app/src/main/java/com/keylesspalace/tusky/settings/{AccountPreferenceHandler.kt => AccountPreferenceDataStore.kt} (67%) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/preference/AccountPreferencesFragment.kt b/app/src/main/java/com/keylesspalace/tusky/components/preference/AccountPreferencesFragment.kt index 49107339c..b0fedb2b1 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/preference/AccountPreferencesFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/preference/AccountPreferencesFragment.kt @@ -21,7 +21,6 @@ import android.os.Build import android.os.Bundle import android.util.Log import androidx.annotation.DrawableRes -import androidx.lifecycle.lifecycleScope import androidx.preference.PreferenceFragmentCompat import com.google.android.material.color.MaterialColors import com.google.android.material.snackbar.Snackbar @@ -30,7 +29,6 @@ import com.keylesspalace.tusky.BuildConfig import com.keylesspalace.tusky.R import com.keylesspalace.tusky.TabPreferenceActivity import com.keylesspalace.tusky.appstore.EventHub -import com.keylesspalace.tusky.appstore.PreferenceChangedEvent import com.keylesspalace.tusky.components.accountlist.AccountListActivity import com.keylesspalace.tusky.components.filters.FiltersActivity import com.keylesspalace.tusky.components.followedtags.FollowedTagsActivity @@ -42,7 +40,7 @@ import com.keylesspalace.tusky.di.Injectable import com.keylesspalace.tusky.entity.Account import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.network.MastodonApi -import com.keylesspalace.tusky.settings.AccountPreferenceHandler +import com.keylesspalace.tusky.settings.AccountPreferenceDataStore import com.keylesspalace.tusky.settings.PrefKeys import com.keylesspalace.tusky.settings.listPreference import com.keylesspalace.tusky.settings.makePreferenceScreen @@ -58,7 +56,6 @@ import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import com.mikepenz.iconics.utils.colorInt import com.mikepenz.iconics.utils.sizeRes -import kotlinx.coroutines.launch import retrofit2.Call import retrofit2.Callback import retrofit2.Response @@ -74,6 +71,9 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable { @Inject lateinit var eventHub: EventHub + @Inject + lateinit var accountPreferenceDataStore: AccountPreferenceDataStore + private val iconSize by unsafeLazy { resources.getDimensionPixelSize(R.dimen.preference_icon_size) } override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { @@ -245,27 +245,26 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable { preferenceCategory(R.string.pref_title_timelines) { // TODO having no activeAccount in this fragment does not really make sense, enforce it? // All other locations here make it optional, however. - val accountPreferenceHandler = AccountPreferenceHandler(accountManager.activeAccount!!, accountManager, ::dispatchEvent) switchPreference { key = PrefKeys.MEDIA_PREVIEW_ENABLED setTitle(R.string.pref_title_show_media_preview) isSingleLineTitle = false - preferenceDataStore = accountPreferenceHandler + preferenceDataStore = accountPreferenceDataStore } switchPreference { key = PrefKeys.ALWAYS_SHOW_SENSITIVE_MEDIA setTitle(R.string.pref_title_alway_show_sensitive_media) isSingleLineTitle = false - preferenceDataStore = accountPreferenceHandler + preferenceDataStore = accountPreferenceDataStore } switchPreference { key = PrefKeys.ALWAYS_OPEN_SPOILER setTitle(R.string.pref_title_alway_open_spoiler) isSingleLineTitle = false - preferenceDataStore = accountPreferenceHandler + preferenceDataStore = accountPreferenceDataStore } } } @@ -353,12 +352,6 @@ class AccountPreferencesFragment : PreferenceFragmentCompat(), Injectable { activity?.overridePendingTransition(R.anim.slide_from_right, R.anim.slide_to_left) } - private fun dispatchEvent(event: PreferenceChangedEvent) { - lifecycleScope.launch { - eventHub.dispatch(event) - } - } - companion object { fun newInstance() = AccountPreferencesFragment() } diff --git a/app/src/main/java/com/keylesspalace/tusky/di/AppComponent.kt b/app/src/main/java/com/keylesspalace/tusky/di/AppComponent.kt index 73aceeab2..d922ab370 100644 --- a/app/src/main/java/com/keylesspalace/tusky/di/AppComponent.kt +++ b/app/src/main/java/com/keylesspalace/tusky/di/AppComponent.kt @@ -29,6 +29,7 @@ import javax.inject.Singleton @Component( modules = [ AppModule::class, + CoroutineScopeModule::class, NetworkModule::class, AndroidSupportInjectionModule::class, ActivitiesModule::class, diff --git a/app/src/main/java/com/keylesspalace/tusky/di/CoroutineScopeModule.kt b/app/src/main/java/com/keylesspalace/tusky/di/CoroutineScopeModule.kt new file mode 100644 index 000000000..bee62f7ec --- /dev/null +++ b/app/src/main/java/com/keylesspalace/tusky/di/CoroutineScopeModule.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Tusky Contributors + * + * This file is a part of Tusky. + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Tusky; if not, + * see . + */ + +package com.keylesspalace.tusky.di + +import dagger.Module +import dagger.Provides +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import javax.inject.Qualifier + +/** + * Scope for potentially long-running tasks that should outlive the viewmodel that + * started them. For example, if the API call to bookmark a status is taking a long + * time, that call should not be cancelled because the user has navigated away from + * the viewmodel that made the call. + * + * @see https://developer.android.com/topic/architecture/data-layer#make_an_operation_live_longer_than_the_screen + */ +@Retention(AnnotationRetention.BINARY) +@Qualifier +annotation class ApplicationScope + +@Module +class CoroutineScopeModule { + @ApplicationScope + @Provides + fun providesApplicationScope() = CoroutineScope(SupervisorJob() + Dispatchers.Default) +} diff --git a/app/src/main/java/com/keylesspalace/tusky/settings/AccountPreferenceHandler.kt b/app/src/main/java/com/keylesspalace/tusky/settings/AccountPreferenceDataStore.kt similarity index 67% rename from app/src/main/java/com/keylesspalace/tusky/settings/AccountPreferenceHandler.kt rename to app/src/main/java/com/keylesspalace/tusky/settings/AccountPreferenceDataStore.kt index cfdc27b44..a95134414 100644 --- a/app/src/main/java/com/keylesspalace/tusky/settings/AccountPreferenceHandler.kt +++ b/app/src/main/java/com/keylesspalace/tusky/settings/AccountPreferenceDataStore.kt @@ -1,15 +1,21 @@ package com.keylesspalace.tusky.settings import androidx.preference.PreferenceDataStore +import com.keylesspalace.tusky.appstore.EventHub import com.keylesspalace.tusky.appstore.PreferenceChangedEvent import com.keylesspalace.tusky.db.AccountEntity import com.keylesspalace.tusky.db.AccountManager +import com.keylesspalace.tusky.di.ApplicationScope +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import javax.inject.Inject -class AccountPreferenceHandler( - private val account: AccountEntity, +class AccountPreferenceDataStore @Inject constructor( private val accountManager: AccountManager, - private val dispatchEvent: (PreferenceChangedEvent) -> Unit + private val eventHub: EventHub, + @ApplicationScope private val externalScope: CoroutineScope ) : PreferenceDataStore() { + private val account: AccountEntity = accountManager.activeAccount!! override fun getBoolean(key: String, defValue: Boolean): Boolean { return when (key) { @@ -29,6 +35,8 @@ class AccountPreferenceHandler( accountManager.saveAccount(account) - dispatchEvent(PreferenceChangedEvent(key)) + externalScope.launch { + eventHub.dispatch(PreferenceChangedEvent(key)) + } } }