diff --git a/app/core/src/main/java/com/fsck/k9/notification/AuthenticationErrorNotificationController.kt b/app/core/src/main/java/com/fsck/k9/notification/AuthenticationErrorNotificationController.kt index 2231a3fd3925c9ec9e6d5a5c5b909ae503e59749..332c7dbb42685c79541b61cc2f6bd882b6f9b681 100644 --- a/app/core/src/main/java/com/fsck/k9/notification/AuthenticationErrorNotificationController.kt +++ b/app/core/src/main/java/com/fsck/k9/notification/AuthenticationErrorNotificationController.kt @@ -30,15 +30,7 @@ internal open class AuthenticationErrorNotificationController( .setStyle(NotificationCompat.BigTextStyle().bigText(text)) .setPublicVersion(createLockScreenNotification(account)) .setCategory(NotificationCompat.CATEGORY_ERROR) - - notificationHelper.configureNotification( - builder = notificationBuilder, - ringtone = null, - vibrationPattern = null, - ledColor = NotificationHelper.NOTIFICATION_LED_FAILURE_COLOR, - ledSpeed = NotificationHelper.NOTIFICATION_LED_BLINK_FAST, - ringAndVibrate = true - ) + .setErrorAppearance() notificationManager.notify(notificationId, notificationBuilder.build()) } diff --git a/app/core/src/main/java/com/fsck/k9/notification/CertificateErrorNotificationController.kt b/app/core/src/main/java/com/fsck/k9/notification/CertificateErrorNotificationController.kt index ba657a240261aee784a6031ccb9aec89a48e1306..bae4f7e45b6098ef8b7ed889246d6dc24fe0cf97 100644 --- a/app/core/src/main/java/com/fsck/k9/notification/CertificateErrorNotificationController.kt +++ b/app/core/src/main/java/com/fsck/k9/notification/CertificateErrorNotificationController.kt @@ -30,15 +30,7 @@ internal open class CertificateErrorNotificationController( .setStyle(NotificationCompat.BigTextStyle().bigText(text)) .setPublicVersion(createLockScreenNotification(account)) .setCategory(NotificationCompat.CATEGORY_ERROR) - - notificationHelper.configureNotification( - builder = notificationBuilder, - ringtone = null, - vibrationPattern = null, - ledColor = NotificationHelper.NOTIFICATION_LED_FAILURE_COLOR, - ledSpeed = NotificationHelper.NOTIFICATION_LED_BLINK_FAST, - ringAndVibrate = true - ) + .setErrorAppearance() notificationManager.notify(notificationId, notificationBuilder.build()) } diff --git a/app/core/src/main/java/com/fsck/k9/notification/NotificationHelper.kt b/app/core/src/main/java/com/fsck/k9/notification/NotificationHelper.kt index 4b85b884cb47ef30df3a93f73ddacddbf508bde8..c51736c6c1978f102e7f52b2f935078ed10b4118 100644 --- a/app/core/src/main/java/com/fsck/k9/notification/NotificationHelper.kt +++ b/app/core/src/main/java/com/fsck/k9/notification/NotificationHelper.kt @@ -2,7 +2,7 @@ package com.fsck.k9.notification import android.content.Context import android.net.Uri -import android.text.TextUtils +import android.os.Build import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import com.fsck.k9.Account @@ -13,47 +13,6 @@ class NotificationHelper( private val notificationManager: NotificationManagerCompat, private val channelUtils: NotificationChannelManager ) { - fun configureNotification( - builder: NotificationCompat.Builder, - ringtone: String?, - vibrationPattern: LongArray?, - ledColor: Int?, - ledSpeed: Int, - ringAndVibrate: Boolean - ) { - - if (K9.isQuietTime) { - builder.setNotificationSilent() - return - } - - if (ringAndVibrate) { - if (ringtone != null && !TextUtils.isEmpty(ringtone)) { - builder.setSound(Uri.parse(ringtone)) - } - - if (vibrationPattern != null) { - builder.setVibrate(vibrationPattern) - } - } else { - builder.setNotificationSilent() - } - - if (ledColor != null) { - val ledOnMS: Int - val ledOffMS: Int - if (ledSpeed == NOTIFICATION_LED_BLINK_SLOW) { - ledOnMS = NOTIFICATION_LED_ON_TIME - ledOffMS = NOTIFICATION_LED_OFF_TIME - } else { - ledOnMS = NOTIFICATION_LED_FAST_ON_TIME - ledOffMS = NOTIFICATION_LED_FAST_OFF_TIME - } - - builder.setLights(ledColor, ledOnMS, ledOffMS) - } - } - fun getContext(): Context { return context } @@ -75,12 +34,22 @@ class NotificationHelper( companion object { internal const val NOTIFICATION_LED_ON_TIME = 500 internal const val NOTIFICATION_LED_OFF_TIME = 2000 - private const val NOTIFICATION_LED_FAST_ON_TIME = 100 - private const val NOTIFICATION_LED_FAST_OFF_TIME = 100 + internal const val NOTIFICATION_LED_FAST_ON_TIME = 100 + internal const val NOTIFICATION_LED_FAST_OFF_TIME = 100 - internal const val NOTIFICATION_LED_BLINK_SLOW = 0 - internal const val NOTIFICATION_LED_BLINK_FAST = 1 - internal const val NOTIFICATION_LED_FAILURE_COLOR = -0x10000 + internal const val NOTIFICATION_LED_FAILURE_COLOR = 0xFFFF0000L.toInt() + } +} + +internal fun NotificationCompat.Builder.setErrorAppearance(): NotificationCompat.Builder = apply { + setSilent(true) + + if (!K9.isQuietTime) { + setLights( + NotificationHelper.NOTIFICATION_LED_FAILURE_COLOR, + NotificationHelper.NOTIFICATION_LED_FAST_ON_TIME, + NotificationHelper.NOTIFICATION_LED_FAST_OFF_TIME + ) } } @@ -90,7 +59,7 @@ internal fun NotificationCompat.Builder.setAppearance( ): NotificationCompat.Builder = apply { if (silent) { setSilent(true) - } else { + } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { if (!appearance.ringtone.isNullOrEmpty()) { setSound(Uri.parse(appearance.ringtone)) } diff --git a/app/core/src/main/java/com/fsck/k9/notification/NotificationSettingsUpdater.kt b/app/core/src/main/java/com/fsck/k9/notification/NotificationSettingsUpdater.kt index 193e99c8724599df62230a9890cadc0b7cdb72cb..00ea6e8da528cb3535119b88e624e8db0e5cfdf3 100644 --- a/app/core/src/main/java/com/fsck/k9/notification/NotificationSettingsUpdater.kt +++ b/app/core/src/main/java/com/fsck/k9/notification/NotificationSettingsUpdater.kt @@ -18,17 +18,19 @@ class NotificationSettingsUpdater( accountUuids .mapNotNull { accountUuid -> preferences.getAccount(accountUuid) } - .forEach { account -> updateNotificationSettings(account) } + .forEach { account -> + updateNotificationSettings(account) + preferences.saveAccount(account) + } } @RequiresApi(Build.VERSION_CODES.O) - private fun updateNotificationSettings(account: Account) { + fun updateNotificationSettings(account: Account) { val notificationConfiguration = notificationChannelManager.getNotificationConfiguration(account) val notificationSettings = notificationConfigurationConverter.convert(account, notificationConfiguration) if (notificationSettings != account.notificationSettings) { account.updateNotificationSettings { notificationSettings } - preferences.saveAccount(account) } } } diff --git a/app/core/src/main/java/com/fsck/k9/notification/NotificationVibrationDecoder.kt b/app/core/src/main/java/com/fsck/k9/notification/NotificationVibrationDecoder.kt index 554479b1f6de619eb6ee3403c7b9d57e1e508c57..248b58f4f137ec453ba3469e5b8cba6a2e5be28d 100644 --- a/app/core/src/main/java/com/fsck/k9/notification/NotificationVibrationDecoder.kt +++ b/app/core/src/main/java/com/fsck/k9/notification/NotificationVibrationDecoder.kt @@ -9,7 +9,7 @@ import com.fsck.k9.VibratePattern class NotificationVibrationDecoder { fun decode(isVibrationEnabled: Boolean, systemPattern: List?): NotificationVibration { if (systemPattern == null || systemPattern.size < 2 || systemPattern.size % 2 != 0) { - return NotificationVibration.DEFAULT + return NotificationVibration(isVibrationEnabled, VibratePattern.Default, repeatCount = 1) } val systemPatternArray = systemPattern.toLongArray() diff --git a/app/core/src/main/java/com/fsck/k9/notification/SendFailedNotificationController.kt b/app/core/src/main/java/com/fsck/k9/notification/SendFailedNotificationController.kt index a75b73f5af942343e049a948464ae3be4320048a..3dd80b75ed82e145e405154609c0c259d1bbd792 100644 --- a/app/core/src/main/java/com/fsck/k9/notification/SendFailedNotificationController.kt +++ b/app/core/src/main/java/com/fsck/k9/notification/SendFailedNotificationController.kt @@ -42,15 +42,7 @@ internal class SendFailedNotificationController( .setStyle(NotificationCompat.BigTextStyle().bigText(text)) .setPublicVersion(createLockScreenNotification(account)) .setCategory(NotificationCompat.CATEGORY_ERROR) - - notificationHelper.configureNotification( - builder = notificationBuilder, - ringtone = null, - vibrationPattern = null, - ledColor = NotificationHelper.NOTIFICATION_LED_FAILURE_COLOR, - ledSpeed = NotificationHelper.NOTIFICATION_LED_BLINK_FAST, - ringAndVibrate = true - ) + .setErrorAppearance() notificationManager.notify(notificationId, notificationBuilder.build()) } diff --git a/app/core/src/main/java/com/fsck/k9/notification/SyncNotificationController.kt b/app/core/src/main/java/com/fsck/k9/notification/SyncNotificationController.kt index fa318ffa976575190a2f654c785a9d2273e73b94..3140fb813a829843f9bfe312336c952358b0b6f3 100644 --- a/app/core/src/main/java/com/fsck/k9/notification/SyncNotificationController.kt +++ b/app/core/src/main/java/com/fsck/k9/notification/SyncNotificationController.kt @@ -6,8 +6,6 @@ import androidx.core.app.NotificationManagerCompat import com.fsck.k9.Account import com.fsck.k9.mailstore.LocalFolder -private const val NOTIFICATION_LED_WHILE_SYNCING = false - internal class SyncNotificationController( private val notificationHelper: NotificationHelper, private val actionBuilder: NotificationActionCreator, @@ -36,17 +34,6 @@ internal class SyncNotificationController( .setContentIntent(showMessageListPendingIntent) .setPublicVersion(createSendingLockScreenNotification(account)) - if (NOTIFICATION_LED_WHILE_SYNCING) { - notificationHelper.configureNotification( - builder = notificationBuilder, - ringtone = null, - vibrationPattern = null, - ledColor = account.notificationSettings.light.toColor(account), - ledSpeed = NotificationHelper.NOTIFICATION_LED_BLINK_FAST, - ringAndVibrate = true - ) - } - notificationManager.notify(notificationId, notificationBuilder.build()) } @@ -83,17 +70,6 @@ internal class SyncNotificationController( .setPublicVersion(createFetchingMailLockScreenNotification(account)) .setCategory(NotificationCompat.CATEGORY_SERVICE) - if (NOTIFICATION_LED_WHILE_SYNCING) { - notificationHelper.configureNotification( - builder = notificationBuilder, - ringtone = null, - vibrationPattern = null, - ledColor = account.notificationSettings.light.toColor(account), - ledSpeed = NotificationHelper.NOTIFICATION_LED_BLINK_FAST, - ringAndVibrate = true - ) - } - notificationManager.notify(notificationId, notificationBuilder.build()) } @@ -113,17 +89,6 @@ internal class SyncNotificationController( .setPublicVersion(createFetchingMailLockScreenNotification(account)) .setCategory(NotificationCompat.CATEGORY_SERVICE) - if (NOTIFICATION_LED_WHILE_SYNCING) { - notificationHelper.configureNotification( - builder = notificationBuilder, - ringtone = null, - vibrationPattern = null, - ledColor = account.notificationSettings.light.toColor(account), - ledSpeed = NotificationHelper.NOTIFICATION_LED_BLINK_FAST, - ringAndVibrate = true - ) - } - notificationManager.notify(notificationId, notificationBuilder.build()) } diff --git a/app/core/src/main/java/com/fsck/k9/preferences/SettingsImporter.java b/app/core/src/main/java/com/fsck/k9/preferences/SettingsImporter.java index ba83f5a8c93f256104f126622a05354478e42e4e..661ab013cf5cdc4d31cb931d623d33a9b8a66e67 100644 --- a/app/core/src/main/java/com/fsck/k9/preferences/SettingsImporter.java +++ b/app/core/src/main/java/com/fsck/k9/preferences/SettingsImporter.java @@ -18,6 +18,7 @@ import android.text.TextUtils; import androidx.annotation.VisibleForTesting; import com.fsck.k9.Account; import com.fsck.k9.AccountPreferenceSerializer; +import com.fsck.k9.Clock; import com.fsck.k9.Core; import com.fsck.k9.DI; import com.fsck.k9.Identity; @@ -228,7 +229,7 @@ public class SettingsImporter { editor = preferences.createStorageEditor(); String newUuid = importResult.imported.uuid; - String oldAccountUuids = storage.getString("accountUuids", ""); + String oldAccountUuids = preferences.getStorage().getString("accountUuids", ""); String newAccountUuids = (oldAccountUuids.length() > 0) ? oldAccountUuids + "," + newUuid : newUuid; @@ -456,7 +457,12 @@ public class SettingsImporter { } } - //TODO: sync folder settings with localstore? + // When deleting an account and then restoring it using settings import, the same account UUID will be used. + // To avoid reusing a previously existing notification channel ID, we need to make sure to use a unique value + // for `messagesNotificationChannelVersion`. + Clock clock = DI.get(Clock.class); + String messageNotificationChannelVersion = Long.toString(clock.getTime() / 1000); + putString(editor, accountKeyPrefix + "messagesNotificationChannelVersion", messageNotificationChannelVersion); AccountDescription imported = new AccountDescription(accountName, uuid); return new AccountDescriptionPair(original, imported, mergeImportedAccount, diff --git a/app/k9mail/build.gradle b/app/k9mail/build.gradle index f6c923edfb4a5327643619ffef5b0419d9cbaacb..fdad471916c50664589122ff1cb5ec22c98e8ff0 100644 --- a/app/k9mail/build.gradle +++ b/app/k9mail/build.gradle @@ -48,8 +48,8 @@ android { applicationId "foundation.e.mail" testApplicationId "foundation.e.mail.tests" - versionCode 29013 - versionName '5.913' + versionCode 30000 + versionName '6.000' // Keep in sync with the resource string array 'supported_languages' resConfigs "in", "br", "ca", "cs", "cy", "da", "de", "et", "en", "en_GB", "es", "eo", "eu", "fr", "gd", "gl", 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 68991190e99caab3e54bce25b5d9c1ca037e3f6e..0598ad958b54d05de816484745250e8d6f8c13de 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 27dc68b7a44d3f48686f057867382385ce044b01..f69b1cd3c079c31dd052c18a2d4c56e16427141b 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 328490f52eebec712b1288040bc79948eccddf65..dd306f095938cd3dd76779262186dec55c1b5e23 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/K9Activity.kt b/app/ui/base/src/main/java/com/fsck/k9/ui/base/K9Activity.kt index ba70202d1fe0a4e0b7a9a0ad73ba6dbb99b00a2f..94cbc703ba386a806d861c299ce79649ecf7106c 100644 --- a/app/ui/base/src/main/java/com/fsck/k9/ui/base/K9Activity.kt +++ b/app/ui/base/src/main/java/com/fsck/k9/ui/base/K9Activity.kt @@ -1,10 +1,12 @@ package com.fsck.k9.ui.base import android.content.Context +import android.os.Build import android.os.Bundle import androidx.annotation.LayoutRes import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar +import androidx.core.app.ActivityCompat import androidx.lifecycle.asLiveData import com.fsck.k9.controller.push.PushController import java.util.Locale @@ -34,13 +36,22 @@ abstract class K9Activity(private val themeType: ThemeType) : AppCompatActivity( initializePushController() super.onCreate(savedInstanceState) + setLayoutDirection() listenForAppLanguageChanges() } + // On Android 12+ the layout direction doesn't seem to be updated when recreating the activity. This is a problem + // when switching from an LTR to an RTL language (or the other way around) using the language picker in the app. + private fun setLayoutDirection() { + if (Build.VERSION.SDK_INT >= 31) { + window.decorView.layoutDirection = resources.configuration.layoutDirection + } + } + private fun listenForAppLanguageChanges() { appLanguageManager.overrideLocale.asLiveData().observe(this) { overrideLocale -> if (overrideLocale != overrideLocaleOnLaunch) { - recreate() + recreateCompat() } } } @@ -64,6 +75,10 @@ abstract class K9Activity(private val themeType: ThemeType) : AppCompatActivity( setSupportActionBar(toolbar) } + + protected fun recreateCompat() { + ActivityCompat.recreate(this) + } } enum class ThemeType { 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 a23592e87bb4b1e8800aa271a6ddbab7115f8eab..7fc76c25287b36c17d04dbee5421a7dcf20d4027 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 0000000000000000000000000000000000000000..cbcb78b08da9296c8f320b3b8f54abd0c88fdfb7 --- /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 0000000000000000000000000000000000000000..f3d1674e293e08e57aeaceec06be25fdf37f0456 --- /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 47a5dc234dce2ac8342920ac98cf3dc6f4d6bda0..9c86fcce148cde45b20273d9c853edfba645e273 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 @@ -47,7 +47,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 @@ -98,7 +97,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() @@ -213,7 +211,6 @@ open class MessageList : initializeFragments() displayViews() initializeRecentChangesSnackbar() - channelUtils.updateChannels() if (savedInstanceState == null) { checkAndRequestPermissions() @@ -552,7 +549,7 @@ open class MessageList : if (messageListActivityAppearance == null) { messageListActivityAppearance = MessageListActivityAppearance.create(generalSettingsManager) } else if (messageListActivityAppearance != MessageListActivityAppearance.create(generalSettingsManager)) { - recreate() + recreateCompat() } if (displayMode != DisplayMode.MESSAGE_VIEW) { @@ -688,7 +685,7 @@ open class MessageList : override fun dispatchKeyEvent(event: KeyEvent): Boolean { var eventHandled = false - if (event.action == KeyEvent.ACTION_DOWN && searchView.isIconified) { + if (event.action == KeyEvent.ACTION_DOWN && ::searchView.isInitialized && searchView.isIconified) { eventHandled = onCustomKeyDown(event) } @@ -1526,7 +1523,7 @@ open class MessageList : private fun onToggleTheme() { themeManager.toggleMessageViewTheme() - recreate() + recreateCompat() } private fun showDefaultTitleView() { @@ -1665,6 +1662,11 @@ open class MessageList : permissionUiHelper.requestPermission(permission) } + override fun onFolderNotFoundError() { + val defaultFolderId = defaultFolderProvider.getDefaultFolder(account!!) + openFolderImmediately(defaultFolderId) + } + private enum class DisplayMode { MESSAGE_LIST, MESSAGE_VIEW, SPLIT_VIEW } diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/fragment/MessageListFragment.kt b/app/ui/legacy/src/main/java/com/fsck/k9/fragment/MessageListFragment.kt index 72faaa8683c1849f97dd310512758a42e0ce50bb..e021b14045d782e8d3bead0da4f42beed1555fd1 100644 --- a/app/ui/legacy/src/main/java/com/fsck/k9/fragment/MessageListFragment.kt +++ b/app/ui/legacy/src/main/java/com/fsck/k9/fragment/MessageListFragment.kt @@ -151,7 +151,7 @@ class MessageListFragment : super.onCreate(savedInstanceState) restoreInstanceState(savedInstanceState) - decodeArguments() + decodeArguments() ?: return viewModel.getMessageListLiveData().observe(this) { messageListInfo: MessageListInfo -> setMessageList(messageListInfo) @@ -175,6 +175,7 @@ class MessageListFragment : private fun restoreInstanceState(savedInstanceState: Bundle?) { if (savedInstanceState == null) return + activeMessages = savedInstanceState.getStringArray(STATE_ACTIVE_MESSAGES)?.map { MessageReference.parse(it)!! } restoreSelectedMessages(savedInstanceState) isRemoteSearch = savedInstanceState.getBoolean(STATE_REMOTE_SEARCH_PERFORMED) savedListState = savedInstanceState.getParcelable(STATE_MESSAGE_LIST) @@ -193,7 +194,7 @@ class MessageListFragment : listView.onRestoreInstanceState(savedListState) } - private fun decodeArguments() { + private fun decodeArguments(): MessageListFragment? { val arguments = requireArguments() showingThreadedList = arguments.getBoolean(ARG_THREADED_LIST, false) isThreadDisplay = arguments.getBoolean(ARG_IS_THREAD_DISPLAY, false) @@ -214,10 +215,17 @@ class MessageListFragment : isSingleFolderMode = false if (isSingleAccountMode && localSearch.folderIds.size == 1) { - isSingleFolderMode = true - val folderId = localSearch.folderIds[0] - currentFolder = getFolderInfoHolder(folderId, account!!) + try { + val folderId = localSearch.folderIds[0] + currentFolder = getFolderInfoHolder(folderId, account!!) + isSingleFolderMode = true + } catch (e: MessagingException) { + fragmentListener.onFolderNotFoundError() + return null + } } + + return this } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -455,6 +463,10 @@ class MessageListFragment : saveListState(outState) outState.putLongArray(STATE_SELECTED_MESSAGES, selected.toLongArray()) outState.putBoolean(STATE_REMOTE_SEARCH_PERFORMED, isRemoteSearch) + outState.putStringArray( + STATE_ACTIVE_MESSAGES, + activeMessages?.map(MessageReference::toIdentityString)?.toTypedArray() + ) if (activeMessage != null) { outState.putString(STATE_ACTIVE_MESSAGE, activeMessage!!.toIdentityString()) } @@ -482,12 +494,8 @@ class MessageListFragment : ) private fun getFolderInfoHolder(folderId: Long, account: Account): FolderInfoHolder { - return try { - val localFolder = MlfUtils.getOpenFolder(folderId, account) - FolderInfoHolder(folderNameFormatter, localFolder, account) - } catch (e: MessagingException) { - throw RuntimeException(e) - } + val localFolder = MlfUtils.getOpenFolder(folderId, account) + return FolderInfoHolder(folderNameFormatter, localFolder, account) } override fun onResume() { @@ -1951,6 +1959,7 @@ class MessageListFragment : fun remoteSearchStarted() fun goBack() fun updateMenu() + fun onFolderNotFoundError() companion object { const val MAX_PROGRESS = 10000 @@ -1966,6 +1975,7 @@ class MessageListFragment : private const val ARG_IS_THREAD_DISPLAY = "isThreadedDisplay" private const val STATE_SELECTED_MESSAGES = "selectedMessages" + private const val STATE_ACTIVE_MESSAGES = "activeMessages" private const val STATE_ACTIVE_MESSAGE = "activeMessage" private const val STATE_REMOTE_SEARCH_PERFORMED = "remoteSearchPerformed" private const val STATE_MESSAGE_LIST = "listState" diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/account/AccountSettingsFragment.kt b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/account/AccountSettingsFragment.kt index 5c9ad9f150a49402803a30dcc1212f22822b0531..b79ef71701f78573a69c401719082ec310c97bb3 100644 --- a/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/account/AccountSettingsFragment.kt +++ b/app/ui/legacy/src/main/java/com/fsck/k9/ui/settings/account/AccountSettingsFragment.kt @@ -10,6 +10,7 @@ import android.view.MenuInflater import android.view.MenuItem import android.widget.Toast import androidx.core.content.getSystemService +import androidx.core.net.toUri import androidx.preference.ListPreference import androidx.preference.Preference import androidx.preference.PreferenceCategory @@ -28,8 +29,7 @@ import com.fsck.k9.mailstore.FolderType import com.fsck.k9.mailstore.RemoteFolder import com.fsck.k9.notification.NotificationChannelManager import com.fsck.k9.notification.NotificationChannelManager.ChannelType -import com.fsck.k9.notification.NotificationLightDecoder -import com.fsck.k9.notification.NotificationVibrationDecoder +import com.fsck.k9.notification.NotificationSettingsUpdater import com.fsck.k9.ui.R import com.fsck.k9.ui.endtoend.AutocryptKeyTransferActivity import com.fsck.k9.ui.settings.onClick @@ -52,8 +52,7 @@ class AccountSettingsFragment : PreferenceFragmentCompat(), ConfirmationDialogFr private val messagingController: MessagingController by inject() private val accountRemover: BackgroundAccountRemover by inject() private val notificationChannelManager: NotificationChannelManager by inject() - private val notificationLightDecoder: NotificationLightDecoder by inject() - private val notificationVibrationDecoder: NotificationVibrationDecoder by inject() + private val notificationSettingsUpdater: NotificationSettingsUpdater by inject() private val vibrator by lazy { requireContext().getSystemService() } private lateinit var dataStore: AccountSettingsDataStore @@ -251,28 +250,21 @@ class AccountSettingsFragment : PreferenceFragmentCompat(), ConfirmationDialogFr @SuppressLint("NewApi") private fun updateNotificationPreferences(account: Account) { - val notificationConfiguration = notificationChannelManager.getNotificationConfiguration(account) + notificationSettingsUpdater.updateNotificationSettings(account) + val notificationSettings = account.notificationSettings notificationSoundPreference?.let { preference -> - preference.setNotificationSound(notificationConfiguration.sound) + preference.setNotificationSound(notificationSettings.ringtone?.toUri()) preference.isEnabled = true } notificationLightPreference?.let { preference -> - val notificationLightSetting = notificationLightDecoder.decode( - isBlinkLightsEnabled = notificationConfiguration.isBlinkLightsEnabled, - lightColor = notificationConfiguration.lightColor, - accountColor = account.chipColor - ) - preference.value = notificationLightSetting.name + preference.value = notificationSettings.light.name preference.isEnabled = true } notificationVibrationPreference?.let { preference -> - val notificationVibration = notificationVibrationDecoder.decode( - isVibrationEnabled = notificationConfiguration.isVibrationEnabled, - systemPattern = notificationConfiguration.vibrationPattern - ) + val notificationVibration = notificationSettings.vibration preference.setVibration( isVibrationEnabled = notificationVibration.isEnabled, vibratePattern = notificationVibration.pattern, diff --git a/app/ui/legacy/src/main/res/raw/changelog_master.xml b/app/ui/legacy/src/main/res/raw/changelog_master.xml index 7ccc0dc42069a076354ad8e8927021f4df3fab23..abb0760cb922f93f81b93c25fa3856976e1631ca 100644 --- a/app/ui/legacy/src/main/res/raw/changelog_master.xml +++ b/app/ui/legacy/src/main/res/raw/changelog_master.xml @@ -5,6 +5,42 @@ Locale-specific versions are kept in res/raw-/changelog.xml. --> + + Added support for setting the notification vibration pattern on Android 8+ + Added support for setting a custom notification light color on Android 8+ + Added a setting to configure the notification sound on Android 8+ (because some vendor-specific Android versions removed this feature from their user interface) + Restore 'new mail' notifications when the app is restarted + Open message from notification in Unified Inbox if possible + Hide sensitive information when sync and error notifications are displayed on the lock screen + Added a setting to suppress notifications for chat messages + Don't create notifications when manually refreshing the message list + Don't show notifications for new messages when syncing a folder for the first time + Removed the "hide subject in notifications" setting; use the "lock screen notifications" setting instead + Fixed back button behavior when opening a single message from a notification + A lot of other fixes related to notifications + Added support for entering Reply-To addresses when composing a message + Optionally show starred message count in side drawer + Require the user to authenticate before unmasking server passwords + Added a menu option to export the debug log under Settings → General settings → Debugging + IMAP: Removed setting to enable remote search (it's now enabled by default; still requires an additional button press) + Numerous other bug fixes and improvements (see change log entries for 5.9xx) + + + Fixed bug that crashed the app when setting a notification sound on certain devices + Fixed crash when rotating the device while a delete confirmation dialog was showing + + + Fixed settings import + Fixed bug where the configuration of a notification category wasn't properly synced with in-app notification settings + Fixed display issues when switching the language inside the app + Updated translations + + + The name and description of notification categories are now updated when the app language is changed + Fixed bug where notification settings might not have been properly restored on settings import + Fixed a crash when tapping the unread widget tries to open a folder that no longer exists + Updated translations + Don't create notifications when manually refreshing the message list Fixed import and export of notification settings diff --git a/app/ui/legacy/src/main/res/values-br/strings.xml b/app/ui/legacy/src/main/res/values-br/strings.xml index a92b7eaacdaa4bddce321937b716e33271820b50..8c26a1b593e56dac313da5a0bedfe44282b55f40 100644 --- a/app/ui/legacy/src/main/res/values-br/strings.xml +++ b/app/ui/legacy/src/main/res/values-br/strings.xml @@ -13,36 +13,8 @@ Levraouegoù Lañvaz - Donemat war K-9 Mail - -K-9 Mail a zo un arval posteloù digoust galloudus evit Android -

-Kavet e vez e-touez e geweriusterioù: -

-
    -
  • Bountañ posteloù gant IMAP IDLE
  • -
  • Digonusted gwelloc’h
  • -
  • Adleuniadenn kemennadennoù
  • -
  • Sinadurioù postel
  • -
  • Bcc-to-self
  • -
  • Koumanant da deuliadoù
  • -
  • Goubredañ d’an holl deuliadoù
  • -
  • Kefluniadur ar chomlec’h daskor
  • -
  • Berradennoù klavier
  • -
  • Skor IMAP gwelloc’h
  • -
  • Enrollañ kenstagadennoù d’ar gartenn SD
  • -
  • Goullonderiñ ar c’hest
  • -
  • Urzhiañ ar c’hemennadennoù
  • -
  • …ha muioc’h c’hoazh
  • -
-

-K- ne skor ket lodenn vrasañ ar c’hontoù Hotmail digoust hag evel loden vrasañ an arvaloù postel eo diaes dezhañ eskemm gant Microsoft Exchange. -

-Danevellit beugoù, kenlabourit war keweriusterioù nevez ha savit goulennoù war -https://github.com/k9mail/k-9/. -

- ]]>
+ Donemat war Mail + Mail is the default mail client for /e/ -- \nKaset eus ma fellgomzer Android gant K-9 Mail. diff --git a/app/ui/legacy/src/main/res/values-da/strings.xml b/app/ui/legacy/src/main/res/values-da/strings.xml index 157ff2bf72fafd9fb524192b221e76b061b2cc0d..ee7f94e0bc36bb257290e626200f9abec4e31013 100644 --- a/app/ui/legacy/src/main/res/values-da/strings.xml +++ b/app/ui/legacy/src/main/res/values-da/strings.xml @@ -136,6 +136,7 @@ Arkivere Arkivér alle Spam + Certifikat fejl Certifikat fejl for %s Kontroller dine serverindstillinger Godkendelse mislykkedes @@ -249,6 +250,7 @@ Mail addresse Password + Verificer din identitet Manuel opsætning Henter konto-information\u2026 @@ -503,11 +505,25 @@ Kontonavn Dit navn Beskeder + Vibration Vibrer + Vibrations mønster Standard + Mønster 1 + Mønster 2 + Mønster 3 + Mønster 4 + Mønster 5 Gentag vibration Ringetone ved ny mail Kontofarve + Hvid + Rød + Grøn + Blå + Gul + Cyan + Magenta Indstillinger for skrivning af mail Standardindstillinger Standardindstilling for afsender, Bcc og signatur diff --git a/app/ui/legacy/src/main/res/values-fa/strings.xml b/app/ui/legacy/src/main/res/values-fa/strings.xml index cea4b18f07b90cab9931324e8208af87fcac5086..0cfeeead51218463ec2eb6088f3d659d8ccc541e 100644 --- a/app/ui/legacy/src/main/res/values-fa/strings.xml +++ b/app/ui/legacy/src/main/res/values-fa/strings.xml @@ -77,6 +77,7 @@ جستجو جستجو در همه پوشه‌ها نتایج جستجو + پیام‌های جدید تنظیمات مدیریت پوشه‌ها تنظیمات حساب @@ -134,6 +135,7 @@ بایگانی بایگانی همه هرزنامه + خطای گواهی خطای گواهی برای %s تنظیمات کارساز خود را بررسی کنید احراز هویت ناموفق بود @@ -160,6 +162,9 @@ ثبت اطلاعات عیب‌یابی بیشتر ثبت اطلاعات حساس شاید گذرواژه‌ها در گزارش بیاید. + گزارش‌های برون‌برد + برون‌برد باموفقیت انجام شد. گزارش‌ها ممکن است حاوی اطلاعات حساس باشند. مراقب باشید آن‌ها را برای چه کسی ارسال می‌کنید. + برون‌برد انجام نشد. بارگیری پیام‌های بیشتر به:%s موضوع @@ -530,6 +535,7 @@ نام حساب نام شما اعلان‌ها + لرزش لرزش لرزش به‌هنگام رسیدن رایانامه الگوهای لرزش @@ -540,8 +546,19 @@ الگوی ۴ الگوی ۵ تکرار لرزش + غیرفعال زنگ رایانامهٔ جدید + چراغ اعلان + غیرفعال رنگ حساب کاربری + رنگ پیش‌فرض سیستم + سفید + قرمز + سبز + آبی + زرد + فیروزه‌ای + ارغوانی گزینه‌های نوشتن پیام پیش‌فرض‌های نوشتن پیش‌فرض‌های خودتان را برای «از»، «مخفیانه به» و «امضا» تنظیم کنید diff --git a/app/ui/legacy/src/main/res/values-pl/strings.xml b/app/ui/legacy/src/main/res/values-pl/strings.xml index dc5c3692f95e7c6e42c573ec468ad0f8e98595a6..880904abb09a79bc01910729d6f59159dc39fd84 100644 --- a/app/ui/legacy/src/main/res/values-pl/strings.xml +++ b/app/ui/legacy/src/main/res/values-pl/strings.xml @@ -215,6 +215,7 @@ Użyj nazw nadawców, jeżeli występują w Twojej książce kontaktowej Koloruj kontakty Koloruj nazwiska na liście kontaktów + Kolor nazwy kontaktu Czcionka o stałej szer. Użyj czcionki o stałej szerokości do wyświetlania wiadomości tekstowych Dopasuj wiadomość do rozmiaru ekranu @@ -517,6 +518,7 @@ Nazwa konta Twoje imię i nazwisko Powiadomienia + Wibracja Wibracja Wzór wibracji. Standardowa @@ -526,8 +528,19 @@ Wzór 4 Wzór 5 Liczba wibracji + Wyłączone Sygnał dzwiękowy + Światło powiadomienia + Wyłączone Kolor konta + Domyślny kolor systemowy + Biały + Czerwony + Zielony + Niebieski + Żółty + Cyjan + Różowy Opcje tworzenia wiadomości Tworzenie wiadomości Ustaw podpis oraz domyślne wartości Do, UDW diff --git a/app/ui/legacy/src/main/res/values-ro/strings.xml b/app/ui/legacy/src/main/res/values-ro/strings.xml index a8e4d118d4fd3cbdc559ff1ecb28ca8a3ba677c4..724e62e58280698a2350064009b22aaae9378191 100644 --- a/app/ui/legacy/src/main/res/values-ro/strings.xml +++ b/app/ui/legacy/src/main/res/values-ro/strings.xml @@ -214,6 +214,7 @@ cel mult încă %d Foloseşte numele destinatarilor din Contacte atunci când sunt disponibile Colorează contactele Colorează numele în lista de contacte + Culoare nume de contact Fonturi cu lățime fixă Utilizează un font cu lățime fixă când mesajele sunt afișate ca text simplu Auto-dimensionează mesajele @@ -518,6 +519,7 @@ Uneori datorită faptului că cineva încearcă să te atace pe tine sau serveru Nume cont Nume Notificări + Vibrație Vibrație Model de vibrații Implicit @@ -527,8 +529,19 @@ Uneori datorită faptului că cineva încearcă să te atace pe tine sau serveru Modelul 4 Modelul 5 Repetă vibrație + Dezactivat Ton mesaje noi + Lumină de notificare + Dezactivat Culoarea contului + Culoarea implicită a sistemului + Alb + Roșu + Verde + Albastru + Galben + Cian + Magenta Opțiuni compunere mesaj Setări implicite compunere mesaj Setează ”De la”, Bcc și semnătură diff --git a/app/ui/legacy/src/main/res/values-sv/strings.xml b/app/ui/legacy/src/main/res/values-sv/strings.xml index d6b5fc01eeb7ba4379a175d5ab503617d628c269..8ddb11fe22e25a4a383464f2e45b8c686a24ba84 100644 --- a/app/ui/legacy/src/main/res/values-sv/strings.xml +++ b/app/ui/legacy/src/main/res/values-sv/strings.xml @@ -893,7 +893,7 @@ Ingen nyckel konfigurerad för detta konto! Kontrollera dina inställningar. Kryptoleverantören använder inkompatibel version. Kontrollera dina inställningar! Kan inte ansluta till kryptoleverantör, granska dina inställningar eller klicka på kryptoikonen för nytt försök. - Det gick inte att initiera ände-till-ände-kryptering, vänligen kontrollera dina inställningar + Det gick inte att initiera ände-till-ände-kryptering, kontrollera dina inställningar PGP/INLINE läge stöder inte bilagor! Aktivera PGP/INLINE Inaktivera PGP/INLINE diff --git a/app/ui/legacy/src/main/res/values-uk/strings.xml b/app/ui/legacy/src/main/res/values-uk/strings.xml index a1fa08d40762dee0a8297d5d59cef654c3c2ad5e..413e48451fdd21bcaaab26d246a265dcd54af6a9 100644 --- a/app/ui/legacy/src/main/res/values-uk/strings.xml +++ b/app/ui/legacy/src/main/res/values-uk/strings.xml @@ -46,6 +46,8 @@ Переслати як вкладення Вибрати обліковий запис Вибрати теку + Посунути до… + Копіювати до… %d вибрано Наступний Попередній @@ -78,6 +80,7 @@ Пошук Шукати всюди Результати пошуку + Нові повідомлення Налаштування Керування теками Налаштування облікового запису @@ -137,6 +140,7 @@ Архівувати Архівувати всі Спам + Помилка сертифікату Помилка сертифікату для %s Перевірте налаштування вашого серверу Помилка автентифікації @@ -163,6 +167,9 @@ Записувати додаткову діагностичну інформацію Записувати конфіденційну інформацію Може відображати паролі в журналах. + Експортувати журнали + Експорт успішний. Журнали можуть містити чутливі дані. Обачно обирайте адресата. + Не вдалося експортувати. Завантажити більше повідомлень До:%s Тема @@ -209,6 +216,7 @@ Використовувати імена одержувачів із Контактів, якщо це можливо Позначати контакти кольором Позначати кольором імена з вашого списку контактів + Колір імені контакту Моноширинні шрифти Використовувати моноширинний шрифт для відображення звичайних текстових повідомлень Припасовувати текст повідомлень до ширини екрану @@ -236,6 +244,7 @@ Сповіщення на заблокованому екрані Без сповіщень на заблокованому екрані Ім\'я додатку + Лічильник нових повідомлень Кількість повідомлень і відправники Так само, як і при розблокованому екрані Тиха Година @@ -248,6 +257,9 @@ Адреса електронної пошти Пароль + Щоб переглянути пароль, налаштуйте цьому пристрою блокування екрану. + Звірте свою особу + Розблокуйте, щоб переглянути пароль Ручне налаштування Отримання інформації про обліковий запис\u2026 @@ -372,6 +384,8 @@ Показати сповіщення про надіслані мною повідомлення Лише контактам Показувати сповіщення лише для повідомлень від вже відомих контактів + Нехтувати повідомленнями розмови + Не показувати сповіщень про повідомлення поштової розмови Позначати повідомлення прочитаним після відкриття Позначати повідомлення прочитаним після відкриття для перегляду Позначити прочитаним після видалення @@ -505,11 +519,29 @@ Ім’я облікового запису Ваше ім’я Сповіщення + Вібрація Вібрація + Ритм вібрації Типовий + Ритм 1 + Ритм 2 + Ритм 3 + Ритм 4 + Ритм 5 Повторити вібрацію + Вимкнено Звук сповіщення про новий лист + Спалах сповіщення + Вимкнено Колір облікового запису + Типовий колір системи + Білий + Червоний + Зелений + Синій + Жовтий + Бірюзовий + Рожевий Параметри створення повідомлень Типові налаштування написання листів Встановити типові значення полів \"Від\", \"Прихована копія\" і підпис @@ -620,6 +652,7 @@ при перегляді повідомлень у списках Показувати Об\'єднані Вхідні + Показати лічильник зірочок Об\'єднані Вхідні Усі повідомлення в об\'єднаних теках Об’єднати @@ -831,6 +864,7 @@ Прихована копія До Від + Відповісти до <Невідомий Адресат> <Невідомий Відправник> Домашній diff --git a/fastlane/metadata/android/en-US/changelogs/29014.txt b/fastlane/metadata/android/en-US/changelogs/29014.txt new file mode 100644 index 0000000000000000000000000000000000000000..168ad2c90c9f26ffcadda0be136219f2e4e9012e --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/29014.txt @@ -0,0 +1,4 @@ +- The name and description of notification categories are now updated when the app language is changed +- Fixed bug where notification settings might not have been properly restored on settings import +- Fixed a crash when tapping the unread widget tries to open a folder that no longer exists +- Updated translations diff --git a/fastlane/metadata/android/en-US/changelogs/29015.txt b/fastlane/metadata/android/en-US/changelogs/29015.txt new file mode 100644 index 0000000000000000000000000000000000000000..9343ebeabb5651c0e7bf97cd69b3753e3135b609 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/29015.txt @@ -0,0 +1,4 @@ +- Fixed settings import +- Fixed bug where the configuration of a notification category wasn't properly synced with in-app notification settings +- Fixed display issues when switching the language inside the app +- Updated translations diff --git a/fastlane/metadata/android/en-US/changelogs/29016.txt b/fastlane/metadata/android/en-US/changelogs/29016.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc48a67fd5ba6f1ec1148910f835e0c775af58c6 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/29016.txt @@ -0,0 +1,2 @@ +- Fixed bug that crashed the app when setting a notification sound on certain devices +- Fixed crash when rotating the device while a delete confirmation dialog was showing diff --git a/fastlane/metadata/android/en-US/changelogs/30000.txt b/fastlane/metadata/android/en-US/changelogs/30000.txt new file mode 100644 index 0000000000000000000000000000000000000000..2d51a19b5c18baecb671a03740f2ede119983a2e --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/30000.txt @@ -0,0 +1,18 @@ +- Added support for setting the notification vibration pattern on Android 8+ +- Added support for setting a custom notification light color on Android 8+ +- Added a setting to configure the notification sound on Android 8+ (because some vendor-specific Android versions removed this feature from their user interface) +- Restore 'new mail' notifications when the app is restarted +- Open message from notification in Unified Inbox if possible +- Hide sensitive information when sync and error notifications are displayed on the lock screen +- Added a setting to suppress notifications for chat messages +- Don't create notifications when manually refreshing the message list +- Don't show notifications for new messages when syncing a folder for the first time +- Removed the "hide subject in notifications" setting; use the "lock screen notifications" setting instead +- Fixed back button behavior when opening a single message from a notification +- A lot of other fixes related to notifications +- Added support for entering Reply-To addresses when composing a message +- Optionally show starred message count in side drawer +- Require the user to authenticate before unmasking server passwords +- Added a menu option to export the debug log under Settings → General settings → Debugging +- IMAP: Removed setting to enable remote search (it's now enabled by default; still requires an additional button press) +- Numerous other bug fixes and improvements (see change log entries for 5.9xx)