diff --git a/app/k9mail/src/main/java/com/fsck/k9/App.kt b/app/k9mail/src/main/java/com/fsck/k9/App.kt index 68991190e..0598ad958 100644 --- a/app/k9mail/src/main/java/com/fsck/k9/App.kt +++ b/app/k9mail/src/main/java/com/fsck/k9/App.kt @@ -6,6 +6,7 @@ import android.content.res.Resources import com.fsck.k9.activity.MessageCompose import com.fsck.k9.controller.MessagingController import com.fsck.k9.external.MessageProvider +import com.fsck.k9.notification.NotificationChannelManager import com.fsck.k9.ui.base.AppLanguageManager import com.fsck.k9.ui.base.ThemeManager import com.fsck.k9.ui.base.extensions.currentLocale @@ -13,6 +14,7 @@ import java.util.Locale import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -25,6 +27,7 @@ class App : Application() { private val messagingListenerProvider: MessagingListenerProvider by inject() private val themeManager: ThemeManager by inject() private val appLanguageManager: AppLanguageManager by inject() + private val notificationChannelManager: NotificationChannelManager by inject() private val appCoroutineScope: CoroutineScope = GlobalScope + Dispatchers.Main private var appLanguageManagerInitialized = false @@ -39,6 +42,7 @@ class App : Application() { Core.init(this) MessageProvider.init() initializeAppLanguage() + updateNotificationChannelsOnAppLanguageChanges() themeManager.init() messagingListenerProvider.listeners.forEach { listener -> @@ -109,6 +113,13 @@ class App : Application() { return resources } + private fun updateNotificationChannelsOnAppLanguageChanges() { + appLanguageManager.appLocale + .distinctUntilChanged() + .onEach { notificationChannelManager.updateChannels() } + .launchIn(appCoroutineScope) + } + companion object { val appConfig = AppConfig( componentsToDisable = listOf(MessageCompose::class.java) diff --git a/app/ui/base/src/main/AndroidManifest.xml b/app/ui/base/src/main/AndroidManifest.xml index 27dc68b7a..f69b1cd3c 100644 --- a/app/ui/base/src/main/AndroidManifest.xml +++ b/app/ui/base/src/main/AndroidManifest.xml @@ -1,2 +1,18 @@ - + + + + + + + + + + + + + diff --git a/app/ui/base/src/main/java/com/fsck/k9/ui/base/AppLanguageManager.kt b/app/ui/base/src/main/java/com/fsck/k9/ui/base/AppLanguageManager.kt index 328490f52..dd306f095 100644 --- a/app/ui/base/src/main/java/com/fsck/k9/ui/base/AppLanguageManager.kt +++ b/app/ui/base/src/main/java/com/fsck/k9/ui/base/AppLanguageManager.kt @@ -3,6 +3,8 @@ package com.fsck.k9.ui.base import android.content.res.Resources import com.fsck.k9.K9 import com.fsck.k9.ui.base.extensions.currentLocale +import com.fsck.k9.ui.base.locale.SystemLocaleChangeListener +import com.fsck.k9.ui.base.locale.SystemLocaleManager import java.util.Locale import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -19,11 +21,20 @@ import kotlinx.coroutines.plus * - Notifies listeners when the app language has changed. */ class AppLanguageManager( + private val systemLocaleManager: SystemLocaleManager, private val coroutineScope: CoroutineScope = GlobalScope + Dispatchers.Main ) { private var currentOverrideLocale: Locale? = null private val _overrideLocale = MutableSharedFlow(replay = 1) + private val _appLocale = MutableSharedFlow(replay = 1) val overrideLocale: Flow = _overrideLocale + val appLocale: Flow = _appLocale + + private val systemLocaleListener = SystemLocaleChangeListener { + coroutineScope.launch { + _appLocale.emit(systemLocale) + } + } fun init() { setLocale(K9.k9Language) @@ -58,8 +69,15 @@ class AppLanguageManager( val locale = overrideLocale ?: systemLocale Locale.setDefault(locale) + if (overrideLocale == null) { + systemLocaleManager.addListener(systemLocaleListener) + } else { + systemLocaleManager.removeListener(systemLocaleListener) + } + coroutineScope.launch { _overrideLocale.emit(overrideLocale) + _appLocale.emit(locale) } } diff --git a/app/ui/base/src/main/java/com/fsck/k9/ui/base/KoinModule.kt b/app/ui/base/src/main/java/com/fsck/k9/ui/base/KoinModule.kt index a23592e87..7fc76c252 100644 --- a/app/ui/base/src/main/java/com/fsck/k9/ui/base/KoinModule.kt +++ b/app/ui/base/src/main/java/com/fsck/k9/ui/base/KoinModule.kt @@ -1,9 +1,18 @@ package com.fsck.k9.ui.base +import com.fsck.k9.ui.base.locale.SystemLocaleManager import org.koin.core.qualifier.named import org.koin.dsl.module val uiBaseModule = module { - single { ThemeManager(context = get(), themeProvider = get(), generalSettingsManager = get(), appCoroutineScope = get(named("AppCoroutineScope"))) } - single { AppLanguageManager() } + single { + ThemeManager( + context = get(), + themeProvider = get(), + generalSettingsManager = get(), + appCoroutineScope = get(named("AppCoroutineScope")) + ) + } + single { AppLanguageManager(systemLocaleManager = get()) } + single { SystemLocaleManager(context = get()) } } diff --git a/app/ui/base/src/main/java/com/fsck/k9/ui/base/locale/LocaleBroadcastReceiver.kt b/app/ui/base/src/main/java/com/fsck/k9/ui/base/locale/LocaleBroadcastReceiver.kt new file mode 100644 index 000000000..cbcb78b08 --- /dev/null +++ b/app/ui/base/src/main/java/com/fsck/k9/ui/base/locale/LocaleBroadcastReceiver.kt @@ -0,0 +1,17 @@ +package com.fsck.k9.ui.base.locale + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +class LocaleBroadcastReceiver : BroadcastReceiver(), KoinComponent { + private val systemLocaleManager: SystemLocaleManager by inject() + + override fun onReceive(context: Context, intent: Intent?) { + if (intent?.action == Intent.ACTION_LOCALE_CHANGED) { + systemLocaleManager.notifyListeners() + } + } +} diff --git a/app/ui/base/src/main/java/com/fsck/k9/ui/base/locale/SystemLocaleManager.kt b/app/ui/base/src/main/java/com/fsck/k9/ui/base/locale/SystemLocaleManager.kt new file mode 100644 index 000000000..f3d1674e2 --- /dev/null +++ b/app/ui/base/src/main/java/com/fsck/k9/ui/base/locale/SystemLocaleManager.kt @@ -0,0 +1,68 @@ +package com.fsck.k9.ui.base.locale + +import android.content.ComponentName +import android.content.Context +import android.content.pm.PackageManager +import java.util.concurrent.CopyOnWriteArraySet +import timber.log.Timber + +class SystemLocaleManager(context: Context) { + private val packageManager = context.packageManager + private val componentName = ComponentName(context, LocaleBroadcastReceiver::class.java) + + private val listeners = CopyOnWriteArraySet() + + @Synchronized + fun addListener(listener: SystemLocaleChangeListener) { + if (listeners.isEmpty()) { + enableReceiver() + } + + listeners.add(listener) + } + + @Synchronized + fun removeListener(listener: SystemLocaleChangeListener) { + listeners.remove(listener) + + if (listeners.isEmpty()) { + disableReceiver() + } + } + + internal fun notifyListeners() { + for (listener in listeners) { + listener.onSystemLocaleChanged() + } + } + + private fun enableReceiver() { + Timber.v("Enable LocaleBroadcastReceiver") + try { + packageManager.setComponentEnabledSetting( + componentName, + PackageManager.COMPONENT_ENABLED_STATE_ENABLED, + PackageManager.DONT_KILL_APP + ) + } catch (e: Exception) { + Timber.e(e, "Error enabling LocaleBroadcastReceiver") + } + } + + private fun disableReceiver() { + Timber.v("Disable LocaleBroadcastReceiver") + try { + packageManager.setComponentEnabledSetting( + componentName, + PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + PackageManager.DONT_KILL_APP + ) + } catch (e: Exception) { + Timber.e(e, "Error disabling LocaleBroadcastReceiver") + } + } +} + +fun interface SystemLocaleChangeListener { + fun onSystemLocaleChanged() +} diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt b/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt index 510982e81..da6bce71a 100644 --- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt +++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt @@ -38,7 +38,6 @@ import com.fsck.k9.fragment.MessageListFragment.MessageListFragmentListener import com.fsck.k9.helper.Contacts import com.fsck.k9.helper.ParcelableUtil import com.fsck.k9.mailstore.SearchStatusManager -import com.fsck.k9.notification.NotificationChannelManager import com.fsck.k9.preferences.GeneralSettingsManager import com.fsck.k9.search.LocalSearch import com.fsck.k9.search.SearchAccount @@ -90,7 +89,6 @@ open class MessageList : protected val searchStatusManager: SearchStatusManager by inject() private val preferences: Preferences by inject() - private val channelUtils: NotificationChannelManager by inject() private val defaultFolderProvider: DefaultFolderProvider by inject() private val accountRemover: BackgroundAccountRemover by inject() private val generalSettingsManager: GeneralSettingsManager by inject() @@ -205,7 +203,6 @@ open class MessageList : initializeFragments() displayViews() initializeRecentChangesSnackbar() - channelUtils.updateChannels() if (savedInstanceState == null) { checkAndRequestPermissions()