1 /*
2  * 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.app.settings.SettingsEnums
20 import android.content.Context
21 import android.content.Intent
22 import android.content.pm.ActivityInfo
23 import android.content.pm.ApplicationInfo
24 import androidx.compose.runtime.Composable
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 com.android.settings.R
30 import androidx.lifecycle.compose.collectAsStateWithLifecycle
31 import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
32 import com.android.settingslib.spa.widget.preference.Preference
33 import com.android.settingslib.spa.widget.preference.PreferenceModel
34 import com.android.settingslib.spaprivileged.model.app.resolveActionForApp
35 import com.android.settingslib.spaprivileged.model.app.userHandle
36 import kotlinx.coroutines.CoroutineScope
37 import kotlinx.coroutines.Dispatchers
38 import kotlinx.coroutines.flow.SharingStarted
39 import kotlinx.coroutines.flow.firstOrNull
40 import kotlinx.coroutines.flow.flow
41 import kotlinx.coroutines.flow.map
42 import kotlinx.coroutines.flow.shareIn
43 import kotlinx.coroutines.launch
44 import kotlinx.coroutines.plus
45 
46 @Composable
AppSettingsPreferencenull47 fun AppSettingsPreference(app: ApplicationInfo) {
48     val context = LocalContext.current
49     val coroutineScope = rememberCoroutineScope()
50     val presenter = remember(app) { AppSettingsPresenter(context, app, coroutineScope) }
51     if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
52 
53     Preference(object : PreferenceModel {
54         override val title = stringResource(R.string.app_settings_link)
55         override val onClick = presenter::startActivity
56     })
57 }
58 
59 private class AppSettingsPresenter(
60     private val context: Context,
61     private val app: ApplicationInfo,
62     private val coroutineScope: CoroutineScope,
63 ) {
64     private val packageManager = context.packageManager
65 
<lambda>null66     private val intentFlow = flow {
67         emit(packageManager.resolveActionForApp(app, Intent.ACTION_APPLICATION_PREFERENCES))
68     }.shareIn(coroutineScope + Dispatchers.IO, SharingStarted.WhileSubscribed(), 1)
69 
<lambda>null70     val isAvailableFlow = intentFlow.map { it != null }
71 
startActivitynull72     fun startActivity() {
73         coroutineScope.launch {
74             intentFlow.firstOrNull()?.let(::startActivity)
75         }
76     }
77 
startActivitynull78     private fun startActivity(activityInfo: ActivityInfo) {
79         featureFactory.metricsFeatureProvider.action(
80             SettingsEnums.PAGE_UNKNOWN,
81             SettingsEnums.ACTION_OPEN_APP_SETTING,
82             AppInfoSettingsProvider.METRICS_CATEGORY,
83             null,
84             0,
85         )
86         val intent = Intent(Intent.ACTION_APPLICATION_PREFERENCES).apply {
87             component = activityInfo.componentName
88         }
89         context.startActivityAsUser(intent, app.userHandle)
90     }
91 }
92