android: workaround of system restricted background (#2873)
* android: workaround of system restricted background * strings * exclude lockscreen call from requirements to unrestrict * texts * added button and changed behaviour * texts 2 * button instead of alert * padding * bigger padding * refactor * don't jump on button hide * no exceptions for lockscreen * sometimes do not show off alert --------- Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
38dc14f041
commit
9543af4784
@@ -93,12 +93,12 @@ class SimplexApp: Application(), LifecycleEventObserver {
|
||||
|
||||
fun allowToStartServiceAfterAppExit() = with(chatModel.controller) {
|
||||
appPrefs.notificationsMode.get() == NotificationsMode.SERVICE &&
|
||||
(!NotificationsMode.SERVICE.requiresIgnoringBattery || SimplexService.isIgnoringBatteryOptimizations())
|
||||
(!NotificationsMode.SERVICE.requiresIgnoringBattery || SimplexService.isBackgroundAllowed())
|
||||
}
|
||||
|
||||
private fun allowToStartPeriodically() = with(chatModel.controller) {
|
||||
appPrefs.notificationsMode.get() == NotificationsMode.PERIODIC &&
|
||||
(!NotificationsMode.PERIODIC.requiresIgnoringBattery || SimplexService.isIgnoringBatteryOptimizations())
|
||||
(!NotificationsMode.PERIODIC.requiresIgnoringBattery || SimplexService.isBackgroundAllowed())
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -158,7 +158,7 @@ class SimplexApp: Application(), LifecycleEventObserver {
|
||||
}
|
||||
|
||||
override fun androidNotificationsModeChanged(mode: NotificationsMode) {
|
||||
if (mode.requiresIgnoringBattery && !SimplexService.isIgnoringBatteryOptimizations()) {
|
||||
if (mode.requiresIgnoringBattery && !SimplexService.isBackgroundAllowed()) {
|
||||
appPrefs.backgroundServiceNoticeShown.set(false)
|
||||
}
|
||||
SimplexService.StartReceiver.toggleReceiver(mode == NotificationsMode.SERVICE)
|
||||
@@ -172,7 +172,7 @@ class SimplexApp: Application(), LifecycleEventObserver {
|
||||
if (mode != NotificationsMode.PERIODIC) {
|
||||
MessagesFetcherWorker.cancelAll()
|
||||
}
|
||||
SimplexService.showBackgroundServiceNoticeIfNeeded()
|
||||
SimplexService.showBackgroundServiceNoticeIfNeeded(showOffAlert = false)
|
||||
}
|
||||
|
||||
override fun androidChatStartedAfterBeingOff() {
|
||||
@@ -199,6 +199,19 @@ class SimplexApp: Application(), LifecycleEventObserver {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun androidIsBackgroundCallAllowed(): Boolean = !SimplexService.isBackgroundRestricted()
|
||||
|
||||
override suspend fun androidAskToAllowBackgroundCalls(): Boolean {
|
||||
if (SimplexService.isBackgroundRestricted()) {
|
||||
val userChoice: CompletableDeferred<Boolean> = CompletableDeferred()
|
||||
SimplexService.showBGRestrictedInCall {
|
||||
userChoice.complete(it)
|
||||
}
|
||||
return userChoice.await()
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import android.os.*
|
||||
import android.provider.Settings
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -17,7 +18,6 @@ import androidx.core.app.NotificationCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.work.*
|
||||
import chat.simplex.common.AppLock
|
||||
import chat.simplex.common.AppLock.clearAuthState
|
||||
import chat.simplex.common.helpers.requiresIgnoringBattery
|
||||
import chat.simplex.common.model.ChatController
|
||||
import chat.simplex.common.model.NotificationsMode
|
||||
@@ -50,6 +50,7 @@ class SimplexService: Service() {
|
||||
} else {
|
||||
Log.d(TAG, "null intent. Probably restarted by the system.")
|
||||
}
|
||||
startForeground(SIMPLEX_SERVICE_ID, serviceNotification)
|
||||
return START_STICKY // to restart if killed
|
||||
}
|
||||
|
||||
@@ -353,7 +354,7 @@ class SimplexService: Service() {
|
||||
|
||||
private fun getPreferences(context: Context): SharedPreferences = context.getSharedPreferences(SHARED_PREFS_ID, Context.MODE_PRIVATE)
|
||||
|
||||
fun showBackgroundServiceNoticeIfNeeded() {
|
||||
fun showBackgroundServiceNoticeIfNeeded(showOffAlert: Boolean = true) {
|
||||
val appPrefs = ChatController.appPrefs
|
||||
val mode = appPrefs.notificationsMode.get()
|
||||
Log.d(TAG, "showBackgroundServiceNoticeIfNeeded")
|
||||
@@ -362,27 +363,28 @@ class SimplexService: Service() {
|
||||
|
||||
if (!appPrefs.backgroundServiceNoticeShown.get()) {
|
||||
// the branch for the new users who have never seen service notice
|
||||
if (!mode.requiresIgnoringBattery || isIgnoringBatteryOptimizations()) {
|
||||
if (!mode.requiresIgnoringBattery || isBackgroundAllowed()) {
|
||||
showBGServiceNotice(mode)
|
||||
} else {
|
||||
showBGServiceNoticeIgnoreOptimization(mode)
|
||||
} else if (isBackgroundRestricted()) {
|
||||
showBGServiceNoticeSystemRestricted(mode, showOffAlert)
|
||||
} else if (!isIgnoringBatteryOptimizations()) {
|
||||
showBGServiceNoticeIgnoreOptimization(mode, showOffAlert)
|
||||
}
|
||||
// set both flags, so that if the user doesn't allow ignoring optimizations, the service will be disabled without additional notice
|
||||
appPrefs.backgroundServiceNoticeShown.set(true)
|
||||
appPrefs.backgroundServiceBatteryNoticeShown.set(true)
|
||||
} else if (mode.requiresIgnoringBattery && isBackgroundRestricted()) {
|
||||
// the branch for users who have app installed, and have seen the service notice,
|
||||
// but the service is running AND system background restriction is on OR the battery optimization for the app is in RESTRICTED state
|
||||
showBGServiceNoticeSystemRestricted(mode, showOffAlert)
|
||||
if (!appPrefs.backgroundServiceBatteryNoticeShown.get()) {
|
||||
appPrefs.backgroundServiceBatteryNoticeShown.set(true)
|
||||
}
|
||||
} else if (mode.requiresIgnoringBattery && !isIgnoringBatteryOptimizations()) {
|
||||
// the branch for users who have app installed, and have seen the service notice,
|
||||
// but the battery optimization for the app is on (Android 12) AND the service is running
|
||||
if (appPrefs.backgroundServiceBatteryNoticeShown.get()) {
|
||||
// users have been presented with battery notice before - they did not allow ignoring optimizations -> disable service
|
||||
showDisablingServiceNotice(mode)
|
||||
appPrefs.notificationsMode.set(NotificationsMode.OFF)
|
||||
StartReceiver.toggleReceiver(false)
|
||||
MessagesFetcherWorker.cancelAll()
|
||||
safeStopService()
|
||||
} else {
|
||||
// show battery optimization notice
|
||||
showBGServiceNoticeIgnoreOptimization(mode)
|
||||
// but the battery optimization for the app is in OPTIMIZED state (Android 12+) AND the service is running
|
||||
showBGServiceNoticeIgnoreOptimization(mode, showOffAlert)
|
||||
if (!appPrefs.backgroundServiceBatteryNoticeShown.get()) {
|
||||
appPrefs.backgroundServiceBatteryNoticeShown.set(true)
|
||||
}
|
||||
} else {
|
||||
@@ -425,13 +427,17 @@ class SimplexService: Service() {
|
||||
)
|
||||
}
|
||||
|
||||
private fun showBGServiceNoticeIgnoreOptimization(mode: NotificationsMode) = AlertManager.shared.showAlert {
|
||||
private fun showBGServiceNoticeIgnoreOptimization(mode: NotificationsMode, showOffAlert: Boolean) = AlertManager.shared.showAlert {
|
||||
val ignoreOptimization = {
|
||||
AlertManager.shared.hideAlert()
|
||||
askAboutIgnoringBatteryOptimization()
|
||||
}
|
||||
val disableNotifications = {
|
||||
AlertManager.shared.hideAlert()
|
||||
disableNotifications(mode, showOffAlert)
|
||||
}
|
||||
AlertDialog(
|
||||
onDismissRequest = ignoreOptimization,
|
||||
onDismissRequest = disableNotifications,
|
||||
title = {
|
||||
Row {
|
||||
Icon(
|
||||
@@ -454,12 +460,98 @@ class SimplexService: Service() {
|
||||
Text(annotatedStringResource(MR.strings.turn_off_battery_optimization))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = disableNotifications) { Text(stringResource(MR.strings.disable_notifications_button), color = MaterialTheme.colors.error) }
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = ignoreOptimization) { Text(stringResource(MR.strings.ok)) }
|
||||
TextButton(onClick = ignoreOptimization) { Text(stringResource(MR.strings.turn_off_battery_optimization_button)) }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun showBGServiceNoticeSystemRestricted(mode: NotificationsMode, showOffAlert: Boolean) = AlertManager.shared.showAlert {
|
||||
val unrestrict = {
|
||||
AlertManager.shared.hideAlert()
|
||||
askToUnrestrictBackground()
|
||||
}
|
||||
val disableNotifications = {
|
||||
AlertManager.shared.hideAlert()
|
||||
disableNotifications(mode, showOffAlert)
|
||||
}
|
||||
AlertDialog(
|
||||
onDismissRequest = disableNotifications,
|
||||
title = {
|
||||
Row {
|
||||
Icon(
|
||||
painterResource(MR.images.ic_bolt),
|
||||
contentDescription =
|
||||
if (mode == NotificationsMode.SERVICE) stringResource(MR.strings.icon_descr_instant_notifications) else stringResource(MR.strings.periodic_notifications),
|
||||
)
|
||||
Text(
|
||||
if (mode == NotificationsMode.SERVICE) stringResource(MR.strings.service_notifications) else stringResource(MR.strings.periodic_notifications),
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
},
|
||||
text = {
|
||||
Column {
|
||||
Text(
|
||||
annotatedStringResource(MR.strings.system_restricted_background_desc),
|
||||
Modifier.padding(bottom = 8.dp)
|
||||
)
|
||||
Text(annotatedStringResource(MR.strings.system_restricted_background_warn))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = disableNotifications) { Text(stringResource(MR.strings.disable_notifications_button), color = MaterialTheme.colors.error) }
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = unrestrict) { Text(stringResource(MR.strings.turn_off_system_restriction_button)) }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun showBGRestrictedInCall(onDismiss: (allowedCall: Boolean) -> Unit) = AlertManager.shared.showAlert {
|
||||
val unrestrict = {
|
||||
askToUnrestrictBackground()
|
||||
}
|
||||
AlertDialog(
|
||||
onDismissRequest = AlertManager.shared::hideAlert,
|
||||
title = {
|
||||
Text(
|
||||
stringResource(MR.strings.system_restricted_background_in_call_title),
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
},
|
||||
text = {
|
||||
Column {
|
||||
Text(
|
||||
annotatedStringResource(MR.strings.system_restricted_background_in_call_desc),
|
||||
Modifier.padding(bottom = 8.dp)
|
||||
)
|
||||
Text(annotatedStringResource(MR.strings.system_restricted_background_in_call_warn))
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = unrestrict) { Text(stringResource(MR.strings.turn_off_system_restriction_button)) }
|
||||
}
|
||||
)
|
||||
val scope = rememberCoroutineScope()
|
||||
DisposableEffect(Unit) {
|
||||
scope.launch {
|
||||
repeat(10000) {
|
||||
delay(200)
|
||||
if (!isBackgroundRestricted()) {
|
||||
AlertManager.shared.hideAlert()
|
||||
}
|
||||
}
|
||||
}
|
||||
onDispose {
|
||||
onDismiss(!isBackgroundRestricted())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showDisablingServiceNotice(mode: NotificationsMode) = AlertManager.shared.showAlert {
|
||||
AlertDialog(
|
||||
onDismissRequest = AlertManager.shared::hideAlert,
|
||||
@@ -490,11 +582,22 @@ class SimplexService: Service() {
|
||||
)
|
||||
}
|
||||
|
||||
fun isBackgroundAllowed(): Boolean = isIgnoringBatteryOptimizations() && !isBackgroundRestricted()
|
||||
|
||||
fun isIgnoringBatteryOptimizations(): Boolean {
|
||||
val powerManager = androidAppContext.getSystemService(Application.POWER_SERVICE) as PowerManager
|
||||
return powerManager.isIgnoringBatteryOptimizations(androidAppContext.packageName)
|
||||
}
|
||||
|
||||
fun isBackgroundRestricted(): Boolean {
|
||||
return if (Build.VERSION.SDK_INT >= 28) {
|
||||
val activityService = androidAppContext.getSystemService(ACTIVITY_SERVICE) as ActivityManager
|
||||
activityService.isBackgroundRestricted
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun askAboutIgnoringBatteryOptimization() {
|
||||
Intent().apply {
|
||||
@SuppressLint("BatteryLife")
|
||||
@@ -505,5 +608,29 @@ class SimplexService: Service() {
|
||||
androidAppContext.startActivity(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun askToUnrestrictBackground() {
|
||||
Intent().apply {
|
||||
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
||||
data = Uri.parse("package:${androidAppContext.packageName}")
|
||||
// This flag is needed when you start a new activity from non-Activity context
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
try {
|
||||
androidAppContext.startActivity(this)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Log.e(TAG, e.stackTraceToString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun disableNotifications(mode: NotificationsMode, showOffAlert: Boolean) {
|
||||
if (showOffAlert) {
|
||||
showDisablingServiceNotice(mode)
|
||||
}
|
||||
ChatController.appPrefs.notificationsMode.set(NotificationsMode.OFF)
|
||||
StartReceiver.toggleReceiver(false)
|
||||
MessagesFetcherWorker.cancelAll()
|
||||
safeStopService()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,9 @@ import android.os.PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.*
|
||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
@@ -33,11 +35,10 @@ import androidx.webkit.WebViewAssetLoader
|
||||
import androidx.webkit.WebViewClientCompat
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.helpers.ProfileImage
|
||||
import chat.simplex.common.views.helpers.withApi
|
||||
import chat.simplex.common.model.ChatModel
|
||||
import chat.simplex.common.model.Contact
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.res.MR
|
||||
import com.google.accompanist.permissions.rememberMultiplePermissionsState
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
@@ -267,7 +268,9 @@ private fun ActiveCallOverlayLayout(
|
||||
when (call.peerMedia ?: call.localMedia) {
|
||||
CallMediaType.Video -> {
|
||||
CallInfoView(call, alignment = Alignment.Start)
|
||||
Spacer(Modifier.fillMaxHeight().weight(1f))
|
||||
Box(Modifier.fillMaxWidth().fillMaxHeight().weight(1f), contentAlignment = Alignment.BottomCenter) {
|
||||
DisabledBackgroundCallsButton()
|
||||
}
|
||||
Row(Modifier.fillMaxWidth().padding(horizontal = 6.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically) {
|
||||
ToggleAudioButton(call, toggleAudio)
|
||||
Spacer(Modifier.size(40.dp))
|
||||
@@ -293,7 +296,9 @@ private fun ActiveCallOverlayLayout(
|
||||
ProfileImage(size = 192.dp, image = call.contact.profile.image)
|
||||
CallInfoView(call, alignment = Alignment.CenterHorizontally)
|
||||
}
|
||||
Spacer(Modifier.fillMaxHeight().weight(1f))
|
||||
Box(Modifier.fillMaxWidth().fillMaxHeight().weight(1f), contentAlignment = Alignment.BottomCenter) {
|
||||
DisabledBackgroundCallsButton()
|
||||
}
|
||||
Box(Modifier.fillMaxWidth().padding(bottom = DEFAULT_BOTTOM_PADDING), contentAlignment = Alignment.CenterStart) {
|
||||
Box(Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
|
||||
IconButton(onClick = dismiss) {
|
||||
@@ -358,6 +363,31 @@ fun CallInfoView(call: Call, alignment: Alignment.Horizontal) {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DisabledBackgroundCallsButton() {
|
||||
var show by remember { mutableStateOf(!platform.androidIsBackgroundCallAllowed()) }
|
||||
if (show) {
|
||||
Row(
|
||||
Modifier
|
||||
.padding(bottom = 24.dp)
|
||||
.clickable {
|
||||
withBGApi {
|
||||
show = !platform.androidAskToAllowBackgroundCalls()
|
||||
}
|
||||
}
|
||||
.background(WarningOrange.copy(0.3f), RoundedCornerShape(50))
|
||||
.padding(start = 14.dp, top = 4.dp, end = 8.dp, bottom = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(stringResource(MR.strings.system_restricted_background_in_call_title), color = WarningOrange)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
IconButton(onClick = { show = false }, Modifier.size(24.dp)) {
|
||||
Icon(painterResource(MR.images.ic_close), null, tint = WarningOrange)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//@Composable
|
||||
//fun CallViewDebug(close: () -> Unit) {
|
||||
// val callCommand = remember { mutableStateOf<WCallCommand?>(null)}
|
||||
|
||||
@@ -9,6 +9,8 @@ interface PlatformInterface {
|
||||
fun androidChatStartedAfterBeingOff() {}
|
||||
fun androidChatStopped() {}
|
||||
fun androidChatInitializedAndStarted() {}
|
||||
fun androidIsBackgroundCallAllowed(): Boolean = true
|
||||
suspend fun androidAskToAllowBackgroundCalls(): Boolean = true
|
||||
}
|
||||
/**
|
||||
* Multiplatform project has separate directories per platform + common directory that contains directories per platform + common for all of them.
|
||||
|
||||
@@ -3,6 +3,7 @@ package chat.simplex.common.views.call
|
||||
import chat.simplex.common.model.ChatModel
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.views.helpers.withApi
|
||||
import chat.simplex.common.views.helpers.withBGApi
|
||||
import chat.simplex.common.views.usersettings.showInDevelopingAlert
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
@@ -28,6 +29,7 @@ class CallManager(val chatModel: ChatModel) {
|
||||
if (appPlatform.isDesktop) {
|
||||
return showInDevelopingAlert()
|
||||
}
|
||||
|
||||
val call = chatModel.activeCall.value
|
||||
if (call == null) {
|
||||
justAcceptIncomingCall(invitation = invitation)
|
||||
@@ -50,7 +52,7 @@ class CallManager(val chatModel: ChatModel) {
|
||||
contact = invitation.contact,
|
||||
callState = CallState.InvitationAccepted,
|
||||
localMedia = invitation.callType.media,
|
||||
sharedKey = invitation.sharedKey
|
||||
sharedKey = invitation.sharedKey,
|
||||
)
|
||||
showCallView.value = true
|
||||
val useRelay = controller.appPrefs.webrtcPolicyRelay.get()
|
||||
|
||||
@@ -239,11 +239,13 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: () -> Unit) {
|
||||
if (appPlatform.isDesktop) {
|
||||
return@out showInDevelopingAlert()
|
||||
}
|
||||
val cInfo = chat.chatInfo
|
||||
if (cInfo is ChatInfo.Direct) {
|
||||
chatModel.activeCall.value = Call(contact = cInfo.contact, callState = CallState.WaitCapabilities, localMedia = media)
|
||||
chatModel.showCallView.value = true
|
||||
chatModel.callCommand.value = WCallCommand.Capabilities
|
||||
withBGApi {
|
||||
val cInfo = chat.chatInfo
|
||||
if (cInfo is ChatInfo.Direct) {
|
||||
chatModel.activeCall.value = Call(contact = cInfo.contact, callState = CallState.WaitCapabilities, localMedia = media)
|
||||
chatModel.showCallView.value = true
|
||||
chatModel.callCommand.value = WCallCommand.Capabilities
|
||||
}
|
||||
}
|
||||
},
|
||||
acceptCall = { contact ->
|
||||
|
||||
@@ -43,6 +43,7 @@ fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit, drawerSt
|
||||
profile = user.profile,
|
||||
stopped,
|
||||
chatModel.chatDbEncrypted.value == true,
|
||||
remember { chatModel.controller.appPrefs.notificationsMode.state },
|
||||
user.displayName,
|
||||
setPerformLA = setPerformLA,
|
||||
showModal = { modalView -> { ModalManager.start.showModal { modalView(chatModel) } } },
|
||||
@@ -114,6 +115,7 @@ fun SettingsLayout(
|
||||
profile: LocalProfile,
|
||||
stopped: Boolean,
|
||||
encrypted: Boolean,
|
||||
notificationsMode: State<NotificationsMode>,
|
||||
userDisplayName: String,
|
||||
setPerformLA: (Boolean) -> Unit,
|
||||
showModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit),
|
||||
@@ -155,7 +157,7 @@ fun SettingsLayout(
|
||||
SectionDividerSpaced()
|
||||
|
||||
SectionView(stringResource(MR.strings.settings_section_title_settings)) {
|
||||
SettingsActionItem(painterResource(MR.images.ic_bolt), stringResource(MR.strings.notifications), showSettingsModal { NotificationsSettingsView(it) }, disabled = stopped, extraPadding = true)
|
||||
SettingsActionItem(painterResource(if (notificationsMode.value == NotificationsMode.OFF) MR.images.ic_bolt_off else MR.images.ic_bolt), stringResource(MR.strings.notifications), showSettingsModal { NotificationsSettingsView(it) }, disabled = stopped, extraPadding = true)
|
||||
SettingsActionItem(painterResource(MR.images.ic_wifi_tethering), stringResource(MR.strings.network_and_servers), showSettingsModal { NetworkAndServersView(it, showModal, showSettingsModal, showCustomModal) }, disabled = stopped, extraPadding = true)
|
||||
SettingsActionItem(painterResource(MR.images.ic_videocam), stringResource(MR.strings.settings_audio_video_calls), showSettingsModal { CallSettingsView(it, showModal) }, disabled = stopped, extraPadding = true)
|
||||
SettingsActionItem(painterResource(MR.images.ic_lock), stringResource(MR.strings.privacy_and_security), showSettingsModal { PrivacySettingsView(it, showSettingsModal, setPerformLA) }, disabled = stopped, extraPadding = true)
|
||||
@@ -464,6 +466,7 @@ fun PreviewSettingsLayout() {
|
||||
profile = LocalProfile.sampleData,
|
||||
stopped = false,
|
||||
encrypted = false,
|
||||
notificationsMode = remember { mutableStateOf(NotificationsMode.OFF) },
|
||||
userDisplayName = "Alice",
|
||||
setPerformLA = { _ -> },
|
||||
showModal = { {} },
|
||||
|
||||
@@ -132,11 +132,19 @@
|
||||
<string name="service_notifications_disabled">Instant notifications are disabled!</string>
|
||||
<string name="to_preserve_privacy_simplex_has_background_service_instead_of_push_notifications_it_uses_a_few_pc_battery"><![CDATA[To preserve your privacy, instead of push notifications the app has a <b>SimpleX background service</b> – it uses a few percent of the battery per day.]]></string>
|
||||
<string name="it_can_disabled_via_settings_notifications_still_shown"><![CDATA[<b>It can be disabled via settings</b> – notifications will still be shown while the app is running.]]></string>
|
||||
<string name="turn_off_battery_optimization"><![CDATA[In order to use it, please <b>disable battery optimization</b> for SimpleX in the next dialog. Otherwise, the notifications will be disabled.]]></string>
|
||||
<string name="turn_off_battery_optimization"><![CDATA[To use it, please <b>allow SimpleX to run in background</b> in the next dialog. Otherwise, the notifications will be disabled.]]></string>
|
||||
<string name="turning_off_service_and_periodic">Battery optimization is active, turning off background service and periodic requests for new messages. You can re-enable them via settings.</string>
|
||||
<string name="periodic_notifications">Periodic notifications</string>
|
||||
<string name="periodic_notifications_disabled">Periodic notifications are disabled!</string>
|
||||
<string name="periodic_notifications_desc">The app fetches new messages periodically — it uses a few percent of the battery per day. The app doesn\'t use push notifications — data from your device is not sent to the servers.</string>
|
||||
<string name="turn_off_battery_optimization_button">Allow</string>
|
||||
<string name="turn_off_system_restriction_button">Open app settings</string>
|
||||
<string name="disable_notifications_button">Disable notifications</string>
|
||||
<string name="system_restricted_background_desc"><![CDATA[SimpleX can\'t run in background. You will receive the notifications only when the app is running.]]></string>
|
||||
<string name="system_restricted_background_warn"><![CDATA[To enable notifications, please choose <b>App battery usage</b> / <b>Unrestricted</b> in the app settings.]]></string>
|
||||
<string name="system_restricted_background_in_call_title">No background calls</string>
|
||||
<string name="system_restricted_background_in_call_desc"><![CDATA[The app may be closed after 1 minute in background.]]></string>
|
||||
<string name="system_restricted_background_in_call_warn"><![CDATA[To make calls in background, please choose <b>App battery usage</b> / <b>Unrestricted</b> in the app settings]]>.</string>
|
||||
<string name="enter_passphrase_notification_title">Passphrase is needed</string>
|
||||
<string name="enter_passphrase_notification_desc">To receive notifications, please, enter the database passphrase</string>
|
||||
<string name="database_initialization_error_title">Can\'t initialize the database</string>
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="24"
|
||||
viewBox="0 -960 960 960"
|
||||
width="24"
|
||||
version="1.1"
|
||||
id="svg864"
|
||||
sodipodi:docname="ic_bolt_off.svg"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs868">
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath1042">
|
||||
<g
|
||||
id="g1046">
|
||||
<g
|
||||
inkscape:label="Clip"
|
||||
id="use1044"
|
||||
clip-path="url(#clipPath1042)">
|
||||
<g
|
||||
id="g1054" />
|
||||
</g>
|
||||
</g>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="namedview866"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#ffffff"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="1"
|
||||
inkscape:deskcolor="#505050"
|
||||
showgrid="false"
|
||||
inkscape:zoom="5.0911688"
|
||||
inkscape:cx="-25.239784"
|
||||
inkscape:cy="-0.49104638"
|
||||
inkscape:window-width="1440"
|
||||
inkscape:window-height="856"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg864" />
|
||||
<path
|
||||
id="path1067"
|
||||
d="M 367.38281 -591.32812 L 326.67969 -632.65625 L 279.53125 -564.57031 L 166.67969 -401.5625 C 159.67969 -391.89551 158.77538 -382.00292 163.98438 -371.83594 C 169.19336 -361.66896 177.59871 -356.5625 189.17969 -356.5625 L 333.67188 -356.5625 L 314.6875 -222.8125 L 295.70312 -89.0625 C 294.37014 -80.895816 295.62013 -73.294223 299.45312 -66.210938 C 303.28612 -59.127653 308.83888 -53.987005 316.17188 -50.820312 C 323.50486 -47.320319 331.02496 -46.494079 338.71094 -48.359375 C 346.39692 -50.224071 353.04388 -54.490464 358.67188 -61.09375 L 482.53906 -209.72656 L 601.99219 -353.125 L 561.75781 -393.98438 L 552.69531 -383.08594 L 458.20312 -269.57031 L 363.67188 -156.09375 L 381.67969 -285.07812 L 399.6875 -414.0625 L 244.6875 -414.0625 L 330.50781 -538.08594 L 367.38281 -591.32812 z " />
|
||||
<path
|
||||
id="path1128"
|
||||
d="M 523.94531 -904.84375 C 516.11233 -902.34376 509.86231 -897.58593 505.19531 -890.58594 L 392.34375 -727.57812 L 371.91406 -698.04688 L 413.04688 -657.26562 L 416.36719 -662.07031 L 502.1875 -786.09375 L 483.94531 -641.32812 L 477.85156 -593.00781 L 575.15625 -496.5625 L 647.1875 -496.5625 L 614.60938 -457.46094 L 655.27344 -417.10938 L 730.19531 -507.07031 C 737.86229 -516.73729 739.14756 -526.97952 734.10156 -537.8125 C 729.05558 -548.64547 720.4441 -554.0625 708.20312 -554.0625 L 530.70312 -554.0625 L 550.42969 -709.84375 L 570.19531 -865.58594 C 571.19531 -873.35892 569.42186 -880.88407 564.92188 -888.16406 C 560.42188 -895.44405 554.52048 -900.7295 547.1875 -904.0625 C 539.52052 -907.06249 531.77829 -907.34375 523.94531 -904.84375 z " />
|
||||
<path
|
||||
d="m 98.757614,-815.50208 c -6,-5.6667 -9,-12.4944 -9,-20.483 0,-7.9894 3,-14.995 9,-21.017 5.666666,-5.6667 12.499996,-8.5 20.499996,-8.5 8,0 14.87033,2.8703 20.611,8.611 l 703.77795,703.77797 c 5.7407,5.74067 8.611,12.52767 8.611,20.361 0,7.83333 -2.8333,14.58333 -8.5,20.25 -5.6666,6 -12.58329,8.91667 -20.74999,8.75 -8.1666,-0.16666 -15.0646,-3.06466 -20.694,-8.694 C 515.62153,-363.72015 368.7575,-545.07432 98.757614,-815.50208 Z"
|
||||
id="path114"
|
||||
sodipodi:nodetypes="cscsssscscc" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
Reference in New Issue
Block a user