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