blob: 2db466aa78890a7aec3c51bffaa304a8480fc90c [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;
Sander Alewijnsef6545332014-10-31 12:39:02 +0000103 private int mProfileSwitchMessageId = -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104
Adam Powell09a65602014-07-20 16:23:14 -0700105 private UsageStatsManager mUsm;
Adam Lesinskicc562a82014-08-27 11:52:52 -0700106 private Map<String, UsageStats> mStats;
Adam Powell09a65602014-07-20 16:23:14 -0700107 private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14;
108
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700109 private boolean mRegistered;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800110 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
111 @Override public void onSomePackagesChanged() {
112 mAdapter.handlePackagesChanged();
113 }
114 };
115
Adam Powell278902c2014-07-12 18:33:22 -0700116 private enum ActionTitle {
117 VIEW(Intent.ACTION_VIEW,
118 com.android.internal.R.string.whichViewApplication,
119 com.android.internal.R.string.whichViewApplicationNamed),
120 EDIT(Intent.ACTION_EDIT,
121 com.android.internal.R.string.whichEditApplication,
122 com.android.internal.R.string.whichEditApplicationNamed),
123 SEND(Intent.ACTION_SEND,
124 com.android.internal.R.string.whichSendApplication,
125 com.android.internal.R.string.whichSendApplicationNamed),
126 SENDTO(Intent.ACTION_SENDTO,
127 com.android.internal.R.string.whichSendApplication,
128 com.android.internal.R.string.whichSendApplicationNamed),
129 SEND_MULTIPLE(Intent.ACTION_SEND_MULTIPLE,
130 com.android.internal.R.string.whichSendApplication,
131 com.android.internal.R.string.whichSendApplicationNamed),
132 DEFAULT(null,
133 com.android.internal.R.string.whichApplication,
Adam Powella35c77a2014-09-25 16:46:36 -0700134 com.android.internal.R.string.whichApplicationNamed),
135 HOME(Intent.ACTION_MAIN,
136 com.android.internal.R.string.whichHomeApplication,
137 com.android.internal.R.string.whichHomeApplicationNamed);
Adam Powell278902c2014-07-12 18:33:22 -0700138
139 public final String action;
140 public final int titleRes;
141 public final int namedTitleRes;
142
143 ActionTitle(String action, int titleRes, int namedTitleRes) {
144 this.action = action;
145 this.titleRes = titleRes;
146 this.namedTitleRes = namedTitleRes;
147 }
148
149 public static ActionTitle forAction(String action) {
150 for (ActionTitle title : values()) {
Adam Powella35c77a2014-09-25 16:46:36 -0700151 if (title != HOME && action != null && action.equals(title.action)) {
Adam Powell278902c2014-07-12 18:33:22 -0700152 return title;
153 }
154 }
155 return DEFAULT;
156 }
157 }
158
Dianne Hackborn905577f2011-09-07 18:31:28 -0700159 private Intent makeMyIntent() {
160 Intent intent = new Intent(getIntent());
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700161 intent.setComponent(null);
Dianne Hackborn905577f2011-09-07 18:31:28 -0700162 // The resolver activity is set to be hidden from recent tasks.
163 // we don't want this attribute to be propagated to the next activity
164 // being launched. Note that if the original Intent also had this
165 // flag set, we are now losing it. That should be a very rare case
166 // and we can live with this.
167 intent.setFlags(intent.getFlags()&~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
168 return intent;
169 }
170
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 @Override
172 protected void onCreate(Bundle savedInstanceState) {
Christopher Tateb72b3632013-09-30 17:50:32 -0700173 // Use a specialized prompt when we're handling the 'Home' app startActivity()
Christopher Tateb72b3632013-09-30 17:50:32 -0700174 final Intent intent = makeMyIntent();
175 final Set<String> categories = intent.getCategories();
176 if (Intent.ACTION_MAIN.equals(intent.getAction())
177 && categories != null
178 && categories.size() == 1
179 && categories.contains(Intent.CATEGORY_HOME)) {
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100180 // Note: this field is not set to true in the compatibility version.
181 mResolvingHome = true;
Christopher Tateb72b3632013-09-30 17:50:32 -0700182 }
183
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700184 setSafeForwardingMode(true);
185
Adam Powella35c77a2014-09-25 16:46:36 -0700186 onCreate(savedInstanceState, intent, null, 0, null, null, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187 }
188
Adam Powell278902c2014-07-12 18:33:22 -0700189 /**
190 * Compatibility version for other bundled services that use this ocerload without
191 * a default title resource
192 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 protected void onCreate(Bundle savedInstanceState, Intent intent,
Adam Powell278902c2014-07-12 18:33:22 -0700194 CharSequence title, Intent[] initialIntents,
195 List<ResolveInfo> rList, boolean alwaysUseOption) {
Adam Powell09a65602014-07-20 16:23:14 -0700196 onCreate(savedInstanceState, intent, title, 0, initialIntents, rList, alwaysUseOption);
Adam Powell278902c2014-07-12 18:33:22 -0700197 }
198
199 protected void onCreate(Bundle savedInstanceState, Intent intent,
200 CharSequence title, int defaultTitleRes, Intent[] initialIntents,
201 List<ResolveInfo> rList, boolean alwaysUseOption) {
Adam Powelle9414d92014-07-05 17:44:18 -0700202 setTheme(R.style.Theme_DeviceDefault_Resolver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 super.onCreate(savedInstanceState);
Sander Alewijnsef6545332014-10-31 12:39:02 +0000204
205 // Determine whether we should show that intent is forwarded
206 // from managed profile to owner or other way around.
207 setProfileSwitchMessageId(intent.getContentUserHint());
208
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700209 try {
210 mLaunchedFromUid = ActivityManagerNative.getDefault().getLaunchedFromUid(
211 getActivityToken());
212 } catch (RemoteException e) {
213 mLaunchedFromUid = -1;
214 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 mPm = getPackageManager();
Adam Powell09a65602014-07-20 16:23:14 -0700216 mUsm = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
217
218 final long sinceTime = System.currentTimeMillis() - USAGE_STATS_PERIOD;
Adam Lesinski35168002014-07-21 15:25:30 -0700219 mStats = mUsm.queryAndAggregateUsageStats(sinceTime, System.currentTimeMillis());
Adam Powell09a65602014-07-20 16:23:14 -0700220 Log.d(TAG, "sinceTime=" + sinceTime);
221
Adam Powell589e6f92012-05-06 20:52:38 -0700222 mMaxColumns = getResources().getInteger(R.integer.config_maxResolverActivityColumns);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223
Dianne Hackbornd0d75032012-04-19 23:12:09 -0700224 mPackageMonitor.register(this, getMainLooper(), false);
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700225 mRegistered = true;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800226
Adam Powellc5878612012-05-04 18:42:38 -0700227 final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
228 mIconDpi = am.getLauncherLargeIconDensity();
229 mIconSize = am.getLauncherLargeIconSize();
230
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700231 mAdapter = new ResolveListAdapter(this, intent, initialIntents, rList,
Adam Powell278902c2014-07-12 18:33:22 -0700232 mLaunchedFromUid, alwaysUseOption);
233
234 final int layoutId;
Adam Powellfd1e93d2014-09-07 16:52:22 -0700235 final boolean useHeader;
Adam Powell278902c2014-07-12 18:33:22 -0700236 if (mAdapter.hasFilteredItem()) {
237 layoutId = R.layout.resolver_list_with_default;
238 alwaysUseOption = false;
Adam Powellfd1e93d2014-09-07 16:52:22 -0700239 useHeader = true;
Adam Powell278902c2014-07-12 18:33:22 -0700240 } else {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700241 useHeader = false;
Adam Powell278902c2014-07-12 18:33:22 -0700242 layoutId = R.layout.resolver_list;
243 }
244 mAlwaysUseOption = alwaysUseOption;
245
Craig Mautner1c8eb5e32014-08-04 17:08:25 -0700246 int count = mAdapter.mList.size();
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700247 if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) {
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700248 // Gulp!
249 finish();
250 return;
251 } else if (count > 1) {
Adam Powell278902c2014-07-12 18:33:22 -0700252 setContentView(layoutId);
Adam Powellfd1e93d2014-09-07 16:52:22 -0700253 mListView = (ListView) findViewById(R.id.resolver_list);
254 mListView.setAdapter(mAdapter);
255 mListView.setOnItemClickListener(this);
256 mListView.setOnItemLongClickListener(new ItemLongClickListener());
Adam Powellc5878612012-05-04 18:42:38 -0700257
258 if (alwaysUseOption) {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700259 mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
Adam Powellc5878612012-05-04 18:42:38 -0700260 }
Adam Powelle9414d92014-07-05 17:44:18 -0700261
Adam Powellfd1e93d2014-09-07 16:52:22 -0700262 if (useHeader) {
263 mListView.addHeaderView(LayoutInflater.from(this).inflate(
264 R.layout.resolver_different_item_header, mListView, false));
265 }
Mike Lockwood02eb8742011-02-27 09:10:37 -0800266 } else if (count == 1) {
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700267 safelyStartActivity(mAdapter.intentForPosition(0, false));
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700268 mPackageMonitor.unregister();
269 mRegistered = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270 finish();
271 return;
272 } else {
Adam Powelle9414d92014-07-05 17:44:18 -0700273 setContentView(R.layout.resolver_list);
274
275 final TextView empty = (TextView) findViewById(R.id.empty);
276 empty.setVisibility(View.VISIBLE);
277
Adam Powellfd1e93d2014-09-07 16:52:22 -0700278 mListView = (ListView) findViewById(R.id.resolver_list);
279 mListView.setVisibility(View.GONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280 }
Adrian Roos27c3ab62014-10-15 17:21:31 +0200281 // Prevent the Resolver window from becoming the top fullscreen window and thus from taking
282 // control of the system bars.
283 getWindow().clearFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284
Adam Powell4f6c2052014-07-07 18:49:10 -0700285 final ResolverDrawerLayout rdl = (ResolverDrawerLayout) findViewById(R.id.contentPanel);
286 if (rdl != null) {
Adam Powell5dd072d2014-10-30 19:51:41 -0700287 rdl.setOnDismissedListener(new ResolverDrawerLayout.OnDismissedListener() {
Adam Powell4f6c2052014-07-07 18:49:10 -0700288 @Override
Adam Powell5dd072d2014-10-30 19:51:41 -0700289 public void onDismissed() {
Adam Powell4f6c2052014-07-07 18:49:10 -0700290 finish();
291 }
292 });
293 }
294
Adam Powell11f59a02014-08-20 13:22:16 -0700295 if (title == null) {
296 title = getTitleForAction(intent.getAction(), defaultTitleRes);
297 }
298 if (!TextUtils.isEmpty(title)) {
299 final TextView titleView = (TextView) findViewById(R.id.title);
300 if (titleView != null) {
301 titleView.setText(title);
Adam Powell278902c2014-07-12 18:33:22 -0700302 }
Adam Powell11f59a02014-08-20 13:22:16 -0700303 setTitle(title);
Adam Powelle9414d92014-07-05 17:44:18 -0700304 }
Adam Powell2d809622012-03-22 15:24:43 -0700305
Adam Powell278902c2014-07-12 18:33:22 -0700306 final ImageView iconView = (ImageView) findViewById(R.id.icon);
307 final DisplayResolveInfo iconInfo = mAdapter.getFilteredItem();
308 if (iconView != null && iconInfo != null) {
309 new LoadIconIntoViewTask(iconView).execute(iconInfo);
310 }
311
312 if (alwaysUseOption || mAdapter.hasFilteredItem()) {
Adam Powellc5878612012-05-04 18:42:38 -0700313 final ViewGroup buttonLayout = (ViewGroup) findViewById(R.id.button_bar);
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700314 if (buttonLayout != null) {
315 buttonLayout.setVisibility(View.VISIBLE);
316 mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
317 mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
318 } else {
319 mAlwaysUseOption = false;
320 }
Adam Powell278902c2014-07-12 18:33:22 -0700321 }
322
323 if (mAdapter.hasFilteredItem()) {
324 setAlwaysButtonEnabled(true, mAdapter.getFilteredPosition(), false);
325 mOnceButton.setEnabled(true);
326 }
327 }
328
Sander Alewijnsef6545332014-10-31 12:39:02 +0000329 private void setProfileSwitchMessageId(int contentUserHint) {
330 if (contentUserHint != UserHandle.USER_CURRENT &&
331 contentUserHint != UserHandle.myUserId()) {
332 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
333 UserInfo originUserInfo = userManager.getUserInfo(contentUserHint);
334 boolean originIsManaged = originUserInfo != null ? originUserInfo.isManagedProfile()
335 : false;
336 boolean targetIsManaged = userManager.isManagedProfile();
337 if (originIsManaged && !targetIsManaged) {
338 mProfileSwitchMessageId = com.android.internal.R.string.forward_intent_to_owner;
339 } else if (!originIsManaged && targetIsManaged) {
340 mProfileSwitchMessageId = com.android.internal.R.string.forward_intent_to_work;
341 }
342 }
343 }
344
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700345 /**
346 * Turn on launch mode that is safe to use when forwarding intents received from
347 * applications and running in system processes. This mode uses Activity.startActivityAsCaller
348 * instead of the normal Activity.startActivity for launching the activity selected
349 * by the user.
350 *
351 * <p>This mode is set to true by default if the activity is initialized through
352 * {@link #onCreate(android.os.Bundle)}. If a subclass calls one of the other onCreate
353 * methods, it is set to false by default. You must set it before calling one of the
354 * more detailed onCreate methods, so that it will be set correctly in the case where
355 * there is only one intent to resolve and it is thus started immediately.</p>
356 */
357 public void setSafeForwardingMode(boolean safeForwarding) {
358 mSafeForwardingMode = safeForwarding;
359 }
360
Adam Powell278902c2014-07-12 18:33:22 -0700361 protected CharSequence getTitleForAction(String action, int defaultTitleRes) {
Adam Powella35c77a2014-09-25 16:46:36 -0700362 final ActionTitle title = mResolvingHome ? ActionTitle.HOME : ActionTitle.forAction(action);
Adam Powell278902c2014-07-12 18:33:22 -0700363 final boolean named = mAdapter.hasFilteredItem();
364 if (title == ActionTitle.DEFAULT && defaultTitleRes != 0) {
365 return getString(defaultTitleRes);
366 } else {
367 return named ? getString(title.namedTitleRes, mAdapter.getFilteredItem().displayLabel) :
368 getString(title.titleRes);
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700369 }
Adam Powellc5878612012-05-04 18:42:38 -0700370 }
371
Adam Powelle9414d92014-07-05 17:44:18 -0700372 void dismiss() {
373 if (!isFinishing()) {
374 finish();
375 }
376 }
377
Adam Powellc5878612012-05-04 18:42:38 -0700378 Drawable getIcon(Resources res, int resId) {
379 Drawable result;
380 try {
381 result = res.getDrawableForDensity(resId, mIconDpi);
382 } catch (Resources.NotFoundException e) {
383 result = null;
384 }
385
386 return result;
387 }
388
389 Drawable loadIconForResolveInfo(ResolveInfo ri) {
390 Drawable dr;
391 try {
392 if (ri.resolvePackageName != null && ri.icon != 0) {
393 dr = getIcon(mPm.getResourcesForApplication(ri.resolvePackageName), ri.icon);
394 if (dr != null) {
395 return dr;
396 }
397 }
398 final int iconRes = ri.getIconResource();
399 if (iconRes != 0) {
400 dr = getIcon(mPm.getResourcesForApplication(ri.activityInfo.packageName), iconRes);
401 if (dr != null) {
402 return dr;
403 }
404 }
405 } catch (NameNotFoundException e) {
406 Log.e(TAG, "Couldn't find resources for package", e);
407 }
408 return ri.loadIcon(mPm);
409 }
410
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800411 @Override
412 protected void onRestart() {
413 super.onRestart();
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700414 if (!mRegistered) {
415 mPackageMonitor.register(this, getMainLooper(), false);
416 mRegistered = true;
417 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800418 mAdapter.handlePackagesChanged();
419 }
420
421 @Override
422 protected void onStop() {
423 super.onStop();
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700424 if (mRegistered) {
425 mPackageMonitor.unregister();
426 mRegistered = false;
427 }
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700428 if ((getIntent().getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
429 // This resolver is in the unusual situation where it has been
430 // launched at the top of a new task. We don't let it be added
431 // to the recent tasks shown to the user, and we need to make sure
432 // that each time we are launched we get the correct launching
433 // uid (not re-using the same resolver from an old launching uid),
434 // so we will now finish ourself since being no longer visible,
435 // the user probably can't get back to us.
436 if (!isChangingConfigurations()) {
437 finish();
438 }
439 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800440 }
441
Adam Powellc5878612012-05-04 18:42:38 -0700442 @Override
Adam Powell9bee4662012-05-08 11:07:23 -0700443 protected void onRestoreInstanceState(Bundle savedInstanceState) {
444 super.onRestoreInstanceState(savedInstanceState);
445 if (mAlwaysUseOption) {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700446 final int checkedPos = mListView.getCheckedItemPosition();
Nicolas Prevot50449882014-06-23 12:42:37 +0100447 final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
Adam Powelld81cc4a2012-07-19 13:51:39 -0700448 mLastSelected = checkedPos;
Adam Powell278902c2014-07-12 18:33:22 -0700449 setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
Nicolas Prevot50449882014-06-23 12:42:37 +0100450 mOnceButton.setEnabled(hasValidSelection);
451 if (hasValidSelection) {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700452 mListView.setSelection(checkedPos);
Adam Powell9bee4662012-05-08 11:07:23 -0700453 }
454 }
455 }
456
457 @Override
Adam Powellc5878612012-05-04 18:42:38 -0700458 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700459 position -= mListView.getHeaderViewsCount();
460 if (position < 0) {
461 // Header views don't count.
462 return;
463 }
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100464 ResolveInfo resolveInfo = mAdapter.resolveInfoForPosition(position, true);
465 if (mResolvingHome && hasManagedProfile()
466 && !supportsManagedProfiles(resolveInfo)) {
467 Toast.makeText(this, String.format(getResources().getString(
468 com.android.internal.R.string.activity_resolver_work_profiles_support),
469 resolveInfo.activityInfo.loadLabel(getPackageManager()).toString()),
470 Toast.LENGTH_LONG).show();
471 return;
472 }
Adam Powellfd1e93d2014-09-07 16:52:22 -0700473 final int checkedPos = mListView.getCheckedItemPosition();
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700474 final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
Adam Powellbdda4e72012-07-20 11:22:03 -0700475 if (mAlwaysUseOption && (!hasValidSelection || mLastSelected != checkedPos)) {
Adam Powell278902c2014-07-12 18:33:22 -0700476 setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
Adam Powelld81cc4a2012-07-19 13:51:39 -0700477 mOnceButton.setEnabled(hasValidSelection);
478 if (hasValidSelection) {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700479 mListView.smoothScrollToPosition(checkedPos);
Adam Powell9bee4662012-05-08 11:07:23 -0700480 }
Adam Powelld81cc4a2012-07-19 13:51:39 -0700481 mLastSelected = checkedPos;
Adam Powellc5878612012-05-04 18:42:38 -0700482 } else {
Adam Powell278902c2014-07-12 18:33:22 -0700483 startSelected(position, false, true);
Adam Powellc5878612012-05-04 18:42:38 -0700484 }
485 }
486
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100487 private boolean hasManagedProfile() {
488 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
489 if (userManager == null) {
490 return false;
491 }
492
493 try {
494 List<UserInfo> profiles = userManager.getProfiles(getUserId());
495 for (UserInfo userInfo : profiles) {
496 if (userInfo != null && userInfo.isManagedProfile()) {
497 return true;
498 }
499 }
500 } catch (SecurityException e) {
501 return false;
502 }
503 return false;
504 }
505
506 private boolean supportsManagedProfiles(ResolveInfo resolveInfo) {
507 try {
508 ApplicationInfo appInfo = getPackageManager().getApplicationInfo(
509 resolveInfo.activityInfo.packageName, 0 /* default flags */);
510 return versionNumberAtLeastL(appInfo.targetSdkVersion);
511 } catch (NameNotFoundException e) {
512 return false;
513 }
514 }
515
516 private boolean versionNumberAtLeastL(int versionNumber) {
Dianne Hackborn955d8d62014-10-07 20:17:19 -0700517 return versionNumber >= Build.VERSION_CODES.LOLLIPOP;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100518 }
519
Adam Powell278902c2014-07-12 18:33:22 -0700520 private void setAlwaysButtonEnabled(boolean hasValidSelection, int checkedPos,
521 boolean filtered) {
Nicolas Prevot50449882014-06-23 12:42:37 +0100522 boolean enabled = false;
523 if (hasValidSelection) {
Adam Powell278902c2014-07-12 18:33:22 -0700524 ResolveInfo ri = mAdapter.resolveInfoForPosition(checkedPos, filtered);
Nicolas Prevot50449882014-06-23 12:42:37 +0100525 if (ri.targetUserId == UserHandle.USER_CURRENT) {
526 enabled = true;
527 }
528 }
529 mAlwaysButton.setEnabled(enabled);
530 }
531
Adam Powellc5878612012-05-04 18:42:38 -0700532 public void onButtonClick(View v) {
533 final int id = v.getId();
Adam Powell278902c2014-07-12 18:33:22 -0700534 startSelected(mAlwaysUseOption ?
Adam Powellfd1e93d2014-09-07 16:52:22 -0700535 mListView.getCheckedItemPosition() : mAdapter.getFilteredPosition(),
Adam Powell278902c2014-07-12 18:33:22 -0700536 id == R.id.button_always,
537 mAlwaysUseOption);
Adam Powellc5878612012-05-04 18:42:38 -0700538 dismiss();
539 }
540
Adam Powell278902c2014-07-12 18:33:22 -0700541 void startSelected(int which, boolean always, boolean filtered) {
Amith Yamasani07cd3512013-09-18 13:16:00 -0700542 if (isFinishing()) {
543 return;
544 }
Adam Powell278902c2014-07-12 18:33:22 -0700545 ResolveInfo ri = mAdapter.resolveInfoForPosition(which, filtered);
546 Intent intent = mAdapter.intentForPosition(which, filtered);
Adam Powellc5878612012-05-04 18:42:38 -0700547 onIntentSelected(ri, intent, always);
Mike Lockwood02eb8742011-02-27 09:10:37 -0800548 finish();
549 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800550
Adam Powelle49d9392014-07-17 18:45:19 -0700551 /**
552 * Replace me in subclasses!
553 */
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000554 public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) {
Adam Powelle49d9392014-07-17 18:45:19 -0700555 return defIntent;
556 }
557
Mike Lockwood02eb8742011-02-27 09:10:37 -0800558 protected void onIntentSelected(ResolveInfo ri, Intent intent, boolean alwaysCheck) {
Adam Powell278902c2014-07-12 18:33:22 -0700559 if ((mAlwaysUseOption || mAdapter.hasFilteredItem()) && mAdapter.mOrigResolveList != null) {
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700560 // Build a reasonable intent filter, based on what matched.
561 IntentFilter filter = new IntentFilter();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800562
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700563 if (intent.getAction() != null) {
564 filter.addAction(intent.getAction());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700566 Set<String> categories = intent.getCategories();
567 if (categories != null) {
568 for (String cat : categories) {
569 filter.addCategory(cat);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800570 }
571 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700572 filter.addCategory(Intent.CATEGORY_DEFAULT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800573
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700574 int cat = ri.match&IntentFilter.MATCH_CATEGORY_MASK;
575 Uri data = intent.getData();
576 if (cat == IntentFilter.MATCH_CATEGORY_TYPE) {
577 String mimeType = intent.resolveType(this);
578 if (mimeType != null) {
579 try {
580 filter.addDataType(mimeType);
581 } catch (IntentFilter.MalformedMimeTypeException e) {
582 Log.w("ResolverActivity", e);
583 filter = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800584 }
585 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700586 }
587 if (data != null && data.getScheme() != null) {
588 // We need the data specification if there was no type,
589 // OR if the scheme is not one of our magical "file:"
590 // or "content:" schemes (see IntentFilter for the reason).
591 if (cat != IntentFilter.MATCH_CATEGORY_TYPE
592 || (!"file".equals(data.getScheme())
593 && !"content".equals(data.getScheme()))) {
594 filter.addDataScheme(data.getScheme());
595
596 // Look through the resolved filter to determine which part
597 // of it matched the original Intent.
598 Iterator<PatternMatcher> pIt = ri.filter.schemeSpecificPartsIterator();
599 if (pIt != null) {
600 String ssp = data.getSchemeSpecificPart();
601 while (ssp != null && pIt.hasNext()) {
602 PatternMatcher p = pIt.next();
603 if (p.match(ssp)) {
604 filter.addDataSchemeSpecificPart(p.getPath(), p.getType());
605 break;
606 }
Dianne Hackborndf1c0bf2013-06-12 16:21:38 -0700607 }
608 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700609 Iterator<IntentFilter.AuthorityEntry> aIt = ri.filter.authoritiesIterator();
610 if (aIt != null) {
611 while (aIt.hasNext()) {
612 IntentFilter.AuthorityEntry a = aIt.next();
613 if (a.match(data) >= 0) {
614 int port = a.getPort();
615 filter.addDataAuthority(a.getHost(),
616 port >= 0 ? Integer.toString(port) : null);
617 break;
618 }
619 }
620 }
621 pIt = ri.filter.pathsIterator();
622 if (pIt != null) {
623 String path = data.getPath();
624 while (path != null && pIt.hasNext()) {
625 PatternMatcher p = pIt.next();
626 if (p.match(path)) {
627 filter.addDataPath(p.getPath(), p.getType());
628 break;
629 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800630 }
631 }
632 }
633 }
634
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700635 if (filter != null) {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700636 final int N = mAdapter.mOrigResolveList.size();
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700637 ComponentName[] set = new ComponentName[N];
638 int bestMatch = 0;
639 for (int i=0; i<N; i++) {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700640 ResolveInfo r = mAdapter.mOrigResolveList.get(i);
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700641 set[i] = new ComponentName(r.activityInfo.packageName,
642 r.activityInfo.name);
643 if (r.match > bestMatch) bestMatch = r.match;
644 }
645 if (alwaysCheck) {
646 getPackageManager().addPreferredActivity(filter, bestMatch, set,
647 intent.getComponent());
648 } else {
649 try {
650 AppGlobals.getPackageManager().setLastChosenActivity(intent,
651 intent.resolveTypeIfNeeded(getContentResolver()),
652 PackageManager.MATCH_DEFAULT_ONLY,
653 filter, bestMatch, intent.getComponent());
654 } catch (RemoteException re) {
655 Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
656 }
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700657 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 }
659 }
660
661 if (intent != null) {
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700662 safelyStartActivity(intent);
663 }
664 }
665
666 public void safelyStartActivity(Intent intent) {
Sander Alewijnsef6545332014-10-31 12:39:02 +0000667 // If needed, show that intent is forwarded
668 // from managed profile to owner or other way around.
669 if (mProfileSwitchMessageId != -1) {
670 Toast.makeText(this, getString(mProfileSwitchMessageId), Toast.LENGTH_LONG).show();
671 }
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700672 if (!mSafeForwardingMode) {
Amith Yamasani203a2f42012-10-03 20:16:40 -0700673 startActivity(intent);
Adam Powell0b3c1122014-10-09 12:50:14 -0700674 onActivityStarted(intent);
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700675 return;
676 }
677 try {
Jeff Sharkey97978802014-10-14 10:48:18 -0700678 startActivityAsCaller(intent, null, UserHandle.USER_NULL);
Adam Powell0b3c1122014-10-09 12:50:14 -0700679 onActivityStarted(intent);
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700680 } catch (RuntimeException e) {
681 String launchedFromPackage;
682 try {
683 launchedFromPackage = ActivityManagerNative.getDefault().getLaunchedFromPackage(
684 getActivityToken());
685 } catch (RemoteException e2) {
686 launchedFromPackage = "??";
687 }
688 Slog.wtf(TAG, "Unable to launch as uid " + mLaunchedFromUid
689 + " package " + launchedFromPackage + ", while running in "
690 + ActivityThread.currentProcessName(), e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800691 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800692 }
693
Adam Powell0b3c1122014-10-09 12:50:14 -0700694 public void onActivityStarted(Intent intent) {
695 // Do nothing
696 }
697
Adam Powellc5878612012-05-04 18:42:38 -0700698 void showAppDetails(ResolveInfo ri) {
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700699 Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
Adam Powell0fc5b2b2012-07-18 18:20:29 -0700700 .setData(Uri.fromParts("package", ri.activityInfo.packageName, null))
701 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
Amith Yamasani203a2f42012-10-03 20:16:40 -0700702 startActivity(in);
Adam Powellc5878612012-05-04 18:42:38 -0700703 }
704
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800705 private final class DisplayResolveInfo {
706 ResolveInfo ri;
707 CharSequence displayLabel;
Dianne Hackborneb034652009-09-07 00:49:58 -0700708 Drawable displayIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800709 CharSequence extendedInfo;
Dianne Hackborneb034652009-09-07 00:49:58 -0700710 Intent origIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800711
Dianne Hackborneb034652009-09-07 00:49:58 -0700712 DisplayResolveInfo(ResolveInfo pri, CharSequence pLabel,
713 CharSequence pInfo, Intent pOrigIntent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800714 ri = pri;
715 displayLabel = pLabel;
716 extendedInfo = pInfo;
Dianne Hackborneb034652009-09-07 00:49:58 -0700717 origIntent = pOrigIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800718 }
719 }
720
721 private final class ResolveListAdapter extends BaseAdapter {
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800722 private final Intent[] mInitialIntents;
723 private final List<ResolveInfo> mBaseResolveList;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700724 private ResolveInfo mLastChosen;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800725 private final Intent mIntent;
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700726 private final int mLaunchedFromUid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800727 private final LayoutInflater mInflater;
728
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700729 List<DisplayResolveInfo> mList;
730 List<ResolveInfo> mOrigResolveList;
731
Adam Powell278902c2014-07-12 18:33:22 -0700732 private int mLastChosenPosition = -1;
733 private boolean mFilterLastUsed;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800734
Dianne Hackborneb034652009-09-07 00:49:58 -0700735 public ResolveListAdapter(Context context, Intent intent,
Adam Powell278902c2014-07-12 18:33:22 -0700736 Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid,
737 boolean filterLastUsed) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800738 mIntent = new Intent(intent);
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800739 mInitialIntents = initialIntents;
740 mBaseResolveList = rList;
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700741 mLaunchedFromUid = launchedFromUid;
Adam Powelle9414d92014-07-05 17:44:18 -0700742 mInflater = LayoutInflater.from(context);
You Kim43a5070e2012-11-21 23:23:45 +0900743 mList = new ArrayList<DisplayResolveInfo>();
Adam Powell278902c2014-07-12 18:33:22 -0700744 mFilterLastUsed = filterLastUsed;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800745 rebuildList();
746 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800747
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800748 public void handlePackagesChanged() {
Adam Powellc5878612012-05-04 18:42:38 -0700749 final int oldItemCount = getCount();
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800750 rebuildList();
751 notifyDataSetChanged();
You Kim43a5070e2012-11-21 23:23:45 +0900752 final int newItemCount = getCount();
753 if (newItemCount == 0) {
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800754 // We no longer have any items... just finish the activity.
755 finish();
Adam Powellc5878612012-05-04 18:42:38 -0700756 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800757 }
758
Adam Powell278902c2014-07-12 18:33:22 -0700759 public DisplayResolveInfo getFilteredItem() {
760 if (mFilterLastUsed && mLastChosenPosition >= 0) {
761 // Not using getItem since it offsets to dodge this position for the list
762 return mList.get(mLastChosenPosition);
763 }
764 return null;
765 }
766
767 public int getFilteredPosition() {
768 if (mFilterLastUsed && mLastChosenPosition >= 0) {
769 return mLastChosenPosition;
770 }
771 return AbsListView.INVALID_POSITION;
772 }
773
774 public boolean hasFilteredItem() {
775 return mFilterLastUsed && mLastChosenPosition >= 0;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700776 }
777
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800778 private void rebuildList() {
You Kim43a5070e2012-11-21 23:23:45 +0900779 List<ResolveInfo> currentResolveList;
780
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700781 try {
782 mLastChosen = AppGlobals.getPackageManager().getLastChosenActivity(
783 mIntent, mIntent.resolveTypeIfNeeded(getContentResolver()),
784 PackageManager.MATCH_DEFAULT_ONLY);
785 } catch (RemoteException re) {
786 Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
787 }
788
You Kim43a5070e2012-11-21 23:23:45 +0900789 mList.clear();
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800790 if (mBaseResolveList != null) {
Xiong Lie88b0422014-04-10 15:25:45 +0800791 currentResolveList = mOrigResolveList = mBaseResolveList;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800792 } else {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700793 currentResolveList = mOrigResolveList = mPm.queryIntentActivities(
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800794 mIntent, PackageManager.MATCH_DEFAULT_ONLY
Adam Powell278902c2014-07-12 18:33:22 -0700795 | (mFilterLastUsed ? PackageManager.GET_RESOLVED_FILTER : 0));
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700796 // Filter out any activities that the launched uid does not
797 // have permission for. We don't do this when we have an explicit
798 // list of resolved activities, because that only happens when
799 // we are being subclassed, so we can safely launch whatever
800 // they gave us.
You Kim43a5070e2012-11-21 23:23:45 +0900801 if (currentResolveList != null) {
802 for (int i=currentResolveList.size()-1; i >= 0; i--) {
803 ActivityInfo ai = currentResolveList.get(i).activityInfo;
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700804 int granted = ActivityManager.checkComponentPermission(
805 ai.permission, mLaunchedFromUid,
806 ai.applicationInfo.uid, ai.exported);
807 if (granted != PackageManager.PERMISSION_GRANTED) {
808 // Access not allowed!
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700809 if (mOrigResolveList == currentResolveList) {
810 mOrigResolveList = new ArrayList<ResolveInfo>(mOrigResolveList);
811 }
You Kim43a5070e2012-11-21 23:23:45 +0900812 currentResolveList.remove(i);
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700813 }
814 }
815 }
Jeff Hamiltond88e9aa2011-01-24 14:53:00 -0600816 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800817 int N;
You Kim43a5070e2012-11-21 23:23:45 +0900818 if ((currentResolveList != null) && ((N = currentResolveList.size()) > 0)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800819 // Only display the first matches that are either of equal
820 // priority or have asked to be default options.
You Kim43a5070e2012-11-21 23:23:45 +0900821 ResolveInfo r0 = currentResolveList.get(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800822 for (int i=1; i<N; i++) {
You Kim43a5070e2012-11-21 23:23:45 +0900823 ResolveInfo ri = currentResolveList.get(i);
824 if (DEBUG) Log.v(
Adam Powell09a65602014-07-20 16:23:14 -0700825 TAG,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800826 r0.activityInfo.name + "=" +
827 r0.priority + "/" + r0.isDefault + " vs " +
828 ri.activityInfo.name + "=" +
829 ri.priority + "/" + ri.isDefault);
You Kim43a5070e2012-11-21 23:23:45 +0900830 if (r0.priority != ri.priority ||
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800831 r0.isDefault != ri.isDefault) {
832 while (i < N) {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700833 if (mOrigResolveList == currentResolveList) {
834 mOrigResolveList = new ArrayList<ResolveInfo>(mOrigResolveList);
835 }
You Kim43a5070e2012-11-21 23:23:45 +0900836 currentResolveList.remove(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800837 N--;
838 }
839 }
840 }
841 if (N > 1) {
Adam Powell09a65602014-07-20 16:23:14 -0700842 Comparator<ResolveInfo> rComparator =
Dianne Hackbornec452d92014-11-11 17:16:56 -0800843 new ResolverComparator(ResolverActivity.this, mIntent);
You Kim43a5070e2012-11-21 23:23:45 +0900844 Collections.sort(currentResolveList, rComparator);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800845 }
Dianne Hackborneb034652009-09-07 00:49:58 -0700846 // First put the initial items at the top.
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800847 if (mInitialIntents != null) {
848 for (int i=0; i<mInitialIntents.length; i++) {
849 Intent ii = mInitialIntents[i];
Dianne Hackborneb034652009-09-07 00:49:58 -0700850 if (ii == null) {
851 continue;
852 }
853 ActivityInfo ai = ii.resolveActivityInfo(
854 getPackageManager(), 0);
855 if (ai == null) {
Adam Powell09a65602014-07-20 16:23:14 -0700856 Log.w(TAG, "No activity found for " + ii);
Dianne Hackborneb034652009-09-07 00:49:58 -0700857 continue;
858 }
859 ResolveInfo ri = new ResolveInfo();
860 ri.activityInfo = ai;
Nicolas Prevot1a815922014-10-10 16:22:38 +0100861 UserManager userManager =
862 (UserManager) getSystemService(Context.USER_SERVICE);
863 if (userManager.isManagedProfile()) {
864 ri.noResourceId = true;
865 }
Dianne Hackborneb034652009-09-07 00:49:58 -0700866 if (ii instanceof LabeledIntent) {
867 LabeledIntent li = (LabeledIntent)ii;
868 ri.resolvePackageName = li.getSourcePackage();
869 ri.labelRes = li.getLabelResource();
870 ri.nonLocalizedLabel = li.getNonLocalizedLabel();
871 ri.icon = li.getIconResource();
872 }
873 mList.add(new DisplayResolveInfo(ri,
874 ri.loadLabel(getPackageManager()), null, ii));
875 }
876 }
You Kim43a5070e2012-11-21 23:23:45 +0900877
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 // Check for applications with same name and use application name or
879 // package name if necessary
You Kim43a5070e2012-11-21 23:23:45 +0900880 r0 = currentResolveList.get(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800881 int start = 0;
882 CharSequence r0Label = r0.loadLabel(mPm);
Adam Powellc5878612012-05-04 18:42:38 -0700883 mShowExtended = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800884 for (int i = 1; i < N; i++) {
885 if (r0Label == null) {
886 r0Label = r0.activityInfo.packageName;
887 }
You Kim43a5070e2012-11-21 23:23:45 +0900888 ResolveInfo ri = currentResolveList.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800889 CharSequence riLabel = ri.loadLabel(mPm);
890 if (riLabel == null) {
891 riLabel = ri.activityInfo.packageName;
892 }
893 if (riLabel.equals(r0Label)) {
894 continue;
895 }
You Kim43a5070e2012-11-21 23:23:45 +0900896 processGroup(currentResolveList, start, (i-1), r0, r0Label);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800897 r0 = ri;
898 r0Label = riLabel;
899 start = i;
900 }
901 // Process last group
You Kim43a5070e2012-11-21 23:23:45 +0900902 processGroup(currentResolveList, start, (N-1), r0, r0Label);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 }
904 }
905
906 private void processGroup(List<ResolveInfo> rList, int start, int end, ResolveInfo ro,
907 CharSequence roLabel) {
908 // Process labels from start to i
909 int num = end - start+1;
910 if (num == 1) {
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700911 if (mLastChosen != null
912 && mLastChosen.activityInfo.packageName.equals(
913 ro.activityInfo.packageName)
914 && mLastChosen.activityInfo.name.equals(ro.activityInfo.name)) {
Adam Powell278902c2014-07-12 18:33:22 -0700915 mLastChosenPosition = mList.size();
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700916 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800917 // No duplicate labels. Use label for entry at start
Dianne Hackborneb034652009-09-07 00:49:58 -0700918 mList.add(new DisplayResolveInfo(ro, roLabel, null, null));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800919 } else {
Adam Powellc5878612012-05-04 18:42:38 -0700920 mShowExtended = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800921 boolean usePkg = false;
922 CharSequence startApp = ro.activityInfo.applicationInfo.loadLabel(mPm);
923 if (startApp == null) {
924 usePkg = true;
925 }
926 if (!usePkg) {
927 // Use HashSet to track duplicates
928 HashSet<CharSequence> duplicates =
929 new HashSet<CharSequence>();
930 duplicates.add(startApp);
931 for (int j = start+1; j <= end ; j++) {
932 ResolveInfo jRi = rList.get(j);
933 CharSequence jApp = jRi.activityInfo.applicationInfo.loadLabel(mPm);
934 if ( (jApp == null) || (duplicates.contains(jApp))) {
935 usePkg = true;
936 break;
937 } else {
938 duplicates.add(jApp);
939 }
940 }
941 // Clear HashSet for later use
942 duplicates.clear();
943 }
944 for (int k = start; k <= end; k++) {
945 ResolveInfo add = rList.get(k);
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700946 if (mLastChosen != null
947 && mLastChosen.activityInfo.packageName.equals(
948 add.activityInfo.packageName)
949 && mLastChosen.activityInfo.name.equals(add.activityInfo.name)) {
Adam Powell278902c2014-07-12 18:33:22 -0700950 mLastChosenPosition = mList.size();
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700951 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800952 if (usePkg) {
953 // Use application name for all entries from start to end-1
954 mList.add(new DisplayResolveInfo(add, roLabel,
Dianne Hackborneb034652009-09-07 00:49:58 -0700955 add.activityInfo.packageName, null));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800956 } else {
957 // Use package name for all entries from start to end-1
958 mList.add(new DisplayResolveInfo(add, roLabel,
Dianne Hackborneb034652009-09-07 00:49:58 -0700959 add.activityInfo.applicationInfo.loadLabel(mPm), null));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800960 }
961 }
962 }
963 }
964
Adam Powell278902c2014-07-12 18:33:22 -0700965 public ResolveInfo resolveInfoForPosition(int position, boolean filtered) {
966 return (filtered ? getItem(position) : mList.get(position)).ri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800967 }
968
Adam Powell278902c2014-07-12 18:33:22 -0700969 public Intent intentForPosition(int position, boolean filtered) {
970 DisplayResolveInfo dri = filtered ? getItem(position) : mList.get(position);
Adam Powelle49d9392014-07-17 18:45:19 -0700971
972 Intent intent = new Intent(dri.origIntent != null ? dri.origIntent :
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000973 getReplacementIntent(dri.ri.activityInfo, mIntent));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800974 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
975 |Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
Dianne Hackborneb034652009-09-07 00:49:58 -0700976 ActivityInfo ai = dri.ri.activityInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800977 intent.setComponent(new ComponentName(
978 ai.applicationInfo.packageName, ai.name));
979 return intent;
980 }
981
982 public int getCount() {
Adam Powell278902c2014-07-12 18:33:22 -0700983 int result = mList.size();
984 if (mFilterLastUsed && mLastChosenPosition >= 0) {
985 result--;
986 }
987 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800988 }
989
Adam Powell278902c2014-07-12 18:33:22 -0700990 public DisplayResolveInfo getItem(int position) {
991 if (mFilterLastUsed && mLastChosenPosition >= 0 && position >= mLastChosenPosition) {
992 position++;
993 }
You Kim43a5070e2012-11-21 23:23:45 +0900994 return mList.get(position);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800995 }
996
997 public long getItemId(int position) {
998 return position;
999 }
1000
1001 public View getView(int position, View convertView, ViewGroup parent) {
Adam Powellfd1e93d2014-09-07 16:52:22 -07001002 View view = convertView;
1003 if (view == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001004 view = mInflater.inflate(
1005 com.android.internal.R.layout.resolve_list_item, parent, false);
Adam Powellc5878612012-05-04 18:42:38 -07001006
Adam Powell0256c6f2013-05-29 16:42:33 -07001007 final ViewHolder holder = new ViewHolder(view);
1008 view.setTag(holder);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001009 }
Adam Powell278902c2014-07-12 18:33:22 -07001010 bindView(view, getItem(position));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001011 return view;
1012 }
1013
1014 private final void bindView(View view, DisplayResolveInfo info) {
Adam Powell0256c6f2013-05-29 16:42:33 -07001015 final ViewHolder holder = (ViewHolder) view.getTag();
1016 holder.text.setText(info.displayLabel);
Adam Powellc5878612012-05-04 18:42:38 -07001017 if (mShowExtended) {
Adam Powell0256c6f2013-05-29 16:42:33 -07001018 holder.text2.setVisibility(View.VISIBLE);
1019 holder.text2.setText(info.extendedInfo);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001020 } else {
Adam Powell0256c6f2013-05-29 16:42:33 -07001021 holder.text2.setVisibility(View.GONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001022 }
Dianne Hackborneb034652009-09-07 00:49:58 -07001023 if (info.displayIcon == null) {
Adam Powell0256c6f2013-05-29 16:42:33 -07001024 new LoadIconTask().execute(info);
Dianne Hackborneb034652009-09-07 00:49:58 -07001025 }
Adam Powell0256c6f2013-05-29 16:42:33 -07001026 holder.icon.setImageDrawable(info.displayIcon);
1027 }
1028 }
1029
1030 static class ViewHolder {
1031 public TextView text;
1032 public TextView text2;
1033 public ImageView icon;
1034
1035 public ViewHolder(View view) {
1036 text = (TextView) view.findViewById(com.android.internal.R.id.text1);
1037 text2 = (TextView) view.findViewById(com.android.internal.R.id.text2);
1038 icon = (ImageView) view.findViewById(R.id.icon);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 }
1040 }
1041
Adam Powell2d809622012-03-22 15:24:43 -07001042 class ItemLongClickListener implements AdapterView.OnItemLongClickListener {
1043
1044 @Override
1045 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Adam Powellfd1e93d2014-09-07 16:52:22 -07001046 position -= mListView.getHeaderViewsCount();
1047 if (position < 0) {
1048 // Header views don't count.
1049 return false;
1050 }
Adam Powell278902c2014-07-12 18:33:22 -07001051 ResolveInfo ri = mAdapter.resolveInfoForPosition(position, true);
Adam Powellc5878612012-05-04 18:42:38 -07001052 showAppDetails(ri);
Adam Powell2d809622012-03-22 15:24:43 -07001053 return true;
1054 }
1055
1056 }
Adam Powell0256c6f2013-05-29 16:42:33 -07001057
1058 class LoadIconTask extends AsyncTask<DisplayResolveInfo, Void, DisplayResolveInfo> {
1059 @Override
1060 protected DisplayResolveInfo doInBackground(DisplayResolveInfo... params) {
1061 final DisplayResolveInfo info = params[0];
1062 if (info.displayIcon == null) {
1063 info.displayIcon = loadIconForResolveInfo(info.ri);
1064 }
1065 return info;
1066 }
1067
1068 @Override
1069 protected void onPostExecute(DisplayResolveInfo info) {
1070 mAdapter.notifyDataSetChanged();
1071 }
1072 }
Adam Powell278902c2014-07-12 18:33:22 -07001073
1074 class LoadIconIntoViewTask extends AsyncTask<DisplayResolveInfo, Void, DisplayResolveInfo> {
1075 final ImageView mTargetView;
1076
1077 public LoadIconIntoViewTask(ImageView target) {
1078 mTargetView = target;
1079 }
1080
1081 @Override
1082 protected DisplayResolveInfo doInBackground(DisplayResolveInfo... params) {
1083 final DisplayResolveInfo info = params[0];
1084 if (info.displayIcon == null) {
1085 info.displayIcon = loadIconForResolveInfo(info.ri);
1086 }
1087 return info;
1088 }
1089
1090 @Override
1091 protected void onPostExecute(DisplayResolveInfo info) {
1092 mTargetView.setImageDrawable(info.displayIcon);
1093 }
1094 }
Adam Powell09a65602014-07-20 16:23:14 -07001095
Dianne Hackbornec452d92014-11-11 17:16:56 -08001096 static final boolean isSpecificUriMatch(int match) {
1097 match = match&IntentFilter.MATCH_CATEGORY_MASK;
1098 return match >= IntentFilter.MATCH_CATEGORY_HOST
1099 && match <= IntentFilter.MATCH_CATEGORY_PATH;
1100 }
1101
Adam Powell09a65602014-07-20 16:23:14 -07001102 class ResolverComparator implements Comparator<ResolveInfo> {
1103 private final Collator mCollator;
Dianne Hackbornec452d92014-11-11 17:16:56 -08001104 private final boolean mHttp;
Adam Powell09a65602014-07-20 16:23:14 -07001105
Dianne Hackbornec452d92014-11-11 17:16:56 -08001106 public ResolverComparator(Context context, Intent intent) {
Adam Powell09a65602014-07-20 16:23:14 -07001107 mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
Dianne Hackbornec452d92014-11-11 17:16:56 -08001108 String scheme = intent.getScheme();
1109 mHttp = "http".equals(scheme) || "https".equals(scheme);
Adam Powell09a65602014-07-20 16:23:14 -07001110 }
1111
1112 @Override
1113 public int compare(ResolveInfo lhs, ResolveInfo rhs) {
1114 // We want to put the one targeted to another user at the end of the dialog.
1115 if (lhs.targetUserId != UserHandle.USER_CURRENT) {
1116 return 1;
1117 }
Adam Powell09a65602014-07-20 16:23:14 -07001118
Dianne Hackbornec452d92014-11-11 17:16:56 -08001119 if (mHttp) {
1120 // Special case: we want filters that match URI paths/schemes to be
1121 // ordered before others. This is for the case when opening URIs,
1122 // to make native apps go above browsers.
1123 final boolean lhsSpecific = isSpecificUriMatch(lhs.match);
1124 final boolean rhsSpecific = isSpecificUriMatch(rhs.match);
1125 if (lhsSpecific != rhsSpecific) {
1126 return lhsSpecific ? -1 : 1;
1127 }
1128 }
1129
Adam Powell09a65602014-07-20 16:23:14 -07001130 if (mStats != null) {
1131 final long timeDiff =
1132 getPackageTimeSpent(rhs.activityInfo.packageName) -
1133 getPackageTimeSpent(lhs.activityInfo.packageName);
1134
1135 if (timeDiff != 0) {
1136 return timeDiff > 0 ? 1 : -1;
1137 }
1138 }
1139
1140 CharSequence sa = lhs.loadLabel(mPm);
1141 if (sa == null) sa = lhs.activityInfo.name;
1142 CharSequence sb = rhs.loadLabel(mPm);
1143 if (sb == null) sb = rhs.activityInfo.name;
1144
1145 return mCollator.compare(sa.toString(), sb.toString());
1146 }
1147
1148 private long getPackageTimeSpent(String packageName) {
1149 if (mStats != null) {
Adam Lesinski35168002014-07-21 15:25:30 -07001150 final UsageStats stats = mStats.get(packageName);
Adam Powell09a65602014-07-20 16:23:14 -07001151 if (stats != null) {
Adam Lesinski35168002014-07-21 15:25:30 -07001152 return stats.getTotalTimeInForeground();
Adam Powell09a65602014-07-20 16:23:14 -07001153 }
1154
1155 }
1156 return 0;
1157 }
1158 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001159}
1160