blob: a6b24104fa90e4a8bbf4da2b7a51f6fb58c22606 [file] [log] [blame]
Jason Monk744b6362015-11-03 18:24:29 -05001/**
2 * Copyright (C) 2015 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 *
Fan Zhang83dde972018-07-23 16:51:14 -07008 * http://www.apache.org/licenses/LICENSE-2.0
Jason Monk744b6362015-11-03 18:24:29 -05009 *
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.settingslib.drawer;
18
Fan Zhang4c07a712018-08-06 10:04:14 -070019import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_ORDER;
arangelov24eec2f2018-05-30 18:24:23 +010020import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
Fan Zhang83dde972018-07-23 16:51:14 -070021import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON;
22import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI;
Fan Zhang4c07a712018-08-06 10:04:14 -070023import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
Fan Zhang4aa6da62018-08-20 14:13:59 -070024import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
25import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY_URI;
Fan Zhanga6afef82018-08-17 11:34:27 -070026import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;
arangelov24eec2f2018-05-30 18:24:23 +010027import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
28import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
29
Fan Zhang0e201232018-07-26 11:26:44 -070030import android.content.Context;
Jason Monk744b6362015-11-03 18:24:29 -050031import android.content.Intent;
Fan Zhang83dde972018-07-23 16:51:14 -070032import android.content.pm.ActivityInfo;
Fan Zhang0e201232018-07-26 11:26:44 -070033import android.content.pm.PackageManager;
34import android.content.pm.ResolveInfo;
Fan Zhanga6afef82018-08-17 11:34:27 -070035import android.content.res.Resources;
Jason Monk744b6362015-11-03 18:24:29 -050036import android.graphics.drawable.Icon;
37import android.os.Bundle;
38import android.os.Parcel;
39import android.os.Parcelable;
40import android.os.UserHandle;
41import android.text.TextUtils;
Fan Zhanga6afef82018-08-17 11:34:27 -070042import android.util.Log;
Jason Monk744b6362015-11-03 18:24:29 -050043
44import java.util.ArrayList;
Fan Zhang23baea92018-08-02 13:20:22 -070045import java.util.Comparator;
Fan Zhang0e201232018-07-26 11:26:44 -070046import java.util.List;
Fan Zhanga6afef82018-08-17 11:34:27 -070047import java.util.Objects;
Jason Monk744b6362015-11-03 18:24:29 -050048
49/**
50 * Description of a single dashboard tile that the user can select.
51 */
Jason Monkf509d7e2016-01-07 16:22:53 -050052public class Tile implements Parcelable {
Jason Monk744b6362015-11-03 18:24:29 -050053
Fan Zhang83dde972018-07-23 16:51:14 -070054 private static final String TAG = "Tile";
Fan Zhang83dde972018-07-23 16:51:14 -070055
Jason Monk744b6362015-11-03 18:24:29 -050056 /**
Jason Monk744b6362015-11-03 18:24:29 -050057 * Optional list of user handles which the intent should be launched on.
58 */
59 public ArrayList<UserHandle> userHandle = new ArrayList<>();
60
Fan Zhang0e201232018-07-26 11:26:44 -070061 private final String mActivityPackage;
62 private final String mActivityName;
Fan Zhanga79ab482018-08-07 13:04:20 -070063 private final Intent mIntent;
Fan Zhang83961e12018-08-21 18:06:24 -070064
Fan Zhang0e201232018-07-26 11:26:44 -070065 private ActivityInfo mActivityInfo;
Fan Zhang4aa6da62018-08-20 14:13:59 -070066 private CharSequence mSummaryOverride;
Fan Zhang83961e12018-08-21 18:06:24 -070067 private Bundle mMetaData;
Fan Zhanga79ab482018-08-07 13:04:20 -070068 private String mCategory;
Fan Zhang0e201232018-07-26 11:26:44 -070069
Fan Zhangf5c1d762018-08-03 09:14:32 -070070 public Tile(ActivityInfo activityInfo, String category) {
Fan Zhang83dde972018-07-23 16:51:14 -070071 mActivityInfo = activityInfo;
Fan Zhang0e201232018-07-26 11:26:44 -070072 mActivityPackage = mActivityInfo.packageName;
73 mActivityName = mActivityInfo.name;
Fan Zhangf5c1d762018-08-03 09:14:32 -070074 mMetaData = activityInfo.metaData;
75 mCategory = category;
Fan Zhanga79ab482018-08-07 13:04:20 -070076 mIntent = new Intent().setClassName(mActivityPackage, mActivityName);
77 }
78
79 Tile(Parcel in) {
80 mActivityPackage = in.readString();
81 mActivityName = in.readString();
82 mIntent = new Intent().setClassName(mActivityPackage, mActivityName);
Fan Zhanga79ab482018-08-07 13:04:20 -070083 final int N = in.readInt();
84 for (int i = 0; i < N; i++) {
85 userHandle.add(UserHandle.CREATOR.createFromParcel(in));
86 }
87 mCategory = in.readString();
Fan Zhanga79ab482018-08-07 13:04:20 -070088 mMetaData = in.readBundle();
Jason Monk744b6362015-11-03 18:24:29 -050089 }
90
91 @Override
92 public int describeContents() {
93 return 0;
94 }
95
96 @Override
97 public void writeToParcel(Parcel dest, int flags) {
Fan Zhang0e201232018-07-26 11:26:44 -070098 dest.writeString(mActivityPackage);
99 dest.writeString(mActivityName);
Jason Monk744b6362015-11-03 18:24:29 -0500100 final int N = userHandle.size();
101 dest.writeInt(N);
102 for (int i = 0; i < N; i++) {
103 userHandle.get(i).writeToParcel(dest, flags);
104 }
Fan Zhangf5c1d762018-08-03 09:14:32 -0700105 dest.writeString(mCategory);
Fan Zhangf5c1d762018-08-03 09:14:32 -0700106 dest.writeBundle(mMetaData);
Fan Zhangf5c1d762018-08-03 09:14:32 -0700107 }
108
Fan Zhanga6afef82018-08-17 11:34:27 -0700109 public int getId() {
110 return Objects.hash(mActivityPackage, mActivityName);
111 }
112
113 public String getDescription() {
114 return mActivityPackage + "/" + mActivityName;
115 }
116
Fan Zhanga79ab482018-08-07 13:04:20 -0700117 public String getPackageName() {
118 return mActivityPackage;
119 }
120
121 /**
122 * Intent to launch when the preference is selected.
123 */
124 public Intent getIntent() {
125 return mIntent;
126 }
127
Fan Zhangf5c1d762018-08-03 09:14:32 -0700128 /**
129 * Category in which the tile should be placed.
130 */
131 public String getCategory() {
132 return mCategory;
133 }
134
135 public void setCategory(String newCategoryKey) {
136 mCategory = newCategoryKey;
137 }
138
139 /**
Fan Zhang4c07a712018-08-06 10:04:14 -0700140 * Priority of this tile, used for display ordering.
Fan Zhangf5c1d762018-08-03 09:14:32 -0700141 */
Fan Zhang4c07a712018-08-06 10:04:14 -0700142 public int getOrder() {
143 if (hasOrder()) {
144 return mMetaData.getInt(META_DATA_KEY_ORDER);
145 } else {
146 return 0;
147 }
148 }
149
150 public boolean hasOrder() {
151 return mMetaData.containsKey(META_DATA_KEY_ORDER)
152 && mMetaData.get(META_DATA_KEY_ORDER) instanceof Integer;
Fan Zhangf5c1d762018-08-03 09:14:32 -0700153 }
154
Fan Zhanga6afef82018-08-17 11:34:27 -0700155 /**
156 * Title of the tile that is shown to the user.
157 */
158 public CharSequence getTitle(Context context) {
159 CharSequence title = null;
160 final PackageManager packageManager = context.getPackageManager();
161 if (mMetaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
162 if (mMetaData.get(META_DATA_PREFERENCE_TITLE) instanceof Integer) {
163 try {
164 final Resources res =
165 packageManager.getResourcesForApplication(mActivityPackage);
166 title = res.getString(mMetaData.getInt(META_DATA_PREFERENCE_TITLE));
167 } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
168 Log.d(TAG, "Couldn't find info", e);
169 }
170 } else {
171 title = mMetaData.getString(META_DATA_PREFERENCE_TITLE);
172 }
173 }
174 // Set the preference title to the activity's label if no
175 // meta-data is found
176 if (title == null) {
177 title = getActivityInfo(context).loadLabel(packageManager);
178 }
179 return title;
180 }
181
Fan Zhang4aa6da62018-08-20 14:13:59 -0700182 /**
183 * Returns the raw metadata for summary, this is used for comparing 2 summary text without
184 * loading the real string.
185 */
186 public String getSummaryReference() {
187 if (mSummaryOverride != null) {
188 return mSummaryOverride.toString();
189 }
190 if (mMetaData != null && mMetaData.containsKey(META_DATA_PREFERENCE_SUMMARY)) {
191 return mMetaData.get(META_DATA_PREFERENCE_SUMMARY).toString();
192 }
193 return null;
194 }
195
196 /**
197 * Overrides the summary. This can happen when injected tile wants to provide dynamic summary.
198 */
199 public void overrideSummary(CharSequence summaryOverride) {
200 mSummaryOverride = summaryOverride;
201 }
202
203 /**
204 * Optional summary describing what this tile controls.
205 */
206 public CharSequence getSummary(Context context) {
207 if (mSummaryOverride != null) {
208 return mSummaryOverride;
209 }
210 CharSequence summary = null;
211 final PackageManager packageManager = context.getPackageManager();
212 if (mMetaData != null) {
213 if (mMetaData.containsKey(META_DATA_PREFERENCE_SUMMARY_URI)) {
214 return null;
215 }
216 if (mMetaData.containsKey(META_DATA_PREFERENCE_SUMMARY)) {
217 if (mMetaData.get(META_DATA_PREFERENCE_SUMMARY) instanceof Integer) {
218 try {
219 final Resources res =
220 packageManager.getResourcesForApplication(mActivityPackage);
221 summary = res.getString(mMetaData.getInt(META_DATA_PREFERENCE_SUMMARY));
222 } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
223 Log.d(TAG, "Couldn't find info", e);
224 }
225 } else {
226 summary = mMetaData.getString(META_DATA_PREFERENCE_SUMMARY);
227 }
228 }
229 }
230 return summary;
231 }
232
Fan Zhang83961e12018-08-21 18:06:24 -0700233 public void setMetaData(Bundle metaData) {
234 mMetaData = metaData;
235 }
236
237 /**
238 * The metaData from the activity that defines this tile.
239 */
Fan Zhangf5c1d762018-08-03 09:14:32 -0700240 public Bundle getMetaData() {
241 return mMetaData;
Jason Monk744b6362015-11-03 18:24:29 -0500242 }
243
Fan Zhang83dde972018-07-23 16:51:14 -0700244 /**
Fan Zhang4c07a712018-08-06 10:04:14 -0700245 * Optional key to use for this tile.
246 */
247 public String getKey(Context context) {
248 if (!hasKey()) {
249 return null;
250 }
251 if (mMetaData.get(META_DATA_PREFERENCE_KEYHINT) instanceof Integer) {
252 return context.getResources().getString(mMetaData.getInt(META_DATA_PREFERENCE_KEYHINT));
253 } else {
254 return mMetaData.getString(META_DATA_PREFERENCE_KEYHINT);
255 }
256 }
257
258 public boolean hasKey() {
259 return mMetaData != null && mMetaData.containsKey(META_DATA_PREFERENCE_KEYHINT);
260 }
261
262 /**
Fan Zhang83dde972018-07-23 16:51:14 -0700263 * Optional icon to show for this tile.
264 *
265 * @attr ref android.R.styleable#PreferenceHeader_icon
266 */
Fan Zhang0e201232018-07-26 11:26:44 -0700267 public Icon getIcon(Context context) {
Fan Zhangf5c1d762018-08-03 09:14:32 -0700268 if (context == null || mMetaData == null) {
Fan Zhang83dde972018-07-23 16:51:14 -0700269 return null;
270 }
Fan Zhang0e201232018-07-26 11:26:44 -0700271
Fan Zhangf5c1d762018-08-03 09:14:32 -0700272 int iconResId = mMetaData.getInt(META_DATA_PREFERENCE_ICON);
Fan Zhang83dde972018-07-23 16:51:14 -0700273 // Set the icon
274 if (iconResId == 0) {
275 // Only fallback to activityinfo.icon if metadata does not contain ICON_URI.
276 // ICON_URI should be loaded in app UI when need the icon object. Handling IPC at this
277 // level is too complex because we don't have a strong threading contract for this class
Fan Zhangf5c1d762018-08-03 09:14:32 -0700278 if (!mMetaData.containsKey(META_DATA_PREFERENCE_ICON_URI)) {
Fan Zhang0e201232018-07-26 11:26:44 -0700279 iconResId = getActivityInfo(context).icon;
Fan Zhang83dde972018-07-23 16:51:14 -0700280 }
281 }
282 if (iconResId != 0) {
Fan Zhang0e201232018-07-26 11:26:44 -0700283 return Icon.createWithResource(getActivityInfo(context).packageName, iconResId);
Fan Zhang83dde972018-07-23 16:51:14 -0700284 } else {
285 return null;
286 }
287 }
288
Fan Zhangf5c1d762018-08-03 09:14:32 -0700289 /**
290 * Whether the icon can be tinted. This is true when icon needs to be monochrome (single-color)
291 */
292 public boolean isIconTintable(Context context) {
293 if (mMetaData != null
294 && mMetaData.containsKey(TileUtils.META_DATA_PREFERENCE_ICON_TINTABLE)) {
295 return mMetaData.getBoolean(TileUtils.META_DATA_PREFERENCE_ICON_TINTABLE);
296 }
297 final String pkgName = context.getPackageName();
298 // If this drawable is coming from outside Settings, tint it to match the color.
299 final ActivityInfo activityInfo = getActivityInfo(context);
300 return activityInfo != null
301 && !TextUtils.equals(pkgName, activityInfo.packageName);
302 }
303
Fan Zhang0e201232018-07-26 11:26:44 -0700304 private ActivityInfo getActivityInfo(Context context) {
305 if (mActivityInfo == null) {
306 final PackageManager pm = context.getApplicationContext().getPackageManager();
307 final Intent intent = new Intent().setClassName(mActivityPackage, mActivityName);
308 final List<ResolveInfo> infoList =
309 pm.queryIntentActivities(intent, PackageManager.GET_META_DATA);
310 if (infoList != null && !infoList.isEmpty()) {
311 mActivityInfo = infoList.get(0).activityInfo;
312 }
313 }
314 return mActivityInfo;
Jason Monk744b6362015-11-03 18:24:29 -0500315 }
316
Jason Monkf509d7e2016-01-07 16:22:53 -0500317 public static final Creator<Tile> CREATOR = new Creator<Tile>() {
318 public Tile createFromParcel(Parcel source) {
319 return new Tile(source);
Jason Monk744b6362015-11-03 18:24:29 -0500320 }
Fan Zhang83dde972018-07-23 16:51:14 -0700321
Jason Monkf509d7e2016-01-07 16:22:53 -0500322 public Tile[] newArray(int size) {
323 return new Tile[size];
Jason Monk744b6362015-11-03 18:24:29 -0500324 }
325 };
arangelov24eec2f2018-05-30 18:24:23 +0100326
327 public boolean isPrimaryProfileOnly() {
Fan Zhangf5c1d762018-08-03 09:14:32 -0700328 String profile = mMetaData != null ?
329 mMetaData.getString(META_DATA_KEY_PROFILE) : PROFILE_ALL;
arangelov24eec2f2018-05-30 18:24:23 +0100330 profile = (profile != null ? profile : PROFILE_ALL);
331 return TextUtils.equals(profile, PROFILE_PRIMARY);
332 }
Fan Zhang23baea92018-08-02 13:20:22 -0700333
334 public static final Comparator<Tile> TILE_COMPARATOR =
Fan Zhang4c07a712018-08-06 10:04:14 -0700335 (lhs, rhs) -> rhs.getOrder() - lhs.getOrder();
Jason Monk744b6362015-11-03 18:24:29 -0500336}