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.Intent
21 import android.content.pm.ApplicationInfo
22 import android.content.pm.PackageManager
23 import android.content.res.Resources
24 import android.os.Bundle
25 import android.util.Log
26 import androidx.compose.runtime.Composable
27 import androidx.compose.runtime.getValue
28 import androidx.compose.runtime.remember
29 import androidx.compose.runtime.rememberCoroutineScope
30 import androidx.compose.ui.platform.LocalContext
31 import androidx.compose.ui.res.stringResource
32 import androidx.lifecycle.compose.collectAsStateWithLifecycle
33 import com.android.settings.R
34 import com.android.settingslib.spa.widget.preference.Preference
35 import com.android.settingslib.spa.widget.preference.PreferenceModel
36 import com.android.settingslib.spaprivileged.model.app.resolveActionForApp
37 import com.android.settingslib.spaprivileged.model.app.userHandle
38 import kotlinx.coroutines.CoroutineScope
39 import kotlinx.coroutines.Dispatchers
40 import kotlinx.coroutines.flow.SharingStarted
41 import kotlinx.coroutines.flow.firstOrNull
42 import kotlinx.coroutines.flow.flow
43 import kotlinx.coroutines.flow.flowOn
44 import kotlinx.coroutines.flow.map
45 import kotlinx.coroutines.flow.shareIn
46 import kotlinx.coroutines.launch
47 import kotlinx.coroutines.plus
48 
49 @Composable
50 fun AppAllServicesPreference(app: ApplicationInfo) {
51     val context = LocalContext.current
52     val coroutineScope = rememberCoroutineScope()
53     val presenter = remember { AppAllServicesPresenter(context, app, coroutineScope) }
54     if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
55 
56     val summary by presenter.summaryFlow.collectAsStateWithLifecycle(
57         initialValue = stringResource(R.string.summary_placeholder),
58     )
59     Preference(object : PreferenceModel {
60         override val title = stringResource(R.string.app_info_all_services_label)
61         override val summary = { summary }
62         override val onClick = presenter::startActivity
63     })
64 }
65 
66 private class AppAllServicesPresenter(
67     private val context: Context,
68     private val app: ApplicationInfo,
69     private val coroutineScope: CoroutineScope,
70 ) {
71     private val packageManager = context.packageManager
72 
<lambda>null73     private val activityInfoFlow = flow {
74         emit(packageManager.resolveActionForApp(
75             app = app,
76             action = Intent.ACTION_VIEW_APP_FEATURES,
77             flags = PackageManager.GET_META_DATA,
78         ))
79     }.shareIn(coroutineScope + Dispatchers.IO, SharingStarted.WhileSubscribed(), 1)
80 
<lambda>null81     val isAvailableFlow = activityInfoFlow.map { it != null }
82 
activityInfonull83     val summaryFlow = activityInfoFlow.map { activityInfo ->
84         activityInfo?.metaData?.getSummary() ?: ""
85     }.flowOn(Dispatchers.IO)
86 
getSummarynull87     private fun Bundle.getSummary(): String {
88         val resources = try {
89             packageManager.getResourcesForApplication(app)
90         } catch (exception: PackageManager.NameNotFoundException) {
91             Log.d(TAG, "Name not found for the application.")
92             return ""
93         }
94 
95         return try {
96             resources.getString(getInt(SUMMARY_METADATA_KEY))
97         } catch (exception: Resources.NotFoundException) {
98             Log.d(TAG, "Resource not found for summary string.")
99             ""
100         }
101     }
102 
startActivitynull103     fun startActivity() {
104         coroutineScope.launch {
105             activityInfoFlow.firstOrNull()?.let { activityInfo ->
106                 val intent = Intent(Intent.ACTION_VIEW_APP_FEATURES).apply {
107                     component = activityInfo.componentName
108                 }
109                 context.startActivityAsUser(intent, app.userHandle)
110             }
111         }
112     }
113 
114     companion object {
115         private const val TAG = "AppAllServicesPresenter"
116         private const val SUMMARY_METADATA_KEY = "app_features_preference_summary"
117     }
118 }
119