Remove Request uninstall feature

Scroll between pages in Network stats viewer
This commit is contained in:
BinTianqi
2025-03-15 17:54:12 +08:00
parent 3c0696faa3
commit 52a29331be
9 changed files with 158 additions and 138 deletions

View File

@@ -67,7 +67,7 @@ fun SettingsScreen(onNavigateUp: () -> Unit, onNavigate: (Any) -> Unit) {
FunctionItem(title = R.string.options, icon = R.drawable.tune_fill0) { onNavigate(SettingsOptions) } FunctionItem(title = R.string.options, icon = R.drawable.tune_fill0) { onNavigate(SettingsOptions) }
FunctionItem(title = R.string.appearance, icon = R.drawable.format_paint_fill0) { onNavigate(Appearance) } FunctionItem(title = R.string.appearance, icon = R.drawable.format_paint_fill0) { onNavigate(Appearance) }
FunctionItem(R.string.app_lock, icon = R.drawable.lock_fill0) { onNavigate(AppLockSettings) } FunctionItem(R.string.app_lock, icon = R.drawable.lock_fill0) { onNavigate(AppLockSettings) }
FunctionItem(title = R.string.api, icon = R.drawable.apps_fill0) { onNavigate(ApiSettings) } FunctionItem(title = R.string.api, icon = R.drawable.code_fill0) { onNavigate(ApiSettings) }
FunctionItem(R.string.notifications, icon = R.drawable.notifications_fill0) { onNavigate(Notifications) } FunctionItem(R.string.notifications, icon = R.drawable.notifications_fill0) { onNavigate(Notifications) }
FunctionItem(title = R.string.export_logs, icon = R.drawable.description_fill0) { FunctionItem(title = R.string.export_logs, icon = R.drawable.description_fill0) {
val time = SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).format(Date(System.currentTimeMillis())) val time = SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).format(Date(System.currentTimeMillis()))

View File

@@ -100,6 +100,9 @@ val Long.humanReadableDate: String
val Long.humanReadableDateTime: String val Long.humanReadableDateTime: String
get() = SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault()).format(Date(this)) get() = SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault()).format(Date(this))
fun formatDate(pattern: String, value: Long): String
= SimpleDateFormat(pattern, Locale.getDefault()).format(Date(value))
fun Context.showOperationResultToast(success: Boolean) { fun Context.showOperationResultToast(success: Boolean) {
Toast.makeText(this, if(success) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show() Toast.makeText(this, if(success) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
} }

View File

@@ -90,6 +90,7 @@ import com.bintianqi.owndroid.HorizontalPadding
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.showOperationResultToast import com.bintianqi.owndroid.showOperationResultToast
import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.Animations
import com.bintianqi.owndroid.ui.ErrorDialog
import com.bintianqi.owndroid.ui.FunctionItem import com.bintianqi.owndroid.ui.FunctionItem
import com.bintianqi.owndroid.ui.ListItem import com.bintianqi.owndroid.ui.ListItem
import com.bintianqi.owndroid.ui.NavIcon import com.bintianqi.owndroid.ui.NavIcon
@@ -157,7 +158,6 @@ fun ApplicationsScreen(onNavigateUp: () -> Unit) {
composable<PermittedAccessibilityServices> { PermittedAccessibilityServicesScreen(pkgName) } composable<PermittedAccessibilityServices> { PermittedAccessibilityServicesScreen(pkgName) }
composable<PermittedInputMethods> { PermittedInputMethodsScreen(pkgName) } composable<PermittedInputMethods> { PermittedInputMethodsScreen(pkgName) }
composable<KeepUninstalledPackages> { KeepUninstalledPackagesScreen(pkgName) } composable<KeepUninstalledPackages> { KeepUninstalledPackagesScreen(pkgName) }
composable<UninstallPackage> { UninstallPackageScreen(pkgName) }
} }
} }
} }
@@ -166,8 +166,9 @@ fun ApplicationsScreen(onNavigateUp: () -> Unit) {
@Composable @Composable
private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) { private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
/** 1:Enable system app, 2:Clear app storage, 3:Set default dialer, 4:App control, 5:Install existing app */ /** 1:Enable system app, 2:Clear app storage, 3:Set default dialer, 4:App control, 5:Install existing app, 6:Uninstall confirmation */
var dialogStatus by remember { mutableIntStateOf(0) } var dialogStatus by remember { mutableIntStateOf(0) }
var errorMessage by remember { mutableStateOf("") }
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
val receiver = context.getReceiver() val receiver = context.getReceiver()
@@ -271,8 +272,12 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
Toast.makeText(context, R.string.choose_apk_file, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.choose_apk_file, Toast.LENGTH_SHORT).show()
chooseApks.launch(APK_MIME) chooseApks.launch(APK_MIME)
} }
if(VERSION.SDK_INT >= 28) FunctionItem(R.string.install_existing_app, icon = R.drawable.install_mobile_fill0) { dialogStatus = 5 } if(VERSION.SDK_INT >= 28) FunctionItem(R.string.install_existing_app, icon = R.drawable.install_mobile_fill0) {
FunctionItem(title = R.string.uninstall_app, icon = R.drawable.delete_fill0) { onNavigate(UninstallPackage) } if(pkgName.isNotBlank()) dialogStatus = 5
}
FunctionItem(title = R.string.uninstall_app, icon = R.drawable.delete_fill0) {
if(pkgName.isNotBlank()) dialogStatus = 6
}
if(VERSION.SDK_INT >= 34 && (deviceOwner || dpm.isOrgProfile(receiver))) { if(VERSION.SDK_INT >= 34 && (deviceOwner || dpm.isOrgProfile(receiver))) {
FunctionItem(title = R.string.set_default_dialer, icon = R.drawable.call_fill0) { FunctionItem(title = R.string.set_default_dialer, icon = R.drawable.call_fill0) {
if(pkgName != "") dialogStatus = 3 if(pkgName != "") dialogStatus = 3
@@ -428,6 +433,27 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
}, },
onDismissRequest = { dialogStatus = 0 } onDismissRequest = { dialogStatus = 0 }
) )
if(dialogStatus == 6) AlertDialog(
title = { Text(stringResource(R.string.uninstall)) },
text = { Text(pkgName) },
onDismissRequest = { dialogStatus = 0 },
confirmButton = {
TextButton(
onClick = { uninstallPackage(context, pkgName) { errorMessage = it } },
colors = ButtonDefaults.textButtonColors(contentColor = colorScheme.error)
) {
Text(stringResource(R.string.confirm))
}
},
dismissButton = {
TextButton({
dialogStatus = 0
}) {
Text(stringResource(R.string.cancel))
}
}
)
ErrorDialog(errorMessage) { errorMessage = "" }
LaunchedEffect(dialogStatus) { focusMgr.clearFocus() } LaunchedEffect(dialogStatus) { focusMgr.clearFocus() }
} }
@@ -706,7 +732,7 @@ private fun CrossProfileWidgetProvidersScreen(pkgName: String) {
@RequiresApi(34) @RequiresApi(34)
@Composable @Composable
private fun CredentialManagerPolicyScreen(pkgName: String) { // TODO: rename "manage" to "manager" private fun CredentialManagerPolicyScreen(pkgName: String) {
val context = LocalContext.current val context = LocalContext.current
val dpm = context.getDPM() val dpm = context.getDPM()
var policy: PackagePolicy? var policy: PackagePolicy?
@@ -940,74 +966,34 @@ private fun KeepUninstalledPackagesScreen(pkgName: String) {
} }
} }
@Serializable private object UninstallPackage private fun uninstallPackage(context: Context, packageName: String, onError: (String) -> Unit) {
val receiver = object : BroadcastReceiver() {
@Composable override fun onReceive(context: Context, intent: Intent) {
private fun UninstallPackageScreen(pkgName: String) { val statusExtra = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 999)
val context = LocalContext.current if(statusExtra == PackageInstaller.STATUS_PENDING_USER_ACTION) {
var errorMessage by remember { mutableStateOf<String?>(null) } @SuppressWarnings("UnsafeIntentLaunch")
Column(modifier = Modifier.fillMaxSize().padding(horizontal = HorizontalPadding).verticalScroll(rememberScrollState())) { context.startActivity(intent.getParcelableExtra(Intent.EXTRA_INTENT) as Intent?)
Spacer(Modifier.padding(vertical = 10.dp)) } else {
Text(text = stringResource(R.string.uninstall_app), style = typography.headlineLarge) context.unregisterReceiver(this)
Spacer(Modifier.padding(vertical = 5.dp)) if(statusExtra == PackageInstaller.STATUS_SUCCESS) {
Column(modifier = Modifier.fillMaxWidth()) { context.showOperationResultToast(true)
Button( } else {
onClick = { onError(parsePackageInstallerMessage(context, intent))
val receiver = object : BroadcastReceiver() { }
override fun onReceive(context: Context, intent: Intent) {
val statusExtra = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 999)
if(statusExtra == PackageInstaller.STATUS_PENDING_USER_ACTION) {
@SuppressWarnings("UnsafeIntentLaunch")
context.startActivity(intent.getParcelableExtra(Intent.EXTRA_INTENT) as Intent?)
} else {
context.unregisterReceiver(this)
if(statusExtra == PackageInstaller.STATUS_SUCCESS) {
context.showOperationResultToast(true)
} else {
errorMessage = parsePackageInstallerMessage(context, intent)
}
}
}
}
ContextCompat.registerReceiver(
context, receiver, IntentFilter(AppInstallerViewModel.ACTION), null,
null, ContextCompat.RECEIVER_EXPORTED
)
val pi = if(VERSION.SDK_INT >= 34) {
PendingIntent.getBroadcast(
context, 0, Intent(AppInstallerViewModel.ACTION),
PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT or PendingIntent.FLAG_MUTABLE
).intentSender
} else {
PendingIntent.getBroadcast(context, 0, Intent(AppInstallerViewModel.ACTION), PendingIntent.FLAG_MUTABLE).intentSender
}
context.getPackageInstaller().uninstall(pkgName, pi)
},
enabled = pkgName != "",
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.silent_uninstall))
}
Button(
onClick = {
val intent = Intent(Intent.ACTION_UNINSTALL_PACKAGE)
intent.setData(Uri.parse("package:$pkgName"))
context.startActivity(intent)
},
enabled = pkgName != "",
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.request_uninstall))
} }
} }
} }
if(errorMessage != null) AlertDialog( ContextCompat.registerReceiver(
title = { Text(stringResource(R.string.failure)) }, context, receiver, IntentFilter(AppInstallerViewModel.ACTION), null,
text = { Text(errorMessage!!) }, null, ContextCompat.RECEIVER_EXPORTED
confirmButton = {
TextButton({ errorMessage = null }) { Text(stringResource(R.string.confirm)) }
},
onDismissRequest = { errorMessage = null }
) )
val pi = if(VERSION.SDK_INT >= 34) {
PendingIntent.getBroadcast(
context, 0, Intent(AppInstallerViewModel.ACTION),
PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT or PendingIntent.FLAG_MUTABLE
).intentSender
} else {
PendingIntent.getBroadcast(context, 0, Intent(AppInstallerViewModel.ACTION), PendingIntent.FLAG_MUTABLE).intentSender
}
context.getPackageInstaller().uninstall(packageName, pi)
} }

View File

@@ -88,6 +88,7 @@ import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.MenuAnchorType import androidx.compose.material3.MenuAnchorType
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.ScrollableTabRow
import androidx.compose.material3.SegmentedButton import androidx.compose.material3.SegmentedButton
import androidx.compose.material3.SegmentedButtonDefaults import androidx.compose.material3.SegmentedButtonDefaults
import androidx.compose.material3.SingleChoiceSegmentedButtonRow import androidx.compose.material3.SingleChoiceSegmentedButtonRow
@@ -122,12 +123,14 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import com.bintianqi.owndroid.ChoosePackageContract import com.bintianqi.owndroid.ChoosePackageContract
import com.bintianqi.owndroid.HorizontalPadding import com.bintianqi.owndroid.HorizontalPadding
import com.bintianqi.owndroid.R import com.bintianqi.owndroid.R
import com.bintianqi.owndroid.SharedPrefs import com.bintianqi.owndroid.SharedPrefs
import com.bintianqi.owndroid.formatDate
import com.bintianqi.owndroid.formatFileSize import com.bintianqi.owndroid.formatFileSize
import com.bintianqi.owndroid.humanReadableDate import com.bintianqi.owndroid.humanReadableDate
import com.bintianqi.owndroid.humanReadableDateTime import com.bintianqi.owndroid.humanReadableDateTime
@@ -153,6 +156,9 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.net.InetAddress import java.net.InetAddress
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import kotlin.reflect.jvm.jvmErasure import kotlin.reflect.jvm.jvmErasure
@Serializable object Network @Serializable object Network
@@ -917,7 +923,8 @@ fun NetworkStatsScreen(onNavigateUp: () -> Unit, onNavigateToViewer: (NetworkSta
MyScaffold(R.string.network_stats, onNavigateUp) { MyScaffold(R.string.network_stats, onNavigateUp) {
ExposedDropdownMenuBox( ExposedDropdownMenuBox(
activeTextField == NetworkStatsActiveTextField.Type, activeTextField == NetworkStatsActiveTextField.Type,
{ activeTextField = if(it) NetworkStatsActiveTextField.Type else NetworkStatsActiveTextField.Type } { activeTextField = if(it) NetworkStatsActiveTextField.Type else NetworkStatsActiveTextField.Type },
Modifier.padding(top = 8.dp, bottom = 4.dp)
) { ) {
val typeTextMap = mapOf( val typeTextMap = mapOf(
1 to R.string.summary, 1 to R.string.summary,
@@ -927,7 +934,7 @@ fun NetworkStatsScreen(onNavigateUp: () -> Unit, onNavigateToViewer: (NetworkSta
value = stringResource(typeTextMap[queryType]!!), onValueChange = {}, readOnly = true, value = stringResource(typeTextMap[queryType]!!), onValueChange = {}, readOnly = true,
label = { Text(stringResource(R.string.type)) }, label = { Text(stringResource(R.string.type)) },
trailingIcon = { ExpandExposedTextFieldIcon(activeTextField == NetworkStatsActiveTextField.Type) }, trailingIcon = { ExpandExposedTextFieldIcon(activeTextField == NetworkStatsActiveTextField.Type) },
modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryNotEditable).fillMaxWidth().padding(bottom = 4.dp) modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryNotEditable).fillMaxWidth()
) )
ExposedDropdownMenu( ExposedDropdownMenu(
activeTextField == NetworkStatsActiveTextField.Type, { activeTextField = NetworkStatsActiveTextField.None } activeTextField == NetworkStatsActiveTextField.Type, { activeTextField = NetworkStatsActiveTextField.None }
@@ -1185,7 +1192,7 @@ fun NetworkStatsScreen(onNavigateUp: () -> Unit, onNavigateToViewer: (NetworkSta
Button( Button(
onClick = { onClick = {
querying = true querying = true
coroutine.launch { coroutine.launch(Dispatchers.IO) {
val buckets = try { val buckets = try {
@Suppress("NewApi") if(queryType == 1) { @Suppress("NewApi") if(queryType == 1) {
if(target == NetworkStatsTarget.Device) if(target == NetworkStatsTarget.Device)
@@ -1287,79 +1294,102 @@ data class NetworkStatsViewer(
fun NetworkStatsViewerScreen(nsv: NetworkStatsViewer, onNavigateUp: () -> Unit) { fun NetworkStatsViewerScreen(nsv: NetworkStatsViewer, onNavigateUp: () -> Unit) {
var index by remember { mutableIntStateOf(0) } var index by remember { mutableIntStateOf(0) }
val size = nsv.stats.size val size = nsv.stats.size
MySmallTitleScaffold(R.string.network_stats, onNavigateUp) { val ps = rememberPagerState { size }
index = ps.currentPage
val coroutine = rememberCoroutineScope()
MySmallTitleScaffold(R.string.network_stats, onNavigateUp, 0.dp) {
if(size > 1) Row( if(size > 1) Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.align(Alignment.CenterHorizontally).padding(bottom = 8.dp) modifier = Modifier.align(Alignment.CenterHorizontally).padding(top = 8.dp)
) { ) {
IconButton( IconButton(
onClick = { index -= 1 }, onClick = {
coroutine.launch {
ps.animateScrollToPage(index - 1)
}
},
enabled = index > 0 enabled = index > 0
) { ) {
Icon(imageVector = Icons.AutoMirrored.Default.KeyboardArrowLeft, contentDescription = null) Icon(imageVector = Icons.AutoMirrored.Default.KeyboardArrowLeft, contentDescription = null)
} }
Text("${index + 1} / $size", modifier = Modifier.padding(horizontal = 8.dp)) Text("${index + 1} / $size", modifier = Modifier.padding(horizontal = 8.dp))
IconButton( IconButton(
onClick = { index += 1 }, onClick = {
coroutine.launch {
ps.animateScrollToPage(index + 1)
}
},
enabled = index < size - 1 enabled = index < size - 1
) { ) {
Icon(imageVector = Icons.AutoMirrored.Default.KeyboardArrowRight, contentDescription = null) Icon(imageVector = Icons.AutoMirrored.Default.KeyboardArrowRight, contentDescription = null)
} }
} }
val data = nsv.stats[index] HorizontalPager(ps, Modifier.padding(top = 8.dp)) { page ->
Text( val data = nsv.stats[page]
data.startTime.humanReadableDateTime + " ~ " + data.endTime.humanReadableDateTime, Column(Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding)) {
modifier = Modifier.align(Alignment.CenterHorizontally).padding(bottom = 8.dp) Row(Modifier.align(Alignment.CenterHorizontally).padding(bottom = 8.dp), verticalAlignment = Alignment.CenterVertically) {
) SimpleDateFormat("", Locale.getDefault()).format(Date(data.startTime))
val txBytes = data.txBytes Text(
Text(stringResource(R.string.transmitted), style = typography.titleLarge) formatDate("yyyy/MM/dd", data.startTime) + "\n" + formatDate("HH:mm:ss", data.startTime),
Column(modifier = Modifier.padding(start = 8.dp, bottom = 4.dp)) { textAlign = TextAlign.Center
Text("$txBytes bytes (${formatFileSize(txBytes)})") )
Text(data.txPackets.toString() + " packets") Text("~", Modifier.padding(horizontal = 8.dp))
} Text(
val rxBytes = data.rxBytes formatDate("yyyy/MM/dd", data.endTime) + "\n" + formatDate("HH:mm:ss", data.endTime),
Text(stringResource(R.string.received), style = typography.titleLarge) textAlign = TextAlign.Center
Column(modifier = Modifier.padding(start = 8.dp, bottom = 8.dp)) { )
Text("$rxBytes bytes (${formatFileSize(rxBytes)})") }
Text(data.rxPackets.toString() + " packets") val txBytes = data.txBytes
} Text(stringResource(R.string.transmitted), style = typography.titleLarge)
Row(verticalAlignment = Alignment.CenterVertically) { Column(modifier = Modifier.padding(start = 8.dp, bottom = 4.dp)) {
val text = when(data.state) { Text("$txBytes bytes (${formatFileSize(txBytes)})")
NetworkStats.Bucket.STATE_ALL -> R.string.all Text(data.txPackets.toString() + " packets")
NetworkStats.Bucket.STATE_DEFAULT -> R.string.default_str }
NetworkStats.Bucket.STATE_FOREGROUND -> R.string.foreground val rxBytes = data.rxBytes
else -> R.string.unknown Text(stringResource(R.string.received), style = typography.titleLarge)
} Column(modifier = Modifier.padding(start = 8.dp, bottom = 8.dp)) {
Text(stringResource(R.string.state), style = typography.titleMedium, modifier = Modifier.padding(end = 8.dp)) Text("$rxBytes bytes (${formatFileSize(rxBytes)})")
Text(stringResource(text)) Text(data.rxPackets.toString() + " packets")
} }
if(VERSION.SDK_INT >= 24) { Row(verticalAlignment = Alignment.CenterVertically) {
Row(verticalAlignment = Alignment.CenterVertically) { val text = when(data.state) {
val tag = data.tag NetworkStats.Bucket.STATE_ALL -> R.string.all
Text(stringResource(R.string.tag), style = typography.titleMedium, modifier = Modifier.padding(end = 8.dp)) NetworkStats.Bucket.STATE_DEFAULT -> R.string.default_str
Text(if(tag == NetworkStats.Bucket.TAG_NONE) stringResource(R.string.all) else tag.toString()) NetworkStats.Bucket.STATE_FOREGROUND -> R.string.foreground
} else -> R.string.unknown
Row(verticalAlignment = Alignment.CenterVertically) { }
val text = when(data.roaming) { Text(stringResource(R.string.state), style = typography.titleMedium, modifier = Modifier.padding(end = 8.dp))
NetworkStats.Bucket.ROAMING_ALL -> R.string.all Text(stringResource(text))
NetworkStats.Bucket.ROAMING_YES -> R.string.yes }
NetworkStats.Bucket.ROAMING_NO -> R.string.no if(VERSION.SDK_INT >= 24) {
else -> R.string.unknown Row(verticalAlignment = Alignment.CenterVertically) {
val tag = data.tag
Text(stringResource(R.string.tag), style = typography.titleMedium, modifier = Modifier.padding(end = 8.dp))
Text(if(tag == NetworkStats.Bucket.TAG_NONE) stringResource(R.string.all) else tag.toString())
}
Row(verticalAlignment = Alignment.CenterVertically) {
val text = when(data.roaming) {
NetworkStats.Bucket.ROAMING_ALL -> R.string.all
NetworkStats.Bucket.ROAMING_YES -> R.string.yes
NetworkStats.Bucket.ROAMING_NO -> R.string.no
else -> R.string.unknown
}
Text(stringResource(R.string.roaming), style = typography.titleMedium, modifier = Modifier.padding(end = 8.dp))
Text(stringResource(text))
}
}
if(VERSION.SDK_INT >= 26) Row(verticalAlignment = Alignment.CenterVertically) {
val text = when(data.metered) {
NetworkStats.Bucket.METERED_ALL -> R.string.all
NetworkStats.Bucket.METERED_YES -> R.string.yes
NetworkStats.Bucket.METERED_NO -> R.string.no
else -> R.string.unknown
}
Text(stringResource(R.string.metered), style = typography.titleMedium, modifier = Modifier.padding(end = 8.dp))
Text(stringResource(text))
} }
Text(stringResource(R.string.roaming), style = typography.titleMedium, modifier = Modifier.padding(end = 8.dp))
Text(stringResource(text))
} }
} }
if(VERSION.SDK_INT >= 26) Row(verticalAlignment = Alignment.CenterVertically) {
val text = when(data.metered) {
NetworkStats.Bucket.METERED_ALL -> R.string.all
NetworkStats.Bucket.METERED_YES -> R.string.yes
NetworkStats.Bucket.METERED_NO -> R.string.no
else -> R.string.unknown
}
Text(stringResource(R.string.metered), style = typography.titleMedium, modifier = Modifier.padding(end = 8.dp))
Text(stringResource(text))
}
} }
} }

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:pathData="M320,720 L80,480l240,-240 57,57 -184,184 183,183 -56,56ZM640,720 L583,663 767,479 584,296 640,240 880,480 640,720Z"
android:fillColor="#000000"/>
</vector>

View File

@@ -406,8 +406,6 @@
<string name="set_default_dialer">Установить приложение для звонков по умолчанию</string> <string name="set_default_dialer">Установить приложение для звонков по умолчанию</string>
<string name="app_will_be_default_dialer">Это приложение будет установлено в качестве приложения для звонков по умолчанию.</string> <string name="app_will_be_default_dialer">Это приложение будет установлено в качестве приложения для звонков по умолчанию.</string>
<string name="uninstall_app">Удалить приложение</string> <string name="uninstall_app">Удалить приложение</string>
<string name="silent_uninstall">Тихое удаление</string>
<string name="request_uninstall">Запросить удаление</string>
<string name="install_app">Установить приложение</string> <string name="install_app">Установить приложение</string>
<!--TODO: 2 strings--> <!--TODO: 2 strings-->
<string name="choose_apk_file">Choose an APK file</string> <string name="choose_apk_file">Choose an APK file</string>

View File

@@ -414,8 +414,6 @@
<string name="set_default_dialer">Varsayılan Arama Uygulamasını Ayarla</string> <string name="set_default_dialer">Varsayılan Arama Uygulamasını Ayarla</string>
<string name="app_will_be_default_dialer">Bu uygulama varsayılan arama uygulaması olarak ayarlanacak.</string> <string name="app_will_be_default_dialer">Bu uygulama varsayılan arama uygulaması olarak ayarlanacak.</string>
<string name="uninstall_app">Uygulamayı Kaldır</string> <string name="uninstall_app">Uygulamayı Kaldır</string>
<string name="silent_uninstall">Sessiz Kaldırma</string>
<string name="request_uninstall">Kaldırma İsteği</string>
<string name="install_app">Uygulama Yükle</string> <string name="install_app">Uygulama Yükle</string>
<string name="choose_apk_file">Bir APK Dosyası Seç</string> <string name="choose_apk_file">Bir APK Dosyası Seç</string>
<string name="install_existing_app">Mevcut Uygulamayı Yükle</string> <string name="install_existing_app">Mevcut Uygulamayı Yükle</string>

View File

@@ -394,8 +394,6 @@
<string name="set_default_dialer">设为默认拨号应用</string> <string name="set_default_dialer">设为默认拨号应用</string>
<string name="app_will_be_default_dialer">这个应用将被设为默认拨号应用</string> <string name="app_will_be_default_dialer">这个应用将被设为默认拨号应用</string>
<string name="uninstall_app">卸载应用</string> <string name="uninstall_app">卸载应用</string>
<string name="silent_uninstall">静默卸载</string>
<string name="request_uninstall">请求卸载</string>
<string name="install_app">安装应用</string> <string name="install_app">安装应用</string>
<string name="choose_apk_file">选择一个APK文件</string> <string name="choose_apk_file">选择一个APK文件</string>
<string name="install_existing_app">安装已存在的应用</string> <string name="install_existing_app">安装已存在的应用</string>

View File

@@ -433,8 +433,6 @@
<string name="set_default_dialer">Set default dialer</string> <string name="set_default_dialer">Set default dialer</string>
<string name="app_will_be_default_dialer">This app will be set as the default dialer application.</string> <string name="app_will_be_default_dialer">This app will be set as the default dialer application.</string>
<string name="uninstall_app">Uninstall app</string> <string name="uninstall_app">Uninstall app</string>
<string name="silent_uninstall">Silent uninstall</string>
<string name="request_uninstall">Request uninstall</string>
<string name="install_app">Install app</string> <string name="install_app">Install app</string>
<string name="choose_apk_file">Choose an APK file</string> <string name="choose_apk_file">Choose an APK file</string>
<string name="install_existing_app">Install existing app</string> <string name="install_existing_app">Install existing app</string>