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