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