Add DefaultAppShortcuts for Spa

Including the following,
- Home app
- Browser app
- Phone app
- Emergency app
- SMS app

Bug: 236346018
Test: Manual with App Info page
Test: Settings Unit tests
Change-Id: I4ceb31ed521b758a6f91d7e86fd34c780442b1ac
This commit is contained in:
Chaohui Wang
2022-10-21 14:34:04 +08:00
parent b267f66627
commit a8e19e0f2c
4 changed files with 327 additions and 0 deletions

View File

@@ -98,6 +98,7 @@ private fun AppInfoSettings(packageInfoPresenter: PackageInfoPresenter) {
// TODO: battery
// TODO: app_language_setting
AppOpenByDefaultPreference(app)
DefaultAppShortcuts(app)
Category(title = stringResource(R.string.advanced_apps)) {
DisplayOverOtherAppsAppListProvider.InfoPageEntryItem(app)

View File

@@ -0,0 +1,111 @@
/*
* 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.appinfo
import android.app.role.RoleManager
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
import androidx.annotation.StringRes
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.liveData
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.framework.common.userManager
import com.android.settingslib.spaprivileged.model.app.userHandle
import com.android.settingslib.spaprivileged.model.app.userId
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
data class DefaultAppShortcut(
val roleName: String,
@StringRes val titleResId: Int,
)
@Composable
fun DefaultAppShortcutPreference(shortcut: DefaultAppShortcut, app: ApplicationInfo) {
val context = LocalContext.current
val presenter = remember { DefaultAppShortcutPresenter(context, shortcut.roleName, app) }
if (!presenter.isAvailable()) return
if (presenter.isVisible().observeAsState().value != true) return
Preference(object : PreferenceModel {
override val title = stringResource(shortcut.titleResId)
override val summary = presenter.summaryLiveData.observeAsState(
initial = stringResource(R.string.summary_placeholder),
)
override val onClick = presenter::startActivity
})
}
private class DefaultAppShortcutPresenter(
private val context: Context,
private val roleName: String,
private val app: ApplicationInfo,
) {
private val roleManager = context.getSystemService(RoleManager::class.java)!!
private val executor = Dispatchers.IO.asExecutor()
fun isAvailable() = !context.userManager.isManagedProfile(app.userId)
fun isVisible() = liveData {
coroutineScope {
val roleVisible = async { isRoleVisible() }
val applicationVisibleForRole = async { isApplicationVisibleForRole() }
emit(roleVisible.await() && applicationVisibleForRole.await())
}
}
private suspend fun isRoleVisible(): Boolean {
return suspendCoroutine { continuation ->
roleManager.isRoleVisible(roleName, executor) {
continuation.resume(it)
}
}
}
private suspend fun isApplicationVisibleForRole() = suspendCoroutine { continuation ->
roleManager.isApplicationVisibleForRole(roleName, app.packageName, executor) {
continuation.resume(it)
}
}
val summaryLiveData = liveData(Dispatchers.IO) {
val defaultApp = roleManager.getRoleHoldersAsUser(roleName, app.userHandle).firstOrNull()
emit(context.getString(when (defaultApp) {
app.packageName -> R.string.yes
else -> R.string.no
}))
}
fun startActivity() {
val intent = Intent(Intent.ACTION_MANAGE_DEFAULT_APP).apply {
putExtra(Intent.EXTRA_ROLE_NAME, roleName)
}
context.startActivityAsUser(intent, app.userHandle)
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.appinfo
import android.app.role.RoleManager
import android.content.pm.ApplicationInfo
import androidx.compose.runtime.Composable
import com.android.settings.R
@Composable
fun DefaultAppShortcuts(app: ApplicationInfo) {
for (shortCut in SHORT_CUTS) {
DefaultAppShortcutPreference(shortCut, app)
}
}
private val SHORT_CUTS = listOf(
DefaultAppShortcut(RoleManager.ROLE_HOME, R.string.home_app),
DefaultAppShortcut(RoleManager.ROLE_BROWSER, R.string.default_browser_title),
DefaultAppShortcut(RoleManager.ROLE_DIALER, R.string.default_phone_title),
DefaultAppShortcut(RoleManager.ROLE_EMERGENCY, R.string.default_emergency_app),
DefaultAppShortcut(RoleManager.ROLE_SMS, R.string.sms_application_title),
)