mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 11:05:59 +00:00
Remove Request uninstall feature
Scroll between pages in Network stats viewer
This commit is contained in:
@@ -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.appearance, icon = R.drawable.format_paint_fill0) { onNavigate(Appearance) }
|
||||
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(title = R.string.export_logs, icon = R.drawable.description_fill0) {
|
||||
val time = SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).format(Date(System.currentTimeMillis()))
|
||||
|
||||
@@ -100,6 +100,9 @@ val Long.humanReadableDate: String
|
||||
val Long.humanReadableDateTime: String
|
||||
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) {
|
||||
Toast.makeText(this, if(success) R.string.success else R.string.failed, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ import com.bintianqi.owndroid.HorizontalPadding
|
||||
import com.bintianqi.owndroid.R
|
||||
import com.bintianqi.owndroid.showOperationResultToast
|
||||
import com.bintianqi.owndroid.ui.Animations
|
||||
import com.bintianqi.owndroid.ui.ErrorDialog
|
||||
import com.bintianqi.owndroid.ui.FunctionItem
|
||||
import com.bintianqi.owndroid.ui.ListItem
|
||||
import com.bintianqi.owndroid.ui.NavIcon
|
||||
@@ -157,7 +158,6 @@ fun ApplicationsScreen(onNavigateUp: () -> Unit) {
|
||||
composable<PermittedAccessibilityServices> { PermittedAccessibilityServicesScreen(pkgName) }
|
||||
composable<PermittedInputMethods> { PermittedInputMethodsScreen(pkgName) }
|
||||
composable<KeepUninstalledPackages> { KeepUninstalledPackagesScreen(pkgName) }
|
||||
composable<UninstallPackage> { UninstallPackageScreen(pkgName) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,8 +166,9 @@ fun ApplicationsScreen(onNavigateUp: () -> Unit) {
|
||||
|
||||
@Composable
|
||||
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 errorMessage by remember { mutableStateOf("") }
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
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()
|
||||
chooseApks.launch(APK_MIME)
|
||||
}
|
||||
if(VERSION.SDK_INT >= 28) FunctionItem(R.string.install_existing_app, icon = R.drawable.install_mobile_fill0) { dialogStatus = 5 }
|
||||
FunctionItem(title = R.string.uninstall_app, icon = R.drawable.delete_fill0) { onNavigate(UninstallPackage) }
|
||||
if(VERSION.SDK_INT >= 28) FunctionItem(R.string.install_existing_app, icon = R.drawable.install_mobile_fill0) {
|
||||
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))) {
|
||||
FunctionItem(title = R.string.set_default_dialer, icon = R.drawable.call_fill0) {
|
||||
if(pkgName != "") dialogStatus = 3
|
||||
@@ -428,6 +433,27 @@ private fun HomeScreen(pkgName: String, onNavigate: (Any) -> Unit) {
|
||||
},
|
||||
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() }
|
||||
}
|
||||
|
||||
@@ -706,7 +732,7 @@ private fun CrossProfileWidgetProvidersScreen(pkgName: String) {
|
||||
|
||||
@RequiresApi(34)
|
||||
@Composable
|
||||
private fun CredentialManagerPolicyScreen(pkgName: String) { // TODO: rename "manage" to "manager"
|
||||
private fun CredentialManagerPolicyScreen(pkgName: String) {
|
||||
val context = LocalContext.current
|
||||
val dpm = context.getDPM()
|
||||
var policy: PackagePolicy?
|
||||
@@ -940,19 +966,7 @@ private fun KeepUninstalledPackagesScreen(pkgName: String) {
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable private object UninstallPackage
|
||||
|
||||
@Composable
|
||||
private fun UninstallPackageScreen(pkgName: String) {
|
||||
val context = LocalContext.current
|
||||
var errorMessage by remember { mutableStateOf<String?>(null) }
|
||||
Column(modifier = Modifier.fillMaxSize().padding(horizontal = HorizontalPadding).verticalScroll(rememberScrollState())) {
|
||||
Spacer(Modifier.padding(vertical = 10.dp))
|
||||
Text(text = stringResource(R.string.uninstall_app), style = typography.headlineLarge)
|
||||
Spacer(Modifier.padding(vertical = 5.dp))
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
Button(
|
||||
onClick = {
|
||||
private fun uninstallPackage(context: Context, packageName: String, onError: (String) -> Unit) {
|
||||
val receiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val statusExtra = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 999)
|
||||
@@ -964,7 +978,7 @@ private fun UninstallPackageScreen(pkgName: String) {
|
||||
if(statusExtra == PackageInstaller.STATUS_SUCCESS) {
|
||||
context.showOperationResultToast(true)
|
||||
} else {
|
||||
errorMessage = parsePackageInstallerMessage(context, intent)
|
||||
onError(parsePackageInstallerMessage(context, intent))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -981,33 +995,5 @@ private fun UninstallPackageScreen(pkgName: String) {
|
||||
} 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))
|
||||
context.getPackageInstaller().uninstall(packageName, pi)
|
||||
}
|
||||
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(
|
||||
title = { Text(stringResource(R.string.failure)) },
|
||||
text = { Text(errorMessage!!) },
|
||||
confirmButton = {
|
||||
TextButton({ errorMessage = null }) { Text(stringResource(R.string.confirm)) }
|
||||
},
|
||||
onDismissRequest = { errorMessage = null }
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ import androidx.compose.material3.MaterialTheme.typography
|
||||
import androidx.compose.material3.MenuAnchorType
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.ScrollableTabRow
|
||||
import androidx.compose.material3.SegmentedButton
|
||||
import androidx.compose.material3.SegmentedButtonDefaults
|
||||
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.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.os.bundleOf
|
||||
import com.bintianqi.owndroid.ChoosePackageContract
|
||||
import com.bintianqi.owndroid.HorizontalPadding
|
||||
import com.bintianqi.owndroid.R
|
||||
import com.bintianqi.owndroid.SharedPrefs
|
||||
import com.bintianqi.owndroid.formatDate
|
||||
import com.bintianqi.owndroid.formatFileSize
|
||||
import com.bintianqi.owndroid.humanReadableDate
|
||||
import com.bintianqi.owndroid.humanReadableDateTime
|
||||
@@ -153,6 +156,9 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.net.InetAddress
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import kotlin.reflect.jvm.jvmErasure
|
||||
|
||||
@Serializable object Network
|
||||
@@ -917,7 +923,8 @@ fun NetworkStatsScreen(onNavigateUp: () -> Unit, onNavigateToViewer: (NetworkSta
|
||||
MyScaffold(R.string.network_stats, onNavigateUp) {
|
||||
ExposedDropdownMenuBox(
|
||||
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(
|
||||
1 to R.string.summary,
|
||||
@@ -927,7 +934,7 @@ fun NetworkStatsScreen(onNavigateUp: () -> Unit, onNavigateToViewer: (NetworkSta
|
||||
value = stringResource(typeTextMap[queryType]!!), onValueChange = {}, readOnly = true,
|
||||
label = { Text(stringResource(R.string.type)) },
|
||||
trailingIcon = { ExpandExposedTextFieldIcon(activeTextField == NetworkStatsActiveTextField.Type) },
|
||||
modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryNotEditable).fillMaxWidth().padding(bottom = 4.dp)
|
||||
modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryNotEditable).fillMaxWidth()
|
||||
)
|
||||
ExposedDropdownMenu(
|
||||
activeTextField == NetworkStatsActiveTextField.Type, { activeTextField = NetworkStatsActiveTextField.None }
|
||||
@@ -1185,7 +1192,7 @@ fun NetworkStatsScreen(onNavigateUp: () -> Unit, onNavigateToViewer: (NetworkSta
|
||||
Button(
|
||||
onClick = {
|
||||
querying = true
|
||||
coroutine.launch {
|
||||
coroutine.launch(Dispatchers.IO) {
|
||||
val buckets = try {
|
||||
@Suppress("NewApi") if(queryType == 1) {
|
||||
if(target == NetworkStatsTarget.Device)
|
||||
@@ -1287,30 +1294,51 @@ data class NetworkStatsViewer(
|
||||
fun NetworkStatsViewerScreen(nsv: NetworkStatsViewer, onNavigateUp: () -> Unit) {
|
||||
var index by remember { mutableIntStateOf(0) }
|
||||
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(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally).padding(bottom = 8.dp)
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally).padding(top = 8.dp)
|
||||
) {
|
||||
IconButton(
|
||||
onClick = { index -= 1 },
|
||||
onClick = {
|
||||
coroutine.launch {
|
||||
ps.animateScrollToPage(index - 1)
|
||||
}
|
||||
},
|
||||
enabled = index > 0
|
||||
) {
|
||||
Icon(imageVector = Icons.AutoMirrored.Default.KeyboardArrowLeft, contentDescription = null)
|
||||
}
|
||||
Text("${index + 1} / $size", modifier = Modifier.padding(horizontal = 8.dp))
|
||||
IconButton(
|
||||
onClick = { index += 1 },
|
||||
onClick = {
|
||||
coroutine.launch {
|
||||
ps.animateScrollToPage(index + 1)
|
||||
}
|
||||
},
|
||||
enabled = index < size - 1
|
||||
) {
|
||||
Icon(imageVector = Icons.AutoMirrored.Default.KeyboardArrowRight, contentDescription = null)
|
||||
}
|
||||
}
|
||||
val data = nsv.stats[index]
|
||||
HorizontalPager(ps, Modifier.padding(top = 8.dp)) { page ->
|
||||
val data = nsv.stats[page]
|
||||
Column(Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding)) {
|
||||
Row(Modifier.align(Alignment.CenterHorizontally).padding(bottom = 8.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
SimpleDateFormat("", Locale.getDefault()).format(Date(data.startTime))
|
||||
Text(
|
||||
data.startTime.humanReadableDateTime + " ~ " + data.endTime.humanReadableDateTime,
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally).padding(bottom = 8.dp)
|
||||
formatDate("yyyy/MM/dd", data.startTime) + "\n" + formatDate("HH:mm:ss", data.startTime),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Text("~", Modifier.padding(horizontal = 8.dp))
|
||||
Text(
|
||||
formatDate("yyyy/MM/dd", data.endTime) + "\n" + formatDate("HH:mm:ss", data.endTime),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
val txBytes = data.txBytes
|
||||
Text(stringResource(R.string.transmitted), style = typography.titleLarge)
|
||||
Column(modifier = Modifier.padding(start = 8.dp, bottom = 4.dp)) {
|
||||
@@ -1362,6 +1390,8 @@ fun NetworkStatsViewerScreen(nsv: NetworkStatsViewer, onNavigateUp: () -> Unit)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable object PrivateDns
|
||||
|
||||
|
||||
9
app/src/main/res/drawable/code_fill0.xml
Normal file
9
app/src/main/res/drawable/code_fill0.xml
Normal 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>
|
||||
@@ -406,8 +406,6 @@
|
||||
<string name="set_default_dialer">Установить приложение для звонков по умолчанию</string>
|
||||
<string name="app_will_be_default_dialer">Это приложение будет установлено в качестве приложения для звонков по умолчанию.</string>
|
||||
<string name="uninstall_app">Удалить приложение</string>
|
||||
<string name="silent_uninstall">Тихое удаление</string>
|
||||
<string name="request_uninstall">Запросить удаление</string>
|
||||
<string name="install_app">Установить приложение</string>
|
||||
<!--TODO: 2 strings-->
|
||||
<string name="choose_apk_file">Choose an APK file</string>
|
||||
|
||||
@@ -414,8 +414,6 @@
|
||||
<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="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="choose_apk_file">Bir APK Dosyası Seç</string>
|
||||
<string name="install_existing_app">Mevcut Uygulamayı Yükle</string>
|
||||
|
||||
@@ -394,8 +394,6 @@
|
||||
<string name="set_default_dialer">设为默认拨号应用</string>
|
||||
<string name="app_will_be_default_dialer">这个应用将被设为默认拨号应用</string>
|
||||
<string name="uninstall_app">卸载应用</string>
|
||||
<string name="silent_uninstall">静默卸载</string>
|
||||
<string name="request_uninstall">请求卸载</string>
|
||||
<string name="install_app">安装应用</string>
|
||||
<string name="choose_apk_file">选择一个APK文件</string>
|
||||
<string name="install_existing_app">安装已存在的应用</string>
|
||||
|
||||
@@ -433,8 +433,6 @@
|
||||
<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="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="choose_apk_file">Choose an APK file</string>
|
||||
<string name="install_existing_app">Install existing app</string>
|
||||
|
||||
Reference in New Issue
Block a user