• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.content.Context
20 import android.content.pm.ApplicationInfo
21 import androidx.compose.runtime.Composable
22 import androidx.compose.runtime.remember
23 import androidx.compose.runtime.rememberCoroutineScope
24 import androidx.compose.ui.platform.LocalContext
25 import androidx.compose.ui.res.stringResource
26 import com.android.settings.R
27 import com.android.settings.Utils
28 import com.android.settings.applications.AppStoreUtil
29 import com.android.settingslib.applications.AppUtils
30 import androidx.lifecycle.compose.ExperimentalLifecycleComposeApi
31 import androidx.lifecycle.compose.collectAsStateWithLifecycle
32 import com.android.settingslib.spa.widget.preference.Preference
33 import com.android.settingslib.spa.widget.preference.PreferenceModel
34 import com.android.settingslib.spaprivileged.framework.common.asUser
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 kotlinx.coroutines.CoroutineScope
39 import kotlinx.coroutines.Dispatchers
40 import kotlinx.coroutines.flow.Flow
41 import kotlinx.coroutines.flow.SharingStarted
42 import kotlinx.coroutines.flow.flow
43 import kotlinx.coroutines.flow.map
44 import kotlinx.coroutines.flow.shareIn
45 import kotlinx.coroutines.launch
46 import kotlinx.coroutines.withContext
47 
48 @OptIn(ExperimentalLifecycleComposeApi::class)
49 @Composable
50 fun AppInstallerInfoPreference(app: ApplicationInfo) {
51     val context = LocalContext.current
52     val coroutineScope = rememberCoroutineScope()
53     val presenter = remember { AppInstallerInfoPresenter(context, app, coroutineScope) }
54     if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
55 
56     Preference(object : PreferenceModel {
57         override val title = stringResource(R.string.app_install_details_title)
58         override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
59             initialValue = stringResource(R.string.summary_placeholder),
60         )
61         override val enabled =
62             presenter.enabledFlow.collectAsStateWithLifecycle(initialValue = false)
63         override val onClick = presenter::startActivity
64     })
65 }
66 
67 private class AppInstallerInfoPresenter(
68     private val context: Context,
69     private val app: ApplicationInfo,
70     private val coroutineScope: CoroutineScope,
71 ) {
72     private val userContext = context.asUser(app.userHandle)
73     private val packageManager = userContext.packageManager
74     private val userManager = context.userManager
75 
<lambda>null76     private val installerPackageFlow = flow {
77         emit(withContext(Dispatchers.IO) {
78             AppStoreUtil.getInstallerPackageName(userContext, app.packageName)
79         })
80     }.sharedFlow()
81 
installerPackagenull82     private val installerLabelFlow = installerPackageFlow.map { installerPackage ->
83         installerPackage ?: return@map null
84         withContext(Dispatchers.IO) {
85             Utils.getApplicationLabel(context, installerPackage)
86         }
87     }.sharedFlow()
88 
installerLabelnull89     val isAvailableFlow = installerLabelFlow.map { installerLabel ->
90         withContext(Dispatchers.IO) {
91             !userManager.isManagedProfile(app.userId) &&
92                 !AppUtils.isMainlineModule(packageManager, app.packageName) &&
93                 installerLabel != null
94         }
95     }
96 
installerLabelnull97     val summaryFlow = installerLabelFlow.map { installerLabel ->
98         val detailsStringId = when {
99             app.isInstantApp -> R.string.instant_app_details_summary
100             else -> R.string.app_install_details_summary
101         }
102         context.getString(detailsStringId, installerLabel)
103     }
104 
installerPackagenull105     private val intentFlow = installerPackageFlow.map { installerPackage ->
106         withContext(Dispatchers.IO) {
107             AppStoreUtil.getAppStoreLink(context, installerPackage, app.packageName)
108         }
109     }.sharedFlow()
110 
<lambda>null111     val enabledFlow = intentFlow.map { it != null }
112 
startActivitynull113     fun startActivity() {
114         coroutineScope.launch {
115             intentFlow.collect { intent ->
116                 if (intent != null) {
117                     context.startActivityAsUser(intent, app.userHandle)
118                 }
119             }
120         }
121     }
122 
sharedFlownull123     private fun <T> Flow<T>.sharedFlow() =
124         shareIn(coroutineScope, SharingStarted.WhileSubscribed(), 1)
125 }
126