1 /*
2  * Copyright (C) 2023 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 package com.android.car.settings.applications.appinfo
17 
18 import android.car.drivingstate.CarUxRestrictions
19 import android.content.ActivityNotFoundException
20 import android.content.ComponentName
21 import android.content.Context
22 import android.content.Intent
23 import android.content.pm.PackageManager
24 import android.content.pm.ResolveInfo
25 import android.content.res.Resources
26 import android.location.LocationManager
27 import androidx.preference.Preference
28 import com.android.car.settings.common.FragmentController
29 import com.android.car.settings.common.Logger
30 import com.android.car.settings.common.PreferenceController
31 
32 /** Preference Controller for the "All Services" preference in the "App Info" page. */
33 class AppAllServicesPreferenceController(
34     context: Context,
35     preferenceKey: String,
36     fragmentController: FragmentController,
37     uxRestrictions: CarUxRestrictions
38 ) : PreferenceController<Preference>(context, preferenceKey, fragmentController, uxRestrictions) {
39     private val packageManager = context.packageManager
40     private var packageName: String? = null
41 
onStartInternalnull42     override fun onStartInternal() {
43         getStorageSummary()?.let { preference.summary = it }
44     }
45 
getPreferenceTypenull46     override fun getPreferenceType(): Class<Preference> = Preference::class.java
47 
48     override fun getDefaultAvailabilityStatus(): Int {
49         return if (canPackageHandleIntent() && isLocationProvider()) {
50             AVAILABLE
51         } else {
52             CONDITIONALLY_UNAVAILABLE
53         }
54     }
55 
handlePreferenceClickednull56     override fun handlePreferenceClicked(preference: Preference): Boolean {
57         startAllServicesActivity()
58         return true
59     }
60 
61     /**
62      * Set the package name of the package for which the "All Services" activity needs to be shown.
63      *
64      * @param packageName Name of the package for which the services need to be shown.
65      */
setPackageNamenull66     fun setPackageName(packageName: String?) {
67         this.packageName = packageName
68     }
69 
getStorageSummarynull70     private fun getStorageSummary(): CharSequence? {
71         val resolveInfo = getResolveInfo(PackageManager.GET_META_DATA)
72         if (resolveInfo == null) {
73             LOG.d("mResolveInfo is null.")
74             return null
75         }
76         val metaData = resolveInfo.activityInfo.metaData
77         try {
78             val pkgRes = packageManager.getResourcesForActivity(
79                 ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name))
80             return pkgRes.getString(metaData.getInt(SUMMARY_METADATA_KEY))
81         } catch (exception: Resources.NotFoundException) {
82             LOG.d("Resource not found for summary string.")
83         } catch (exception: PackageManager.NameNotFoundException) {
84             LOG.d("Name of resource not found for summary string.")
85         }
86         return null
87     }
88 
isLocationProvidernull89     private fun isLocationProvider(): Boolean {
90         val locationManagerService = context.getSystemService(LocationManager::class.java)
91         return packageName?.let {
92             locationManagerService?.isProviderPackage(
93                 /* provider = */ null,
94                 /* packageName = */ it,
95                 /* attributionTag = */ null)
96         } ?: false
97     }
98 
canPackageHandleIntentnull99     private fun canPackageHandleIntent(): Boolean = getResolveInfo(/* flags = */ 0) != null
100 
101     private fun startAllServicesActivity() {
102         val featuresIntent = Intent(Intent.ACTION_VIEW_APP_FEATURES)
103         // Resolve info won't be null as it is only shown for packages that can
104         // handle the intent
105         val resolveInfo = getResolveInfo(/* flags = */ 0)
106         if (resolveInfo == null) {
107             LOG.e("Resolve info is null, package unable to handle all services intent")
108             return
109         }
110         featuresIntent.component =
111                 ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name)
112         LOG.v("Starting the All Services activity with intent:" +
113             featuresIntent.toUri(/* flags = */ 0))
114         try {
115             context.startActivity(featuresIntent)
116         } catch (e: ActivityNotFoundException) {
117             LOG.e("The app cannot handle android.intent.action.VIEW_APP_FEATURES")
118         }
119     }
120 
getResolveInfonull121     private fun getResolveInfo(flags: Int): ResolveInfo? {
122         if (packageName == null) {
123             return null
124         }
125         val featuresIntent = Intent(Intent.ACTION_VIEW_APP_FEATURES).apply {
126             `package` = packageName
127         }
128         return packageManager.resolveActivity(featuresIntent, flags)
129     }
130 
131     private companion object {
132         val LOG = Logger(AppAllServicesPreferenceController::class.java)
133         const val SUMMARY_METADATA_KEY = "app_features_preference_summary"
134     }
135 }
136