1 /*
<lambda>null2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.spa.app.appinfo
18 
19 import android.app.role.RoleManager
20 import android.content.Context
21 import android.content.Intent
22 import android.content.pm.ApplicationInfo
23 import androidx.annotation.StringRes
24 import androidx.compose.runtime.Composable
25 import androidx.compose.runtime.getValue
26 import androidx.compose.runtime.livedata.observeAsState
27 import androidx.compose.runtime.remember
28 import androidx.compose.ui.platform.LocalContext
29 import androidx.compose.ui.res.stringResource
30 import androidx.lifecycle.compose.collectAsStateWithLifecycle
31 import androidx.lifecycle.liveData
32 import com.android.settings.R
33 import com.android.settingslib.spa.widget.preference.Preference
34 import com.android.settingslib.spa.widget.preference.PreferenceModel
35 import com.android.settingslib.spaprivileged.framework.common.userManager
36 import com.android.settingslib.spaprivileged.model.app.userHandle
37 import com.android.settingslib.spaprivileged.model.app.userId
38 import kotlin.coroutines.resume
39 import kotlin.coroutines.suspendCoroutine
40 import kotlinx.coroutines.Dispatchers
41 import kotlinx.coroutines.asExecutor
42 import kotlinx.coroutines.async
43 import kotlinx.coroutines.coroutineScope
44 import kotlinx.coroutines.flow.flow
45 import kotlinx.coroutines.flow.flowOn
46 
47 data class DefaultAppShortcut(
48     val roleName: String,
49     @StringRes val titleResId: Int,
50 )
51 
52 @Composable
53 fun DefaultAppShortcutPreference(shortcut: DefaultAppShortcut, app: ApplicationInfo) {
54     val context = LocalContext.current
55     val presenter = remember(shortcut.roleName, app) {
56         DefaultAppShortcutPresenter(context, shortcut.roleName, app)
57     }
58     if (remember(presenter) { !presenter.isAvailable() }) return
59     if (presenter.isVisible().observeAsState().value != true) return
60 
61     val summary by presenter.summaryFlow.collectAsStateWithLifecycle(
62         initialValue = stringResource(R.string.summary_placeholder),
63     )
64     Preference(object : PreferenceModel {
65         override val title = stringResource(shortcut.titleResId)
66         override val summary = { summary }
67         override val onClick = presenter::startActivity
68     })
69 }
70 
71 private class DefaultAppShortcutPresenter(
72     private val context: Context,
73     private val roleName: String,
74     private val app: ApplicationInfo,
75 ) {
76     private val roleManager = context.getSystemService(RoleManager::class.java)!!
77     private val executor = Dispatchers.IO.asExecutor()
78 
isAvailablenull79     fun isAvailable() = !context.userManager.isManagedProfile(app.userId)
80 
81     fun isVisible() = liveData {
82         coroutineScope {
83             val roleVisible = async { isRoleVisible() }
84             val applicationVisibleForRole = async { isApplicationVisibleForRole() }
85             emit(roleVisible.await() && applicationVisibleForRole.await())
86         }
87     }
88 
isRoleVisiblenull89     private suspend fun isRoleVisible(): Boolean {
90         return suspendCoroutine { continuation ->
91             roleManager.isRoleVisible(roleName, executor) {
92                 continuation.resume(it)
93             }
94         }
95     }
96 
isApplicationVisibleForRolenull97     private suspend fun isApplicationVisibleForRole() = suspendCoroutine { continuation ->
98         roleManager.isApplicationVisibleForRole(roleName, app.packageName, executor) {
99             continuation.resume(it)
100         }
101     }
102 
<lambda>null103     val summaryFlow = flow { emit(getSummary()) }.flowOn(Dispatchers.IO)
104 
getSummarynull105     private fun getSummary(): String {
106         val defaultApp = roleManager.getRoleHoldersAsUser(roleName, app.userHandle).firstOrNull()
107         return context.getString(
108             if (defaultApp == app.packageName) R.string.yes else R.string.no
109         )
110     }
111 
startActivitynull112     fun startActivity() {
113         val intent = Intent(Intent.ACTION_MANAGE_DEFAULT_APP).apply {
114             putExtra(Intent.EXTRA_ROLE_NAME, roleName)
115         }
116         context.startActivityAsUser(intent, app.userHandle)
117     }
118 }
119