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.common;
18 
19 import static com.android.settingslib.drawer.CategoryKey.CATEGORY_DEVICE;
20 import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
21 import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
22 import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;
23 
24 import android.app.ActivityManager;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.pm.ActivityInfo;
28 import android.content.pm.PackageManager;
29 import android.content.pm.ResolveInfo;
30 import android.content.res.Resources;
31 import android.graphics.drawable.Icon;
32 import android.os.Bundle;
33 import android.text.TextUtils;
34 
35 import androidx.preference.Preference;
36 
37 import com.android.car.apps.common.util.Themes;
38 import com.android.car.settings.R;
39 import com.android.car.ui.preference.CarUiPreference;
40 
41 import java.util.HashMap;
42 import java.util.List;
43 import java.util.Map;
44 
45 /**
46  * Loads Activity with TileUtils.EXTRA_SETTINGS_ACTION.
47  */
48 // TODO: investigate using SettingsLib Tiles.
49 public class ExtraSettingsLoader {
50     private static final Logger LOG = new Logger(ExtraSettingsLoader.class);
51     private static final String META_DATA_PREFERENCE_CATEGORY = "com.android.settings.category";
52     private Map<Preference, Bundle> mPreferenceBundleMap;
53     private final Context mContext;
54 
ExtraSettingsLoader(Context context)55     public ExtraSettingsLoader(Context context) {
56         mContext = context;
57         mPreferenceBundleMap = new HashMap<>();
58     }
59 
60     /**
61      * Returns a map of {@link Preference} and {@link Bundle} representing settings injected from
62      * system apps and their metadata. The given intent must specify the action to use for
63      * resolving activities and a category with the key "com.android.settings.category" and one of
64      * the values in {@link com.android.settingslib.drawer.CategoryKey}.
65      *
66      * @param intent intent specifying the extra settings category to load
67      */
loadPreferences(Intent intent)68     public Map<Preference, Bundle> loadPreferences(Intent intent) {
69         PackageManager pm = mContext.getPackageManager();
70         List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
71                 PackageManager.GET_META_DATA, ActivityManager.getCurrentUser());
72 
73         String extraCategory = intent.getStringExtra(META_DATA_PREFERENCE_CATEGORY);
74         for (ResolveInfo resolved : results) {
75             if (!resolved.system) {
76                 // Do not allow any app to be added to settings, only system ones.
77                 continue;
78             }
79             String title = null;
80             String summary = null;
81             String category = null;
82             ActivityInfo activityInfo = resolved.activityInfo;
83             Bundle metaData = activityInfo.metaData;
84             try {
85                 Resources res = pm.getResourcesForApplication(activityInfo.packageName);
86                 if (metaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
87                     if (metaData.get(META_DATA_PREFERENCE_TITLE) instanceof Integer) {
88                         title = res.getString(metaData.getInt(META_DATA_PREFERENCE_TITLE));
89                     } else {
90                         title = metaData.getString(META_DATA_PREFERENCE_TITLE);
91                     }
92                 }
93                 if (TextUtils.isEmpty(title)) {
94                     LOG.d("no title.");
95                     title = activityInfo.loadLabel(pm).toString();
96                 }
97                 if (metaData.containsKey(META_DATA_PREFERENCE_SUMMARY)) {
98                     if (metaData.get(META_DATA_PREFERENCE_SUMMARY) instanceof Integer) {
99                         summary = res.getString(metaData.getInt(META_DATA_PREFERENCE_SUMMARY));
100                     } else {
101                         summary = metaData.getString(META_DATA_PREFERENCE_SUMMARY);
102                     }
103                 } else {
104                     LOG.d("no description.");
105                 }
106                 if (metaData.containsKey(META_DATA_PREFERENCE_CATEGORY)) {
107                     if (metaData.get(META_DATA_PREFERENCE_CATEGORY) instanceof Integer) {
108                         category = res.getString(metaData.getInt(META_DATA_PREFERENCE_CATEGORY));
109                     } else {
110                         category = metaData.getString(META_DATA_PREFERENCE_CATEGORY);
111                     }
112                 } else {
113                     LOG.d("no category.");
114                 }
115             } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
116                 LOG.d("Couldn't find info", e);
117             }
118             Icon icon = null;
119             if (metaData.containsKey(META_DATA_PREFERENCE_ICON)) {
120                 int iconRes = metaData.getInt(META_DATA_PREFERENCE_ICON);
121                 icon = Icon.createWithResource(activityInfo.packageName, iconRes);
122             } else {
123                 icon = Icon.createWithResource(mContext, R.drawable.ic_settings_gear);
124                 LOG.d("use default icon.");
125             }
126             Intent extraSettingIntent =
127                     new Intent().setClassName(activityInfo.packageName, activityInfo.name);
128             if (category == null) {
129                 // If category is not specified or not supported, default to device.
130                 category = CATEGORY_DEVICE;
131             }
132 
133             if (!TextUtils.equals(extraCategory, category)) {
134                 continue;
135             }
136             CarUiPreference preference = new CarUiPreference(mContext);
137             preference.setTitle(title);
138             preference.setSummary(summary);
139             if (icon != null) {
140                 preference.setIcon(icon.loadDrawable(mContext));
141                 preference.getIcon().setTintList(
142                         Themes.getAttrColorStateList(mContext, R.attr.iconColor));
143             }
144             preference.setIntent(extraSettingIntent);
145             mPreferenceBundleMap.put(preference, metaData);
146         }
147         return mPreferenceBundleMap;
148     }
149 }
150