Obadah Diar Bakerli | 1d63ac5 | 2018-11-02 13:20:26 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2018 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.car.settings.location; |
| 18 | |
Heemin Seog | e0d5735 | 2018-12-18 14:47:33 -0800 | [diff] [blame] | 19 | import android.car.drivingstate.CarUxRestrictions; |
Obadah Diar Bakerli | 1d63ac5 | 2018-11-02 13:20:26 -0700 | [diff] [blame] | 20 | import android.content.Context; |
| 21 | import android.content.Intent; |
| 22 | import android.content.pm.ActivityInfo; |
| 23 | import android.content.pm.ApplicationInfo; |
| 24 | import android.content.pm.PackageManager; |
| 25 | import android.content.pm.ResolveInfo; |
| 26 | import android.location.LocationManager; |
| 27 | |
| 28 | import androidx.annotation.StringRes; |
| 29 | import androidx.annotation.VisibleForTesting; |
Obadah Diar Bakerli | 1d63ac5 | 2018-11-02 13:20:26 -0700 | [diff] [blame] | 30 | import androidx.preference.PreferenceGroup; |
Obadah Diar Bakerli | 1d63ac5 | 2018-11-02 13:20:26 -0700 | [diff] [blame] | 31 | |
| 32 | import com.android.car.settings.R; |
| 33 | import com.android.car.settings.common.FragmentController; |
| 34 | import com.android.car.settings.common.Logger; |
Heemin Seog | e0d5735 | 2018-12-18 14:47:33 -0800 | [diff] [blame] | 35 | import com.android.car.settings.common.PreferenceController; |
Peter Li | 62e62f2 | 2019-10-15 16:24:34 -0700 | [diff] [blame] | 36 | import com.android.car.ui.preference.CarUiPreference; |
Obadah Diar Bakerli | 1d63ac5 | 2018-11-02 13:20:26 -0700 | [diff] [blame] | 37 | |
| 38 | import java.util.ArrayList; |
| 39 | import java.util.Collections; |
| 40 | import java.util.List; |
| 41 | |
| 42 | /** |
| 43 | * Injects Location Footers into a {@link PreferenceGroup} with a matching key. |
| 44 | */ |
Heemin Seog | e0d5735 | 2018-12-18 14:47:33 -0800 | [diff] [blame] | 45 | public class LocationFooterPreferenceController extends PreferenceController<PreferenceGroup> { |
Obadah Diar Bakerli | 1d63ac5 | 2018-11-02 13:20:26 -0700 | [diff] [blame] | 46 | private static final Logger LOG = new Logger(LocationFooterPreferenceController.class); |
| 47 | private static final Intent INJECT_INTENT = |
| 48 | new Intent(LocationManager.SETTINGS_FOOTER_DISPLAYED_ACTION); |
| 49 | |
Heemin Seog | e0d5735 | 2018-12-18 14:47:33 -0800 | [diff] [blame] | 50 | private PackageManager mPackageManager; |
Obadah Diar Bakerli | 1d63ac5 | 2018-11-02 13:20:26 -0700 | [diff] [blame] | 51 | |
| 52 | public LocationFooterPreferenceController(Context context, String preferenceKey, |
Heemin Seog | e0d5735 | 2018-12-18 14:47:33 -0800 | [diff] [blame] | 53 | FragmentController fragmentController, CarUxRestrictions uxRestrictions) { |
| 54 | super(context, preferenceKey, fragmentController, uxRestrictions); |
| 55 | mPackageManager = context.getPackageManager(); |
Obadah Diar Bakerli | 1d63ac5 | 2018-11-02 13:20:26 -0700 | [diff] [blame] | 56 | } |
| 57 | |
| 58 | @VisibleForTesting |
Heemin Seog | e0d5735 | 2018-12-18 14:47:33 -0800 | [diff] [blame] | 59 | void setPackageManager(PackageManager packageManager) { |
Obadah Diar Bakerli | 1d63ac5 | 2018-11-02 13:20:26 -0700 | [diff] [blame] | 60 | mPackageManager = packageManager; |
| 61 | } |
| 62 | |
| 63 | @Override |
Heemin Seog | e0d5735 | 2018-12-18 14:47:33 -0800 | [diff] [blame] | 64 | protected Class<PreferenceGroup> getPreferenceType() { |
| 65 | return PreferenceGroup.class; |
| 66 | } |
Obadah Diar Bakerli | 1d63ac5 | 2018-11-02 13:20:26 -0700 | [diff] [blame] | 67 | |
Heemin Seog | e0d5735 | 2018-12-18 14:47:33 -0800 | [diff] [blame] | 68 | @Override |
| 69 | protected void onCreateInternal() { |
Soonil Nagarkar | 487fca4 | 2019-07-11 12:08:34 -0700 | [diff] [blame] | 70 | for (LocationFooter footer : getInjectedLocationFooters()) { |
Heemin Seog | e0d5735 | 2018-12-18 14:47:33 -0800 | [diff] [blame] | 71 | try { |
Soonil Nagarkar | 487fca4 | 2019-07-11 12:08:34 -0700 | [diff] [blame] | 72 | String footerString = mPackageManager |
Heemin Seog | e0d5735 | 2018-12-18 14:47:33 -0800 | [diff] [blame] | 73 | .getResourcesForApplication(footer.mApplicationInfo) |
| 74 | .getString(footer.mFooterStringRes); |
Soonil Nagarkar | 487fca4 | 2019-07-11 12:08:34 -0700 | [diff] [blame] | 75 | |
| 76 | // For each injected footer: Create a new preference, set the summary |
| 77 | // and icon, then inject under the footer preference group. |
Peter Li | 62e62f2 | 2019-10-15 16:24:34 -0700 | [diff] [blame] | 78 | CarUiPreference newPreference = new CarUiPreference(getContext()); |
Soonil Nagarkar | 487fca4 | 2019-07-11 12:08:34 -0700 | [diff] [blame] | 79 | newPreference.setSummary(footerString); |
| 80 | newPreference.setIcon(R.drawable.ic_settings_about); |
JianYang Liu | ff5ddd7 | 2020-02-06 14:10:04 -0800 | [diff] [blame^] | 81 | newPreference.setSelectable(false); |
Soonil Nagarkar | 487fca4 | 2019-07-11 12:08:34 -0700 | [diff] [blame] | 82 | getPreference().addPreference(newPreference); |
Heemin Seog | e0d5735 | 2018-12-18 14:47:33 -0800 | [diff] [blame] | 83 | } catch (PackageManager.NameNotFoundException exception) { |
| 84 | LOG.w("Resources not found for application " |
| 85 | + footer.mApplicationInfo.packageName); |
Obadah Diar Bakerli | 1d63ac5 | 2018-11-02 13:20:26 -0700 | [diff] [blame] | 86 | } |
| 87 | } |
Heemin Seog | e0d5735 | 2018-12-18 14:47:33 -0800 | [diff] [blame] | 88 | } |
| 89 | |
Heemin Seog | e0d5735 | 2018-12-18 14:47:33 -0800 | [diff] [blame] | 90 | @Override |
| 91 | protected void updateState(PreferenceGroup preferenceGroup) { |
| 92 | preferenceGroup.setVisible(preferenceGroup.getPreferenceCount() > 0); |
Obadah Diar Bakerli | 1d63ac5 | 2018-11-02 13:20:26 -0700 | [diff] [blame] | 93 | } |
| 94 | |
| 95 | /** |
| 96 | * Return a list of strings provided by ACTION_INJECT_FOOTER broadcast receivers. If there |
| 97 | * are no injectors, an immutable emptry list is returned. |
| 98 | */ |
| 99 | private List<LocationFooter> getInjectedLocationFooters() { |
| 100 | List<ResolveInfo> resolveInfos = mPackageManager.queryBroadcastReceivers( |
| 101 | INJECT_INTENT, PackageManager.GET_META_DATA); |
| 102 | if (resolveInfos == null) { |
| 103 | LOG.e("Unable to resolve intent " + INJECT_INTENT); |
| 104 | return Collections.emptyList(); |
| 105 | } else { |
| 106 | LOG.d("Found broadcast receivers: " + resolveInfos); |
| 107 | } |
| 108 | |
| 109 | List<LocationFooter> locationFooters = new ArrayList<>(resolveInfos.size()); |
| 110 | for (ResolveInfo resolveInfo : resolveInfos) { |
| 111 | ActivityInfo activityInfo = resolveInfo.activityInfo; |
| 112 | ApplicationInfo appInfo = activityInfo.applicationInfo; |
| 113 | |
| 114 | // If a non-system app tries to inject footer, ignore it |
| 115 | if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { |
| 116 | LOG.w("Ignoring attempt to inject footer from a non-system app: " + resolveInfo); |
| 117 | continue; |
| 118 | } |
| 119 | |
| 120 | // If the injector does not have valid METADATA, ignore it |
| 121 | if (activityInfo.metaData == null) { |
| 122 | LOG.d("No METADATA in broadcast receiver " + activityInfo.name); |
| 123 | continue; |
| 124 | } |
| 125 | |
| 126 | // Get the footer text resource id from broadcast receiver's metadata |
| 127 | int footerTextRes = |
| 128 | activityInfo.metaData.getInt(LocationManager.METADATA_SETTINGS_FOOTER_STRING); |
| 129 | if (footerTextRes == 0) { |
| 130 | LOG.w("No mapping of integer exists for " |
| 131 | + LocationManager.METADATA_SETTINGS_FOOTER_STRING); |
| 132 | continue; |
| 133 | } |
Soonil Nagarkar | 487fca4 | 2019-07-11 12:08:34 -0700 | [diff] [blame] | 134 | locationFooters.add(new LocationFooter(footerTextRes, appInfo)); |
Obadah Diar Bakerli | 1d63ac5 | 2018-11-02 13:20:26 -0700 | [diff] [blame] | 135 | } |
| 136 | return locationFooters; |
| 137 | } |
| 138 | |
Obadah Diar Bakerli | 1d63ac5 | 2018-11-02 13:20:26 -0700 | [diff] [blame] | 139 | /** |
| 140 | * Contains information related to a footer. |
| 141 | */ |
| 142 | private static class LocationFooter { |
| 143 | // The string resource of the footer. |
| 144 | @StringRes |
| 145 | private final int mFooterStringRes; |
| 146 | // Application info of the receiver injecting this footer. |
| 147 | private final ApplicationInfo mApplicationInfo; |
Obadah Diar Bakerli | 1d63ac5 | 2018-11-02 13:20:26 -0700 | [diff] [blame] | 148 | |
Soonil Nagarkar | 487fca4 | 2019-07-11 12:08:34 -0700 | [diff] [blame] | 149 | LocationFooter(@StringRes int footerRes, ApplicationInfo appInfo) { |
Obadah Diar Bakerli | 1d63ac5 | 2018-11-02 13:20:26 -0700 | [diff] [blame] | 150 | mFooterStringRes = footerRes; |
| 151 | mApplicationInfo = appInfo; |
Obadah Diar Bakerli | 1d63ac5 | 2018-11-02 13:20:26 -0700 | [diff] [blame] | 152 | } |
| 153 | } |
| 154 | } |