mirror of
https://github.com/awfixers-stuff/OwnDroid.git
synced 2026-03-23 19:15:58 +00:00
feat: multi-select in Package chooser (#215)
Update dependencies Set minSdk to 23 since many compose libraries requires it
This commit is contained in:
@@ -157,7 +157,6 @@ import com.bintianqi.owndroid.dpm.OrganizationOwnedProfileScreen
|
||||
import com.bintianqi.owndroid.dpm.OverrideApn
|
||||
import com.bintianqi.owndroid.dpm.OverrideApnScreen
|
||||
import com.bintianqi.owndroid.dpm.PackageFunctionScreen
|
||||
import com.bintianqi.owndroid.dpm.PackageFunctionScreenWithoutResult
|
||||
import com.bintianqi.owndroid.dpm.Password
|
||||
import com.bintianqi.owndroid.dpm.PasswordInfo
|
||||
import com.bintianqi.owndroid.dpm.PasswordInfoScreen
|
||||
@@ -290,7 +289,10 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
|
||||
}
|
||||
}
|
||||
fun choosePackage() {
|
||||
navController.navigate(ApplicationsList(false))
|
||||
navController.navigate(ApplicationsList(false, true))
|
||||
}
|
||||
fun chooseSinglePackage() {
|
||||
navController.navigate(ApplicationsList(false, false))
|
||||
}
|
||||
fun navigateToAppGroups() {
|
||||
navController.navigate(ManageAppGroups)
|
||||
@@ -336,7 +338,7 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
|
||||
DelegatedAdminsScreen(vm.delegatedAdmins, vm::getDelegatedAdmins, ::navigateUp, ::navigate)
|
||||
}
|
||||
composable<AddDelegatedAdmin>{
|
||||
AddDelegatedAdminScreen(vm.chosenPackage, ::choosePackage, it.toRoute(),
|
||||
AddDelegatedAdminScreen(vm.chosenPackage, ::chooseSinglePackage, it.toRoute(),
|
||||
vm::setDelegatedAdmin, ::navigateUp)
|
||||
}
|
||||
composable<DeviceInfo> { DeviceInfoScreen(vm, ::navigateUp) }
|
||||
@@ -390,9 +392,11 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
|
||||
vm::getNsNotificationPolicy, vm::setNsNotificationPolicy, ::navigateUp)
|
||||
}
|
||||
composable<LockTaskMode> {
|
||||
LockTaskModeScreen(vm.chosenPackage, ::choosePackage, vm.lockTaskPackages,
|
||||
LockTaskModeScreen(
|
||||
vm.chosenPackage, ::chooseSinglePackage, ::choosePackage, vm.lockTaskPackages,
|
||||
vm::getLockTaskPackages, vm::setLockTaskPackage, vm::startLockTaskMode,
|
||||
vm:: getLockTaskFeatures, vm::setLockTaskFeatures, ::navigateUp)
|
||||
vm:: getLockTaskFeatures, vm::setLockTaskFeatures, ::navigateUp
|
||||
)
|
||||
}
|
||||
composable<CaCert> {
|
||||
CaCertScreen(vm.installedCaCerts, vm::getCaCerts, vm.selectedCaCert, vm::selectCaCert, vm::installCaCert, vm::parseCaCert,
|
||||
@@ -440,7 +444,7 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
|
||||
WifiSsidPolicyScreen(vm::getSsidPolicy, vm::setSsidPolicy, ::navigateUp)
|
||||
}
|
||||
composable<QueryNetworkStats> {
|
||||
NetworkStatsScreen(vm.chosenPackage, ::choosePackage, vm::getPackageUid,
|
||||
NetworkStatsScreen(vm.chosenPackage, ::chooseSinglePackage, vm::getPackageUid,
|
||||
vm::queryNetworkStats, ::navigateUp) { navController.navigate(NetworkStatsViewer) }
|
||||
}
|
||||
composable<NetworkStatsViewer> {
|
||||
@@ -451,7 +455,7 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
|
||||
}
|
||||
composable<AlwaysOnVpnPackage> {
|
||||
AlwaysOnVpnPackageScreen(vm::getAlwaysOnVpnPackage, vm::getAlwaysOnVpnLockdown,
|
||||
vm::setAlwaysOnVpn, vm.chosenPackage, ::choosePackage, ::navigateUp)
|
||||
vm::setAlwaysOnVpn, vm.chosenPackage, ::chooseSinglePackage, ::navigateUp)
|
||||
}
|
||||
composable<RecommendedGlobalProxy> {
|
||||
RecommendedGlobalProxyScreen(vm::setRecommendedGlobalProxy, ::navigateUp)
|
||||
@@ -499,10 +503,10 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
|
||||
composable<DeleteWorkProfile> { DeleteWorkProfileScreen(vm::wipeData, ::navigateUp) }
|
||||
|
||||
composable<ApplicationsList> {
|
||||
val canSwitchView = (it.toRoute() as ApplicationsList).canSwitchView
|
||||
val params = it.toRoute<ApplicationsList>()
|
||||
AppChooserScreen(
|
||||
canSwitchView, vm.installedPackages, vm.refreshPackagesProgress, { name ->
|
||||
if (canSwitchView) {
|
||||
params, vm.installedPackages, vm.refreshPackagesProgress, { name ->
|
||||
if (params.canSwitchView) {
|
||||
if (name == null) {
|
||||
navigateUp()
|
||||
} else {
|
||||
@@ -517,12 +521,12 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
|
||||
navController.navigate(ApplicationsFeatures) {
|
||||
popUpTo(Home)
|
||||
}
|
||||
}, vm::refreshPackageList)
|
||||
}, vm::refreshPackageList, vm::setPackageSuspended, vm::setPackageHidden)
|
||||
}
|
||||
composable<ApplicationsFeatures> {
|
||||
ApplicationsFeaturesScreen(::navigateUp, ::navigate) {
|
||||
SP.applicationsListView = true
|
||||
navController.navigate(ApplicationsList(true)) {
|
||||
navController.navigate(ApplicationsList(true, true)) {
|
||||
popUpTo(Home)
|
||||
}
|
||||
}
|
||||
@@ -531,52 +535,72 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
|
||||
ApplicationDetailsScreen(it.toRoute(), vm, ::navigateUp, ::navigate)
|
||||
}
|
||||
composable<Suspend> {
|
||||
PackageFunctionScreen(R.string.suspend, vm.suspendedPackages, vm::getSuspendedPackaged,
|
||||
PackageFunctionScreen(
|
||||
R.string.suspend, vm.suspendedPackages, vm::getSuspendedPackaged,
|
||||
vm::setPackageSuspended, ::navigateUp, vm.chosenPackage, ::choosePackage,
|
||||
::navigateToAppGroups, vm.appGroups, R.string.info_suspend_app)
|
||||
::navigateToAppGroups, vm.appGroups, R.string.info_suspend_app
|
||||
)
|
||||
}
|
||||
composable<Hide> {
|
||||
PackageFunctionScreen(R.string.hide, vm.hiddenPackages, vm::getHiddenPackages,
|
||||
vm::setPackageHidden, ::navigateUp, vm.chosenPackage, ::choosePackage, ::navigateToAppGroups, vm.appGroups)
|
||||
PackageFunctionScreen(
|
||||
R.string.hide, vm.hiddenPackages, vm::getHiddenPackages, vm::setPackageHidden,
|
||||
::navigateUp, vm.chosenPackage, ::choosePackage, ::navigateToAppGroups, vm.appGroups
|
||||
)
|
||||
}
|
||||
composable<BlockUninstall> {
|
||||
PackageFunctionScreenWithoutResult(R.string.block_uninstall, vm.ubPackages,
|
||||
vm::getUbPackages, vm::setPackageUb, ::navigateUp, vm.chosenPackage, ::choosePackage, ::navigateToAppGroups, vm.appGroups)
|
||||
PackageFunctionScreen(
|
||||
R.string.block_uninstall, vm.ubPackages, vm::getUbPackages, vm::setPackageUb,
|
||||
::navigateUp, vm.chosenPackage, ::choosePackage, ::navigateToAppGroups, vm.appGroups
|
||||
)
|
||||
}
|
||||
composable<DisableUserControl> {
|
||||
PackageFunctionScreenWithoutResult(R.string.disable_user_control, vm.ucdPackages,
|
||||
vm::getUcdPackages, vm::setPackageUcd, ::navigateUp, vm.chosenPackage,
|
||||
::choosePackage, ::navigateToAppGroups, vm.appGroups, R.string.info_disable_user_control)
|
||||
PackageFunctionScreen(
|
||||
R.string.disable_user_control, vm.ucdPackages, vm::getUcdPackages,
|
||||
vm::setPackageUcd, ::navigateUp, vm.chosenPackage, ::choosePackage,
|
||||
::navigateToAppGroups, vm.appGroups, R.string.info_disable_user_control
|
||||
)
|
||||
}
|
||||
composable<PermissionsManager> {
|
||||
PermissionsManagerScreen(vm.packagePermissions, vm::getPackagePermissions,
|
||||
vm::setPackagePermission, ::navigateUp, it.toRoute(), vm.chosenPackage, ::choosePackage)
|
||||
PermissionsManagerScreen(
|
||||
vm.packagePermissions, vm::getPackagePermissions, vm::setPackagePermission,
|
||||
::navigateUp, it.toRoute(), vm.chosenPackage, ::chooseSinglePackage
|
||||
)
|
||||
}
|
||||
composable<DisableMeteredData> {
|
||||
PackageFunctionScreen(R.string.disable_metered_data, vm.mddPackages,
|
||||
vm::getMddPackages, vm::setPackageMdd, ::navigateUp, vm.chosenPackage,
|
||||
::choosePackage, ::navigateToAppGroups, vm.appGroups)
|
||||
PackageFunctionScreen(
|
||||
R.string.disable_metered_data, vm.mddPackages, vm::getMddPackages,
|
||||
vm::setPackageMdd, ::navigateUp, vm.chosenPackage, ::choosePackage,
|
||||
::navigateToAppGroups, vm.appGroups
|
||||
)
|
||||
}
|
||||
composable<ClearAppStorage> {
|
||||
ClearAppStorageScreen(vm.chosenPackage, ::choosePackage, vm::clearAppData, ::navigateUp)
|
||||
ClearAppStorageScreen(
|
||||
vm.chosenPackage, ::chooseSinglePackage, vm::clearAppData, ::navigateUp
|
||||
)
|
||||
}
|
||||
composable<UninstallApp> {
|
||||
UninstallAppScreen(vm.chosenPackage, ::choosePackage, vm::uninstallPackage, ::navigateUp)
|
||||
UninstallAppScreen(
|
||||
vm.chosenPackage, ::chooseSinglePackage, vm::uninstallPackage, ::navigateUp
|
||||
)
|
||||
}
|
||||
composable<KeepUninstalledPackages> {
|
||||
PackageFunctionScreenWithoutResult(R.string.keep_uninstalled_packages, vm.kuPackages,
|
||||
vm::getKuPackages, vm::setPackageKu, ::navigateUp, vm.chosenPackage,
|
||||
::choosePackage, ::navigateToAppGroups, vm.appGroups,
|
||||
R.string.info_keep_uninstalled_apps)
|
||||
PackageFunctionScreen(
|
||||
R.string.keep_uninstalled_packages, vm.kuPackages, vm::getKuPackages,
|
||||
vm::setPackageKu, ::navigateUp, vm.chosenPackage, ::choosePackage,
|
||||
::navigateToAppGroups, vm.appGroups, R.string.info_keep_uninstalled_apps
|
||||
)
|
||||
}
|
||||
composable<InstallExistingApp> {
|
||||
InstallExistingAppScreen(vm.chosenPackage, ::choosePackage,
|
||||
vm::installExistingApp, ::navigateUp)
|
||||
InstallExistingAppScreen(
|
||||
vm.chosenPackage, ::chooseSinglePackage, vm::installExistingApp, ::navigateUp
|
||||
)
|
||||
}
|
||||
composable<CrossProfilePackages> {
|
||||
PackageFunctionScreenWithoutResult(R.string.cross_profile_apps, vm.cpPackages,
|
||||
PackageFunctionScreen(
|
||||
R.string.cross_profile_apps, vm.cpPackages,
|
||||
vm::getCpPackages, vm::setPackageCp, ::navigateUp, vm.chosenPackage,
|
||||
::choosePackage, ::navigateToAppGroups, vm.appGroups)
|
||||
::choosePackage, ::navigateToAppGroups, vm.appGroups
|
||||
)
|
||||
}
|
||||
composable<CrossProfileWidgetProviders> {
|
||||
PackageFunctionScreen(R.string.cross_profile_widget, vm.cpwProviders,
|
||||
@@ -584,24 +608,35 @@ fun Home(vm: MyViewModel, onLock: () -> Unit) {
|
||||
::choosePackage, ::navigateToAppGroups, vm.appGroups)
|
||||
}
|
||||
composable<CredentialManagerPolicy> {
|
||||
CredentialManagerPolicyScreen(vm.chosenPackage, ::choosePackage,
|
||||
vm.cmPackages, vm::getCmPolicy, vm::setCmPackage, vm::setCmPolicy, ::navigateUp)
|
||||
CredentialManagerPolicyScreen(
|
||||
vm.chosenPackage, ::choosePackage, vm.cmPackages, vm::getCmPolicy,
|
||||
vm::setCmPackage, vm::setCmPolicy, ::navigateUp
|
||||
)
|
||||
}
|
||||
composable<PermittedAccessibilityServices> {
|
||||
PermittedAsAndImPackages(R.string.permitted_accessibility_services,
|
||||
PermittedAsAndImPackages(
|
||||
R.string.permitted_accessibility_services,
|
||||
R.string.system_accessibility_always_allowed, vm.chosenPackage, ::choosePackage,
|
||||
vm.pasPackages, vm::getPasPackages, vm::setPasPackage, vm::setPasPolicy, ::navigateUp)
|
||||
vm.pasPackages, vm::getPasPackages, vm::setPasPackage, vm::setPasPolicy,
|
||||
::navigateUp
|
||||
)
|
||||
}
|
||||
composable<PermittedInputMethods> {
|
||||
PermittedAsAndImPackages(R.string.permitted_ime, R.string.system_ime_always_allowed,
|
||||
PermittedAsAndImPackages(
|
||||
R.string.permitted_ime, R.string.system_ime_always_allowed,
|
||||
vm.chosenPackage, ::choosePackage, vm.pimPackages, vm::getPimPackages,
|
||||
vm::setPimPackage, vm::setPimPolicy, ::navigateUp)
|
||||
vm::setPimPackage, vm::setPimPolicy, ::navigateUp
|
||||
)
|
||||
}
|
||||
composable<EnableSystemApp> {
|
||||
EnableSystemAppScreen(vm.chosenPackage, ::choosePackage, vm::enableSystemApp, ::navigateUp)
|
||||
EnableSystemAppScreen(
|
||||
vm.chosenPackage, ::chooseSinglePackage, vm::enableSystemApp, ::navigateUp
|
||||
)
|
||||
}
|
||||
composable<SetDefaultDialer> {
|
||||
SetDefaultDialerScreen(vm.chosenPackage, ::choosePackage, vm::setDefaultDialer, ::navigateUp)
|
||||
SetDefaultDialerScreen(
|
||||
vm.chosenPackage, ::chooseSinglePackage, vm::setDefaultDialer, ::navigateUp
|
||||
)
|
||||
}
|
||||
composable<ManagedConfiguration> {
|
||||
ManagedConfigurationScreen(
|
||||
@@ -762,7 +797,10 @@ private fun HomeScreen(onNavigate: (Any) -> Unit) {
|
||||
}
|
||||
if(privilege.device || privilege.profile) {
|
||||
HomePageItem(R.string.applications, R.drawable.apps_fill0) {
|
||||
onNavigate(if(SP.applicationsListView) ApplicationsList(true) else ApplicationsFeatures)
|
||||
onNavigate(
|
||||
if (SP.applicationsListView) ApplicationsList(true, true)
|
||||
else ApplicationsFeatures
|
||||
)
|
||||
}
|
||||
if(VERSION.SDK_INT >= 24) {
|
||||
HomePageItem(R.string.user_restriction, R.drawable.person_off) { onNavigate(UserRestriction) }
|
||||
|
||||
@@ -224,10 +224,9 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
||||
suspendedPackages.value = packages.map { getAppInfo(it) }
|
||||
}
|
||||
@RequiresApi(24)
|
||||
fun setPackageSuspended(name: String, status: Boolean): Boolean {
|
||||
val result = DPM.setPackagesSuspended(DAR, arrayOf(name), status)
|
||||
fun setPackageSuspended(packages: List<String>, status: Boolean) {
|
||||
DPM.setPackagesSuspended(DAR, packages.toTypedArray(), status)
|
||||
getSuspendedPackaged()
|
||||
return result.isEmpty()
|
||||
}
|
||||
|
||||
val hiddenPackages = MutableStateFlow(emptyList<AppInfo>())
|
||||
@@ -236,10 +235,11 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
||||
DPM.isApplicationHidden(DAR, it.packageName)
|
||||
}.map { getAppInfo(it) }
|
||||
}
|
||||
fun setPackageHidden(name: String, status: Boolean): Boolean {
|
||||
val result = DPM.setApplicationHidden(DAR, name, status)
|
||||
fun setPackageHidden(packages: List<String>, status: Boolean) {
|
||||
for (name in packages) {
|
||||
DPM.setApplicationHidden(DAR, name, status)
|
||||
}
|
||||
getHiddenPackages()
|
||||
return result
|
||||
}
|
||||
|
||||
// Uninstall blocked packages
|
||||
@@ -249,8 +249,10 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
||||
DPM.isUninstallBlocked(DAR, it.packageName)
|
||||
}.map { getAppInfo(it) }
|
||||
}
|
||||
fun setPackageUb(name: String, status: Boolean) {
|
||||
DPM.setUninstallBlocked(DAR, name, status)
|
||||
fun setPackageUb(packages: List<String>, status: Boolean) {
|
||||
for (name in packages) {
|
||||
DPM.setUninstallBlocked(DAR, name, status)
|
||||
}
|
||||
getUbPackages()
|
||||
}
|
||||
|
||||
@@ -263,10 +265,12 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
||||
}
|
||||
}
|
||||
@RequiresApi(30)
|
||||
fun setPackageUcd(name: String, status: Boolean) {
|
||||
fun setPackageUcd(packages: List<String>, status: Boolean) {
|
||||
DPM.setUserControlDisabledPackages(
|
||||
DAR,
|
||||
ucdPackages.value.map { it.name }.run { if (status) plus(name) else minus(name) }
|
||||
ucdPackages.value.map { it.name }.run {
|
||||
if (status) plus(packages) else minus(packages)
|
||||
}
|
||||
)
|
||||
getUcdPackages()
|
||||
}
|
||||
@@ -296,12 +300,13 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
||||
mddPackages.value = DPM.getMeteredDataDisabledPackages(DAR).distinct().map { getAppInfo(it) }
|
||||
}
|
||||
@RequiresApi(28)
|
||||
fun setPackageMdd(name: String, status: Boolean): Boolean {
|
||||
val result = DPM.setMeteredDataDisabledPackages(
|
||||
DAR, mddPackages.value.map { it.name }.run { if (status) plus(name) else minus(name) }
|
||||
fun setPackageMdd(packages: List<String>, status: Boolean) {
|
||||
DPM.setMeteredDataDisabledPackages(
|
||||
DAR, mddPackages.value.map { it.name }.run {
|
||||
if (status) plus(packages) else minus(packages)
|
||||
}
|
||||
)
|
||||
getMddPackages()
|
||||
return result.isEmpty()
|
||||
}
|
||||
|
||||
// Keep uninstalled packages
|
||||
@@ -311,9 +316,11 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
||||
kuPackages.value = DPM.getKeepUninstalledPackages(DAR)?.distinct()?.map { getAppInfo(it) } ?: emptyList()
|
||||
}
|
||||
@RequiresApi(28)
|
||||
fun setPackageKu(name: String, status: Boolean) {
|
||||
fun setPackageKu(packages: List<String>, status: Boolean) {
|
||||
DPM.setKeepUninstalledPackages(
|
||||
DAR, kuPackages.value.map { it.name }.run { if (status) plus(name) else minus(name) }
|
||||
DAR, kuPackages.value.map { it.name }.run {
|
||||
if (status) plus(packages) else minus(packages)
|
||||
}
|
||||
)
|
||||
getKuPackages()
|
||||
}
|
||||
@@ -325,10 +332,12 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
||||
cpPackages.value = DPM.getCrossProfilePackages(DAR).map { getAppInfo(it) }
|
||||
}
|
||||
@RequiresApi(30)
|
||||
fun setPackageCp(name: String, status: Boolean) {
|
||||
fun setPackageCp(packages: List<String>, status: Boolean) {
|
||||
DPM.setCrossProfilePackages(
|
||||
DAR,
|
||||
cpPackages.value.map { it.name }.toSet().run { if (status) plus(name) else minus(name) }
|
||||
cpPackages.value.map { it.name }.toSet().run {
|
||||
if (status) plus(packages) else minus(packages)
|
||||
}
|
||||
)
|
||||
getCpPackages()
|
||||
}
|
||||
@@ -338,14 +347,15 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
||||
fun getCpwProviders() {
|
||||
cpwProviders.value = DPM.getCrossProfileWidgetProviders(DAR).distinct().map { getAppInfo(it) }
|
||||
}
|
||||
fun setCpwProvider(name: String, status: Boolean): Boolean {
|
||||
val result = if (status) {
|
||||
DPM.addCrossProfileWidgetProvider(DAR, name)
|
||||
} else {
|
||||
DPM.removeCrossProfileWidgetProvider(DAR, name)
|
||||
fun setCpwProvider(packages: List<String>, status: Boolean) {
|
||||
for (name in packages) {
|
||||
if (status) {
|
||||
DPM.addCrossProfileWidgetProvider(DAR, name)
|
||||
} else {
|
||||
DPM.removeCrossProfileWidgetProvider(DAR, name)
|
||||
}
|
||||
}
|
||||
getCpwProviders()
|
||||
return result
|
||||
}
|
||||
|
||||
@RequiresApi(28)
|
||||
@@ -401,9 +411,9 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
||||
policy.policyType
|
||||
} ?: -1
|
||||
}
|
||||
fun setCmPackage(name: String, status: Boolean) {
|
||||
cmPackages.update { list ->
|
||||
if (status) list + getAppInfo(name) else list.filter { it.name != name }
|
||||
fun setCmPackage(packages: List<String>, status: Boolean) {
|
||||
cmPackages.update {
|
||||
updateAppInfoList(it, packages, status)
|
||||
}
|
||||
}
|
||||
@RequiresApi(34)
|
||||
@@ -414,6 +424,16 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
||||
getCmPolicy()
|
||||
}
|
||||
|
||||
fun updateAppInfoList(
|
||||
origin: List<AppInfo>, input: List<String>, status: Boolean
|
||||
): List<AppInfo> {
|
||||
return if (status) {
|
||||
origin + input.map { getAppInfo(it) }
|
||||
} else {
|
||||
origin.filter { it.name !in input }
|
||||
}
|
||||
}
|
||||
|
||||
// Permitted input method
|
||||
val pimPackages = MutableStateFlow(emptyList<AppInfo>())
|
||||
fun getPimPackages(): Boolean {
|
||||
@@ -422,9 +442,9 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
||||
packages == null
|
||||
}
|
||||
}
|
||||
fun setPimPackage(name: String, status: Boolean) {
|
||||
pimPackages.update { packages ->
|
||||
if (status) packages + getAppInfo(name) else packages.filter { it.name != name }
|
||||
fun setPimPackage(packages: List<String>, status: Boolean) {
|
||||
pimPackages.update {
|
||||
updateAppInfoList(it, packages, status)
|
||||
}
|
||||
}
|
||||
fun setPimPolicy(allowAll: Boolean): Boolean {
|
||||
@@ -442,9 +462,9 @@ class MyViewModel(application: Application): AndroidViewModel(application) {
|
||||
packages == null
|
||||
}
|
||||
}
|
||||
fun setPasPackage(name: String, status: Boolean) {
|
||||
pasPackages.update { packages ->
|
||||
if (status) packages + getAppInfo(name) else packages.filter { it.name != name }
|
||||
fun setPasPackage(packages: List<String>, status: Boolean) {
|
||||
pasPackages.update {
|
||||
updateAppInfoList(it, packages, status)
|
||||
}
|
||||
}
|
||||
fun setPasPolicy(allowAll: Boolean): Boolean {
|
||||
|
||||
@@ -6,7 +6,9 @@ import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
@@ -22,8 +24,14 @@ import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.filled.List
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material.icons.outlined.Clear
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.FilledIconButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
@@ -37,6 +45,7 @@ import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
@@ -46,8 +55,10 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
@@ -67,17 +78,20 @@ data class AppInfo(
|
||||
private fun searchInString(query: String, content: String)
|
||||
= query.split(' ').all { content.contains(it, true) }
|
||||
|
||||
@Serializable data class ApplicationsList(val canSwitchView: Boolean)
|
||||
@Serializable data class ApplicationsList(val canSwitchView: Boolean, val multiSelect: Boolean)
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun AppChooserScreen(
|
||||
canSwitchView: Boolean, packageList: MutableStateFlow<List<AppInfo>>,
|
||||
params: ApplicationsList, packageList: MutableStateFlow<List<AppInfo>>,
|
||||
refreshProgress: MutableStateFlow<Float>, onChoosePackage: (String?) -> Unit,
|
||||
onSwitchView: () -> Unit, onRefresh: () -> Unit
|
||||
onSwitchView: () -> Unit, onRefresh: () -> Unit,
|
||||
setPackagesSuspend: (List<String>, Boolean) -> Unit,
|
||||
setPackagesHidden: (List<String>, Boolean) -> Unit,
|
||||
) {
|
||||
val packages by packageList.collectAsStateWithLifecycle()
|
||||
val context = LocalContext.current
|
||||
val hf = LocalHapticFeedback.current
|
||||
val progress by refreshProgress.collectAsStateWithLifecycle()
|
||||
var system by rememberSaveable { mutableStateOf(false) }
|
||||
var query by rememberSaveable { mutableStateOf("") }
|
||||
@@ -86,6 +100,7 @@ fun AppChooserScreen(
|
||||
system == (it.flags and ApplicationInfo.FLAG_SYSTEM != 0) &&
|
||||
(query.isEmpty() || (searchInString(query, it.label) || searchInString(query, it.name)))
|
||||
}
|
||||
val selectedPackages = remember { mutableStateListOf<AppInfo>() }
|
||||
val focusMgr = LocalFocusManager.current
|
||||
LaunchedEffect(Unit) {
|
||||
if(packages.size <= 1) onRefresh()
|
||||
@@ -102,18 +117,86 @@ fun AppChooserScreen(
|
||||
system = !system
|
||||
context.popToast(if(system) R.string.show_system_app else R.string.show_user_app)
|
||||
}) {
|
||||
Icon(painter = painterResource(R.drawable.filter_alt_fill0), contentDescription = null)
|
||||
Icon(painterResource(R.drawable.filter_alt_fill0), null)
|
||||
}
|
||||
IconButton(onRefresh, enabled = progress == 1F) {
|
||||
Icon(painter = painterResource(R.drawable.refresh_fill0), contentDescription = null)
|
||||
if (selectedPackages.isEmpty()) {
|
||||
IconButton(onRefresh, enabled = progress == 1F) {
|
||||
Icon(Icons.Default.Refresh, null)
|
||||
}
|
||||
if (params.canSwitchView) IconButton(onSwitchView) {
|
||||
Icon(Icons.AutoMirrored.Default.List, null)
|
||||
}
|
||||
}
|
||||
if (canSwitchView) IconButton(onSwitchView) {
|
||||
Icon(Icons.AutoMirrored.Default.List, null)
|
||||
}
|
||||
if (selectedPackages.isNotEmpty()) {
|
||||
if (params.canSwitchView) {
|
||||
var dropdown by remember { mutableStateOf(false) }
|
||||
Box {
|
||||
IconButton({
|
||||
dropdown = !dropdown
|
||||
}) {
|
||||
Icon(Icons.Default.MoreVert, null)
|
||||
}
|
||||
DropdownMenu(dropdown, { dropdown = false }) {
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
DropdownMenuItem(
|
||||
{ Text(stringResource(R.string.suspend)) },
|
||||
{
|
||||
setPackagesSuspend(selectedPackages.map { it.name }, true)
|
||||
dropdown = false
|
||||
selectedPackages.clear()
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(painterResource(R.drawable.block_fill0), null)
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
{ Text(stringResource(R.string.unsuspend)) },
|
||||
{
|
||||
setPackagesSuspend(selectedPackages.map { it.name }, false)
|
||||
dropdown = false
|
||||
selectedPackages.clear()
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(painterResource(R.drawable.enable_fill0), null)
|
||||
}
|
||||
)
|
||||
}
|
||||
DropdownMenuItem(
|
||||
{ Text(stringResource(R.string.hide)) },
|
||||
{
|
||||
setPackagesHidden(selectedPackages.map { it.name }, true)
|
||||
dropdown = false
|
||||
selectedPackages.clear()
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(painterResource(R.drawable.visibility_off_fill0), null)
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
{ Text(stringResource(R.string.unhide)) },
|
||||
{
|
||||
setPackagesHidden(selectedPackages.map { it.name }, false)
|
||||
dropdown = false
|
||||
selectedPackages.clear()
|
||||
},
|
||||
leadingIcon = {
|
||||
Icon(painterResource(R.drawable.visibility_fill0), null)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FilledIconButton({
|
||||
onChoosePackage(selectedPackages.joinToString("\n") { it.name })
|
||||
}) {
|
||||
Icon(Icons.Default.Check, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
title = {
|
||||
if(searchMode) {
|
||||
if (searchMode) {
|
||||
val fr = remember { FocusRequester() }
|
||||
LaunchedEffect(Unit) { fr.requestFocus() }
|
||||
OutlinedTextField(
|
||||
@@ -133,6 +216,10 @@ fun AppChooserScreen(
|
||||
textStyle = typography.bodyLarge,
|
||||
modifier = Modifier.fillMaxWidth().focusRequester(fr)
|
||||
)
|
||||
} else {
|
||||
if (selectedPackages.isNotEmpty()) {
|
||||
Text(selectedPackages.size.toString())
|
||||
}
|
||||
}
|
||||
},
|
||||
navigationIcon = {
|
||||
@@ -154,10 +241,24 @@ fun AppChooserScreen(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
focusMgr.clearFocus()
|
||||
onChoosePackage(it.name)
|
||||
}
|
||||
.combinedClickable(onLongClick = {
|
||||
if (params.multiSelect) {
|
||||
selectedPackages += it
|
||||
hf.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
}
|
||||
}, onClick = {
|
||||
if (selectedPackages.isEmpty()) {
|
||||
focusMgr.clearFocus()
|
||||
onChoosePackage(it.name)
|
||||
} else {
|
||||
if (it in selectedPackages) selectedPackages -= it
|
||||
else selectedPackages += it
|
||||
}
|
||||
})
|
||||
.background(
|
||||
if (it in selectedPackages) MaterialTheme.colorScheme.primaryContainer
|
||||
else MaterialTheme.colorScheme.background
|
||||
)
|
||||
.padding(horizontal = 8.dp, vertical = 10.dp)
|
||||
.animateItem()
|
||||
) {
|
||||
|
||||
@@ -177,3 +177,5 @@ fun registerPackageRemovedReceiver(
|
||||
filter.addDataScheme("package")
|
||||
ctx.registerReceiver(br, filter)
|
||||
}
|
||||
|
||||
fun parsePackageNames(input: String) = input.split('\n').filter { it.isNotEmpty() }
|
||||
|
||||
@@ -107,6 +107,7 @@ import com.bintianqi.owndroid.MyViewModel
|
||||
import com.bintianqi.owndroid.Privilege
|
||||
import com.bintianqi.owndroid.R
|
||||
import com.bintianqi.owndroid.adaptiveInsets
|
||||
import com.bintianqi.owndroid.parsePackageNames
|
||||
import com.bintianqi.owndroid.showOperationResultToast
|
||||
import com.bintianqi.owndroid.ui.FullWidthRadioButtonItem
|
||||
import com.bintianqi.owndroid.ui.FunctionItem
|
||||
@@ -130,13 +131,18 @@ val String.isValidPackageName
|
||||
@Composable
|
||||
fun LazyItemScope.ApplicationItem(info: AppInfo, onClear: () -> Unit) {
|
||||
Row(
|
||||
Modifier.fillMaxWidth().padding(horizontal = 8.dp, vertical = 6.dp).animateItem(),
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 8.dp, vertical = 6.dp)
|
||||
.animateItem(),
|
||||
Arrangement.SpaceBetween, Alignment.CenterVertically
|
||||
) {
|
||||
Row(Modifier.weight(1F), verticalAlignment = Alignment.CenterVertically) {
|
||||
Image(
|
||||
painter = rememberDrawablePainter(info.icon), contentDescription = null,
|
||||
modifier = Modifier.padding(start = 12.dp, end = 18.dp).size(30.dp)
|
||||
modifier = Modifier
|
||||
.padding(start = 12.dp, end = 18.dp)
|
||||
.size(30.dp)
|
||||
)
|
||||
Column {
|
||||
Text(info.label)
|
||||
@@ -156,7 +162,9 @@ fun PackageNameTextField(
|
||||
) {
|
||||
val fm = LocalFocusManager.current
|
||||
OutlinedTextField(
|
||||
value, onValueChange, Modifier.fillMaxWidth().then(modifier),
|
||||
value, onValueChange, Modifier
|
||||
.fillMaxWidth()
|
||||
.then(modifier),
|
||||
label = { Text(stringResource(R.string.package_name)) },
|
||||
trailingIcon = {
|
||||
IconButton(onChoosePackage) {
|
||||
@@ -273,10 +281,14 @@ fun ApplicationDetailsScreen(
|
||||
if (VERSION.SDK_INT >= 23) vm.getAppRestrictions(packageName)
|
||||
}
|
||||
MySmallTitleScaffold(R.string.place_holder, onNavigateUp, 0.dp) {
|
||||
Column(Modifier.align(Alignment.CenterHorizontally).padding(top = 16.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Column(Modifier
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.padding(top = 16.dp), horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Image(rememberDrawablePainter(info.icon), null, Modifier.size(50.dp))
|
||||
Text(info.label, Modifier.padding(top = 4.dp))
|
||||
Text(info.name, Modifier.alpha(0.7F).padding(bottom = 8.dp), style = typography.bodyMedium)
|
||||
Text(info.name, Modifier
|
||||
.alpha(0.7F)
|
||||
.padding(bottom = 8.dp), style = typography.bodyMedium)
|
||||
}
|
||||
FunctionItem(R.string.permissions, icon = R.drawable.shield_fill0) { onNavigate(PermissionsManager(packageName)) }
|
||||
if(VERSION.SDK_INT >= 24) SwitchItem(
|
||||
@@ -401,7 +413,7 @@ fun PermissionsManagerScreen(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(if(selected) colorScheme.primaryContainer else Color.Transparent)
|
||||
.background(if (selected) colorScheme.primaryContainer else Color.Transparent)
|
||||
.clickable { changeState(status) }
|
||||
.padding(vertical = 16.dp, horizontal = 12.dp),
|
||||
Arrangement.SpaceBetween, Alignment.CenterVertically,
|
||||
@@ -609,14 +621,16 @@ fun InstallExistingAppScreen(
|
||||
fun CredentialManagerPolicyScreen(
|
||||
chosenPackage: Channel<String>, onChoosePackage: () -> Unit,
|
||||
cmPackages: MutableStateFlow<List<AppInfo>>, getCmPolicy: () -> Int,
|
||||
setCmPackage: (String, Boolean) -> Unit, setCmPolicy: (Int) -> Unit, onNavigateUp: () -> Unit
|
||||
setCmPackage: (List<String>, Boolean) -> Unit, setCmPolicy: (Int) -> Unit,
|
||||
onNavigateUp: () -> Unit
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
var policy by rememberSaveable { mutableIntStateOf(getCmPolicy()) }
|
||||
val packages by cmPackages.collectAsStateWithLifecycle()
|
||||
var packageName by rememberSaveable { mutableStateOf("") }
|
||||
var input by rememberSaveable { mutableStateOf("") }
|
||||
val inputPackages = parsePackageNames(input)
|
||||
LaunchedEffect(Unit) {
|
||||
packageName = chosenPackage.receive()
|
||||
input = chosenPackage.receive()
|
||||
}
|
||||
MyLazyScaffold(R.string.credential_manager_policy, onNavigateUp) {
|
||||
item {
|
||||
@@ -631,20 +645,20 @@ fun CredentialManagerPolicyScreen(
|
||||
Spacer(Modifier.padding(vertical = 4.dp))
|
||||
}
|
||||
if (policy != -1) items(packages, { it.name }) {
|
||||
ApplicationItem(it) { setCmPackage(it.name, false) }
|
||||
ApplicationItem(it) { setCmPackage(listOf(it.name), false) }
|
||||
}
|
||||
item {
|
||||
Column(Modifier.padding(horizontal = HorizontalPadding)) {
|
||||
if (policy != -1) {
|
||||
PackageNameTextField(packageName, onChoosePackage,
|
||||
Modifier.padding(vertical = 8.dp)) { packageName = it }
|
||||
PackageNameTextField(input, onChoosePackage,
|
||||
Modifier.padding(vertical = 8.dp)) { input = it }
|
||||
Button(
|
||||
{
|
||||
setCmPackage(packageName, true)
|
||||
packageName = ""
|
||||
setCmPackage(inputPackages, true)
|
||||
input = ""
|
||||
},
|
||||
Modifier.fillMaxWidth(),
|
||||
enabled = packageName.isValidPackageName
|
||||
inputPackages.all { it.isValidPackageName }
|
||||
) {
|
||||
Text(stringResource(R.string.add))
|
||||
}
|
||||
@@ -672,33 +686,37 @@ fun CredentialManagerPolicyScreen(
|
||||
fun PermittedAsAndImPackages(
|
||||
title: Int, note: Int, chosenPackage: Channel<String>, onChoosePackage: () -> Unit,
|
||||
packagesState: MutableStateFlow<List<AppInfo>>, getPackages: () -> Boolean,
|
||||
setPackage: (String, Boolean) -> Unit, setPolicy: (Boolean) -> Boolean, onNavigateUp: () -> Unit
|
||||
setPackage: (List<String>, Boolean) -> Unit, setPolicy: (Boolean) -> Boolean,
|
||||
onNavigateUp: () -> Unit
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val packages by packagesState.collectAsStateWithLifecycle()
|
||||
var packageName by rememberSaveable { mutableStateOf("") }
|
||||
var input by rememberSaveable { mutableStateOf("") }
|
||||
val inputPackages = parsePackageNames(input)
|
||||
var allowAll by rememberSaveable { mutableStateOf(getPackages()) }
|
||||
LaunchedEffect(Unit) {
|
||||
packageName = chosenPackage.receive()
|
||||
input = chosenPackage.receive()
|
||||
}
|
||||
MyLazyScaffold(title, onNavigateUp) {
|
||||
item {
|
||||
SwitchItem(R.string.allow_all, state = allowAll, onCheckedChange = { allowAll = it })
|
||||
}
|
||||
if (!allowAll) items(packages, { it.name }) {
|
||||
ApplicationItem(it) { setPackage(it.name, false) }
|
||||
ApplicationItem(it) { setPackage(listOf(it.name), false) }
|
||||
}
|
||||
item {
|
||||
if (!allowAll) {
|
||||
PackageNameTextField(packageName, onChoosePackage,
|
||||
Modifier.padding(HorizontalPadding, 8.dp)) { packageName = it }
|
||||
PackageNameTextField(input, onChoosePackage,
|
||||
Modifier.padding(HorizontalPadding, 8.dp)) { input = it }
|
||||
Button(
|
||||
{
|
||||
setPackage(packageName, true)
|
||||
packageName = ""
|
||||
setPackage(inputPackages, true)
|
||||
input = ""
|
||||
},
|
||||
Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding),
|
||||
packageName.isValidPackageName
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = HorizontalPadding),
|
||||
inputPackages.all { it.isValidPackageName }
|
||||
) {
|
||||
Text(stringResource(R.string.add))
|
||||
}
|
||||
@@ -707,7 +725,10 @@ fun PermittedAsAndImPackages(
|
||||
{
|
||||
context.showOperationResultToast(setPolicy(allowAll))
|
||||
},
|
||||
Modifier.fillMaxWidth().padding(top = 8.dp).padding(horizontal = HorizontalPadding)
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 8.dp)
|
||||
.padding(horizontal = HorizontalPadding)
|
||||
) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
@@ -777,35 +798,23 @@ fun SetDefaultDialerScreen(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PackageFunctionScreenWithoutResult(
|
||||
title: Int, packagesState: MutableStateFlow<List<AppInfo>>, onGet: () -> Unit,
|
||||
onSet: (String, Boolean) -> Unit, onNavigateUp: () -> Unit,
|
||||
chosenPackage: Channel<String>, onChoosePackage: () -> Unit,
|
||||
navigateToGroups: () -> Unit, appGroups: StateFlow<List<AppGroup>>, notes: Int? = null
|
||||
) {
|
||||
PackageFunctionScreen(
|
||||
title, packagesState, onGet, { name, status -> onSet(name, status); null },
|
||||
onNavigateUp, chosenPackage, onChoosePackage, navigateToGroups, appGroups, notes
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun PackageFunctionScreen(
|
||||
title: Int, packagesState: MutableStateFlow<List<AppInfo>>, onGet: () -> Unit,
|
||||
onSet: (String, Boolean) -> Boolean?, onNavigateUp: () -> Unit,
|
||||
onSet: (List<String>, Boolean) -> Unit, onNavigateUp: () -> Unit,
|
||||
chosenPackage: Channel<String>, onChoosePackage: () -> Unit,
|
||||
navigateToGroups: () -> Unit, appGroups: StateFlow<List<AppGroup>>, notes: Int? = null
|
||||
) {
|
||||
val groups by appGroups.collectAsStateWithLifecycle()
|
||||
val packages by packagesState.collectAsStateWithLifecycle()
|
||||
var packageName by rememberSaveable { mutableStateOf("") }
|
||||
var input by rememberSaveable { mutableStateOf("") }
|
||||
val inputPackages = parsePackageNames(input)
|
||||
var dialog by remember { mutableStateOf(false) }
|
||||
var selectedGroup by remember { mutableStateOf<AppGroup?>(null) }
|
||||
LaunchedEffect(Unit) {
|
||||
onGet()
|
||||
packageName = chosenPackage.receive()
|
||||
input = chosenPackage.receive()
|
||||
}
|
||||
Scaffold(
|
||||
topBar = {
|
||||
@@ -848,21 +857,23 @@ fun PackageFunctionScreen(
|
||||
LazyColumn(Modifier.padding(paddingValues)) {
|
||||
items(packages, { it.name }) {
|
||||
ApplicationItem(it) {
|
||||
onSet(it.name, false)
|
||||
onSet(listOf(it.name), false)
|
||||
}
|
||||
}
|
||||
item {
|
||||
PackageNameTextField(packageName, onChoosePackage,
|
||||
Modifier.padding(HorizontalPadding, 8.dp)) { packageName = it }
|
||||
PackageNameTextField(input, onChoosePackage,
|
||||
Modifier.padding(HorizontalPadding, 8.dp)) { input = it }
|
||||
Button(
|
||||
{
|
||||
if (onSet(packageName, true) != false) {
|
||||
packageName = ""
|
||||
}
|
||||
onSet(inputPackages, true)
|
||||
input = ""
|
||||
},
|
||||
Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding).padding(bottom = 10.dp),
|
||||
packageName.isValidPackageName &&
|
||||
packages.find { it.name == packageName } == null
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = HorizontalPadding)
|
||||
.padding(bottom = 10.dp),
|
||||
inputPackages.all { it.isValidPackageName } &&
|
||||
packages.none { it.name in inputPackages }
|
||||
) {
|
||||
Text(stringResource(R.string.add))
|
||||
}
|
||||
@@ -875,17 +886,13 @@ fun PackageFunctionScreen(
|
||||
text = {
|
||||
Column {
|
||||
Button({
|
||||
selectedGroup!!.apps.forEach {
|
||||
onSet(it, true)
|
||||
}
|
||||
onSet(selectedGroup!!.apps, true)
|
||||
dialog = false
|
||||
}) {
|
||||
Text(stringResource(R.string.add_to_list))
|
||||
}
|
||||
Button({
|
||||
selectedGroup!!.apps.forEach {
|
||||
onSet(it, false)
|
||||
}
|
||||
onSet(selectedGroup!!.apps, false)
|
||||
dialog = false
|
||||
}) {
|
||||
Text(stringResource(R.string.remove_from_list))
|
||||
@@ -930,9 +937,12 @@ fun ManageAppGroupsScreen(
|
||||
LazyColumn(Modifier.padding(paddingValues)) {
|
||||
items(groups, { it.id }) {
|
||||
Column(
|
||||
Modifier.fillMaxWidth().clickable {
|
||||
navigateToEditScreen(it.id, it.name, it.apps)
|
||||
}.padding(HorizontalPadding, 8.dp)
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
navigateToEditScreen(it.id, it.name, it.apps)
|
||||
}
|
||||
.padding(HorizontalPadding, 8.dp)
|
||||
) {
|
||||
Text(it.name)
|
||||
Text(
|
||||
@@ -957,9 +967,10 @@ fun EditAppGroupScreen(
|
||||
var name by rememberSaveable { mutableStateOf(params.name) }
|
||||
val list = rememberSaveable { mutableStateListOf(*params.apps.toTypedArray()) }
|
||||
val appInfoList = list.map { getAppInfo(it) }
|
||||
var packageName by rememberSaveable { mutableStateOf("") }
|
||||
var input by rememberSaveable { mutableStateOf("") }
|
||||
val inputPackages = parsePackageNames(input)
|
||||
LaunchedEffect(Unit) {
|
||||
packageName = chosenPackage.receive()
|
||||
input = chosenPackage.receive()
|
||||
}
|
||||
Scaffold(
|
||||
topBar = {
|
||||
@@ -992,7 +1003,9 @@ fun EditAppGroupScreen(
|
||||
LazyColumn(Modifier.padding(paddingValues)) {
|
||||
item {
|
||||
OutlinedTextField(
|
||||
name, { name = it }, Modifier.fillMaxWidth().padding(HorizontalPadding, 8.dp),
|
||||
name, { name = it }, Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(HorizontalPadding, 8.dp),
|
||||
label = { Text(stringResource(R.string.name)) }
|
||||
)
|
||||
}
|
||||
@@ -1002,15 +1015,18 @@ fun EditAppGroupScreen(
|
||||
}
|
||||
}
|
||||
item {
|
||||
PackageNameTextField(packageName, onChoosePackage,
|
||||
Modifier.padding(HorizontalPadding, 8.dp)) { packageName = it }
|
||||
PackageNameTextField(input, onChoosePackage,
|
||||
Modifier.padding(HorizontalPadding, 8.dp)) { input = it }
|
||||
Button(
|
||||
{
|
||||
list += packageName
|
||||
packageName = ""
|
||||
list += inputPackages
|
||||
input = ""
|
||||
},
|
||||
Modifier.fillMaxWidth().padding(horizontal = HorizontalPadding).padding(bottom = 10.dp),
|
||||
packageName.isValidPackageName && packageName !in list
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = HorizontalPadding)
|
||||
.padding(bottom = 10.dp),
|
||||
inputPackages.all { it.isValidPackageName && it !in list }
|
||||
) {
|
||||
Text(stringResource(R.string.add))
|
||||
}
|
||||
@@ -1053,7 +1069,9 @@ fun ManagedConfigurationScreen(
|
||||
}
|
||||
OutlinedTextField(
|
||||
searchKeyword, { searchKeyword = it },
|
||||
Modifier.fillMaxWidth().focusRequester(fr),
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.focusRequester(fr),
|
||||
textStyle = typography.bodyLarge,
|
||||
placeholder = { Text(stringResource(R.string.search)) },
|
||||
trailingIcon = {
|
||||
@@ -1092,9 +1110,12 @@ fun ManagedConfigurationScreen(
|
||||
LazyColumn(Modifier.padding(paddingValues)) {
|
||||
items(displayRestrictions, { it.key }) { entry ->
|
||||
Row(
|
||||
Modifier.fillMaxWidth().clickable {
|
||||
dialog = entry
|
||||
}.padding(HorizontalPadding, 8.dp),
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
dialog = entry
|
||||
}
|
||||
.padding(HorizontalPadding, 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
val iconId = when (entry) {
|
||||
@@ -1239,7 +1260,9 @@ fun ManagedConfigurationDialog(
|
||||
}
|
||||
}
|
||||
Row(
|
||||
Modifier.fillMaxWidth().padding(bottom = 4.dp),
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 4.dp),
|
||||
Arrangement.SpaceBetween, Alignment.CenterVertically
|
||||
) {
|
||||
Text(stringResource(R.string.specify_value))
|
||||
@@ -1277,9 +1300,12 @@ fun ManagedConfigurationDialog(
|
||||
is AppRestriction.ChoiceItem -> itemsIndexed(restriction.entryValues) { index, value ->
|
||||
val label = restriction.entries.getOrNull(index)
|
||||
Row(
|
||||
Modifier.fillMaxWidth().clickable {
|
||||
input = value
|
||||
}.padding(8.dp, 4.dp)
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
input = value
|
||||
}
|
||||
.padding(8.dp, 4.dp)
|
||||
) {
|
||||
RadioButton(input == value, { input = value })
|
||||
Spacer(Modifier.width(8.dp))
|
||||
@@ -1298,10 +1324,13 @@ fun ManagedConfigurationDialog(
|
||||
) { index, entry ->
|
||||
ReorderableItem(reorderableListState, entry.value) {
|
||||
Row(
|
||||
Modifier.fillMaxWidth().clickable {
|
||||
val old = multiSelectList[index]
|
||||
multiSelectList[index] = old.copy(selected = !old.selected)
|
||||
}.padding(8.dp, 4.dp),
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
val old = multiSelectList[index]
|
||||
multiSelectList[index] = old.copy(selected = !old.selected)
|
||||
}
|
||||
.padding(8.dp, 4.dp),
|
||||
Arrangement.SpaceBetween, Alignment.CenterVertically
|
||||
) {
|
||||
Row(Modifier.weight(1F), verticalAlignment = Alignment.CenterVertically) {
|
||||
@@ -1325,7 +1354,9 @@ fun ManagedConfigurationDialog(
|
||||
}
|
||||
}
|
||||
item {
|
||||
Row(Modifier.fillMaxWidth().padding(top = 4.dp), Arrangement.End) {
|
||||
Row(Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 4.dp), Arrangement.End) {
|
||||
TextButton({
|
||||
setRestriction(null)
|
||||
}, Modifier.padding(end = 4.dp)) {
|
||||
|
||||
@@ -1147,7 +1147,7 @@ fun NearbyStreamingPolicyScreen(
|
||||
@RequiresApi(28)
|
||||
@Composable
|
||||
fun LockTaskModeScreen(
|
||||
chosenPackage: Channel<String>, onChoosePackage: () -> Unit,
|
||||
chosenPackage: Channel<String>, chooseSinglePackage: () -> Unit, choosePackage: () -> Unit,
|
||||
lockTaskPackages: StateFlow<List<AppInfo>>, getLockTaskPackages: () -> Unit,
|
||||
setLockTaskPackage: (String, Boolean) -> Unit,
|
||||
startLockTaskMode: (String, String, Boolean, Boolean) -> Boolean,
|
||||
@@ -1191,9 +1191,9 @@ fun LockTaskModeScreen(
|
||||
}
|
||||
HorizontalPager(pagerState, verticalAlignment = Alignment.Top) { page ->
|
||||
if(page == 0) {
|
||||
StartLockTaskMode(startLockTaskMode, chosenPackage, onChoosePackage)
|
||||
StartLockTaskMode(startLockTaskMode, chosenPackage, chooseSinglePackage)
|
||||
} else if (page == 1) {
|
||||
LockTaskPackages(chosenPackage, onChoosePackage, lockTaskPackages, setLockTaskPackage)
|
||||
LockTaskPackages(chosenPackage, choosePackage, lockTaskPackages, setLockTaskPackage)
|
||||
} else {
|
||||
LockTaskFeatures(getLockTaskFeatures, setLockTaskFeature)
|
||||
}
|
||||
@@ -1231,7 +1231,7 @@ private fun StartLockTaskMode(
|
||||
R.string.lock_task_mode_start_clear_task, clearTask
|
||||
) { clearTask = it }
|
||||
FullWidthCheckBoxItem(
|
||||
R.string.lock_taso_mode_show_notification, showNotification
|
||||
R.string.lock_task_mode_show_notification, showNotification
|
||||
) { showNotification = it }
|
||||
Row(
|
||||
Modifier
|
||||
|
||||
@@ -174,7 +174,7 @@
|
||||
<string name="enable_if_secure_enough">在足够安全时启用</string>
|
||||
<string name="lock_task_mode">锁定任务模式</string>
|
||||
<string name="lock_task_mode_start_clear_task">清除任务(新实例)</string>
|
||||
<string name="lock_taso_mode_show_notification">显示通知以退出</string>
|
||||
<string name="lock_task_mode_show_notification">显示通知以退出</string>
|
||||
<string name="app_not_allowed">应用未被允许</string>
|
||||
<string name="disable_all">禁用全部</string>
|
||||
<string name="ltf_sys_info">允许状态栏信息</string>
|
||||
@@ -344,7 +344,9 @@
|
||||
<string name="show_user_app">显示用户应用</string>
|
||||
<string name="show_system_app">显示系统应用</string>
|
||||
<string name="suspend">挂起</string>
|
||||
<string name="unsuspend">取消挂起</string>
|
||||
<string name="hide">隐藏</string>
|
||||
<string name="unhide">取消隐藏</string>
|
||||
<string name="always_on_vpn">VPN保持打开</string>
|
||||
<string name="enable_lockdown">启用锁定</string>
|
||||
<string name="clear_current_config">清除当前配置</string>
|
||||
|
||||
@@ -202,7 +202,7 @@
|
||||
<string name="enable_if_secure_enough">Same managed account only</string>
|
||||
<string name="lock_task_mode">Lock task mode</string>
|
||||
<string name="lock_task_mode_start_clear_task">Clear task (start fresh)</string>
|
||||
<string name="lock_taso_mode_show_notification">Show a notification to exit</string>
|
||||
<string name="lock_task_mode_show_notification">Show a notification to exit</string>
|
||||
<string name="app_not_allowed">App is not allowed</string>
|
||||
<string name="disable_all">Disable all</string>
|
||||
<!--ltf: lock task feature-->
|
||||
@@ -378,7 +378,9 @@
|
||||
<string name="show_user_app">Show user apps</string>
|
||||
<string name="show_system_app">Show system apps</string>
|
||||
<string name="suspend">Suspend</string>
|
||||
<string name="unsuspend">Unsuspend</string>
|
||||
<string name="hide">Hide</string>
|
||||
<string name="unhide">Unhide</string>
|
||||
<string name="always_on_vpn">Always-on VPN</string>
|
||||
<string name="enable_lockdown">Enable lockdown</string>
|
||||
<string name="clear_current_config">Clear current config</string>
|
||||
|
||||
Reference in New Issue
Block a user