• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.app.settings.SettingsEnums
20 import android.content.Context
21 import android.content.Intent
22 import android.content.IntentFilter
23 import android.content.pm.PackageInfo
24 import android.content.pm.PackageManager
25 import android.os.UserHandle
26 import android.util.Log
27 import androidx.compose.runtime.Composable
28 import com.android.settings.overlay.FeatureFactory
29 import com.android.settings.spa.app.startUninstallActivity
30 import com.android.settingslib.spa.framework.compose.LocalNavController
31 import com.android.settingslib.spaprivileged.framework.common.activityManager
32 import com.android.settingslib.spaprivileged.framework.common.asUser
33 import com.android.settingslib.spaprivileged.framework.compose.DisposableBroadcastReceiverAsUser
34 import com.android.settingslib.spaprivileged.model.app.IPackageManagers
35 import com.android.settingslib.spaprivileged.model.app.PackageManagers
36 import kotlinx.coroutines.CoroutineScope
37 import kotlinx.coroutines.Dispatchers
38 import kotlinx.coroutines.flow.MutableStateFlow
39 import kotlinx.coroutines.flow.StateFlow
40 import kotlinx.coroutines.launch
41 
42 private const val TAG = "PackageInfoPresenter"
43 
44 /**
45  * Presenter which helps to present the status change of [PackageInfo].
46  */
47 class PackageInfoPresenter(
48     val context: Context,
49     val packageName: String,
50     val userId: Int,
51     private val coroutineScope: CoroutineScope,
52     private val packageManagers: IPackageManagers = PackageManagers,
53 ) {
54     private val metricsFeatureProvider = FeatureFactory.getFactory(context).metricsFeatureProvider
55     private val userHandle = UserHandle.of(userId)
56     val userContext by lazy { context.asUser(userHandle) }
57     val userPackageManager: PackageManager by lazy { userContext.packageManager }
58     private val _flow: MutableStateFlow<PackageInfo?> = MutableStateFlow(null)
59 
60     val flow: StateFlow<PackageInfo?> = _flow
61 
62     fun reloadPackageInfo() {
63         coroutineScope.launch(Dispatchers.IO) {
64             _flow.value = getPackageInfo()
65         }
66     }
67 
68     /**
69      * Detects the package removed event.
70      */
71     @Composable
72     fun PackageRemoveDetector() {
73         val intentFilter = IntentFilter(Intent.ACTION_PACKAGE_REMOVED).apply {
74             addDataScheme("package")
75         }
76         val navController = LocalNavController.current
77         DisposableBroadcastReceiverAsUser(intentFilter, userHandle) { intent ->
78             if (packageName == intent.data?.schemeSpecificPart) {
79                 val packageInfo = flow.value
80                 if (packageInfo != null && packageInfo.applicationInfo.isSystemApp) {
81                     // System app still exists after uninstalling the updates, refresh the page.
82                     reloadPackageInfo()
83                 } else {
84                     navController.navigateBack()
85                 }
86             }
87         }
88     }
89 
90     /** Enables this package. */
91     fun enable() {
92         logAction(SettingsEnums.ACTION_SETTINGS_ENABLE_APP)
93         coroutineScope.launch(Dispatchers.IO) {
94             userPackageManager.setApplicationEnabledSetting(
95                 packageName, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, 0
96             )
97             reloadPackageInfo()
98         }
99     }
100 
101     /** Disables this package. */
102     fun disable() {
103         logAction(SettingsEnums.ACTION_SETTINGS_DISABLE_APP)
104         coroutineScope.launch(Dispatchers.IO) {
105             userPackageManager.setApplicationEnabledSetting(
106                 packageName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER, 0
107             )
108             reloadPackageInfo()
109         }
110     }
111 
112     /** Starts the uninstallation activity. */
113     fun startUninstallActivity(forAllUsers: Boolean = false) {
114         logAction(SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP)
115         context.startUninstallActivity(packageName, userHandle, forAllUsers)
116     }
117 
118     /** Clears this instant app. */
119     fun clearInstantApp() {
120         logAction(SettingsEnums.ACTION_SETTINGS_CLEAR_INSTANT_APP)
121         coroutineScope.launch(Dispatchers.IO) {
122             userPackageManager.deletePackageAsUser(packageName, null, 0, userId)
123             reloadPackageInfo()
124         }
125     }
126 
127     /** Force stops this package. */
128     fun forceStop() {
129         logAction(SettingsEnums.ACTION_APP_FORCE_STOP)
130         coroutineScope.launch(Dispatchers.Default) {
131             Log.d(TAG, "Stopping package $packageName")
132             context.activityManager.forceStopPackageAsUser(packageName, userId)
133             reloadPackageInfo()
134         }
135     }
136 
137     fun logAction(category: Int) {
138         metricsFeatureProvider.action(context, category, packageName)
139     }
140 
141     private fun getPackageInfo() =
142         packageManagers.getPackageInfoAsUser(
143             packageName = packageName,
144             flags = PackageManager.MATCH_ANY_USER or
145                 PackageManager.MATCH_DISABLED_COMPONENTS or
146                 PackageManager.GET_PERMISSIONS,
147             userId = userId,
148         )
149 }
150