blob: d7eeac86248c41eb3e66ccd0b3a26779a7c4764b [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2008 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.internal.app;
18
Adam Powelle9414d92014-07-05 17:44:18 -070019import android.app.Activity;
Dianne Hackborn028ceeb2014-08-17 17:45:48 -070020import android.app.ActivityThread;
Adam Powell09a65602014-07-20 16:23:14 -070021import android.app.usage.UsageStats;
22import android.app.usage.UsageStatsManager;
Adam Powell0256c6f2013-05-29 16:42:33 -070023import android.os.AsyncTask;
Dianne Hackborn028ceeb2014-08-17 17:45:48 -070024import android.provider.Settings;
Adam Powell11f59a02014-08-20 13:22:16 -070025import android.text.TextUtils;
Dianne Hackborn028ceeb2014-08-17 17:45:48 -070026import android.util.Slog;
Adam Powell278902c2014-07-12 18:33:22 -070027import android.widget.AbsListView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import com.android.internal.R;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -080029import com.android.internal.content.PackageMonitor;
30
Adam Powellc5878612012-05-04 18:42:38 -070031import android.app.ActivityManager;
Dianne Hackborn5320eb82012-05-18 12:05:04 -070032import android.app.ActivityManagerNative;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -070033import android.app.AppGlobals;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.content.ComponentName;
35import android.content.Context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.content.Intent;
37import android.content.IntentFilter;
38import android.content.pm.ActivityInfo;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010039import android.content.pm.ApplicationInfo;
Dianne Hackborneb034652009-09-07 00:49:58 -070040import android.content.pm.LabeledIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.content.pm.PackageManager;
Adam Powellc5878612012-05-04 18:42:38 -070042import android.content.pm.PackageManager.NameNotFoundException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.content.pm.ResolveInfo;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010044import android.content.pm.UserInfo;
Adam Powellc5878612012-05-04 18:42:38 -070045import android.content.res.Resources;
Dianne Hackborneb034652009-09-07 00:49:58 -070046import android.graphics.drawable.Drawable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.net.Uri;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010048import android.os.Build;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.os.Bundle;
50import android.os.PatternMatcher;
Dianne Hackborn5320eb82012-05-18 12:05:04 -070051import android.os.RemoteException;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070052import android.os.UserHandle;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010053import android.os.UserManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import android.util.Log;
Adam Powell2d809622012-03-22 15:24:43 -070055import android.view.LayoutInflater;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.view.View;
57import android.view.ViewGroup;
Adam Powell2d809622012-03-22 15:24:43 -070058import android.widget.AdapterView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059import android.widget.BaseAdapter;
Adam Powellc5878612012-05-04 18:42:38 -070060import android.widget.Button;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061import android.widget.ImageView;
Adam Powell2d809622012-03-22 15:24:43 -070062import android.widget.ListView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063import android.widget.TextView;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010064import android.widget.Toast;
Adam Powell4f6c2052014-07-07 18:49:10 -070065import com.android.internal.widget.ResolverDrawerLayout;
Adam Powell2d809622012-03-22 15:24:43 -070066
Adam Powell09a65602014-07-20 16:23:14 -070067import java.text.Collator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068import java.util.ArrayList;
69import java.util.Collections;
Adam Powell09a65602014-07-20 16:23:14 -070070import java.util.Comparator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071import java.util.HashSet;
72import java.util.Iterator;
73import java.util.List;
Adam Lesinskicc562a82014-08-27 11:52:52 -070074import java.util.Map;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075import java.util.Set;
76
Adrian Roos27c3ab62014-10-15 17:21:31 +020077import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
78import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
79
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080/**
81 * This activity is displayed when the system attempts to start an Intent for
82 * which there is more than one matching activity, allowing the user to decide
83 * which to go to. It is not normally used directly by application developers.
84 */
Adam Powelle9414d92014-07-05 17:44:18 -070085public class ResolverActivity extends Activity implements AdapterView.OnItemClickListener {
Adam Powellc5878612012-05-04 18:42:38 -070086 private static final String TAG = "ResolverActivity";
Adam Powell09a65602014-07-20 16:23:14 -070087 private static final boolean DEBUG = false;
Adam Powellc5878612012-05-04 18:42:38 -070088
Dianne Hackborn5320eb82012-05-18 12:05:04 -070089 private int mLaunchedFromUid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090 private ResolveListAdapter mAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091 private PackageManager mPm;
Dianne Hackborn028ceeb2014-08-17 17:45:48 -070092 private boolean mSafeForwardingMode;
Adam Powellc5878612012-05-04 18:42:38 -070093 private boolean mAlwaysUseOption;
94 private boolean mShowExtended;
Adam Powellfd1e93d2014-09-07 16:52:22 -070095 private ListView mListView;
Adam Powellc5878612012-05-04 18:42:38 -070096 private Button mAlwaysButton;
97 private Button mOnceButton;
98 private int mIconDpi;
99 private int mIconSize;
Adam Powell589e6f92012-05-06 20:52:38 -0700100 private int mMaxColumns;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700101 private int mLastSelected = ListView.INVALID_POSITION;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100102 private boolean mResolvingHome = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103
Adam Powell09a65602014-07-20 16:23:14 -0700104 private UsageStatsManager mUsm;
Adam Lesinskicc562a82014-08-27 11:52:52 -0700105 private Map<String, UsageStats> mStats;
Adam Powell09a65602014-07-20 16:23:14 -0700106 private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14;
107
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700108 private boolean mRegistered;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800109 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
110 @Override public void onSomePackagesChanged() {
111 mAdapter.handlePackagesChanged();
112 }
113 };
114
Adam Powell278902c2014-07-12 18:33:22 -0700115 private enum ActionTitle {
116 VIEW(Intent.ACTION_VIEW,
117 com.android.internal.R.string.whichViewApplication,
118 com.android.internal.R.string.whichViewApplicationNamed),
119 EDIT(Intent.ACTION_EDIT,
120 com.android.internal.R.string.whichEditApplication,
121 com.android.internal.R.string.whichEditApplicationNamed),
122 SEND(Intent.ACTION_SEND,
123 com.android.internal.R.string.whichSendApplication,
124 com.android.internal.R.string.whichSendApplicationNamed),
125 SENDTO(Intent.ACTION_SENDTO,
126 com.android.internal.R.string.whichSendApplication,
127 com.android.internal.R.string.whichSendApplicationNamed),
128 SEND_MULTIPLE(Intent.ACTION_SEND_MULTIPLE,
129 com.android.internal.R.string.whichSendApplication,
130 com.android.internal.R.string.whichSendApplicationNamed),
131 DEFAULT(null,
132 com.android.internal.R.string.whichApplication,
Adam Powella35c77a2014-09-25 16:46:36 -0700133 com.android.internal.R.string.whichApplicationNamed),
134 HOME(Intent.ACTION_MAIN,
135 com.android.internal.R.string.whichHomeApplication,
136 com.android.internal.R.string.whichHomeApplicationNamed);
Adam Powell278902c2014-07-12 18:33:22 -0700137
138 public final String action;
139 public final int titleRes;
140 public final int namedTitleRes;
141
142 ActionTitle(String action, int titleRes, int namedTitleRes) {
143 this.action = action;
144 this.titleRes = titleRes;
145 this.namedTitleRes = namedTitleRes;
146 }
147
148 public static ActionTitle forAction(String action) {
149 for (ActionTitle title : values()) {
Adam Powella35c77a2014-09-25 16:46:36 -0700150 if (title != HOME && action != null && action.equals(title.action)) {
Adam Powell278902c2014-07-12 18:33:22 -0700151 return title;
152 }
153 }
154 return DEFAULT;
155 }
156 }
157
Dianne Hackborn905577f2011-09-07 18:31:28 -0700158 private Intent makeMyIntent() {
159 Intent intent = new Intent(getIntent());
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700160 intent.setComponent(null);
Dianne Hackborn905577f2011-09-07 18:31:28 -0700161 // The resolver activity is set to be hidden from recent tasks.
162 // we don't want this attribute to be propagated to the next activity
163 // being launched. Note that if the original Intent also had this
164 // flag set, we are now losing it. That should be a very rare case
165 // and we can live with this.
166 intent.setFlags(intent.getFlags()&~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
167 return intent;
168 }
169
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 @Override
171 protected void onCreate(Bundle savedInstanceState) {
Christopher Tateb72b3632013-09-30 17:50:32 -0700172 // Use a specialized prompt when we're handling the 'Home' app startActivity()
Christopher Tateb72b3632013-09-30 17:50:32 -0700173 final Intent intent = makeMyIntent();
174 final Set<String> categories = intent.getCategories();
175 if (Intent.ACTION_MAIN.equals(intent.getAction())
176 && categories != null
177 && categories.size() == 1
178 && categories.contains(Intent.CATEGORY_HOME)) {
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100179 // Note: this field is not set to true in the compatibility version.
180 mResolvingHome = true;
Christopher Tateb72b3632013-09-30 17:50:32 -0700181 }
182
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700183 setSafeForwardingMode(true);
184
Adam Powella35c77a2014-09-25 16:46:36 -0700185 onCreate(savedInstanceState, intent, null, 0, null, null, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 }
187
Adam Powell278902c2014-07-12 18:33:22 -0700188 /**
189 * Compatibility version for other bundled services that use this ocerload without
190 * a default title resource
191 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 protected void onCreate(Bundle savedInstanceState, Intent intent,
Adam Powell278902c2014-07-12 18:33:22 -0700193 CharSequence title, Intent[] initialIntents,
194 List<ResolveInfo> rList, boolean alwaysUseOption) {
Adam Powell09a65602014-07-20 16:23:14 -0700195 onCreate(savedInstanceState, intent, title, 0, initialIntents, rList, alwaysUseOption);
Adam Powell278902c2014-07-12 18:33:22 -0700196 }
197
198 protected void onCreate(Bundle savedInstanceState, Intent intent,
199 CharSequence title, int defaultTitleRes, Intent[] initialIntents,
200 List<ResolveInfo> rList, boolean alwaysUseOption) {
Adam Powelle9414d92014-07-05 17:44:18 -0700201 setTheme(R.style.Theme_DeviceDefault_Resolver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 super.onCreate(savedInstanceState);
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700203 try {
204 mLaunchedFromUid = ActivityManagerNative.getDefault().getLaunchedFromUid(
205 getActivityToken());
206 } catch (RemoteException e) {
207 mLaunchedFromUid = -1;
208 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 mPm = getPackageManager();
Adam Powell09a65602014-07-20 16:23:14 -0700210 mUsm = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
211
212 final long sinceTime = System.currentTimeMillis() - USAGE_STATS_PERIOD;
Adam Lesinski35168002014-07-21 15:25:30 -0700213 mStats = mUsm.queryAndAggregateUsageStats(sinceTime, System.currentTimeMillis());
Adam Powell09a65602014-07-20 16:23:14 -0700214 Log.d(TAG, "sinceTime=" + sinceTime);
215
Adam Powell589e6f92012-05-06 20:52:38 -0700216 mMaxColumns = getResources().getInteger(R.integer.config_maxResolverActivityColumns);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217
Dianne Hackbornd0d75032012-04-19 23:12:09 -0700218 mPackageMonitor.register(this, getMainLooper(), false);
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700219 mRegistered = true;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800220
Adam Powellc5878612012-05-04 18:42:38 -0700221 final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
222 mIconDpi = am.getLauncherLargeIconDensity();
223 mIconSize = am.getLauncherLargeIconSize();
224
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700225 mAdapter = new ResolveListAdapter(this, intent, initialIntents, rList,
Adam Powell278902c2014-07-12 18:33:22 -0700226 mLaunchedFromUid, alwaysUseOption);
227
228 final int layoutId;
Adam Powellfd1e93d2014-09-07 16:52:22 -0700229 final boolean useHeader;
Adam Powell278902c2014-07-12 18:33:22 -0700230 if (mAdapter.hasFilteredItem()) {
231 layoutId = R.layout.resolver_list_with_default;
232 alwaysUseOption = false;
Adam Powellfd1e93d2014-09-07 16:52:22 -0700233 useHeader = true;
Adam Powell278902c2014-07-12 18:33:22 -0700234 } else {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700235 useHeader = false;
Adam Powell278902c2014-07-12 18:33:22 -0700236 layoutId = R.layout.resolver_list;
237 }
238 mAlwaysUseOption = alwaysUseOption;
239
Craig Mautner1c8eb5e32014-08-04 17:08:25 -0700240 int count = mAdapter.mList.size();
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700241 if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) {
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700242 // Gulp!
243 finish();
244 return;
245 } else if (count > 1) {
Adam Powell278902c2014-07-12 18:33:22 -0700246 setContentView(layoutId);
Adam Powellfd1e93d2014-09-07 16:52:22 -0700247 mListView = (ListView) findViewById(R.id.resolver_list);
248 mListView.setAdapter(mAdapter);
249 mListView.setOnItemClickListener(this);
250 mListView.setOnItemLongClickListener(new ItemLongClickListener());
Adam Powellc5878612012-05-04 18:42:38 -0700251
252 if (alwaysUseOption) {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700253 mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
Adam Powellc5878612012-05-04 18:42:38 -0700254 }
Adam Powelle9414d92014-07-05 17:44:18 -0700255
Adam Powellfd1e93d2014-09-07 16:52:22 -0700256 if (useHeader) {
257 mListView.addHeaderView(LayoutInflater.from(this).inflate(
258 R.layout.resolver_different_item_header, mListView, false));
259 }
Mike Lockwood02eb8742011-02-27 09:10:37 -0800260 } else if (count == 1) {
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700261 safelyStartActivity(mAdapter.intentForPosition(0, false));
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700262 mPackageMonitor.unregister();
263 mRegistered = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264 finish();
265 return;
266 } else {
Adam Powelle9414d92014-07-05 17:44:18 -0700267 setContentView(R.layout.resolver_list);
268
269 final TextView empty = (TextView) findViewById(R.id.empty);
270 empty.setVisibility(View.VISIBLE);
271
Adam Powellfd1e93d2014-09-07 16:52:22 -0700272 mListView = (ListView) findViewById(R.id.resolver_list);
273 mListView.setVisibility(View.GONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800274 }
Adrian Roos27c3ab62014-10-15 17:21:31 +0200275 // Prevent the Resolver window from becoming the top fullscreen window and thus from taking
276 // control of the system bars.
277 getWindow().clearFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800278
Adam Powell4f6c2052014-07-07 18:49:10 -0700279 final ResolverDrawerLayout rdl = (ResolverDrawerLayout) findViewById(R.id.contentPanel);
280 if (rdl != null) {
281 rdl.setOnClickOutsideListener(new View.OnClickListener() {
282 @Override
283 public void onClick(View v) {
284 finish();
285 }
286 });
287 }
288
Adam Powell11f59a02014-08-20 13:22:16 -0700289 if (title == null) {
290 title = getTitleForAction(intent.getAction(), defaultTitleRes);
291 }
292 if (!TextUtils.isEmpty(title)) {
293 final TextView titleView = (TextView) findViewById(R.id.title);
294 if (titleView != null) {
295 titleView.setText(title);
Adam Powell278902c2014-07-12 18:33:22 -0700296 }
Adam Powell11f59a02014-08-20 13:22:16 -0700297 setTitle(title);
Adam Powelle9414d92014-07-05 17:44:18 -0700298 }
Adam Powell2d809622012-03-22 15:24:43 -0700299
Adam Powell278902c2014-07-12 18:33:22 -0700300 final ImageView iconView = (ImageView) findViewById(R.id.icon);
301 final DisplayResolveInfo iconInfo = mAdapter.getFilteredItem();
302 if (iconView != null && iconInfo != null) {
303 new LoadIconIntoViewTask(iconView).execute(iconInfo);
304 }
305
306 if (alwaysUseOption || mAdapter.hasFilteredItem()) {
Adam Powellc5878612012-05-04 18:42:38 -0700307 final ViewGroup buttonLayout = (ViewGroup) findViewById(R.id.button_bar);
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700308 if (buttonLayout != null) {
309 buttonLayout.setVisibility(View.VISIBLE);
310 mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
311 mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
312 } else {
313 mAlwaysUseOption = false;
314 }
Adam Powell278902c2014-07-12 18:33:22 -0700315 }
316
317 if (mAdapter.hasFilteredItem()) {
318 setAlwaysButtonEnabled(true, mAdapter.getFilteredPosition(), false);
319 mOnceButton.setEnabled(true);
320 }
321 }
322
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700323 /**
324 * Turn on launch mode that is safe to use when forwarding intents received from
325 * applications and running in system processes. This mode uses Activity.startActivityAsCaller
326 * instead of the normal Activity.startActivity for launching the activity selected
327 * by the user.
328 *
329 * <p>This mode is set to true by default if the activity is initialized through
330 * {@link #onCreate(android.os.Bundle)}. If a subclass calls one of the other onCreate
331 * methods, it is set to false by default. You must set it before calling one of the
332 * more detailed onCreate methods, so that it will be set correctly in the case where
333 * there is only one intent to resolve and it is thus started immediately.</p>
334 */
335 public void setSafeForwardingMode(boolean safeForwarding) {
336 mSafeForwardingMode = safeForwarding;
337 }
338
Adam Powell278902c2014-07-12 18:33:22 -0700339 protected CharSequence getTitleForAction(String action, int defaultTitleRes) {
Adam Powella35c77a2014-09-25 16:46:36 -0700340 final ActionTitle title = mResolvingHome ? ActionTitle.HOME : ActionTitle.forAction(action);
Adam Powell278902c2014-07-12 18:33:22 -0700341 final boolean named = mAdapter.hasFilteredItem();
342 if (title == ActionTitle.DEFAULT && defaultTitleRes != 0) {
343 return getString(defaultTitleRes);
344 } else {
345 return named ? getString(title.namedTitleRes, mAdapter.getFilteredItem().displayLabel) :
346 getString(title.titleRes);
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700347 }
Adam Powellc5878612012-05-04 18:42:38 -0700348 }
349
Adam Powelle9414d92014-07-05 17:44:18 -0700350 void dismiss() {
351 if (!isFinishing()) {
352 finish();
353 }
354 }
355
Adam Powellc5878612012-05-04 18:42:38 -0700356 Drawable getIcon(Resources res, int resId) {
357 Drawable result;
358 try {
359 result = res.getDrawableForDensity(resId, mIconDpi);
360 } catch (Resources.NotFoundException e) {
361 result = null;
362 }
363
364 return result;
365 }
366
367 Drawable loadIconForResolveInfo(ResolveInfo ri) {
368 Drawable dr;
369 try {
370 if (ri.resolvePackageName != null && ri.icon != 0) {
371 dr = getIcon(mPm.getResourcesForApplication(ri.resolvePackageName), ri.icon);
372 if (dr != null) {
373 return dr;
374 }
375 }
376 final int iconRes = ri.getIconResource();
377 if (iconRes != 0) {
378 dr = getIcon(mPm.getResourcesForApplication(ri.activityInfo.packageName), iconRes);
379 if (dr != null) {
380 return dr;
381 }
382 }
383 } catch (NameNotFoundException e) {
384 Log.e(TAG, "Couldn't find resources for package", e);
385 }
386 return ri.loadIcon(mPm);
387 }
388
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800389 @Override
390 protected void onRestart() {
391 super.onRestart();
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700392 if (!mRegistered) {
393 mPackageMonitor.register(this, getMainLooper(), false);
394 mRegistered = true;
395 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800396 mAdapter.handlePackagesChanged();
397 }
398
399 @Override
400 protected void onStop() {
401 super.onStop();
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700402 if (mRegistered) {
403 mPackageMonitor.unregister();
404 mRegistered = false;
405 }
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700406 if ((getIntent().getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
407 // This resolver is in the unusual situation where it has been
408 // launched at the top of a new task. We don't let it be added
409 // to the recent tasks shown to the user, and we need to make sure
410 // that each time we are launched we get the correct launching
411 // uid (not re-using the same resolver from an old launching uid),
412 // so we will now finish ourself since being no longer visible,
413 // the user probably can't get back to us.
414 if (!isChangingConfigurations()) {
415 finish();
416 }
417 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800418 }
419
Adam Powellc5878612012-05-04 18:42:38 -0700420 @Override
Adam Powell9bee4662012-05-08 11:07:23 -0700421 protected void onRestoreInstanceState(Bundle savedInstanceState) {
422 super.onRestoreInstanceState(savedInstanceState);
423 if (mAlwaysUseOption) {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700424 final int checkedPos = mListView.getCheckedItemPosition();
Nicolas Prevot50449882014-06-23 12:42:37 +0100425 final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
Adam Powelld81cc4a2012-07-19 13:51:39 -0700426 mLastSelected = checkedPos;
Adam Powell278902c2014-07-12 18:33:22 -0700427 setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
Nicolas Prevot50449882014-06-23 12:42:37 +0100428 mOnceButton.setEnabled(hasValidSelection);
429 if (hasValidSelection) {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700430 mListView.setSelection(checkedPos);
Adam Powell9bee4662012-05-08 11:07:23 -0700431 }
432 }
433 }
434
435 @Override
Adam Powellc5878612012-05-04 18:42:38 -0700436 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700437 position -= mListView.getHeaderViewsCount();
438 if (position < 0) {
439 // Header views don't count.
440 return;
441 }
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100442 ResolveInfo resolveInfo = mAdapter.resolveInfoForPosition(position, true);
443 if (mResolvingHome && hasManagedProfile()
444 && !supportsManagedProfiles(resolveInfo)) {
445 Toast.makeText(this, String.format(getResources().getString(
446 com.android.internal.R.string.activity_resolver_work_profiles_support),
447 resolveInfo.activityInfo.loadLabel(getPackageManager()).toString()),
448 Toast.LENGTH_LONG).show();
449 return;
450 }
Adam Powellfd1e93d2014-09-07 16:52:22 -0700451 final int checkedPos = mListView.getCheckedItemPosition();
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700452 final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
Adam Powellbdda4e72012-07-20 11:22:03 -0700453 if (mAlwaysUseOption && (!hasValidSelection || mLastSelected != checkedPos)) {
Adam Powell278902c2014-07-12 18:33:22 -0700454 setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
Adam Powelld81cc4a2012-07-19 13:51:39 -0700455 mOnceButton.setEnabled(hasValidSelection);
456 if (hasValidSelection) {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700457 mListView.smoothScrollToPosition(checkedPos);
Adam Powell9bee4662012-05-08 11:07:23 -0700458 }
Adam Powelld81cc4a2012-07-19 13:51:39 -0700459 mLastSelected = checkedPos;
Adam Powellc5878612012-05-04 18:42:38 -0700460 } else {
Adam Powell278902c2014-07-12 18:33:22 -0700461 startSelected(position, false, true);
Adam Powellc5878612012-05-04 18:42:38 -0700462 }
463 }
464
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100465 private boolean hasManagedProfile() {
466 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
467 if (userManager == null) {
468 return false;
469 }
470
471 try {
472 List<UserInfo> profiles = userManager.getProfiles(getUserId());
473 for (UserInfo userInfo : profiles) {
474 if (userInfo != null && userInfo.isManagedProfile()) {
475 return true;
476 }
477 }
478 } catch (SecurityException e) {
479 return false;
480 }
481 return false;
482 }
483
484 private boolean supportsManagedProfiles(ResolveInfo resolveInfo) {
485 try {
486 ApplicationInfo appInfo = getPackageManager().getApplicationInfo(
487 resolveInfo.activityInfo.packageName, 0 /* default flags */);
488 return versionNumberAtLeastL(appInfo.targetSdkVersion);
489 } catch (NameNotFoundException e) {
490 return false;
491 }
492 }
493
494 private boolean versionNumberAtLeastL(int versionNumber) {
Dianne Hackborn955d8d62014-10-07 20:17:19 -0700495 return versionNumber >= Build.VERSION_CODES.LOLLIPOP;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100496 }
497
Adam Powell278902c2014-07-12 18:33:22 -0700498 private void setAlwaysButtonEnabled(boolean hasValidSelection, int checkedPos,
499 boolean filtered) {
Nicolas Prevot50449882014-06-23 12:42:37 +0100500 boolean enabled = false;
501 if (hasValidSelection) {
Adam Powell278902c2014-07-12 18:33:22 -0700502 ResolveInfo ri = mAdapter.resolveInfoForPosition(checkedPos, filtered);
Nicolas Prevot50449882014-06-23 12:42:37 +0100503 if (ri.targetUserId == UserHandle.USER_CURRENT) {
504 enabled = true;
505 }
506 }
507 mAlwaysButton.setEnabled(enabled);
508 }
509
Adam Powellc5878612012-05-04 18:42:38 -0700510 public void onButtonClick(View v) {
511 final int id = v.getId();
Adam Powell278902c2014-07-12 18:33:22 -0700512 startSelected(mAlwaysUseOption ?
Adam Powellfd1e93d2014-09-07 16:52:22 -0700513 mListView.getCheckedItemPosition() : mAdapter.getFilteredPosition(),
Adam Powell278902c2014-07-12 18:33:22 -0700514 id == R.id.button_always,
515 mAlwaysUseOption);
Adam Powellc5878612012-05-04 18:42:38 -0700516 dismiss();
517 }
518
Adam Powell278902c2014-07-12 18:33:22 -0700519 void startSelected(int which, boolean always, boolean filtered) {
Amith Yamasani07cd3512013-09-18 13:16:00 -0700520 if (isFinishing()) {
521 return;
522 }
Adam Powell278902c2014-07-12 18:33:22 -0700523 ResolveInfo ri = mAdapter.resolveInfoForPosition(which, filtered);
524 Intent intent = mAdapter.intentForPosition(which, filtered);
Adam Powellc5878612012-05-04 18:42:38 -0700525 onIntentSelected(ri, intent, always);
Mike Lockwood02eb8742011-02-27 09:10:37 -0800526 finish();
527 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528
Adam Powelle49d9392014-07-17 18:45:19 -0700529 /**
530 * Replace me in subclasses!
531 */
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000532 public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) {
Adam Powelle49d9392014-07-17 18:45:19 -0700533 return defIntent;
534 }
535
Mike Lockwood02eb8742011-02-27 09:10:37 -0800536 protected void onIntentSelected(ResolveInfo ri, Intent intent, boolean alwaysCheck) {
Adam Powell278902c2014-07-12 18:33:22 -0700537 if ((mAlwaysUseOption || mAdapter.hasFilteredItem()) && mAdapter.mOrigResolveList != null) {
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700538 // Build a reasonable intent filter, based on what matched.
539 IntentFilter filter = new IntentFilter();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800540
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700541 if (intent.getAction() != null) {
542 filter.addAction(intent.getAction());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700544 Set<String> categories = intent.getCategories();
545 if (categories != null) {
546 for (String cat : categories) {
547 filter.addCategory(cat);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800548 }
549 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700550 filter.addCategory(Intent.CATEGORY_DEFAULT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800551
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700552 int cat = ri.match&IntentFilter.MATCH_CATEGORY_MASK;
553 Uri data = intent.getData();
554 if (cat == IntentFilter.MATCH_CATEGORY_TYPE) {
555 String mimeType = intent.resolveType(this);
556 if (mimeType != null) {
557 try {
558 filter.addDataType(mimeType);
559 } catch (IntentFilter.MalformedMimeTypeException e) {
560 Log.w("ResolverActivity", e);
561 filter = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800562 }
563 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700564 }
565 if (data != null && data.getScheme() != null) {
566 // We need the data specification if there was no type,
567 // OR if the scheme is not one of our magical "file:"
568 // or "content:" schemes (see IntentFilter for the reason).
569 if (cat != IntentFilter.MATCH_CATEGORY_TYPE
570 || (!"file".equals(data.getScheme())
571 && !"content".equals(data.getScheme()))) {
572 filter.addDataScheme(data.getScheme());
573
574 // Look through the resolved filter to determine which part
575 // of it matched the original Intent.
576 Iterator<PatternMatcher> pIt = ri.filter.schemeSpecificPartsIterator();
577 if (pIt != null) {
578 String ssp = data.getSchemeSpecificPart();
579 while (ssp != null && pIt.hasNext()) {
580 PatternMatcher p = pIt.next();
581 if (p.match(ssp)) {
582 filter.addDataSchemeSpecificPart(p.getPath(), p.getType());
583 break;
584 }
Dianne Hackborndf1c0bf2013-06-12 16:21:38 -0700585 }
586 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700587 Iterator<IntentFilter.AuthorityEntry> aIt = ri.filter.authoritiesIterator();
588 if (aIt != null) {
589 while (aIt.hasNext()) {
590 IntentFilter.AuthorityEntry a = aIt.next();
591 if (a.match(data) >= 0) {
592 int port = a.getPort();
593 filter.addDataAuthority(a.getHost(),
594 port >= 0 ? Integer.toString(port) : null);
595 break;
596 }
597 }
598 }
599 pIt = ri.filter.pathsIterator();
600 if (pIt != null) {
601 String path = data.getPath();
602 while (path != null && pIt.hasNext()) {
603 PatternMatcher p = pIt.next();
604 if (p.match(path)) {
605 filter.addDataPath(p.getPath(), p.getType());
606 break;
607 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 }
609 }
610 }
611 }
612
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700613 if (filter != null) {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700614 final int N = mAdapter.mOrigResolveList.size();
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700615 ComponentName[] set = new ComponentName[N];
616 int bestMatch = 0;
617 for (int i=0; i<N; i++) {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700618 ResolveInfo r = mAdapter.mOrigResolveList.get(i);
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700619 set[i] = new ComponentName(r.activityInfo.packageName,
620 r.activityInfo.name);
621 if (r.match > bestMatch) bestMatch = r.match;
622 }
623 if (alwaysCheck) {
624 getPackageManager().addPreferredActivity(filter, bestMatch, set,
625 intent.getComponent());
626 } else {
627 try {
628 AppGlobals.getPackageManager().setLastChosenActivity(intent,
629 intent.resolveTypeIfNeeded(getContentResolver()),
630 PackageManager.MATCH_DEFAULT_ONLY,
631 filter, bestMatch, intent.getComponent());
632 } catch (RemoteException re) {
633 Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
634 }
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700635 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800636 }
637 }
638
639 if (intent != null) {
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700640 safelyStartActivity(intent);
641 }
642 }
643
644 public void safelyStartActivity(Intent intent) {
645 if (!mSafeForwardingMode) {
Amith Yamasani203a2f42012-10-03 20:16:40 -0700646 startActivity(intent);
Adam Powell0b3c1122014-10-09 12:50:14 -0700647 onActivityStarted(intent);
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700648 return;
649 }
650 try {
Jeff Sharkey97978802014-10-14 10:48:18 -0700651 startActivityAsCaller(intent, null, UserHandle.USER_NULL);
Adam Powell0b3c1122014-10-09 12:50:14 -0700652 onActivityStarted(intent);
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700653 } catch (RuntimeException e) {
654 String launchedFromPackage;
655 try {
656 launchedFromPackage = ActivityManagerNative.getDefault().getLaunchedFromPackage(
657 getActivityToken());
658 } catch (RemoteException e2) {
659 launchedFromPackage = "??";
660 }
661 Slog.wtf(TAG, "Unable to launch as uid " + mLaunchedFromUid
662 + " package " + launchedFromPackage + ", while running in "
663 + ActivityThread.currentProcessName(), e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800664 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800665 }
666
Adam Powell0b3c1122014-10-09 12:50:14 -0700667 public void onActivityStarted(Intent intent) {
668 // Do nothing
669 }
670
Adam Powellc5878612012-05-04 18:42:38 -0700671 void showAppDetails(ResolveInfo ri) {
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700672 Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
Adam Powell0fc5b2b2012-07-18 18:20:29 -0700673 .setData(Uri.fromParts("package", ri.activityInfo.packageName, null))
674 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
Amith Yamasani203a2f42012-10-03 20:16:40 -0700675 startActivity(in);
Adam Powellc5878612012-05-04 18:42:38 -0700676 }
677
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800678 private final class DisplayResolveInfo {
679 ResolveInfo ri;
680 CharSequence displayLabel;
Dianne Hackborneb034652009-09-07 00:49:58 -0700681 Drawable displayIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800682 CharSequence extendedInfo;
Dianne Hackborneb034652009-09-07 00:49:58 -0700683 Intent origIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800684
Dianne Hackborneb034652009-09-07 00:49:58 -0700685 DisplayResolveInfo(ResolveInfo pri, CharSequence pLabel,
686 CharSequence pInfo, Intent pOrigIntent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800687 ri = pri;
688 displayLabel = pLabel;
689 extendedInfo = pInfo;
Dianne Hackborneb034652009-09-07 00:49:58 -0700690 origIntent = pOrigIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800691 }
692 }
693
694 private final class ResolveListAdapter extends BaseAdapter {
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800695 private final Intent[] mInitialIntents;
696 private final List<ResolveInfo> mBaseResolveList;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700697 private ResolveInfo mLastChosen;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800698 private final Intent mIntent;
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700699 private final int mLaunchedFromUid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800700 private final LayoutInflater mInflater;
701
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700702 List<DisplayResolveInfo> mList;
703 List<ResolveInfo> mOrigResolveList;
704
Adam Powell278902c2014-07-12 18:33:22 -0700705 private int mLastChosenPosition = -1;
706 private boolean mFilterLastUsed;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800707
Dianne Hackborneb034652009-09-07 00:49:58 -0700708 public ResolveListAdapter(Context context, Intent intent,
Adam Powell278902c2014-07-12 18:33:22 -0700709 Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid,
710 boolean filterLastUsed) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800711 mIntent = new Intent(intent);
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800712 mInitialIntents = initialIntents;
713 mBaseResolveList = rList;
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700714 mLaunchedFromUid = launchedFromUid;
Adam Powelle9414d92014-07-05 17:44:18 -0700715 mInflater = LayoutInflater.from(context);
You Kim43a5070e2012-11-21 23:23:45 +0900716 mList = new ArrayList<DisplayResolveInfo>();
Adam Powell278902c2014-07-12 18:33:22 -0700717 mFilterLastUsed = filterLastUsed;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800718 rebuildList();
719 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800720
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800721 public void handlePackagesChanged() {
Adam Powellc5878612012-05-04 18:42:38 -0700722 final int oldItemCount = getCount();
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800723 rebuildList();
724 notifyDataSetChanged();
You Kim43a5070e2012-11-21 23:23:45 +0900725 final int newItemCount = getCount();
726 if (newItemCount == 0) {
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800727 // We no longer have any items... just finish the activity.
728 finish();
Adam Powellc5878612012-05-04 18:42:38 -0700729 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800730 }
731
Adam Powell278902c2014-07-12 18:33:22 -0700732 public DisplayResolveInfo getFilteredItem() {
733 if (mFilterLastUsed && mLastChosenPosition >= 0) {
734 // Not using getItem since it offsets to dodge this position for the list
735 return mList.get(mLastChosenPosition);
736 }
737 return null;
738 }
739
740 public int getFilteredPosition() {
741 if (mFilterLastUsed && mLastChosenPosition >= 0) {
742 return mLastChosenPosition;
743 }
744 return AbsListView.INVALID_POSITION;
745 }
746
747 public boolean hasFilteredItem() {
748 return mFilterLastUsed && mLastChosenPosition >= 0;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700749 }
750
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800751 private void rebuildList() {
You Kim43a5070e2012-11-21 23:23:45 +0900752 List<ResolveInfo> currentResolveList;
753
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700754 try {
755 mLastChosen = AppGlobals.getPackageManager().getLastChosenActivity(
756 mIntent, mIntent.resolveTypeIfNeeded(getContentResolver()),
757 PackageManager.MATCH_DEFAULT_ONLY);
758 } catch (RemoteException re) {
759 Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
760 }
761
You Kim43a5070e2012-11-21 23:23:45 +0900762 mList.clear();
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800763 if (mBaseResolveList != null) {
Xiong Lie88b0422014-04-10 15:25:45 +0800764 currentResolveList = mOrigResolveList = mBaseResolveList;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800765 } else {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700766 currentResolveList = mOrigResolveList = mPm.queryIntentActivities(
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800767 mIntent, PackageManager.MATCH_DEFAULT_ONLY
Adam Powell278902c2014-07-12 18:33:22 -0700768 | (mFilterLastUsed ? PackageManager.GET_RESOLVED_FILTER : 0));
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700769 // Filter out any activities that the launched uid does not
770 // have permission for. We don't do this when we have an explicit
771 // list of resolved activities, because that only happens when
772 // we are being subclassed, so we can safely launch whatever
773 // they gave us.
You Kim43a5070e2012-11-21 23:23:45 +0900774 if (currentResolveList != null) {
775 for (int i=currentResolveList.size()-1; i >= 0; i--) {
776 ActivityInfo ai = currentResolveList.get(i).activityInfo;
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700777 int granted = ActivityManager.checkComponentPermission(
778 ai.permission, mLaunchedFromUid,
779 ai.applicationInfo.uid, ai.exported);
780 if (granted != PackageManager.PERMISSION_GRANTED) {
781 // Access not allowed!
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700782 if (mOrigResolveList == currentResolveList) {
783 mOrigResolveList = new ArrayList<ResolveInfo>(mOrigResolveList);
784 }
You Kim43a5070e2012-11-21 23:23:45 +0900785 currentResolveList.remove(i);
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700786 }
787 }
788 }
Jeff Hamiltond88e9aa2011-01-24 14:53:00 -0600789 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800790 int N;
You Kim43a5070e2012-11-21 23:23:45 +0900791 if ((currentResolveList != null) && ((N = currentResolveList.size()) > 0)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800792 // Only display the first matches that are either of equal
793 // priority or have asked to be default options.
You Kim43a5070e2012-11-21 23:23:45 +0900794 ResolveInfo r0 = currentResolveList.get(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800795 for (int i=1; i<N; i++) {
You Kim43a5070e2012-11-21 23:23:45 +0900796 ResolveInfo ri = currentResolveList.get(i);
797 if (DEBUG) Log.v(
Adam Powell09a65602014-07-20 16:23:14 -0700798 TAG,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800799 r0.activityInfo.name + "=" +
800 r0.priority + "/" + r0.isDefault + " vs " +
801 ri.activityInfo.name + "=" +
802 ri.priority + "/" + ri.isDefault);
You Kim43a5070e2012-11-21 23:23:45 +0900803 if (r0.priority != ri.priority ||
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804 r0.isDefault != ri.isDefault) {
805 while (i < N) {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700806 if (mOrigResolveList == currentResolveList) {
807 mOrigResolveList = new ArrayList<ResolveInfo>(mOrigResolveList);
808 }
You Kim43a5070e2012-11-21 23:23:45 +0900809 currentResolveList.remove(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800810 N--;
811 }
812 }
813 }
814 if (N > 1) {
Adam Powell09a65602014-07-20 16:23:14 -0700815 Comparator<ResolveInfo> rComparator =
816 new ResolverComparator(ResolverActivity.this);
You Kim43a5070e2012-11-21 23:23:45 +0900817 Collections.sort(currentResolveList, rComparator);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 }
Dianne Hackborneb034652009-09-07 00:49:58 -0700819 // First put the initial items at the top.
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800820 if (mInitialIntents != null) {
821 for (int i=0; i<mInitialIntents.length; i++) {
822 Intent ii = mInitialIntents[i];
Dianne Hackborneb034652009-09-07 00:49:58 -0700823 if (ii == null) {
824 continue;
825 }
826 ActivityInfo ai = ii.resolveActivityInfo(
827 getPackageManager(), 0);
828 if (ai == null) {
Adam Powell09a65602014-07-20 16:23:14 -0700829 Log.w(TAG, "No activity found for " + ii);
Dianne Hackborneb034652009-09-07 00:49:58 -0700830 continue;
831 }
832 ResolveInfo ri = new ResolveInfo();
833 ri.activityInfo = ai;
Nicolas Prevot1a815922014-10-10 16:22:38 +0100834 UserManager userManager =
835 (UserManager) getSystemService(Context.USER_SERVICE);
836 if (userManager.isManagedProfile()) {
837 ri.noResourceId = true;
838 }
Dianne Hackborneb034652009-09-07 00:49:58 -0700839 if (ii instanceof LabeledIntent) {
840 LabeledIntent li = (LabeledIntent)ii;
841 ri.resolvePackageName = li.getSourcePackage();
842 ri.labelRes = li.getLabelResource();
843 ri.nonLocalizedLabel = li.getNonLocalizedLabel();
844 ri.icon = li.getIconResource();
845 }
846 mList.add(new DisplayResolveInfo(ri,
847 ri.loadLabel(getPackageManager()), null, ii));
848 }
849 }
You Kim43a5070e2012-11-21 23:23:45 +0900850
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800851 // Check for applications with same name and use application name or
852 // package name if necessary
You Kim43a5070e2012-11-21 23:23:45 +0900853 r0 = currentResolveList.get(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800854 int start = 0;
855 CharSequence r0Label = r0.loadLabel(mPm);
Adam Powellc5878612012-05-04 18:42:38 -0700856 mShowExtended = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800857 for (int i = 1; i < N; i++) {
858 if (r0Label == null) {
859 r0Label = r0.activityInfo.packageName;
860 }
You Kim43a5070e2012-11-21 23:23:45 +0900861 ResolveInfo ri = currentResolveList.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800862 CharSequence riLabel = ri.loadLabel(mPm);
863 if (riLabel == null) {
864 riLabel = ri.activityInfo.packageName;
865 }
866 if (riLabel.equals(r0Label)) {
867 continue;
868 }
You Kim43a5070e2012-11-21 23:23:45 +0900869 processGroup(currentResolveList, start, (i-1), r0, r0Label);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800870 r0 = ri;
871 r0Label = riLabel;
872 start = i;
873 }
874 // Process last group
You Kim43a5070e2012-11-21 23:23:45 +0900875 processGroup(currentResolveList, start, (N-1), r0, r0Label);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800876 }
877 }
878
879 private void processGroup(List<ResolveInfo> rList, int start, int end, ResolveInfo ro,
880 CharSequence roLabel) {
881 // Process labels from start to i
882 int num = end - start+1;
883 if (num == 1) {
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700884 if (mLastChosen != null
885 && mLastChosen.activityInfo.packageName.equals(
886 ro.activityInfo.packageName)
887 && mLastChosen.activityInfo.name.equals(ro.activityInfo.name)) {
Adam Powell278902c2014-07-12 18:33:22 -0700888 mLastChosenPosition = mList.size();
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700889 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800890 // No duplicate labels. Use label for entry at start
Dianne Hackborneb034652009-09-07 00:49:58 -0700891 mList.add(new DisplayResolveInfo(ro, roLabel, null, null));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800892 } else {
Adam Powellc5878612012-05-04 18:42:38 -0700893 mShowExtended = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800894 boolean usePkg = false;
895 CharSequence startApp = ro.activityInfo.applicationInfo.loadLabel(mPm);
896 if (startApp == null) {
897 usePkg = true;
898 }
899 if (!usePkg) {
900 // Use HashSet to track duplicates
901 HashSet<CharSequence> duplicates =
902 new HashSet<CharSequence>();
903 duplicates.add(startApp);
904 for (int j = start+1; j <= end ; j++) {
905 ResolveInfo jRi = rList.get(j);
906 CharSequence jApp = jRi.activityInfo.applicationInfo.loadLabel(mPm);
907 if ( (jApp == null) || (duplicates.contains(jApp))) {
908 usePkg = true;
909 break;
910 } else {
911 duplicates.add(jApp);
912 }
913 }
914 // Clear HashSet for later use
915 duplicates.clear();
916 }
917 for (int k = start; k <= end; k++) {
918 ResolveInfo add = rList.get(k);
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700919 if (mLastChosen != null
920 && mLastChosen.activityInfo.packageName.equals(
921 add.activityInfo.packageName)
922 && mLastChosen.activityInfo.name.equals(add.activityInfo.name)) {
Adam Powell278902c2014-07-12 18:33:22 -0700923 mLastChosenPosition = mList.size();
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700924 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800925 if (usePkg) {
926 // Use application name for all entries from start to end-1
927 mList.add(new DisplayResolveInfo(add, roLabel,
Dianne Hackborneb034652009-09-07 00:49:58 -0700928 add.activityInfo.packageName, null));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800929 } else {
930 // Use package name for all entries from start to end-1
931 mList.add(new DisplayResolveInfo(add, roLabel,
Dianne Hackborneb034652009-09-07 00:49:58 -0700932 add.activityInfo.applicationInfo.loadLabel(mPm), null));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800933 }
934 }
935 }
936 }
937
Adam Powell278902c2014-07-12 18:33:22 -0700938 public ResolveInfo resolveInfoForPosition(int position, boolean filtered) {
939 return (filtered ? getItem(position) : mList.get(position)).ri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 }
941
Adam Powell278902c2014-07-12 18:33:22 -0700942 public Intent intentForPosition(int position, boolean filtered) {
943 DisplayResolveInfo dri = filtered ? getItem(position) : mList.get(position);
Adam Powelle49d9392014-07-17 18:45:19 -0700944
945 Intent intent = new Intent(dri.origIntent != null ? dri.origIntent :
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000946 getReplacementIntent(dri.ri.activityInfo, mIntent));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800947 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
948 |Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
Dianne Hackborneb034652009-09-07 00:49:58 -0700949 ActivityInfo ai = dri.ri.activityInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800950 intent.setComponent(new ComponentName(
951 ai.applicationInfo.packageName, ai.name));
952 return intent;
953 }
954
955 public int getCount() {
Adam Powell278902c2014-07-12 18:33:22 -0700956 int result = mList.size();
957 if (mFilterLastUsed && mLastChosenPosition >= 0) {
958 result--;
959 }
960 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800961 }
962
Adam Powell278902c2014-07-12 18:33:22 -0700963 public DisplayResolveInfo getItem(int position) {
964 if (mFilterLastUsed && mLastChosenPosition >= 0 && position >= mLastChosenPosition) {
965 position++;
966 }
You Kim43a5070e2012-11-21 23:23:45 +0900967 return mList.get(position);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800968 }
969
970 public long getItemId(int position) {
971 return position;
972 }
973
974 public View getView(int position, View convertView, ViewGroup parent) {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700975 View view = convertView;
976 if (view == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800977 view = mInflater.inflate(
978 com.android.internal.R.layout.resolve_list_item, parent, false);
Adam Powellc5878612012-05-04 18:42:38 -0700979
Adam Powell0256c6f2013-05-29 16:42:33 -0700980 final ViewHolder holder = new ViewHolder(view);
981 view.setTag(holder);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800982 }
Adam Powell278902c2014-07-12 18:33:22 -0700983 bindView(view, getItem(position));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800984 return view;
985 }
986
987 private final void bindView(View view, DisplayResolveInfo info) {
Adam Powell0256c6f2013-05-29 16:42:33 -0700988 final ViewHolder holder = (ViewHolder) view.getTag();
989 holder.text.setText(info.displayLabel);
Adam Powellc5878612012-05-04 18:42:38 -0700990 if (mShowExtended) {
Adam Powell0256c6f2013-05-29 16:42:33 -0700991 holder.text2.setVisibility(View.VISIBLE);
992 holder.text2.setText(info.extendedInfo);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800993 } else {
Adam Powell0256c6f2013-05-29 16:42:33 -0700994 holder.text2.setVisibility(View.GONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800995 }
Dianne Hackborneb034652009-09-07 00:49:58 -0700996 if (info.displayIcon == null) {
Adam Powell0256c6f2013-05-29 16:42:33 -0700997 new LoadIconTask().execute(info);
Dianne Hackborneb034652009-09-07 00:49:58 -0700998 }
Adam Powell0256c6f2013-05-29 16:42:33 -0700999 holder.icon.setImageDrawable(info.displayIcon);
1000 }
1001 }
1002
1003 static class ViewHolder {
1004 public TextView text;
1005 public TextView text2;
1006 public ImageView icon;
1007
1008 public ViewHolder(View view) {
1009 text = (TextView) view.findViewById(com.android.internal.R.id.text1);
1010 text2 = (TextView) view.findViewById(com.android.internal.R.id.text2);
1011 icon = (ImageView) view.findViewById(R.id.icon);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001012 }
1013 }
1014
Adam Powell2d809622012-03-22 15:24:43 -07001015 class ItemLongClickListener implements AdapterView.OnItemLongClickListener {
1016
1017 @Override
1018 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Adam Powellfd1e93d2014-09-07 16:52:22 -07001019 position -= mListView.getHeaderViewsCount();
1020 if (position < 0) {
1021 // Header views don't count.
1022 return false;
1023 }
Adam Powell278902c2014-07-12 18:33:22 -07001024 ResolveInfo ri = mAdapter.resolveInfoForPosition(position, true);
Adam Powellc5878612012-05-04 18:42:38 -07001025 showAppDetails(ri);
Adam Powell2d809622012-03-22 15:24:43 -07001026 return true;
1027 }
1028
1029 }
Adam Powell0256c6f2013-05-29 16:42:33 -07001030
1031 class LoadIconTask extends AsyncTask<DisplayResolveInfo, Void, DisplayResolveInfo> {
1032 @Override
1033 protected DisplayResolveInfo doInBackground(DisplayResolveInfo... params) {
1034 final DisplayResolveInfo info = params[0];
1035 if (info.displayIcon == null) {
1036 info.displayIcon = loadIconForResolveInfo(info.ri);
1037 }
1038 return info;
1039 }
1040
1041 @Override
1042 protected void onPostExecute(DisplayResolveInfo info) {
1043 mAdapter.notifyDataSetChanged();
1044 }
1045 }
Adam Powell278902c2014-07-12 18:33:22 -07001046
1047 class LoadIconIntoViewTask extends AsyncTask<DisplayResolveInfo, Void, DisplayResolveInfo> {
1048 final ImageView mTargetView;
1049
1050 public LoadIconIntoViewTask(ImageView target) {
1051 mTargetView = target;
1052 }
1053
1054 @Override
1055 protected DisplayResolveInfo doInBackground(DisplayResolveInfo... params) {
1056 final DisplayResolveInfo info = params[0];
1057 if (info.displayIcon == null) {
1058 info.displayIcon = loadIconForResolveInfo(info.ri);
1059 }
1060 return info;
1061 }
1062
1063 @Override
1064 protected void onPostExecute(DisplayResolveInfo info) {
1065 mTargetView.setImageDrawable(info.displayIcon);
1066 }
1067 }
Adam Powell09a65602014-07-20 16:23:14 -07001068
1069 class ResolverComparator implements Comparator<ResolveInfo> {
1070 private final Collator mCollator;
1071
1072 public ResolverComparator(Context context) {
1073 mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
1074 }
1075
1076 @Override
1077 public int compare(ResolveInfo lhs, ResolveInfo rhs) {
1078 // We want to put the one targeted to another user at the end of the dialog.
1079 if (lhs.targetUserId != UserHandle.USER_CURRENT) {
1080 return 1;
1081 }
Adam Powell09a65602014-07-20 16:23:14 -07001082
1083 if (mStats != null) {
1084 final long timeDiff =
1085 getPackageTimeSpent(rhs.activityInfo.packageName) -
1086 getPackageTimeSpent(lhs.activityInfo.packageName);
1087
1088 if (timeDiff != 0) {
1089 return timeDiff > 0 ? 1 : -1;
1090 }
1091 }
1092
1093 CharSequence sa = lhs.loadLabel(mPm);
1094 if (sa == null) sa = lhs.activityInfo.name;
1095 CharSequence sb = rhs.loadLabel(mPm);
1096 if (sb == null) sb = rhs.activityInfo.name;
1097
1098 return mCollator.compare(sa.toString(), sb.toString());
1099 }
1100
1101 private long getPackageTimeSpent(String packageName) {
1102 if (mStats != null) {
Adam Lesinski35168002014-07-21 15:25:30 -07001103 final UsageStats stats = mStats.get(packageName);
Adam Powell09a65602014-07-20 16:23:14 -07001104 if (stats != null) {
Adam Lesinski35168002014-07-21 15:25:30 -07001105 return stats.getTotalTimeInForeground();
Adam Powell09a65602014-07-20 16:23:14 -07001106 }
1107
1108 }
1109 return 0;
1110 }
1111 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001112}
1113