• 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 android.content.pm.InstallSourceInfo
22 import android.util.Pair
23 import androidx.compose.runtime.Composable
24 import androidx.compose.runtime.getValue
25 import androidx.compose.runtime.remember
26 import androidx.compose.runtime.rememberCoroutineScope
27 import androidx.compose.ui.platform.LocalContext
28 import androidx.compose.ui.res.stringResource
29 import androidx.lifecycle.compose.collectAsStateWithLifecycle
30 import com.android.settings.R
31 import com.android.settings.Utils
32 import com.android.settings.applications.AppStoreUtil
33 import com.android.settingslib.applications.AppUtils
34 import com.android.settingslib.spa.widget.preference.Preference
35 import com.android.settingslib.spa.widget.preference.PreferenceModel
36 import com.android.settingslib.spaprivileged.framework.common.asUser
37 import com.android.settingslib.spaprivileged.model.app.userHandle
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 @Composable
49 fun AppInstallerInfoPreference(app: ApplicationInfo) {
50     val context = LocalContext.current
51     val coroutineScope = rememberCoroutineScope()
52     val presenter = remember { AppInstallerInfoPresenter(context, app, coroutineScope) }
53     if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
54 
55     val summary by presenter.summaryFlow.collectAsStateWithLifecycle(
56             initialValue = stringResource(R.string.summary_placeholder)
57     )
58 
59     val enabled by presenter.enabledFlow.collectAsStateWithLifecycle(initialValue = false)
60     Preference(object : PreferenceModel {
61         override val title = stringResource(R.string.app_install_details_title)
62         override val summary = { summary }
63         override val enabled = { enabled }
64         override val onClick = presenter::startActivity
65     })
66 }
67 
68 private class AppInstallerInfoPresenter(
69         private val context: Context,
70         private val app: ApplicationInfo,
71         private val coroutineScope: CoroutineScope,
72 ) {
73     private val userContext = context.asUser(app.userHandle)
74     private val packageManager = userContext.packageManager
75     private var installSourceInfo : InstallSourceInfo? = null
76 
<lambda>null77     private val installerPackageFlow = flow {
78         emit(withContext(Dispatchers.IO) {
79             val result : Pair<String, InstallSourceInfo> =
80                     AppStoreUtil.getInstallerPackageNameAndInstallSourceInfo(
81                             userContext, app.packageName)
82             installSourceInfo = result.second
83             result.first
84         })
85     }.sharedFlow()
86 
installerPackagenull87     private val installerLabelFlow = installerPackageFlow.map { installerPackage ->
88         installerPackage ?: return@map null
89         withContext(Dispatchers.IO) {
90             Utils.getApplicationLabel(context, installerPackage)
91         }
92     }.sharedFlow()
93 
installerLabelnull94     val isAvailableFlow = installerLabelFlow.map() { installerLabel ->
95         // Do not show the install info for the special case of the Play Store app.
96         if (app.packageName == context.getString(R.string.config_mainline_module_update_package)) {
97             false
98         } else {
99             withContext(Dispatchers.IO) {
100                 val isMainlineModule = AppUtils.isMainlineModule(packageManager, app.packageName)
101                 !isMainlineModule && installerLabel != null
102             }
103         }
104     }
105 
installerLabelnull106     val summaryFlow = installerLabelFlow.map { installerLabel ->
107         if (app.isInstantApp) {
108             context.getString(R.string.instant_app_details_summary, installerLabel)
109         } else if (AppStoreUtil.isInitiatedFromDifferentPackage(installSourceInfo)) {
110             val initiatingLabel : CharSequence? = Utils.getApplicationLabel(
111                     context, installSourceInfo!!.initiatingPackageName!!)
112             if (initiatingLabel != null) {
113                 context.getString(
114                         R.string.app_install_details_different_initiating_package_summary,
115                         installerLabel,
116                         initiatingLabel
117                 )
118             } else {
119                 context.getString(R.string.app_install_details_summary, installerLabel)
120             }
121         } else {
122             context.getString(R.string.app_install_details_summary, installerLabel)
123         }
124     }
125 
installerPackagenull126     private val intentFlow = installerPackageFlow.map { installerPackage ->
127         withContext(Dispatchers.IO) {
128             AppStoreUtil.getAppStoreLink(context, installerPackage, app.packageName)
129         }
130     }.sharedFlow()
131 
<lambda>null132     val enabledFlow = intentFlow.map { it != null }
133 
startActivitynull134     fun startActivity() {
135         coroutineScope.launch {
136             intentFlow.collect { intent ->
137                 if (intent != null) {
138                     context.startActivityAsUser(intent, app.userHandle)
139                 }
140             }
141         }
142     }
143 
sharedFlownull144     private fun <T> Flow<T>.sharedFlow() =
145             shareIn(coroutineScope, SharingStarted.WhileSubscribed(), 1)
146 }
147