blob: ece3da75563a11cb7c513babe7dab4cd171f89fa [file] [log] [blame]
Obadah Diar Bakerli1d63ac52018-11-02 13:20:26 -07001/*
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
17package com.android.car.settings.location;
18
Heemin Seoge0d57352018-12-18 14:47:33 -080019import android.car.drivingstate.CarUxRestrictions;
Obadah Diar Bakerli1d63ac52018-11-02 13:20:26 -070020import android.content.Context;
21import android.content.Intent;
22import android.content.pm.ActivityInfo;
23import android.content.pm.ApplicationInfo;
24import android.content.pm.PackageManager;
25import android.content.pm.ResolveInfo;
26import android.location.LocationManager;
27
28import androidx.annotation.StringRes;
29import androidx.annotation.VisibleForTesting;
Obadah Diar Bakerli1d63ac52018-11-02 13:20:26 -070030import androidx.preference.PreferenceGroup;
Obadah Diar Bakerli1d63ac52018-11-02 13:20:26 -070031
32import com.android.car.settings.R;
33import com.android.car.settings.common.FragmentController;
34import com.android.car.settings.common.Logger;
Heemin Seoge0d57352018-12-18 14:47:33 -080035import com.android.car.settings.common.PreferenceController;
Peter Li62e62f22019-10-15 16:24:34 -070036import com.android.car.ui.preference.CarUiPreference;
Obadah Diar Bakerli1d63ac52018-11-02 13:20:26 -070037
38import java.util.ArrayList;
39import java.util.Collections;
40import java.util.List;
41
42/**
43 * Injects Location Footers into a {@link PreferenceGroup} with a matching key.
44 */
Heemin Seoge0d57352018-12-18 14:47:33 -080045public class LocationFooterPreferenceController extends PreferenceController<PreferenceGroup> {
Obadah Diar Bakerli1d63ac52018-11-02 13:20:26 -070046 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 Seoge0d57352018-12-18 14:47:33 -080050 private PackageManager mPackageManager;
Obadah Diar Bakerli1d63ac52018-11-02 13:20:26 -070051
52 public LocationFooterPreferenceController(Context context, String preferenceKey,
Heemin Seoge0d57352018-12-18 14:47:33 -080053 FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
54 super(context, preferenceKey, fragmentController, uxRestrictions);
55 mPackageManager = context.getPackageManager();
Obadah Diar Bakerli1d63ac52018-11-02 13:20:26 -070056 }
57
58 @VisibleForTesting
Heemin Seoge0d57352018-12-18 14:47:33 -080059 void setPackageManager(PackageManager packageManager) {
Obadah Diar Bakerli1d63ac52018-11-02 13:20:26 -070060 mPackageManager = packageManager;
61 }
62
63 @Override
Heemin Seoge0d57352018-12-18 14:47:33 -080064 protected Class<PreferenceGroup> getPreferenceType() {
65 return PreferenceGroup.class;
66 }
Obadah Diar Bakerli1d63ac52018-11-02 13:20:26 -070067
Heemin Seoge0d57352018-12-18 14:47:33 -080068 @Override
69 protected void onCreateInternal() {
Soonil Nagarkar487fca42019-07-11 12:08:34 -070070 for (LocationFooter footer : getInjectedLocationFooters()) {
Heemin Seoge0d57352018-12-18 14:47:33 -080071 try {
Soonil Nagarkar487fca42019-07-11 12:08:34 -070072 String footerString = mPackageManager
Heemin Seoge0d57352018-12-18 14:47:33 -080073 .getResourcesForApplication(footer.mApplicationInfo)
74 .getString(footer.mFooterStringRes);
Soonil Nagarkar487fca42019-07-11 12:08:34 -070075
76 // For each injected footer: Create a new preference, set the summary
77 // and icon, then inject under the footer preference group.
Peter Li62e62f22019-10-15 16:24:34 -070078 CarUiPreference newPreference = new CarUiPreference(getContext());
Soonil Nagarkar487fca42019-07-11 12:08:34 -070079 newPreference.setSummary(footerString);
80 newPreference.setIcon(R.drawable.ic_settings_about);
JianYang Liuff5ddd72020-02-06 14:10:04 -080081 newPreference.setSelectable(false);
Soonil Nagarkar487fca42019-07-11 12:08:34 -070082 getPreference().addPreference(newPreference);
Heemin Seoge0d57352018-12-18 14:47:33 -080083 } catch (PackageManager.NameNotFoundException exception) {
84 LOG.w("Resources not found for application "
85 + footer.mApplicationInfo.packageName);
Obadah Diar Bakerli1d63ac52018-11-02 13:20:26 -070086 }
87 }
Heemin Seoge0d57352018-12-18 14:47:33 -080088 }
89
Heemin Seoge0d57352018-12-18 14:47:33 -080090 @Override
91 protected void updateState(PreferenceGroup preferenceGroup) {
92 preferenceGroup.setVisible(preferenceGroup.getPreferenceCount() > 0);
Obadah Diar Bakerli1d63ac52018-11-02 13:20:26 -070093 }
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 Nagarkar487fca42019-07-11 12:08:34 -0700134 locationFooters.add(new LocationFooter(footerTextRes, appInfo));
Obadah Diar Bakerli1d63ac52018-11-02 13:20:26 -0700135 }
136 return locationFooters;
137 }
138
Obadah Diar Bakerli1d63ac52018-11-02 13:20:26 -0700139 /**
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 Bakerli1d63ac52018-11-02 13:20:26 -0700148
Soonil Nagarkar487fca42019-07-11 12:08:34 -0700149 LocationFooter(@StringRes int footerRes, ApplicationInfo appInfo) {
Obadah Diar Bakerli1d63ac52018-11-02 13:20:26 -0700150 mFooterStringRes = footerRes;
151 mApplicationInfo = appInfo;
Obadah Diar Bakerli1d63ac52018-11-02 13:20:26 -0700152 }
153 }
154}