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         withContext(Dispatchers.IO) {
96             !AppUtils.isMainlineModule(packageManager, app.packageName) &&
97                     installerLabel != null
98         }
99     }
100 
installerLabelnull101     val summaryFlow = installerLabelFlow.map { installerLabel ->
102         if (app.isInstantApp) {
103             context.getString(R.string.instant_app_details_summary, installerLabel)
104         } else if (AppStoreUtil.isInitiatedFromDifferentPackage(installSourceInfo)) {
105             val initiatingLabel : CharSequence? = Utils.getApplicationLabel(
106                     context, installSourceInfo!!.initiatingPackageName!!)
107             if (initiatingLabel != null) {
108                 context.getString(
109                         R.string.app_install_details_different_initiating_package_summary,
110                         installerLabel,
111                         initiatingLabel
112                 )
113             } else {
114                 context.getString(R.string.app_install_details_summary, installerLabel)
115             }
116         } else {
117             context.getString(R.string.app_install_details_summary, installerLabel)
118         }
119     }
120 
installerPackagenull121     private val intentFlow = installerPackageFlow.map { installerPackage ->
122         withContext(Dispatchers.IO) {
123             AppStoreUtil.getAppStoreLink(context, installerPackage, app.packageName)
124         }
125     }.sharedFlow()
126 
<lambda>null127     val enabledFlow = intentFlow.map { it != null }
128 
startActivitynull129     fun startActivity() {
130         coroutineScope.launch {
131             intentFlow.collect { intent ->
132                 if (intent != null) {
133                     context.startActivityAsUser(intent, app.userHandle)
134                 }
135             }
136         }
137     }
138 
sharedFlownull139     private fun <T> Flow<T>.sharedFlow() =
140             shareIn(coroutineScope, SharingStarted.WhileSubscribed(), 1)
141 }
142