android: battery saving instructions screen as in the Final 2026 mockup
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9c96c36fc6
commit
8fbebed140
5 files changed
+296
-3
No files matched your search
@@ -79,6 +79,9 @@
|
|||||||
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
|
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name=".ui.BatteryActivity"
|
||||||
|
android:exported="false" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.VpnProfileControlActivity"
|
android:name=".ui.VpnProfileControlActivity"
|
||||||
android:theme="@style/TransparentActivity"
|
android:theme="@style/TransparentActivity"
|
||||||
|
|||||||
@@ -0,0 +1,235 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2026 SHX VPN
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.strongswan.android.ui
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.PowerManager
|
||||||
|
import android.provider.Settings
|
||||||
|
import androidx.activity.compose.setContent
|
||||||
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.LifecycleEventObserver
|
||||||
|
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||||
|
import org.strongswan.android.R
|
||||||
|
import org.strongswan.android.ui.compose.SecondaryScaffold
|
||||||
|
import org.strongswan.android.ui.compose.design.ShxButton
|
||||||
|
import org.strongswan.android.ui.compose.design.ShxButtonStyle
|
||||||
|
import org.strongswan.android.ui.compose.design.ShxChip
|
||||||
|
import org.strongswan.android.ui.compose.design.ShxIcons
|
||||||
|
import org.strongswan.android.ui.compose.theme.LocalShxColors
|
||||||
|
import org.strongswan.android.ui.compose.theme.ShxTheme
|
||||||
|
import org.strongswan.android.ui.compose.theme.ShxType
|
||||||
|
|
||||||
|
/** «Without battery saving» (F_Battery): the system exemption, then the vendor switches. */
|
||||||
|
class BatteryActivity : AppCompatActivity()
|
||||||
|
{
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?)
|
||||||
|
{
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
enableEdgeToEdge()
|
||||||
|
setContent {
|
||||||
|
ShxTheme {
|
||||||
|
BatteryScreen(onBack = { finish() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* phone makers with their own background killers; the steps differ per brand */
|
||||||
|
private enum class Brand(val label: Int, val steps: List<Int>)
|
||||||
|
{
|
||||||
|
XIAOMI(R.string.battery_brand_xiaomi, listOf(R.string.battery_step_open, R.string.battery_xiaomi_2, R.string.battery_xiaomi_3)),
|
||||||
|
SAMSUNG(R.string.battery_brand_samsung, listOf(R.string.battery_step_open, R.string.battery_samsung_2, R.string.battery_samsung_3)),
|
||||||
|
HUAWEI(R.string.battery_brand_huawei, listOf(R.string.battery_step_open, R.string.battery_huawei_2, R.string.battery_huawei_3)),
|
||||||
|
OPPO(R.string.battery_brand_oppo, listOf(R.string.battery_step_open, R.string.battery_oppo_2, R.string.battery_oppo_3)),
|
||||||
|
OTHER(R.string.battery_brand_other, listOf(R.string.battery_step_open, R.string.battery_other_2)),
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun detectBrand(): Brand
|
||||||
|
{
|
||||||
|
val m = (Build.MANUFACTURER + " " + Build.BRAND).lowercase()
|
||||||
|
return when
|
||||||
|
{
|
||||||
|
listOf("xiaomi", "redmi", "poco").any { it in m } -> Brand.XIAOMI
|
||||||
|
"samsung" in m -> Brand.SAMSUNG
|
||||||
|
"huawei" in m || "honor" in m -> Brand.HUAWEI
|
||||||
|
listOf("oppo", "realme", "oneplus").any { it in m } -> Brand.OPPO
|
||||||
|
else -> Brand.OTHER
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isExempt(context: Context): Boolean =
|
||||||
|
(context.getSystemService(Context.POWER_SERVICE) as? PowerManager)?.isIgnoringBatteryOptimizations(context.packageName) ?: true
|
||||||
|
|
||||||
|
@SuppressLint("BatteryLife")
|
||||||
|
@Composable
|
||||||
|
private fun BatteryScreen(onBack: () -> Unit)
|
||||||
|
{
|
||||||
|
val c = LocalShxColors.current
|
||||||
|
val context = LocalContext.current
|
||||||
|
var checks by remember { mutableIntStateOf(0) }
|
||||||
|
val owner = LocalLifecycleOwner.current
|
||||||
|
DisposableEffect(owner) {
|
||||||
|
val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) checks++ }
|
||||||
|
owner.lifecycle.addObserver(observer)
|
||||||
|
onDispose { owner.lifecycle.removeObserver(observer) }
|
||||||
|
}
|
||||||
|
val exempt = remember(checks) { isExempt(context) }
|
||||||
|
val detected = remember { detectBrand() }
|
||||||
|
var brand by rememberSaveable { mutableIntStateOf(detected.ordinal) }
|
||||||
|
fun open(intent: Intent) = runCatching { context.startActivity(intent) }.isSuccess
|
||||||
|
|
||||||
|
SecondaryScaffold(title = stringResource(R.string.battery_title), onBack = onBack) { padding ->
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
|
modifier = Modifier.fillMaxSize().padding(padding).verticalScroll(rememberScrollState())
|
||||||
|
.padding(start = 16.dp, end = 16.dp, bottom = 28.dp),
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.battery_intro), style = ShxType.note.copy(fontSize = 15.sp, lineHeight = 22.sp, fontWeight = FontWeight.Normal),
|
||||||
|
color = c.textTonal, modifier = Modifier.padding(horizontal = 4.dp),
|
||||||
|
)
|
||||||
|
StepCard(1, stringResource(R.string.battery_step1_title), highlight = !exempt)
|
||||||
|
{
|
||||||
|
Text(stringResource(R.string.battery_step1_text), style = ShxType.note.copy(lineHeight = 19.sp, fontWeight = FontWeight.Normal), color = c.textSoft)
|
||||||
|
if (exempt)
|
||||||
|
{
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp))
|
||||||
|
{
|
||||||
|
Box(contentAlignment = Alignment.Center, modifier = Modifier.size(24.dp).clip(CircleShape).background(c.success))
|
||||||
|
{
|
||||||
|
Icon(ShxIcons.Check, contentDescription = null, tint = Color.White, modifier = Modifier.size(14.dp))
|
||||||
|
}
|
||||||
|
Text(stringResource(R.string.battery_step1_done), style = ShxType.body.copy(fontWeight = FontWeight.Bold), color = c.onSuccess)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ShxButton(
|
||||||
|
stringResource(R.string.perm_allow),
|
||||||
|
{ open(Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, Uri.parse("package:" + context.packageName))) },
|
||||||
|
height = 48.dp, modifier = Modifier.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StepCard(2, stringResource(R.string.battery_step2_title), highlight = false)
|
||||||
|
{
|
||||||
|
FlowRow(horizontalArrangement = Arrangement.spacedBy(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp))
|
||||||
|
{
|
||||||
|
Brand.entries.forEach { b -> ShxChip(stringResource(b.label), brand == b.ordinal, { brand = b.ordinal }) }
|
||||||
|
}
|
||||||
|
if (detected != Brand.OTHER)
|
||||||
|
{
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.battery_detected, Build.MANUFACTURER.replaceFirstChar { it.uppercase() }),
|
||||||
|
style = ShxType.meta.copy(fontWeight = FontWeight.Bold), color = c.accentText,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Column(verticalArrangement = Arrangement.spacedBy(8.dp))
|
||||||
|
{
|
||||||
|
Brand.entries[brand].steps.forEachIndexed { i, step ->
|
||||||
|
Row(horizontalArrangement = Arrangement.spacedBy(10.dp))
|
||||||
|
{
|
||||||
|
Box(contentAlignment = Alignment.Center, modifier = Modifier.size(22.dp).clip(CircleShape).background(c.tile))
|
||||||
|
{
|
||||||
|
Text("${i + 1}", style = ShxType.meta.copy(fontWeight = FontWeight.ExtraBold), color = c.onAccentContainer)
|
||||||
|
}
|
||||||
|
Text(stringResource(step), style = ShxType.body.copy(fontWeight = FontWeight.Normal, lineHeight = 20.sp), color = c.text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ShxButton(
|
||||||
|
stringResource(R.string.battery_open_app_settings),
|
||||||
|
{ open(Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.fromParts("package", context.packageName, null))) },
|
||||||
|
style = ShxButtonStyle.OUTLINE, height = 48.dp, modifier = Modifier.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(Modifier.height(4.dp))
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||||
|
modifier = Modifier.fillMaxWidth().clip(RoundedCornerShape(18.dp))
|
||||||
|
.background(if (exempt) c.successSoft else c.warningContainer).padding(horizontal = 14.dp, vertical = 12.dp),
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Icon(if (exempt) ShxIcons.Check else ShxIcons.Alert, contentDescription = null, tint = if (exempt) c.onSuccess else c.onWarningContainer, modifier = Modifier.size(20.dp))
|
||||||
|
Text(
|
||||||
|
stringResource(if (exempt) R.string.battery_ok else R.string.battery_not_done),
|
||||||
|
style = ShxType.note.copy(lineHeight = 19.sp, fontWeight = FontWeight.Normal), color = if (exempt) c.onSuccess else c.onWarningContainer,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ShxButton(stringResource(R.string.battery_check_again), { checks++ }, style = ShxButtonStyle.SOFT, modifier = Modifier.fillMaxWidth())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun StepCard(n: Int, title: String, highlight: Boolean, content: @Composable () -> Unit)
|
||||||
|
{
|
||||||
|
val c = LocalShxColors.current
|
||||||
|
val shape = RoundedCornerShape(24.dp)
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||||
|
modifier = Modifier.fillMaxWidth().clip(shape).background(c.card).border(1.dp, if (highlight) c.accentBorder else c.cardBorder, shape).padding(16.dp),
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(10.dp))
|
||||||
|
{
|
||||||
|
Box(contentAlignment = Alignment.Center, modifier = Modifier.size(28.dp).clip(CircleShape).background(c.accent))
|
||||||
|
{
|
||||||
|
Text("$n", style = ShxType.chip.copy(fontWeight = FontWeight.ExtraBold), color = Color.White)
|
||||||
|
}
|
||||||
|
Text(title, style = ShxType.rowTitle.copy(fontWeight = FontWeight.ExtraBold), color = c.text)
|
||||||
|
}
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-3
@@ -133,7 +133,9 @@ fun PermissionsList(modifier: Modifier = Modifier)
|
|||||||
}
|
}
|
||||||
PermissionCard(
|
PermissionCard(
|
||||||
ShxIcons.Phone, stringResource(R.string.perm_battery), PermissionLevel.IMPORTANT, stringResource(R.string.perm_battery_why), status.battery,
|
ShxIcons.Phone, stringResource(R.string.perm_battery), PermissionLevel.IMPORTANT, stringResource(R.string.perm_battery_why), status.battery,
|
||||||
) { open(Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, Uri.parse("package:" + context.packageName))) }
|
/* the vendor switches (step 2 of F_Battery) matter even when Android allows it */
|
||||||
|
openWhenDone = true,
|
||||||
|
) { open(Intent(context, org.strongswan.android.ui.BatteryActivity::class.java)) }
|
||||||
PermissionCard(
|
PermissionCard(
|
||||||
ShxIcons.Stats, stringResource(R.string.perm_usage), PermissionLevel.OPTIONAL, stringResource(R.string.perm_usage_why), status.usage,
|
ShxIcons.Stats, stringResource(R.string.perm_usage), PermissionLevel.OPTIONAL, stringResource(R.string.perm_usage_why), status.usage,
|
||||||
)
|
)
|
||||||
@@ -150,7 +152,7 @@ fun PermissionsList(modifier: Modifier = Modifier)
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun PermissionCard(icon: ImageVector, title: String, level: PermissionLevel, why: String, done: Boolean, onAllow: () -> Unit)
|
private fun PermissionCard(icon: ImageVector, title: String, level: PermissionLevel, why: String, done: Boolean, openWhenDone: Boolean = false, onAllow: () -> Unit)
|
||||||
{
|
{
|
||||||
val c = LocalShxColors.current
|
val c = LocalShxColors.current
|
||||||
val (tagBg, tagFg, tag) = when (level)
|
val (tagBg, tagFg, tag) = when (level)
|
||||||
@@ -164,7 +166,8 @@ private fun PermissionCard(icon: ImageVector, title: String, level: PermissionLe
|
|||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
modifier = Modifier.fillMaxWidth().clip(shape).background(c.card).border(1.dp, border, shape).padding(14.dp),
|
modifier = Modifier.fillMaxWidth().clip(shape).background(c.card).border(1.dp, border, shape)
|
||||||
|
.then(if (done && openWhenDone) Modifier.clickable(role = Role.Button, onClick = onAllow) else Modifier).padding(14.dp),
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
Box(contentAlignment = Alignment.Center, modifier = Modifier.size(40.dp).clip(RoundedCornerShape(14.dp)).background(if (done) c.successContainer else c.tile))
|
Box(contentAlignment = Alignment.Center, modifier = Modifier.size(40.dp).clip(RoundedCornerShape(14.dp)).background(if (done) c.successContainer else c.tile))
|
||||||
|
|||||||
@@ -1095,4 +1095,30 @@
|
|||||||
<string name="cert_expired_text">«%1$s» не подключается с %2$s. Попросите у администратора новый профиль и добавьте его.</string>
|
<string name="cert_expired_text">«%1$s» не подключается с %2$s. Попросите у администратора новый профиль и добавьте его.</string>
|
||||||
<string name="cert_update_now">Обновить сейчас</string>
|
<string name="cert_update_now">Обновить сейчас</string>
|
||||||
<string name="cert_later">Позже</string>
|
<string name="cert_later">Позже</string>
|
||||||
|
<string name="battery_title">Без экономии батареи</string>
|
||||||
|
<string name="battery_intro">VPN почти не тратит заряд, но Android этого не знает и усыпляет его в фоне. Два шага:</string>
|
||||||
|
<string name="battery_step1_title">Системное разрешение</string>
|
||||||
|
<string name="battery_step1_text">Появится окно «Разрешить работу в фоне?» — нажмите «Разрешить».</string>
|
||||||
|
<string name="battery_step1_done">Разрешено</string>
|
||||||
|
<string name="battery_step2_title">Настройка телефона</string>
|
||||||
|
<string name="battery_detected">Определили: %1$s</string>
|
||||||
|
<string name="battery_brand_xiaomi">Xiaomi</string>
|
||||||
|
<string name="battery_brand_samsung">Samsung</string>
|
||||||
|
<string name="battery_brand_huawei">Honor / Huawei</string>
|
||||||
|
<string name="battery_brand_oppo">Realme / OPPO</string>
|
||||||
|
<string name="battery_brand_other">Другой</string>
|
||||||
|
<string name="battery_step_open">Откройте настройки приложения кнопкой ниже</string>
|
||||||
|
<string name="battery_xiaomi_2">Экономия заряда → «Нет ограничений»</string>
|
||||||
|
<string name="battery_xiaomi_3">Включите «Автозапуск»</string>
|
||||||
|
<string name="battery_samsung_2">Батарея → «Без ограничений»</string>
|
||||||
|
<string name="battery_samsung_3">Обслуживание устройства → Батарея: уберите SHX VPN из «Спящих приложений»</string>
|
||||||
|
<string name="battery_huawei_2">Батарея → Запуск приложений: отключите «Автоматическое управление»</string>
|
||||||
|
<string name="battery_huawei_3">Включите «Автозапуск», «Косвенный запуск» и «Работа в фоне»</string>
|
||||||
|
<string name="battery_oppo_2">Использование батареи → «Разрешить работу в фоне»</string>
|
||||||
|
<string name="battery_oppo_3">Включите «Автозапуск»</string>
|
||||||
|
<string name="battery_other_2">Батарея → «Без ограничений» или «Не оптимизировать»</string>
|
||||||
|
<string name="battery_open_app_settings">Открыть настройки приложения</string>
|
||||||
|
<string name="battery_not_done">Шаг 1 ещё не сделан — VPN может отключаться в фоне</string>
|
||||||
|
<string name="battery_ok">Готово: Android не будет усыплять VPN</string>
|
||||||
|
<string name="battery_check_again">Проверить снова</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1081,4 +1081,30 @@
|
|||||||
<string name="cert_expired_text">«%1$s» no longer connects since %2$s. Ask your admin for a new profile and add it.</string>
|
<string name="cert_expired_text">«%1$s» no longer connects since %2$s. Ask your admin for a new profile and add it.</string>
|
||||||
<string name="cert_update_now">Update now</string>
|
<string name="cert_update_now">Update now</string>
|
||||||
<string name="cert_later">Later</string>
|
<string name="cert_later">Later</string>
|
||||||
|
<string name="battery_title">Without battery saving</string>
|
||||||
|
<string name="battery_intro">The VPN barely uses any charge, but Android does not know that and puts it to sleep in the background. Two steps:</string>
|
||||||
|
<string name="battery_step1_title">System permission</string>
|
||||||
|
<string name="battery_step1_text">A «Let app always run in background?» window appears — tap «Allow».</string>
|
||||||
|
<string name="battery_step1_done">Allowed</string>
|
||||||
|
<string name="battery_step2_title">Phone settings</string>
|
||||||
|
<string name="battery_detected">Detected: %1$s</string>
|
||||||
|
<string name="battery_brand_xiaomi">Xiaomi</string>
|
||||||
|
<string name="battery_brand_samsung">Samsung</string>
|
||||||
|
<string name="battery_brand_huawei">Honor / Huawei</string>
|
||||||
|
<string name="battery_brand_oppo">Realme / OPPO</string>
|
||||||
|
<string name="battery_brand_other">Other</string>
|
||||||
|
<string name="battery_step_open">Open the app settings with the button below</string>
|
||||||
|
<string name="battery_xiaomi_2">Battery saver → «No restrictions»</string>
|
||||||
|
<string name="battery_xiaomi_3">Turn on «Autostart»</string>
|
||||||
|
<string name="battery_samsung_2">Battery → «Unrestricted»</string>
|
||||||
|
<string name="battery_samsung_3">Device care → Battery: remove SHX VPN from «Sleeping apps»</string>
|
||||||
|
<string name="battery_huawei_2">Battery → App launch: turn off «Manage automatically»</string>
|
||||||
|
<string name="battery_huawei_3">Turn on «Auto-launch», «Secondary launch» and «Run in background»</string>
|
||||||
|
<string name="battery_oppo_2">Battery usage → «Allow background activity»</string>
|
||||||
|
<string name="battery_oppo_3">Turn on «Auto launch»</string>
|
||||||
|
<string name="battery_other_2">Battery → «Unrestricted» or «Not optimized»</string>
|
||||||
|
<string name="battery_open_app_settings">Open app settings</string>
|
||||||
|
<string name="battery_not_done">Step 1 is not done yet — the VPN may stop in the background</string>
|
||||||
|
<string name="battery_ok">Done: Android will not put the VPN to sleep</string>
|
||||||
|
<string name="battery_check_again">Check again</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in new issue
Block a user