Merge changes from topic "AppOpPermissionAppListTest"
* changes: Refactor TogglePermissionAppInfoPageProvider Hide not changeable app from AppOpPermissionAppList Add tests for Flows & StateFlowBridge
This commit is contained in:
@@ -16,15 +16,10 @@
|
||||
|
||||
package com.android.settingslib.spa.framework.util
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.State
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChangedBy
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.take
|
||||
|
||||
/**
|
||||
* Returns a [Flow] whose values are a list which containing the results of applying the given
|
||||
@@ -40,34 +35,14 @@ inline fun <T, R> Flow<List<T>>.mapItem(crossinline transform: (T) -> R): Flow<L
|
||||
inline fun <T, R> Flow<List<T>>.asyncMapItem(crossinline transform: (T) -> R): Flow<List<R>> =
|
||||
map { list -> list.asyncMap(transform) }
|
||||
|
||||
/**
|
||||
* Returns a [Flow] whose values are a list containing only elements matching the given [predicate].
|
||||
*/
|
||||
inline fun <T> Flow<List<T>>.filterItem(crossinline predicate: (T) -> Boolean): Flow<List<T>> =
|
||||
map { list -> list.filter(predicate) }
|
||||
|
||||
/**
|
||||
* Delays the flow a little bit, wait the other flow's first value.
|
||||
*/
|
||||
fun <T1, T2> Flow<T1>.waitFirst(otherFlow: Flow<T2>): Flow<T1> =
|
||||
combine(otherFlow.distinctUntilChangedBy {}) { value, _ -> value }
|
||||
|
||||
/**
|
||||
* Returns a [Flow] whose values are generated list by combining the most recently emitted non null
|
||||
* values by each flow.
|
||||
*/
|
||||
inline fun <reified T : Any> combineToList(vararg flows: Flow<T?>): Flow<List<T>> = combine(
|
||||
flows.asList(),
|
||||
) { array: Array<T?> -> array.filterNotNull() }
|
||||
|
||||
class StateFlowBridge<T> {
|
||||
private val stateFlow = MutableStateFlow<T?>(null)
|
||||
val flow = stateFlow.filterNotNull()
|
||||
|
||||
fun setIfAbsent(value: T) {
|
||||
if (stateFlow.value == null) {
|
||||
stateFlow.value = value
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Sync(state: State<T>) {
|
||||
LaunchedEffect(state.value) {
|
||||
stateFlow.value = state.value
|
||||
}
|
||||
}
|
||||
}
|
||||
combine(otherFlow.take(1)) { value, _ -> value }
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settingslib.spa.framework.util
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.State
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
|
||||
/** A StateFlow holder which value could be set or sync from [State]. */
|
||||
class StateFlowBridge<T> {
|
||||
private val stateFlow = MutableStateFlow<T?>(null)
|
||||
val flow = stateFlow.filterNotNull()
|
||||
|
||||
fun setIfAbsent(value: T) {
|
||||
if (stateFlow.value == null) {
|
||||
stateFlow.value = value
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Sync(state: State<T>) {
|
||||
LaunchedEffect(state.value) {
|
||||
stateFlow.value = state.value
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settingslib.spa.framework.util
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.count
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class FlowsTest {
|
||||
@Test
|
||||
fun mapItem() = runTest {
|
||||
val inputFlow = flowOf(listOf("A", "BB", "CCC"))
|
||||
|
||||
val outputFlow = inputFlow.mapItem { it.length }
|
||||
|
||||
assertThat(outputFlow.first()).containsExactly(1, 2, 3).inOrder()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun asyncMapItem() = runTest {
|
||||
val inputFlow = flowOf(listOf("A", "BB", "CCC"))
|
||||
|
||||
val outputFlow = inputFlow.asyncMapItem { it.length }
|
||||
|
||||
assertThat(outputFlow.first()).containsExactly(1, 2, 3).inOrder()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun filterItem() = runTest {
|
||||
val inputFlow = flowOf(listOf("A", "BB", "CCC"))
|
||||
|
||||
val outputFlow = inputFlow.filterItem { it.length >= 2 }
|
||||
|
||||
assertThat(outputFlow.first()).containsExactly("BB", "CCC").inOrder()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun waitFirst_otherFlowEmpty() = runTest {
|
||||
val mainFlow = flowOf("A")
|
||||
val otherFlow = emptyFlow<String>()
|
||||
|
||||
val outputFlow = mainFlow.waitFirst(otherFlow)
|
||||
|
||||
assertThat(outputFlow.count()).isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun waitFirst_otherFlowOneValue() = runTest {
|
||||
val mainFlow = flowOf("A")
|
||||
val otherFlow = flowOf("B")
|
||||
|
||||
val outputFlow = mainFlow.waitFirst(otherFlow)
|
||||
|
||||
assertThat(outputFlow.toList()).containsExactly("A")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun waitFirst_otherFlowTwoValues() = runTest {
|
||||
val mainFlow = flowOf("A")
|
||||
val otherFlow = flowOf("B", "B")
|
||||
|
||||
val outputFlow = mainFlow.waitFirst(otherFlow)
|
||||
|
||||
assertThat(outputFlow.toList()).containsExactly("A")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settingslib.spa.framework.util
|
||||
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settingslib.spa.framework.compose.stateOf
|
||||
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class StateFlowBridgeTest {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
@Test
|
||||
fun stateFlowBridge_initial() = runTest {
|
||||
val stateFlowBridge = StateFlowBridge<String>()
|
||||
|
||||
val flow = stateFlowBridge.flow
|
||||
|
||||
val first = flow.firstWithTimeoutOrNull()
|
||||
assertThat(first).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun stateFlowBridge_setIfAbsent() = runTest {
|
||||
val stateFlowBridge = StateFlowBridge<String>()
|
||||
|
||||
stateFlowBridge.setIfAbsent("A")
|
||||
|
||||
val first = stateFlowBridge.flow.firstWithTimeoutOrNull()
|
||||
assertThat(first).isEqualTo("A")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun stateFlowBridge_sync() = runTest {
|
||||
val stateFlowBridge = StateFlowBridge<String>()
|
||||
|
||||
composeTestRule.setContent {
|
||||
stateFlowBridge.Sync(stateOf("A"))
|
||||
}
|
||||
|
||||
val first = stateFlowBridge.flow.firstWithTimeoutOrNull()
|
||||
assertThat(first).isEqualTo("A")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settingslib.spa.testutils
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
|
||||
suspend fun <T> Flow<T>.firstWithTimeoutOrNull(timeMillis: Long = 500): T? =
|
||||
withTimeoutOrNull(timeMillis) {
|
||||
first()
|
||||
}
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.android.settingslib.spaprivileged.model.app
|
||||
|
||||
import android.app.AppOpsManager
|
||||
import android.app.AppOpsManager.MODE_ALLOWED
|
||||
import android.app.AppOpsManager.MODE_ERRORED
|
||||
import android.app.AppOpsManager.Mode
|
||||
@@ -25,34 +24,41 @@ import android.content.pm.ApplicationInfo
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.map
|
||||
import com.android.settingslib.spaprivileged.framework.common.appOpsManager
|
||||
|
||||
interface IAppOpsController {
|
||||
val mode: LiveData<Int>
|
||||
val isAllowed: LiveData<Boolean>
|
||||
get() = mode.map { it == MODE_ALLOWED }
|
||||
|
||||
fun setAllowed(allowed: Boolean)
|
||||
|
||||
@Mode
|
||||
fun getMode(): Int
|
||||
}
|
||||
|
||||
class AppOpsController(
|
||||
context: Context,
|
||||
private val app: ApplicationInfo,
|
||||
private val op: Int,
|
||||
) {
|
||||
private val appOpsManager = checkNotNull(context.getSystemService(AppOpsManager::class.java))
|
||||
) : IAppOpsController {
|
||||
private val appOpsManager = context.appOpsManager
|
||||
|
||||
val mode: LiveData<Int>
|
||||
override val mode: LiveData<Int>
|
||||
get() = _mode
|
||||
val isAllowed: LiveData<Boolean>
|
||||
get() = _mode.map { it == MODE_ALLOWED }
|
||||
|
||||
fun setAllowed(allowed: Boolean) {
|
||||
override fun setAllowed(allowed: Boolean) {
|
||||
val mode = if (allowed) MODE_ALLOWED else MODE_ERRORED
|
||||
appOpsManager.setMode(op, app.uid, app.packageName, mode)
|
||||
_mode.postValue(mode)
|
||||
}
|
||||
|
||||
@Mode
|
||||
fun getMode(): Int = appOpsManager.checkOpNoThrow(op, app.uid, app.packageName)
|
||||
override fun getMode(): Int = appOpsManager.checkOpNoThrow(op, app.uid, app.packageName)
|
||||
|
||||
private val _mode = object : MutableLiveData<Int>() {
|
||||
override fun onActive() {
|
||||
postValue(getMode())
|
||||
}
|
||||
|
||||
override fun onInactive() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +74,8 @@ interface RestrictionsProvider {
|
||||
fun restrictedModeState(): State<RestrictedMode?>
|
||||
}
|
||||
|
||||
typealias RestrictionsProviderFactory = (Context, Restrictions) -> RestrictionsProvider
|
||||
|
||||
internal class RestrictionsProviderImpl(
|
||||
private val context: Context,
|
||||
private val restrictions: Restrictions,
|
||||
|
||||
@@ -16,11 +16,12 @@
|
||||
|
||||
package com.android.settingslib.spaprivileged.template.app
|
||||
|
||||
import android.content.pm.PackageInfo
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
|
||||
import com.android.settingslib.spa.widget.ui.Footer
|
||||
import com.android.settingslib.spaprivileged.model.app.PackageManagers
|
||||
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
|
||||
|
||||
@Composable
|
||||
fun AppInfoPage(
|
||||
@@ -28,18 +29,16 @@ fun AppInfoPage(
|
||||
packageName: String,
|
||||
userId: Int,
|
||||
footerText: String,
|
||||
content: @Composable () -> Unit,
|
||||
packageManagers: IPackageManagers,
|
||||
content: @Composable PackageInfo.() -> Unit,
|
||||
) {
|
||||
val packageInfo = remember(packageName, userId) {
|
||||
packageManagers.getPackageInfoAsUser(packageName, userId)
|
||||
} ?: return
|
||||
RegularScaffold(title = title) {
|
||||
val appInfoProvider = remember {
|
||||
PackageManagers.getPackageInfoAsUser(packageName, userId)?.let { packageInfo ->
|
||||
AppInfoProvider(packageInfo)
|
||||
}
|
||||
} ?: return@RegularScaffold
|
||||
remember(packageInfo) { AppInfoProvider(packageInfo) }.AppInfo(displayVersion = true)
|
||||
|
||||
appInfoProvider.AppInfo(displayVersion = true)
|
||||
|
||||
content()
|
||||
packageInfo.content()
|
||||
|
||||
Footer(footerText)
|
||||
}
|
||||
|
||||
@@ -25,11 +25,12 @@ import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.remember
|
||||
import com.android.settingslib.spa.framework.util.filterItem
|
||||
import com.android.settingslib.spaprivileged.model.app.AppOpsController
|
||||
import com.android.settingslib.spaprivileged.model.app.AppRecord
|
||||
import com.android.settingslib.spaprivileged.model.app.IAppOpsController
|
||||
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
|
||||
import com.android.settingslib.spaprivileged.model.app.PackageManagers
|
||||
import com.android.settingslib.spaprivileged.model.app.PackageManagers.hasGrantPermission
|
||||
import com.android.settingslib.spaprivileged.model.app.PackageManagers.hasRequestPermission
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.map
|
||||
@@ -37,21 +38,24 @@ import kotlinx.coroutines.flow.map
|
||||
data class AppOpPermissionRecord(
|
||||
override val app: ApplicationInfo,
|
||||
val hasRequestPermission: Boolean,
|
||||
var appOpsController: AppOpsController,
|
||||
var appOpsController: IAppOpsController,
|
||||
) : AppRecord
|
||||
|
||||
abstract class AppOpPermissionListModel(private val context: Context) :
|
||||
TogglePermissionAppListModel<AppOpPermissionRecord> {
|
||||
abstract class AppOpPermissionListModel(
|
||||
private val context: Context,
|
||||
private val packageManagers: IPackageManagers = PackageManagers,
|
||||
) : TogglePermissionAppListModel<AppOpPermissionRecord> {
|
||||
|
||||
abstract val appOp: Int
|
||||
abstract val permission: String
|
||||
|
||||
/** These not changeable packages will also be hidden from app list. */
|
||||
private val notChangeablePackages =
|
||||
setOf("android", "com.android.systemui", context.packageName)
|
||||
|
||||
override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) =
|
||||
userIdFlow.map { userId ->
|
||||
PackageManagers.getAppOpPermissionPackages(userId, permission)
|
||||
packageManagers.getAppOpPermissionPackages(userId, permission)
|
||||
}.combine(appListFlow) { packageNames, appList ->
|
||||
appList.map { app ->
|
||||
AppOpPermissionRecord(
|
||||
@@ -64,14 +68,12 @@ abstract class AppOpPermissionListModel(private val context: Context) :
|
||||
|
||||
override fun transformItem(app: ApplicationInfo) = AppOpPermissionRecord(
|
||||
app = app,
|
||||
hasRequestPermission = app.hasRequestPermission(permission),
|
||||
hasRequestPermission = with(packageManagers) { app.hasRequestPermission(permission) },
|
||||
appOpsController = AppOpsController(context = context, app = app, op = appOp),
|
||||
)
|
||||
|
||||
override fun filter(userIdFlow: Flow<Int>, recordListFlow: Flow<List<AppOpPermissionRecord>>) =
|
||||
recordListFlow.map { recordList ->
|
||||
recordList.filter { it.hasRequestPermission }
|
||||
}
|
||||
recordListFlow.filterItem(::isChangeable)
|
||||
|
||||
/**
|
||||
* Defining the default behavior as permissible as long as the package requested this permission
|
||||
@@ -85,7 +87,9 @@ abstract class AppOpPermissionListModel(private val context: Context) :
|
||||
when (mode.value) {
|
||||
null -> null
|
||||
MODE_ALLOWED -> true
|
||||
MODE_DEFAULT -> record.app.hasGrantPermission(permission)
|
||||
MODE_DEFAULT -> with(packageManagers) {
|
||||
record.app.hasGrantPermission(permission)
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.settingslib.spaprivileged.template.app
|
||||
import android.content.Context
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.State
|
||||
@@ -38,23 +39,15 @@ import com.android.settingslib.spa.widget.preference.Preference
|
||||
import com.android.settingslib.spa.widget.preference.PreferenceModel
|
||||
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
|
||||
import com.android.settingslib.spaprivileged.model.app.AppRecord
|
||||
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
|
||||
import com.android.settingslib.spaprivileged.model.app.PackageManagers
|
||||
import com.android.settingslib.spaprivileged.model.app.toRoute
|
||||
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
|
||||
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory
|
||||
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl
|
||||
import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreference
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
||||
private const val ENTRY_NAME = "AllowControl"
|
||||
private const val PERMISSION = "permission"
|
||||
private const val PACKAGE_NAME = "rt_packageName"
|
||||
private const val USER_ID = "rt_userId"
|
||||
private const val PAGE_NAME = "TogglePermissionAppInfoPage"
|
||||
private val PAGE_PARAMETER = listOf(
|
||||
navArgument(PERMISSION) { type = NavType.StringType },
|
||||
navArgument(PACKAGE_NAME) { type = NavType.StringType },
|
||||
navArgument(USER_ID) { type = NavType.IntType },
|
||||
)
|
||||
|
||||
internal class TogglePermissionAppInfoPageProvider(
|
||||
private val appListTemplate: TogglePermissionAppListTemplate,
|
||||
) : SettingsPageProvider {
|
||||
@@ -64,11 +57,7 @@ internal class TogglePermissionAppInfoPageProvider(
|
||||
|
||||
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
|
||||
val owner = SettingsPage.create(name, parameter = parameter, arguments = arguments)
|
||||
val entryList = mutableListOf<SettingsEntry>()
|
||||
entryList.add(
|
||||
SettingsEntryBuilder.create(ENTRY_NAME, owner).build()
|
||||
)
|
||||
return entryList
|
||||
return listOf(SettingsEntryBuilder.create("AllowControl", owner).build())
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -76,11 +65,22 @@ internal class TogglePermissionAppInfoPageProvider(
|
||||
val permissionType = arguments?.getString(PERMISSION)!!
|
||||
val packageName = arguments.getString(PACKAGE_NAME)!!
|
||||
val userId = arguments.getInt(USER_ID)
|
||||
val listModel = appListTemplate.rememberModel(permissionType)
|
||||
TogglePermissionAppInfoPage(listModel, packageName, userId)
|
||||
appListTemplate.rememberModel(permissionType)
|
||||
.TogglePermissionAppInfoPage(packageName, userId)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PAGE_NAME = "TogglePermissionAppInfoPage"
|
||||
private const val PERMISSION = "permission"
|
||||
private const val PACKAGE_NAME = "rt_packageName"
|
||||
private const val USER_ID = "rt_userId"
|
||||
|
||||
private val PAGE_PARAMETER = listOf(
|
||||
navArgument(PERMISSION) { type = NavType.StringType },
|
||||
navArgument(PACKAGE_NAME) { type = NavType.StringType },
|
||||
navArgument(USER_ID) { type = NavType.IntType },
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun navigator(permissionType: String, app: ApplicationInfo) =
|
||||
navigator(route = "$PAGE_NAME/$permissionType/${app.toRoute()}")
|
||||
@@ -116,43 +116,36 @@ internal class TogglePermissionAppInfoPageProvider(
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Composable
|
||||
private fun TogglePermissionAppInfoPage(
|
||||
listModel: TogglePermissionAppListModel<out AppRecord>,
|
||||
internal fun TogglePermissionAppListModel<out AppRecord>.TogglePermissionAppInfoPage(
|
||||
packageName: String,
|
||||
userId: Int,
|
||||
packageManagers: IPackageManagers = PackageManagers,
|
||||
restrictionsProviderFactory: RestrictionsProviderFactory = ::RestrictionsProviderImpl,
|
||||
) {
|
||||
AppInfoPage(
|
||||
title = stringResource(listModel.pageTitleResId),
|
||||
title = stringResource(pageTitleResId),
|
||||
packageName = packageName,
|
||||
userId = userId,
|
||||
footerText = stringResource(listModel.footerResId),
|
||||
footerText = stringResource(footerResId),
|
||||
packageManagers = packageManagers,
|
||||
) {
|
||||
val model = createSwitchModel(listModel, packageName, userId) ?: return@AppInfoPage
|
||||
LaunchedEffect(model, Dispatchers.Default) {
|
||||
model.initState()
|
||||
}
|
||||
RestrictedSwitchPreference(model, Restrictions(userId, listModel.switchRestrictionKeys))
|
||||
val model = createSwitchModel(applicationInfo)
|
||||
val restrictions = Restrictions(userId, switchRestrictionKeys)
|
||||
RestrictedSwitchPreference(model, restrictions, restrictionsProviderFactory)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun <T : AppRecord> createSwitchModel(
|
||||
listModel: TogglePermissionAppListModel<T>,
|
||||
packageName: String,
|
||||
userId: Int,
|
||||
): TogglePermissionSwitchModel<T>? {
|
||||
val record = remember {
|
||||
PackageManagers.getApplicationInfoAsUser(packageName, userId)?.let { app ->
|
||||
listModel.transformItem(app)
|
||||
}
|
||||
} ?: return null
|
||||
|
||||
private fun <T : AppRecord> TogglePermissionAppListModel<T>.createSwitchModel(
|
||||
app: ApplicationInfo,
|
||||
): TogglePermissionSwitchModel<T> {
|
||||
val context = LocalContext.current
|
||||
val isAllowed = listModel.isAllowed(record)
|
||||
return remember {
|
||||
TogglePermissionSwitchModel(context, listModel, record, isAllowed)
|
||||
}
|
||||
val record = remember(app) { transformItem(app) }
|
||||
val isAllowed = isAllowed(record)
|
||||
return remember(record) { TogglePermissionSwitchModel(context, this, record, isAllowed) }
|
||||
.also { model -> LaunchedEffect(model, Dispatchers.IO) { model.initState() } }
|
||||
}
|
||||
|
||||
private class TogglePermissionSwitchModel<T : AppRecord>(
|
||||
|
||||
@@ -38,19 +38,14 @@ import com.android.settingslib.spaprivileged.model.enterprise.BlockedByAdmin
|
||||
import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
|
||||
import com.android.settingslib.spaprivileged.model.enterprise.RestrictedMode
|
||||
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
|
||||
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProvider
|
||||
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderFactory
|
||||
import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProviderImpl
|
||||
|
||||
@Composable
|
||||
fun RestrictedSwitchPreference(model: SwitchPreferenceModel, restrictions: Restrictions) {
|
||||
RestrictedSwitchPreferenceImpl(model, restrictions, ::RestrictionsProviderImpl)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun RestrictedSwitchPreferenceImpl(
|
||||
fun RestrictedSwitchPreference(
|
||||
model: SwitchPreferenceModel,
|
||||
restrictions: Restrictions,
|
||||
restrictionsProviderFactory: (Context, Restrictions) -> RestrictionsProvider,
|
||||
restrictionsProviderFactory: RestrictionsProviderFactory = ::RestrictionsProviderImpl,
|
||||
) {
|
||||
if (restrictions.keys.isEmpty()) {
|
||||
SwitchPreference(model)
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.settingslib.spaprivileged.tests">
|
||||
package="com.android.settingslib.spaprivileged.test">
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
@@ -24,5 +24,5 @@
|
||||
<instrumentation
|
||||
android:name="androidx.test.runner.AndroidJUnitRunner"
|
||||
android:label="Tests for SpaPrivilegedLib"
|
||||
android:targetPackage="com.android.settingslib.spaprivileged.tests" />
|
||||
android:targetPackage="com.android.settingslib.spaprivileged.test" />
|
||||
</manifest>
|
||||
|
||||
@@ -22,5 +22,14 @@
|
||||
<string name="test_permission_switch_title" translatable="false">Allow Test Permission</string>
|
||||
|
||||
<!-- Test Permission footer. [DO NOT TRANSLATE] -->
|
||||
<string name="test_permission_footer" translatable="false">Test Permission is for demo.</string>
|
||||
<string name="test_permission_footer" translatable="false">Test Permission is for testing.</string>
|
||||
|
||||
<!-- Test App Op Permission title. [DO NOT TRANSLATE] -->
|
||||
<string name="test_app_op_permission_title" translatable="false">Test App Op Permission</string>
|
||||
|
||||
<!-- Test App Op Permission switch title. [DO NOT TRANSLATE] -->
|
||||
<string name="test_app_op_permission_switch_title" translatable="false">Allow Test App Op Permission</string>
|
||||
|
||||
<!-- Test App Op Permission footer. [DO NOT TRANSLATE] -->
|
||||
<string name="test_app_op_permission_footer" translatable="false">Test App Op Permission is for testing.</string>
|
||||
</resources>
|
||||
|
||||
@@ -36,7 +36,7 @@ class AppInfoTest {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private var context: Context = ApplicationProvider.getApplicationContext()
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
|
||||
@Test
|
||||
fun appInfoLabel_isDisplayed() {
|
||||
|
||||
@@ -41,7 +41,7 @@ class AppListPageTest {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private var context: Context = ApplicationProvider.getApplicationContext()
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
|
||||
@Test
|
||||
fun title_isDisplayed() {
|
||||
|
||||
@@ -44,7 +44,7 @@ class AppListTest {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private var context: Context = ApplicationProvider.getApplicationContext()
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
|
||||
@Test
|
||||
fun whenNoApps() {
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settingslib.spaprivileged.template.app
|
||||
|
||||
import android.app.AppOpsManager
|
||||
import android.content.Context
|
||||
import android.content.pm.ApplicationInfo
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.lifecycle.liveData
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
|
||||
import com.android.settingslib.spaprivileged.model.app.IAppOpsController
|
||||
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
|
||||
import com.android.settingslib.spaprivileged.test.R
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import org.mockito.junit.MockitoRule
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class AppOpPermissionAppListTest {
|
||||
@get:Rule
|
||||
val mockito: MockitoRule = MockitoJUnit.rule()
|
||||
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
|
||||
@Mock
|
||||
private lateinit var packageManagers: IPackageManagers
|
||||
|
||||
private lateinit var listModel: TestAppOpPermissionAppListModel
|
||||
|
||||
@Before
|
||||
fun setUp() = runTest {
|
||||
whenever(packageManagers.getAppOpPermissionPackages(USER_ID, PERMISSION))
|
||||
.thenReturn(emptySet())
|
||||
listModel = TestAppOpPermissionAppListModel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transformItem_recordHasCorrectApp() {
|
||||
val record = listModel.transformItem(APP)
|
||||
|
||||
assertThat(record.app).isSameInstanceAs(APP)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transformItem_hasRequestPermission() = runTest {
|
||||
with(packageManagers) {
|
||||
whenever(APP.hasRequestPermission(PERMISSION)).thenReturn(true)
|
||||
}
|
||||
|
||||
val record = listModel.transformItem(APP)
|
||||
|
||||
assertThat(record.hasRequestPermission).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transformItem_notRequestPermission() = runTest {
|
||||
with(packageManagers) {
|
||||
whenever(APP.hasRequestPermission(PERMISSION)).thenReturn(false)
|
||||
}
|
||||
|
||||
val record = listModel.transformItem(APP)
|
||||
|
||||
assertThat(record.hasRequestPermission).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun filter() = runTest {
|
||||
with(packageManagers) {
|
||||
whenever(APP.hasRequestPermission(PERMISSION)).thenReturn(false)
|
||||
}
|
||||
val record = AppOpPermissionRecord(
|
||||
app = APP,
|
||||
hasRequestPermission = false,
|
||||
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
|
||||
)
|
||||
|
||||
val recordListFlow = listModel.filter(flowOf(USER_ID), flowOf(listOf(record)))
|
||||
|
||||
val recordList = recordListFlow.firstWithTimeoutOrNull()!!
|
||||
assertThat(recordList).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isAllowed_allowed() {
|
||||
val record = AppOpPermissionRecord(
|
||||
app = APP,
|
||||
hasRequestPermission = true,
|
||||
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_ALLOWED),
|
||||
)
|
||||
|
||||
val isAllowed = getIsAllowed(record)
|
||||
|
||||
assertThat(isAllowed).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isAllowed_defaultAndHasGrantPermission() {
|
||||
with(packageManagers) {
|
||||
whenever(APP.hasGrantPermission(PERMISSION)).thenReturn(true)
|
||||
}
|
||||
val record = AppOpPermissionRecord(
|
||||
app = APP,
|
||||
hasRequestPermission = true,
|
||||
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
|
||||
)
|
||||
|
||||
val isAllowed = getIsAllowed(record)
|
||||
|
||||
assertThat(isAllowed).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isAllowed_defaultAndNotGrantPermission() {
|
||||
with(packageManagers) {
|
||||
whenever(APP.hasGrantPermission(PERMISSION)).thenReturn(false)
|
||||
}
|
||||
val record = AppOpPermissionRecord(
|
||||
app = APP,
|
||||
hasRequestPermission = true,
|
||||
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
|
||||
)
|
||||
|
||||
val isAllowed = getIsAllowed(record)
|
||||
|
||||
assertThat(isAllowed).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isAllowed_notAllowed() {
|
||||
val record = AppOpPermissionRecord(
|
||||
app = APP,
|
||||
hasRequestPermission = true,
|
||||
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_ERRORED),
|
||||
)
|
||||
|
||||
val isAllowed = getIsAllowed(record)
|
||||
|
||||
assertThat(isAllowed).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isChangeable_notRequestPermission() {
|
||||
val record = AppOpPermissionRecord(
|
||||
app = APP,
|
||||
hasRequestPermission = false,
|
||||
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
|
||||
)
|
||||
|
||||
val isChangeable = listModel.isChangeable(record)
|
||||
|
||||
assertThat(isChangeable).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isChangeable_notChangeablePackages() {
|
||||
val record = AppOpPermissionRecord(
|
||||
app = NOT_CHANGEABLE_APP,
|
||||
hasRequestPermission = true,
|
||||
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
|
||||
)
|
||||
|
||||
val isChangeable = listModel.isChangeable(record)
|
||||
|
||||
assertThat(isChangeable).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isChangeable_hasRequestPermissionAndChangeable() {
|
||||
val record = AppOpPermissionRecord(
|
||||
app = APP,
|
||||
hasRequestPermission = true,
|
||||
appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
|
||||
)
|
||||
|
||||
val isChangeable = listModel.isChangeable(record)
|
||||
|
||||
assertThat(isChangeable).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setAllowed() {
|
||||
val appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT)
|
||||
val record = AppOpPermissionRecord(
|
||||
app = APP,
|
||||
hasRequestPermission = true,
|
||||
appOpsController = appOpsController,
|
||||
)
|
||||
|
||||
listModel.setAllowed(record = record, newAllowed = true)
|
||||
|
||||
assertThat(appOpsController.setAllowedCalledWith).isTrue()
|
||||
}
|
||||
|
||||
private fun getIsAllowed(record: AppOpPermissionRecord): Boolean? {
|
||||
lateinit var isAllowedState: State<Boolean?>
|
||||
composeTestRule.setContent {
|
||||
isAllowedState = listModel.isAllowed(record)
|
||||
}
|
||||
return isAllowedState.value
|
||||
}
|
||||
|
||||
private inner class TestAppOpPermissionAppListModel :
|
||||
AppOpPermissionListModel(context, packageManagers) {
|
||||
override val pageTitleResId = R.string.test_app_op_permission_title
|
||||
override val switchTitleResId = R.string.test_app_op_permission_switch_title
|
||||
override val footerResId = R.string.test_app_op_permission_footer
|
||||
override val appOp = AppOpsManager.OP_MANAGE_MEDIA
|
||||
override val permission = PERMISSION
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val USER_ID = 0
|
||||
const val PACKAGE_NAME = "package.name"
|
||||
const val PERMISSION = "PERMISSION"
|
||||
val APP = ApplicationInfo().apply {
|
||||
packageName = PACKAGE_NAME
|
||||
}
|
||||
val NOT_CHANGEABLE_APP = ApplicationInfo().apply {
|
||||
packageName = "android"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class FakeAppOpsController(private val fakeMode: Int) : IAppOpsController {
|
||||
var setAllowedCalledWith: Boolean? = null
|
||||
|
||||
override val mode = liveData { emit(fakeMode) }
|
||||
|
||||
override fun setAllowed(allowed: Boolean) {
|
||||
setAllowedCalledWith = allowed
|
||||
}
|
||||
|
||||
override fun getMode() = fakeMode
|
||||
}
|
||||
@@ -48,7 +48,7 @@ class AppStorageSizeTest {
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
@Spy
|
||||
private var context: Context = ApplicationProvider.getApplicationContext()
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
|
||||
@Mock
|
||||
private lateinit var storageStatsManager: StorageStatsManager
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settingslib.spaprivileged.template.app
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageInfo
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.assertIsNotEnabled
|
||||
import androidx.compose.ui.test.assertIsOff
|
||||
import androidx.compose.ui.test.assertIsOn
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
|
||||
import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
|
||||
import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsProvider
|
||||
import com.android.settingslib.spaprivileged.tests.testutils.TestTogglePermissionAppListModel
|
||||
import com.android.settingslib.spaprivileged.tests.testutils.TestTogglePermissionAppListProvider
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import org.mockito.junit.MockitoRule
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class TogglePermissionAppInfoPageTest {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
@get:Rule
|
||||
val mockito: MockitoRule = MockitoJUnit.rule()
|
||||
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
|
||||
@Mock
|
||||
private lateinit var packageManagers: IPackageManagers
|
||||
|
||||
private val fakeRestrictionsProvider = FakeRestrictionsProvider()
|
||||
|
||||
private val appListTemplate =
|
||||
TogglePermissionAppListTemplate(listOf(TestTogglePermissionAppListProvider))
|
||||
|
||||
private val appInfoPageProvider = TogglePermissionAppInfoPageProvider(appListTemplate)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
fakeRestrictionsProvider.restrictedMode = NoRestricted
|
||||
whenever(packageManagers.getPackageInfoAsUser(PACKAGE_NAME, USER_ID))
|
||||
.thenReturn(PACKAGE_INFO)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun buildEntry() {
|
||||
val entryList = appInfoPageProvider.buildEntry(null)
|
||||
|
||||
assertThat(entryList).hasSize(1)
|
||||
assertThat(entryList[0].displayName).isEqualTo("AllowControl")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun title_isDisplayed() {
|
||||
val listModel = TestTogglePermissionAppListModel()
|
||||
|
||||
setTogglePermissionAppInfoPage(listModel)
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(listModel.pageTitleResId))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenAllowed_switchIsOn() {
|
||||
val listModel = TestTogglePermissionAppListModel(isAllowed = true)
|
||||
|
||||
setTogglePermissionAppInfoPage(listModel)
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(listModel.switchTitleResId))
|
||||
.assertIsOn()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenNotAllowed_switchIsOff() {
|
||||
val listModel = TestTogglePermissionAppListModel(isAllowed = false)
|
||||
|
||||
setTogglePermissionAppInfoPage(listModel)
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(listModel.switchTitleResId))
|
||||
.assertIsOff()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun whenNotChangeable_switchNotEnabled() {
|
||||
val listModel = TestTogglePermissionAppListModel(isAllowed = false, isChangeable = false)
|
||||
|
||||
setTogglePermissionAppInfoPage(listModel)
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(listModel.switchTitleResId))
|
||||
.assertIsDisplayed()
|
||||
.assertIsNotEnabled()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun footer_isDisplayed() {
|
||||
val listModel = TestTogglePermissionAppListModel()
|
||||
|
||||
setTogglePermissionAppInfoPage(listModel)
|
||||
|
||||
composeTestRule.onNodeWithText(context.getString(listModel.footerResId))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
private fun setTogglePermissionAppInfoPage(listModel: TestTogglePermissionAppListModel) {
|
||||
composeTestRule.setContent {
|
||||
listModel.TogglePermissionAppInfoPage(
|
||||
packageName = PACKAGE_NAME,
|
||||
userId = USER_ID,
|
||||
packageManagers = packageManagers,
|
||||
restrictionsProviderFactory = { _, _ -> fakeRestrictionsProvider },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val USER_ID = 0
|
||||
const val PACKAGE_NAME = "package.name"
|
||||
val APP = ApplicationInfo().apply {
|
||||
packageName = PACKAGE_NAME
|
||||
}
|
||||
val PACKAGE_INFO = PackageInfo().apply {
|
||||
packageName = PACKAGE_NAME
|
||||
applicationInfo = APP
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settingslib.spaprivileged.R
|
||||
import com.android.settingslib.spaprivileged.test.R
|
||||
import com.android.settingslib.spaprivileged.tests.testutils.TestTogglePermissionAppListModel
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Rule
|
||||
@@ -36,7 +36,7 @@ class TogglePermissionAppListPageTest {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private var context: Context = ApplicationProvider.getApplicationContext()
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
|
||||
@Test
|
||||
fun appListInjectEntry_titleDisplayed() {
|
||||
|
||||
@@ -24,8 +24,8 @@ import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settingslib.spaprivileged.R
|
||||
import com.android.settingslib.spaprivileged.tests.testutils.TestTogglePermissionAppListModel
|
||||
import com.android.settingslib.spaprivileged.test.R
|
||||
import com.android.settingslib.spaprivileged.tests.testutils.TestTogglePermissionAppListProvider
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
@@ -36,7 +36,7 @@ class TogglePermissionAppListTest {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private var context: Context = ApplicationProvider.getApplicationContext()
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
|
||||
@Test
|
||||
fun appListInjectEntry_titleDisplayed() {
|
||||
@@ -70,8 +70,3 @@ class TogglePermissionAppListTest {
|
||||
assertThat(createPageProviders.any { it is TogglePermissionAppInfoPageProvider }).isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
private object TestTogglePermissionAppListProvider : TogglePermissionAppListProvider {
|
||||
override val permissionType = "test.PERMISSION"
|
||||
override fun createModel(context: Context) = TestTogglePermissionAppListModel()
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ class RestrictedSwitchPreferenceTest {
|
||||
|
||||
private fun setContent(restrictions: Restrictions) {
|
||||
composeTestRule.setContent {
|
||||
RestrictedSwitchPreferenceImpl(switchPreferenceModel, restrictions) { _, _ ->
|
||||
RestrictedSwitchPreference(switchPreferenceModel, restrictions) { _, _ ->
|
||||
fakeRestrictionsProvider
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,11 +19,14 @@ package com.android.settingslib.spaprivileged.tests.testutils
|
||||
import android.content.pm.ApplicationInfo
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.android.settingslib.spa.framework.compose.stateOf
|
||||
import com.android.settingslib.spaprivileged.R
|
||||
import com.android.settingslib.spaprivileged.test.R
|
||||
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListModel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class TestTogglePermissionAppListModel : TogglePermissionAppListModel<TestAppRecord> {
|
||||
class TestTogglePermissionAppListModel(
|
||||
private val isAllowed: Boolean? = null,
|
||||
private val isChangeable: Boolean = false,
|
||||
) : TogglePermissionAppListModel<TestAppRecord> {
|
||||
override val pageTitleResId = R.string.test_permission_title
|
||||
override val switchTitleResId = R.string.test_permission_switch_title
|
||||
override val footerResId = R.string.test_permission_footer
|
||||
@@ -34,9 +37,9 @@ class TestTogglePermissionAppListModel : TogglePermissionAppListModel<TestAppRec
|
||||
recordListFlow
|
||||
|
||||
@Composable
|
||||
override fun isAllowed(record: TestAppRecord) = stateOf(null)
|
||||
override fun isAllowed(record: TestAppRecord) = stateOf(isAllowed)
|
||||
|
||||
override fun isChangeable(record: TestAppRecord) = false
|
||||
override fun isChangeable(record: TestAppRecord) = isChangeable
|
||||
|
||||
override fun setAllowed(record: TestAppRecord, newAllowed: Boolean) {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settingslib.spaprivileged.tests.testutils
|
||||
|
||||
import android.content.Context
|
||||
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
|
||||
|
||||
object TestTogglePermissionAppListProvider : TogglePermissionAppListProvider {
|
||||
override val permissionType = "test.PERMISSION"
|
||||
override fun createModel(context: Context) = TestTogglePermissionAppListModel()
|
||||
}
|
||||
Reference in New Issue
Block a user