Add AppPermissionPreference for Spa

This is the permission preference in the App Settings page.
The summary is single line.

Add the first Kotlin Robolectric Test for Settings, since Kotlin is not
directly supported by the Robolectric test, using a Java class as a
wrapper.

Bug: 236346018
Test: Manual with App Settings page
Test: Robolectric Test
Change-Id: Ic5a4f7d965885a9cd143428a8cd1900981e316a9
This commit is contained in:
Chaohui Wang
2022-10-09 17:52:36 +08:00
parent fcf10fa0a5
commit 2b11c1fe12
6 changed files with 383 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
/*
* 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.settings.spa.app.appsettings
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import android.util.Log
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import com.android.settings.R
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spaprivileged.model.app.userHandle
private const val TAG = "AppPermissionPreference"
private const val EXTRA_HIDE_INFO_BUTTON = "hideInfoButton"
@Composable
fun AppPermissionPreference(app: ApplicationInfo) {
val context = LocalContext.current
val summaryLiveData = remember { AppPermissionSummaryLiveData(context, app) }
val summaryState = summaryLiveData.observeAsState(initial = AppPermissionSummaryState(
summary = stringResource(R.string.summary_placeholder),
enabled = false,
))
Preference(
model = remember {
object : PreferenceModel {
override val title = context.getString(R.string.permissions_label)
override val summary = derivedStateOf { summaryState.value.summary }
override val enabled = derivedStateOf { summaryState.value.enabled }
override val onClick = { startManagePermissionsActivity(context, app) }
}
},
singleLineSummary = true,
)
}
/** Starts new activity to manage app permissions */
private fun startManagePermissionsActivity(context: Context, app: ApplicationInfo) {
val intent = Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS).apply {
putExtra(Intent.EXTRA_PACKAGE_NAME, app.packageName)
putExtra(EXTRA_HIDE_INFO_BUTTON, true)
}
try {
context.startActivityAsUser(intent, app.userHandle)
} catch (e: ActivityNotFoundException) {
Log.w(TAG, "No app can handle android.intent.action.MANAGE_APP_PERMISSIONS")
}
}

View File

@@ -0,0 +1,102 @@
/*
* 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.settings.spa.app.appsettings
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager.OnPermissionsChangedListener
import android.icu.text.ListFormatter
import androidx.lifecycle.LiveData
import com.android.settings.R
import com.android.settingslib.applications.PermissionsSummaryHelper
import com.android.settingslib.applications.PermissionsSummaryHelper.PermissionsResultCallback
import com.android.settingslib.spaprivileged.model.app.userHandle
data class AppPermissionSummaryState(
val summary: String,
val enabled: Boolean,
)
class AppPermissionSummaryLiveData(
private val context: Context,
private val app: ApplicationInfo,
) : LiveData<AppPermissionSummaryState>() {
private val contextAsUser = context.createContextAsUser(app.userHandle, 0)
private val packageManager = contextAsUser.packageManager
private val onPermissionsChangedListener = OnPermissionsChangedListener { uid ->
if (uid == app.uid) update()
}
override fun onActive() {
packageManager.addOnPermissionsChangeListener(onPermissionsChangedListener)
update()
}
override fun onInactive() {
packageManager.removeOnPermissionsChangeListener(onPermissionsChangedListener)
}
private fun update() {
PermissionsSummaryHelper.getPermissionSummary(
contextAsUser, app.packageName, permissionsCallback
)
}
private val permissionsCallback = object : PermissionsResultCallback {
override fun onPermissionSummaryResult(
requestedPermissionCount: Int,
additionalGrantedPermissionCount: Int,
grantedGroupLabels: List<CharSequence>,
) {
if (requestedPermissionCount == 0) {
postValue(noPermissionRequestedState())
return
}
val labels = getDisplayLabels(additionalGrantedPermissionCount, grantedGroupLabels)
val summary = when {
labels.isEmpty() -> {
context.getString(R.string.runtime_permissions_summary_no_permissions_granted)
}
else -> ListFormatter.getInstance().format(labels)
}
postValue(AppPermissionSummaryState(summary = summary, enabled = true))
}
}
private fun noPermissionRequestedState() = AppPermissionSummaryState(
summary = context.getString(R.string.runtime_permissions_summary_no_permissions_requested),
enabled = false,
)
private fun getDisplayLabels(
additionalGrantedPermissionCount: Int,
grantedGroupLabels: List<CharSequence>,
): List<CharSequence> = when (additionalGrantedPermissionCount) {
0 -> grantedGroupLabels
else -> {
grantedGroupLabels +
// N additional permissions.
context.resources.getQuantityString(
R.plurals.runtime_permissions_additional_count,
additionalGrantedPermissionCount,
additionalGrantedPermissionCount,
)
}
}
}

View File

@@ -90,6 +90,8 @@ private fun AppSettings(packageInfoPresenter: PackageInfoPresenter) {
AppButtons(packageInfoPresenter)
AppPermissionPreference(app)
Category(title = stringResource(R.string.advanced_apps)) {
DisplayOverOtherAppsAppListProvider.InfoPageEntryItem(app)
ModifySystemSettingsAppListProvider.InfoPageEntryItem(app)