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:
Stanislav Dmitrenko
2023-08-09 21:56:53 +03:00
committed by GitHub
parent 38dc14f041
commit 9543af4784
9 changed files with 282 additions and 35 deletions

View File

@@ -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)}

View File

@@ -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.

View File

@@ -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()

View File

@@ -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 ->

View File

@@ -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 = { {} },

View File

@@ -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>

View File

@@ -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