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)