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