blob: 0e02ed6f126d3d656b6ccf7b85eb658825a342ba [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 Powell4c470d62015-06-19 17:46:17 -070019import android.annotation.Nullable;
Adam Powelle9414d92014-07-05 17:44:18 -070020import android.app.Activity;
Dianne Hackborn028ceeb2014-08-17 17:45:48 -070021import android.app.ActivityThread;
Adam Powell4c470d62015-06-19 17:46:17 -070022import android.app.VoiceInteractor.PickOptionRequest;
23import android.app.VoiceInteractor.PickOptionRequest.Option;
24import android.app.VoiceInteractor.Prompt;
Adam Powell23882512016-01-29 10:21:00 -080025import android.content.pm.ComponentInfo;
Adam Powell0256c6f2013-05-29 16:42:33 -070026import android.os.AsyncTask;
Dianne Hackborn028ceeb2014-08-17 17:45:48 -070027import android.provider.Settings;
Adam Powell11f59a02014-08-20 13:22:16 -070028import android.text.TextUtils;
Dianne Hackborn028ceeb2014-08-17 17:45:48 -070029import android.util.Slog;
Adam Powell278902c2014-07-12 18:33:22 -070030import android.widget.AbsListView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import com.android.internal.R;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -080032import com.android.internal.content.PackageMonitor;
33
Adam Powellc5878612012-05-04 18:42:38 -070034import android.app.ActivityManager;
Dianne Hackborn5320eb82012-05-18 12:05:04 -070035import android.app.ActivityManagerNative;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -070036import android.app.AppGlobals;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.content.ComponentName;
38import android.content.Context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.content.Intent;
40import android.content.IntentFilter;
41import android.content.pm.ActivityInfo;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010042import android.content.pm.ApplicationInfo;
Dianne Hackborneb034652009-09-07 00:49:58 -070043import android.content.pm.LabeledIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import android.content.pm.PackageManager;
Adam Powellc5878612012-05-04 18:42:38 -070045import android.content.pm.PackageManager.NameNotFoundException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.content.pm.ResolveInfo;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010047import android.content.pm.UserInfo;
Adam Powellc5878612012-05-04 18:42:38 -070048import android.content.res.Resources;
Dianne Hackborneb034652009-09-07 00:49:58 -070049import android.graphics.drawable.Drawable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.net.Uri;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010051import android.os.Build;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import android.os.Bundle;
53import android.os.PatternMatcher;
Dianne Hackborn5320eb82012-05-18 12:05:04 -070054import android.os.RemoteException;
Jeff Sharkey37355a92016-02-05 16:19:10 -070055import android.os.StrictMode;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070056import android.os.UserHandle;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010057import android.os.UserManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058import android.util.Log;
Adam Powell2d809622012-03-22 15:24:43 -070059import android.view.LayoutInflater;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060import android.view.View;
61import android.view.ViewGroup;
Adam Powell2d809622012-03-22 15:24:43 -070062import android.widget.AdapterView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063import android.widget.BaseAdapter;
Adam Powellc5878612012-05-04 18:42:38 -070064import android.widget.Button;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065import android.widget.ImageView;
Adam Powell2d809622012-03-22 15:24:43 -070066import android.widget.ListView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067import android.widget.TextView;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010068import android.widget.Toast;
Adam Powell4f6c2052014-07-07 18:49:10 -070069import com.android.internal.widget.ResolverDrawerLayout;
Adam Powell2d809622012-03-22 15:24:43 -070070
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071import java.util.ArrayList;
72import java.util.Collections;
73import java.util.HashSet;
74import java.util.Iterator;
75import java.util.List;
Adam Powellc412be62015-06-24 13:54:10 -070076import java.util.Objects;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077import java.util.Set;
78
Wale Ogunwale9014e662016-03-19 14:55:46 -070079import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
Adrian Roos27c3ab62014-10-15 17:21:31 +020080import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
81import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
82
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083/**
84 * This activity is displayed when the system attempts to start an Intent for
85 * which there is more than one matching activity, allowing the user to decide
86 * which to go to. It is not normally used directly by application developers.
87 */
Adam Powell7d758002015-05-06 17:49:36 -070088public class ResolverActivity extends Activity {
Adam Powellc5878612012-05-04 18:42:38 -070089 private static final String TAG = "ResolverActivity";
Adam Powell09a65602014-07-20 16:23:14 -070090 private static final boolean DEBUG = false;
Adam Powellc5878612012-05-04 18:42:38 -070091
Dianne Hackborn5320eb82012-05-18 12:05:04 -070092 private int mLaunchedFromUid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093 private ResolveListAdapter mAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 private PackageManager mPm;
Dianne Hackborn028ceeb2014-08-17 17:45:48 -070095 private boolean mSafeForwardingMode;
Adam Powellc5878612012-05-04 18:42:38 -070096 private boolean mAlwaysUseOption;
Adam Powell24428412015-04-01 17:19:56 -070097 private AbsListView mAdapterView;
Adam Powellc5878612012-05-04 18:42:38 -070098 private Button mAlwaysButton;
99 private Button mOnceButton;
Adam Powell88831a22014-11-20 18:17:00 -0800100 private View mProfileView;
Adam Powellc5878612012-05-04 18:42:38 -0700101 private int mIconDpi;
Adam Powell24428412015-04-01 17:19:56 -0700102 private int mLastSelected = AbsListView.INVALID_POSITION;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100103 private boolean mResolvingHome = false;
Sander Alewijnsef6545332014-10-31 12:39:02 +0000104 private int mProfileSwitchMessageId = -1;
Adam Powell2ed547e2015-04-29 18:45:04 -0700105 private final ArrayList<Intent> mIntents = new ArrayList<>();
Adam Powelld25267c2015-06-05 18:02:21 -0700106 private ResolverComparator mResolverComparator;
Adam Powell4c470d62015-06-19 17:46:17 -0700107 private PickTargetOptionRequest mPickOptionRequest;
Adam Powell09a65602014-07-20 16:23:14 -0700108
Adam Powell63b31692015-09-28 10:45:00 -0700109 protected ResolverDrawerLayout mResolverDrawerLayout;
110
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700111 private boolean mRegistered;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800112 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
113 @Override public void onSomePackagesChanged() {
114 mAdapter.handlePackagesChanged();
Adam Powell88831a22014-11-20 18:17:00 -0800115 if (mProfileView != null) {
116 bindProfileView();
117 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800118 }
119 };
120
Adam Powell278902c2014-07-12 18:33:22 -0700121 private enum ActionTitle {
122 VIEW(Intent.ACTION_VIEW,
123 com.android.internal.R.string.whichViewApplication,
124 com.android.internal.R.string.whichViewApplicationNamed),
125 EDIT(Intent.ACTION_EDIT,
126 com.android.internal.R.string.whichEditApplication,
127 com.android.internal.R.string.whichEditApplicationNamed),
128 SEND(Intent.ACTION_SEND,
129 com.android.internal.R.string.whichSendApplication,
130 com.android.internal.R.string.whichSendApplicationNamed),
131 SENDTO(Intent.ACTION_SENDTO,
Adam Powell13ea8f42016-03-18 09:39:41 -0700132 com.android.internal.R.string.whichSendToApplication,
133 com.android.internal.R.string.whichSendToApplicationNamed),
Adam Powell278902c2014-07-12 18:33:22 -0700134 SEND_MULTIPLE(Intent.ACTION_SEND_MULTIPLE,
135 com.android.internal.R.string.whichSendApplication,
136 com.android.internal.R.string.whichSendApplicationNamed),
137 DEFAULT(null,
138 com.android.internal.R.string.whichApplication,
Adam Powella35c77a2014-09-25 16:46:36 -0700139 com.android.internal.R.string.whichApplicationNamed),
140 HOME(Intent.ACTION_MAIN,
141 com.android.internal.R.string.whichHomeApplication,
142 com.android.internal.R.string.whichHomeApplicationNamed);
Adam Powell278902c2014-07-12 18:33:22 -0700143
144 public final String action;
145 public final int titleRes;
146 public final int namedTitleRes;
147
148 ActionTitle(String action, int titleRes, int namedTitleRes) {
149 this.action = action;
150 this.titleRes = titleRes;
151 this.namedTitleRes = namedTitleRes;
152 }
153
154 public static ActionTitle forAction(String action) {
155 for (ActionTitle title : values()) {
Adam Powella35c77a2014-09-25 16:46:36 -0700156 if (title != HOME && action != null && action.equals(title.action)) {
Adam Powell278902c2014-07-12 18:33:22 -0700157 return title;
158 }
159 }
160 return DEFAULT;
161 }
162 }
163
Dianne Hackborn905577f2011-09-07 18:31:28 -0700164 private Intent makeMyIntent() {
165 Intent intent = new Intent(getIntent());
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700166 intent.setComponent(null);
Dianne Hackborn905577f2011-09-07 18:31:28 -0700167 // The resolver activity is set to be hidden from recent tasks.
168 // we don't want this attribute to be propagated to the next activity
169 // being launched. Note that if the original Intent also had this
170 // flag set, we are now losing it. That should be a very rare case
171 // and we can live with this.
172 intent.setFlags(intent.getFlags()&~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
173 return intent;
174 }
175
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176 @Override
177 protected void onCreate(Bundle savedInstanceState) {
Christopher Tateb72b3632013-09-30 17:50:32 -0700178 // Use a specialized prompt when we're handling the 'Home' app startActivity()
Christopher Tateb72b3632013-09-30 17:50:32 -0700179 final Intent intent = makeMyIntent();
180 final Set<String> categories = intent.getCategories();
181 if (Intent.ACTION_MAIN.equals(intent.getAction())
182 && categories != null
183 && categories.size() == 1
184 && categories.contains(Intent.CATEGORY_HOME)) {
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100185 // Note: this field is not set to true in the compatibility version.
186 mResolvingHome = true;
Christopher Tateb72b3632013-09-30 17:50:32 -0700187 }
188
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700189 setSafeForwardingMode(true);
190
Adam Powella35c77a2014-09-25 16:46:36 -0700191 onCreate(savedInstanceState, intent, null, 0, null, null, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 }
193
Adam Powell278902c2014-07-12 18:33:22 -0700194 /**
Adam Powell24428412015-04-01 17:19:56 -0700195 * Compatibility version for other bundled services that use this overload without
Adam Powell278902c2014-07-12 18:33:22 -0700196 * a default title resource
197 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 protected void onCreate(Bundle savedInstanceState, Intent intent,
Adam Powell278902c2014-07-12 18:33:22 -0700199 CharSequence title, Intent[] initialIntents,
200 List<ResolveInfo> rList, boolean alwaysUseOption) {
Adam Powell09a65602014-07-20 16:23:14 -0700201 onCreate(savedInstanceState, intent, title, 0, initialIntents, rList, alwaysUseOption);
Adam Powell278902c2014-07-12 18:33:22 -0700202 }
203
204 protected void onCreate(Bundle savedInstanceState, Intent intent,
205 CharSequence title, int defaultTitleRes, Intent[] initialIntents,
206 List<ResolveInfo> rList, boolean alwaysUseOption) {
Adam Powelle9414d92014-07-05 17:44:18 -0700207 setTheme(R.style.Theme_DeviceDefault_Resolver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800208 super.onCreate(savedInstanceState);
Sander Alewijnsef6545332014-10-31 12:39:02 +0000209
210 // Determine whether we should show that intent is forwarded
211 // from managed profile to owner or other way around.
212 setProfileSwitchMessageId(intent.getContentUserHint());
213
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700214 try {
215 mLaunchedFromUid = ActivityManagerNative.getDefault().getLaunchedFromUid(
216 getActivityToken());
217 } catch (RemoteException e) {
218 mLaunchedFromUid = -1;
219 }
Adam Powell7d758002015-05-06 17:49:36 -0700220
221 if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) {
222 // Gulp!
223 finish();
224 return;
225 }
226
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227 mPm = getPackageManager();
Adam Powell09a65602014-07-20 16:23:14 -0700228
Dianne Hackbornd0d75032012-04-19 23:12:09 -0700229 mPackageMonitor.register(this, getMainLooper(), false);
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700230 mRegistered = true;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800231
Adam Powellc5878612012-05-04 18:42:38 -0700232 final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
233 mIconDpi = am.getLauncherLargeIconDensity();
Adam Powellc5878612012-05-04 18:42:38 -0700234
Adam Powell7d758002015-05-06 17:49:36 -0700235 // Add our initial intent as the first item, regardless of what else has already been added.
Adam Powell2ed547e2015-04-29 18:45:04 -0700236 mIntents.add(0, new Intent(intent));
Adam Powell278902c2014-07-12 18:33:22 -0700237
Adam Powelld25267c2015-06-05 18:02:21 -0700238 final String referrerPackage = getReferrerPackageName();
239
240 mResolverComparator = new ResolverComparator(this, getTargetIntent(), referrerPackage);
241
Adam Powell39e94eb2015-09-08 17:01:49 -0700242 if (configureContentView(mIntents, initialIntents, rList, alwaysUseOption)) {
243 return;
244 }
Adam Powell278902c2014-07-12 18:33:22 -0700245
Adrian Roos27c3ab62014-10-15 17:21:31 +0200246 // Prevent the Resolver window from becoming the top fullscreen window and thus from taking
247 // control of the system bars.
248 getWindow().clearFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249
Adam Powell4f6c2052014-07-07 18:49:10 -0700250 final ResolverDrawerLayout rdl = (ResolverDrawerLayout) findViewById(R.id.contentPanel);
251 if (rdl != null) {
Adam Powell5dd072d2014-10-30 19:51:41 -0700252 rdl.setOnDismissedListener(new ResolverDrawerLayout.OnDismissedListener() {
Adam Powell4f6c2052014-07-07 18:49:10 -0700253 @Override
Adam Powell5dd072d2014-10-30 19:51:41 -0700254 public void onDismissed() {
Adam Powell4f6c2052014-07-07 18:49:10 -0700255 finish();
256 }
257 });
Adam Powell4c470d62015-06-19 17:46:17 -0700258 if (isVoiceInteraction()) {
259 rdl.setCollapsed(false);
260 }
Adam Powell63b31692015-09-28 10:45:00 -0700261 mResolverDrawerLayout = rdl;
Adam Powell4f6c2052014-07-07 18:49:10 -0700262 }
263
Adam Powell11f59a02014-08-20 13:22:16 -0700264 if (title == null) {
265 title = getTitleForAction(intent.getAction(), defaultTitleRes);
266 }
267 if (!TextUtils.isEmpty(title)) {
268 final TextView titleView = (TextView) findViewById(R.id.title);
269 if (titleView != null) {
270 titleView.setText(title);
Adam Powell278902c2014-07-12 18:33:22 -0700271 }
Adam Powell11f59a02014-08-20 13:22:16 -0700272 setTitle(title);
Adam Powell24428412015-04-01 17:19:56 -0700273
274 // Try to initialize the title icon if we have a view for it and a title to match
275 final ImageView titleIcon = (ImageView) findViewById(R.id.title_icon);
276 if (titleIcon != null) {
Adam Powell24428412015-04-01 17:19:56 -0700277 ApplicationInfo ai = null;
278 try {
279 if (!TextUtils.isEmpty(referrerPackage)) {
280 ai = mPm.getApplicationInfo(referrerPackage, 0);
281 }
282 } catch (NameNotFoundException e) {
283 Log.e(TAG, "Could not find referrer package " + referrerPackage);
284 }
285
286 if (ai != null) {
287 titleIcon.setImageDrawable(ai.loadIcon(mPm));
288 }
289 }
Adam Powelle9414d92014-07-05 17:44:18 -0700290 }
Adam Powell2d809622012-03-22 15:24:43 -0700291
Adam Powell278902c2014-07-12 18:33:22 -0700292 final ImageView iconView = (ImageView) findViewById(R.id.icon);
293 final DisplayResolveInfo iconInfo = mAdapter.getFilteredItem();
294 if (iconView != null && iconInfo != null) {
Adam Powell24428412015-04-01 17:19:56 -0700295 new LoadIconIntoViewTask(iconInfo, iconView).execute();
Adam Powell278902c2014-07-12 18:33:22 -0700296 }
297
298 if (alwaysUseOption || mAdapter.hasFilteredItem()) {
Adam Powellc5878612012-05-04 18:42:38 -0700299 final ViewGroup buttonLayout = (ViewGroup) findViewById(R.id.button_bar);
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700300 if (buttonLayout != null) {
301 buttonLayout.setVisibility(View.VISIBLE);
302 mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
303 mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
304 } else {
305 mAlwaysUseOption = false;
306 }
Adam Powell278902c2014-07-12 18:33:22 -0700307 }
308
309 if (mAdapter.hasFilteredItem()) {
310 setAlwaysButtonEnabled(true, mAdapter.getFilteredPosition(), false);
311 mOnceButton.setEnabled(true);
312 }
Adam Powell88831a22014-11-20 18:17:00 -0800313
314 mProfileView = findViewById(R.id.profile_button);
315 if (mProfileView != null) {
316 mProfileView.setOnClickListener(new View.OnClickListener() {
317 @Override
318 public void onClick(View v) {
319 final DisplayResolveInfo dri = mAdapter.getOtherProfile();
320 if (dri == null) {
321 return;
322 }
323
Sander Alewijnse053d3dd2015-03-09 15:31:10 +0000324 // Do not show the profile switch message anymore.
325 mProfileSwitchMessageId = -1;
326
Adam Powell24428412015-04-01 17:19:56 -0700327 onTargetSelected(dri, false);
Adam Powell88831a22014-11-20 18:17:00 -0800328 finish();
329 }
330 });
331 bindProfileView();
332 }
Adam Powell4c470d62015-06-19 17:46:17 -0700333
334 if (isVoiceInteraction()) {
335 onSetupVoiceInteraction();
336 }
337 }
338
339 /**
340 * Perform any initialization needed for voice interaction.
341 */
Adam Powell23882512016-01-29 10:21:00 -0800342 public void onSetupVoiceInteraction() {
Adam Powell4c470d62015-06-19 17:46:17 -0700343 // Do it right now. Subclasses may delay this and send it later.
344 sendVoiceChoicesIfNeeded();
345 }
346
Adam Powell23882512016-01-29 10:21:00 -0800347 public void sendVoiceChoicesIfNeeded() {
Adam Powell4c470d62015-06-19 17:46:17 -0700348 if (!isVoiceInteraction()) {
349 // Clearly not needed.
350 return;
351 }
352
353
354 final Option[] options = new Option[mAdapter.getCount()];
355 for (int i = 0, N = options.length; i < N; i++) {
356 options[i] = optionForChooserTarget(mAdapter.getItem(i), i);
357 }
358
359 mPickOptionRequest = new PickTargetOptionRequest(
360 new Prompt(getTitle()), options, null);
361 getVoiceInteractor().submitRequest(mPickOptionRequest);
362 }
363
364 Option optionForChooserTarget(TargetInfo target, int index) {
365 return new Option(target.getDisplayLabel(), index);
Adam Powell88831a22014-11-20 18:17:00 -0800366 }
367
Adam Powell2ed547e2015-04-29 18:45:04 -0700368 protected final void setAdditionalTargets(Intent[] intents) {
369 if (intents != null) {
370 for (Intent intent : intents) {
371 mIntents.add(intent);
372 }
373 }
374 }
375
Adam Powell0ccc0e92015-04-23 17:19:37 -0700376 public Intent getTargetIntent() {
Adam Powell2ed547e2015-04-29 18:45:04 -0700377 return mIntents.isEmpty() ? null : mIntents.get(0);
Adam Powell0ccc0e92015-04-23 17:19:37 -0700378 }
379
Adam Powell24428412015-04-01 17:19:56 -0700380 private String getReferrerPackageName() {
381 final Uri referrer = getReferrer();
382 if (referrer != null && "android-app".equals(referrer.getScheme())) {
383 return referrer.getHost();
384 }
385 return null;
386 }
387
Adam Powell23882512016-01-29 10:21:00 -0800388 public int getLayoutResource() {
Adam Powell24428412015-04-01 17:19:56 -0700389 return R.layout.resolver_list;
390 }
391
Adam Powell88831a22014-11-20 18:17:00 -0800392 void bindProfileView() {
393 final DisplayResolveInfo dri = mAdapter.getOtherProfile();
394 if (dri != null) {
395 mProfileView.setVisibility(View.VISIBLE);
Nicolas Prevot256423f2016-02-09 16:33:57 +0000396 final TextView text = (TextView) mProfileView.findViewById(R.id.profile_button);
Adam Powell24428412015-04-01 17:19:56 -0700397 text.setText(dri.getDisplayLabel());
Adam Powell88831a22014-11-20 18:17:00 -0800398 } else {
399 mProfileView.setVisibility(View.GONE);
400 }
Adam Powell278902c2014-07-12 18:33:22 -0700401 }
402
Sander Alewijnsef6545332014-10-31 12:39:02 +0000403 private void setProfileSwitchMessageId(int contentUserHint) {
404 if (contentUserHint != UserHandle.USER_CURRENT &&
405 contentUserHint != UserHandle.myUserId()) {
406 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
407 UserInfo originUserInfo = userManager.getUserInfo(contentUserHint);
408 boolean originIsManaged = originUserInfo != null ? originUserInfo.isManagedProfile()
409 : false;
410 boolean targetIsManaged = userManager.isManagedProfile();
411 if (originIsManaged && !targetIsManaged) {
412 mProfileSwitchMessageId = com.android.internal.R.string.forward_intent_to_owner;
413 } else if (!originIsManaged && targetIsManaged) {
414 mProfileSwitchMessageId = com.android.internal.R.string.forward_intent_to_work;
415 }
416 }
417 }
418
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700419 /**
420 * Turn on launch mode that is safe to use when forwarding intents received from
421 * applications and running in system processes. This mode uses Activity.startActivityAsCaller
422 * instead of the normal Activity.startActivity for launching the activity selected
423 * by the user.
424 *
425 * <p>This mode is set to true by default if the activity is initialized through
426 * {@link #onCreate(android.os.Bundle)}. If a subclass calls one of the other onCreate
427 * methods, it is set to false by default. You must set it before calling one of the
428 * more detailed onCreate methods, so that it will be set correctly in the case where
429 * there is only one intent to resolve and it is thus started immediately.</p>
430 */
431 public void setSafeForwardingMode(boolean safeForwarding) {
432 mSafeForwardingMode = safeForwarding;
433 }
434
Adam Powell278902c2014-07-12 18:33:22 -0700435 protected CharSequence getTitleForAction(String action, int defaultTitleRes) {
Adam Powella35c77a2014-09-25 16:46:36 -0700436 final ActionTitle title = mResolvingHome ? ActionTitle.HOME : ActionTitle.forAction(action);
Adam Powell278902c2014-07-12 18:33:22 -0700437 final boolean named = mAdapter.hasFilteredItem();
438 if (title == ActionTitle.DEFAULT && defaultTitleRes != 0) {
439 return getString(defaultTitleRes);
440 } else {
Adam Powell24428412015-04-01 17:19:56 -0700441 return named
442 ? getString(title.namedTitleRes, mAdapter.getFilteredItem().getDisplayLabel())
443 : getString(title.titleRes);
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700444 }
Adam Powellc5878612012-05-04 18:42:38 -0700445 }
446
Adam Powelle9414d92014-07-05 17:44:18 -0700447 void dismiss() {
448 if (!isFinishing()) {
449 finish();
450 }
451 }
452
Adam Powellc5878612012-05-04 18:42:38 -0700453 Drawable getIcon(Resources res, int resId) {
454 Drawable result;
455 try {
456 result = res.getDrawableForDensity(resId, mIconDpi);
457 } catch (Resources.NotFoundException e) {
458 result = null;
459 }
460
461 return result;
462 }
463
464 Drawable loadIconForResolveInfo(ResolveInfo ri) {
465 Drawable dr;
466 try {
467 if (ri.resolvePackageName != null && ri.icon != 0) {
468 dr = getIcon(mPm.getResourcesForApplication(ri.resolvePackageName), ri.icon);
469 if (dr != null) {
470 return dr;
471 }
472 }
473 final int iconRes = ri.getIconResource();
474 if (iconRes != 0) {
475 dr = getIcon(mPm.getResourcesForApplication(ri.activityInfo.packageName), iconRes);
476 if (dr != null) {
477 return dr;
478 }
479 }
480 } catch (NameNotFoundException e) {
481 Log.e(TAG, "Couldn't find resources for package", e);
482 }
483 return ri.loadIcon(mPm);
484 }
485
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800486 @Override
487 protected void onRestart() {
488 super.onRestart();
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700489 if (!mRegistered) {
490 mPackageMonitor.register(this, getMainLooper(), false);
491 mRegistered = true;
492 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800493 mAdapter.handlePackagesChanged();
Adam Powell88831a22014-11-20 18:17:00 -0800494 if (mProfileView != null) {
495 bindProfileView();
496 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800497 }
498
499 @Override
500 protected void onStop() {
501 super.onStop();
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700502 if (mRegistered) {
503 mPackageMonitor.unregister();
504 mRegistered = false;
505 }
Wale Ogunwale9014e662016-03-19 14:55:46 -0700506 final Intent intent = getIntent();
507 if ((intent.getFlags() & FLAG_ACTIVITY_NEW_TASK) != 0 && !isVoiceInteraction()
508 && !mResolvingHome) {
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700509 // This resolver is in the unusual situation where it has been
510 // launched at the top of a new task. We don't let it be added
511 // to the recent tasks shown to the user, and we need to make sure
512 // that each time we are launched we get the correct launching
513 // uid (not re-using the same resolver from an old launching uid),
514 // so we will now finish ourself since being no longer visible,
515 // the user probably can't get back to us.
516 if (!isChangingConfigurations()) {
517 finish();
518 }
519 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800520 }
521
Adam Powellc5878612012-05-04 18:42:38 -0700522 @Override
Adam Powell4c470d62015-06-19 17:46:17 -0700523 protected void onDestroy() {
524 super.onDestroy();
525 if (!isChangingConfigurations() && mPickOptionRequest != null) {
526 mPickOptionRequest.cancel();
527 }
528 }
529
530 @Override
Adam Powell9bee4662012-05-08 11:07:23 -0700531 protected void onRestoreInstanceState(Bundle savedInstanceState) {
532 super.onRestoreInstanceState(savedInstanceState);
533 if (mAlwaysUseOption) {
Adam Powell24428412015-04-01 17:19:56 -0700534 final int checkedPos = mAdapterView.getCheckedItemPosition();
Nicolas Prevot50449882014-06-23 12:42:37 +0100535 final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
Adam Powelld81cc4a2012-07-19 13:51:39 -0700536 mLastSelected = checkedPos;
Adam Powell278902c2014-07-12 18:33:22 -0700537 setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
Nicolas Prevot50449882014-06-23 12:42:37 +0100538 mOnceButton.setEnabled(hasValidSelection);
539 if (hasValidSelection) {
Adam Powell24428412015-04-01 17:19:56 -0700540 mAdapterView.setSelection(checkedPos);
Adam Powell9bee4662012-05-08 11:07:23 -0700541 }
542 }
543 }
544
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100545 private boolean hasManagedProfile() {
546 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
547 if (userManager == null) {
548 return false;
549 }
550
551 try {
552 List<UserInfo> profiles = userManager.getProfiles(getUserId());
553 for (UserInfo userInfo : profiles) {
554 if (userInfo != null && userInfo.isManagedProfile()) {
555 return true;
556 }
557 }
558 } catch (SecurityException e) {
559 return false;
560 }
561 return false;
562 }
563
564 private boolean supportsManagedProfiles(ResolveInfo resolveInfo) {
565 try {
566 ApplicationInfo appInfo = getPackageManager().getApplicationInfo(
567 resolveInfo.activityInfo.packageName, 0 /* default flags */);
Adam Powell4c470d62015-06-19 17:46:17 -0700568 return appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100569 } catch (NameNotFoundException e) {
570 return false;
571 }
572 }
573
Adam Powell278902c2014-07-12 18:33:22 -0700574 private void setAlwaysButtonEnabled(boolean hasValidSelection, int checkedPos,
575 boolean filtered) {
Nicolas Prevot50449882014-06-23 12:42:37 +0100576 boolean enabled = false;
577 if (hasValidSelection) {
Adam Powell278902c2014-07-12 18:33:22 -0700578 ResolveInfo ri = mAdapter.resolveInfoForPosition(checkedPos, filtered);
Nicolas Prevot50449882014-06-23 12:42:37 +0100579 if (ri.targetUserId == UserHandle.USER_CURRENT) {
580 enabled = true;
581 }
582 }
583 mAlwaysButton.setEnabled(enabled);
584 }
585
Adam Powellc5878612012-05-04 18:42:38 -0700586 public void onButtonClick(View v) {
587 final int id = v.getId();
Adam Powell278902c2014-07-12 18:33:22 -0700588 startSelected(mAlwaysUseOption ?
Adam Powell24428412015-04-01 17:19:56 -0700589 mAdapterView.getCheckedItemPosition() : mAdapter.getFilteredPosition(),
Adam Powell278902c2014-07-12 18:33:22 -0700590 id == R.id.button_always,
591 mAlwaysUseOption);
Adam Powellc5878612012-05-04 18:42:38 -0700592 }
593
Adam Powell23882512016-01-29 10:21:00 -0800594 public void startSelected(int which, boolean always, boolean filtered) {
Amith Yamasani07cd3512013-09-18 13:16:00 -0700595 if (isFinishing()) {
596 return;
597 }
Adam Powell278902c2014-07-12 18:33:22 -0700598 ResolveInfo ri = mAdapter.resolveInfoForPosition(which, filtered);
Sander Alewijnse86d35ba2015-02-04 15:14:53 +0000599 if (mResolvingHome && hasManagedProfile() && !supportsManagedProfiles(ri)) {
600 Toast.makeText(this, String.format(getResources().getString(
601 com.android.internal.R.string.activity_resolver_work_profiles_support),
602 ri.activityInfo.loadLabel(getPackageManager()).toString()),
603 Toast.LENGTH_LONG).show();
604 return;
605 }
606
Adam Powell24428412015-04-01 17:19:56 -0700607 TargetInfo target = mAdapter.targetInfoForPosition(which, filtered);
Adam Powell2ed547e2015-04-29 18:45:04 -0700608 if (onTargetSelected(target, always)) {
609 finish();
610 }
Mike Lockwood02eb8742011-02-27 09:10:37 -0800611 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612
Adam Powelle49d9392014-07-17 18:45:19 -0700613 /**
614 * Replace me in subclasses!
615 */
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000616 public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) {
Adam Powelle49d9392014-07-17 18:45:19 -0700617 return defIntent;
618 }
619
Adam Powell2ed547e2015-04-29 18:45:04 -0700620 protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) {
Adam Powell24428412015-04-01 17:19:56 -0700621 final ResolveInfo ri = target.getResolveInfo();
622 final Intent intent = target != null ? target.getResolvedIntent() : null;
623
624 if (intent != null && (mAlwaysUseOption || mAdapter.hasFilteredItem())
625 && mAdapter.mOrigResolveList != null) {
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700626 // Build a reasonable intent filter, based on what matched.
627 IntentFilter filter = new IntentFilter();
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800628 String action = intent.getAction();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800629
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800630 if (action != null) {
631 filter.addAction(action);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800632 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700633 Set<String> categories = intent.getCategories();
634 if (categories != null) {
635 for (String cat : categories) {
636 filter.addCategory(cat);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800637 }
638 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700639 filter.addCategory(Intent.CATEGORY_DEFAULT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800640
Adam Powell24428412015-04-01 17:19:56 -0700641 int cat = ri.match & IntentFilter.MATCH_CATEGORY_MASK;
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700642 Uri data = intent.getData();
643 if (cat == IntentFilter.MATCH_CATEGORY_TYPE) {
644 String mimeType = intent.resolveType(this);
645 if (mimeType != null) {
646 try {
647 filter.addDataType(mimeType);
648 } catch (IntentFilter.MalformedMimeTypeException e) {
649 Log.w("ResolverActivity", e);
650 filter = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800651 }
652 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700653 }
654 if (data != null && data.getScheme() != null) {
655 // We need the data specification if there was no type,
656 // OR if the scheme is not one of our magical "file:"
657 // or "content:" schemes (see IntentFilter for the reason).
658 if (cat != IntentFilter.MATCH_CATEGORY_TYPE
659 || (!"file".equals(data.getScheme())
660 && !"content".equals(data.getScheme()))) {
661 filter.addDataScheme(data.getScheme());
662
663 // Look through the resolved filter to determine which part
664 // of it matched the original Intent.
665 Iterator<PatternMatcher> pIt = ri.filter.schemeSpecificPartsIterator();
666 if (pIt != null) {
667 String ssp = data.getSchemeSpecificPart();
668 while (ssp != null && pIt.hasNext()) {
669 PatternMatcher p = pIt.next();
670 if (p.match(ssp)) {
671 filter.addDataSchemeSpecificPart(p.getPath(), p.getType());
672 break;
673 }
Dianne Hackborndf1c0bf2013-06-12 16:21:38 -0700674 }
675 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700676 Iterator<IntentFilter.AuthorityEntry> aIt = ri.filter.authoritiesIterator();
677 if (aIt != null) {
678 while (aIt.hasNext()) {
679 IntentFilter.AuthorityEntry a = aIt.next();
680 if (a.match(data) >= 0) {
681 int port = a.getPort();
682 filter.addDataAuthority(a.getHost(),
683 port >= 0 ? Integer.toString(port) : null);
684 break;
685 }
686 }
687 }
688 pIt = ri.filter.pathsIterator();
689 if (pIt != null) {
690 String path = data.getPath();
691 while (path != null && pIt.hasNext()) {
692 PatternMatcher p = pIt.next();
693 if (p.match(path)) {
694 filter.addDataPath(p.getPath(), p.getType());
695 break;
696 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800697 }
698 }
699 }
700 }
701
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700702 if (filter != null) {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700703 final int N = mAdapter.mOrigResolveList.size();
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700704 ComponentName[] set = new ComponentName[N];
705 int bestMatch = 0;
706 for (int i=0; i<N; i++) {
Adam Powell2ed547e2015-04-29 18:45:04 -0700707 ResolveInfo r = mAdapter.mOrigResolveList.get(i).getResolveInfoAt(0);
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700708 set[i] = new ComponentName(r.activityInfo.packageName,
709 r.activityInfo.name);
710 if (r.match > bestMatch) bestMatch = r.match;
711 }
712 if (alwaysCheck) {
Fabrice Di Meglio3453e082015-05-11 17:46:23 -0700713 final int userId = getUserId();
714 final PackageManager pm = getPackageManager();
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800715
716 // Set the preferred Activity
717 pm.addPreferredActivity(filter, bestMatch, set, intent.getComponent());
718
Fabrice Di Meglio3453e082015-05-11 17:46:23 -0700719 if (ri.handleAllWebDataURI) {
720 // Set default Browser if needed
Jeff Sharkeye06b4d12016-01-06 14:51:50 -0700721 final String packageName = pm.getDefaultBrowserPackageNameAsUser(userId);
Fabrice Di Meglio3453e082015-05-11 17:46:23 -0700722 if (TextUtils.isEmpty(packageName)) {
Jeff Sharkeye06b4d12016-01-06 14:51:50 -0700723 pm.setDefaultBrowserPackageNameAsUser(ri.activityInfo.packageName, userId);
Fabrice Di Meglio3453e082015-05-11 17:46:23 -0700724 }
725 } else {
726 // Update Domain Verification status
727 ComponentName cn = intent.getComponent();
728 String packageName = cn.getPackageName();
729 String dataScheme = (data != null) ? data.getScheme() : null;
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800730
Fabrice Di Meglio3453e082015-05-11 17:46:23 -0700731 boolean isHttpOrHttps = (dataScheme != null) &&
732 (dataScheme.equals(IntentFilter.SCHEME_HTTP) ||
733 dataScheme.equals(IntentFilter.SCHEME_HTTPS));
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800734
Fabrice Di Meglio3453e082015-05-11 17:46:23 -0700735 boolean isViewAction = (action != null) && action.equals(Intent.ACTION_VIEW);
736 boolean hasCategoryBrowsable = (categories != null) &&
737 categories.contains(Intent.CATEGORY_BROWSABLE);
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800738
Fabrice Di Meglio3453e082015-05-11 17:46:23 -0700739 if (isHttpOrHttps && isViewAction && hasCategoryBrowsable) {
Jeff Sharkeye06b4d12016-01-06 14:51:50 -0700740 pm.updateIntentVerificationStatusAsUser(packageName,
Fabrice Di Meglio3453e082015-05-11 17:46:23 -0700741 PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS,
742 userId);
743 }
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800744 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700745 } else {
746 try {
747 AppGlobals.getPackageManager().setLastChosenActivity(intent,
748 intent.resolveTypeIfNeeded(getContentResolver()),
749 PackageManager.MATCH_DEFAULT_ONLY,
750 filter, bestMatch, intent.getComponent());
751 } catch (RemoteException re) {
752 Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
753 }
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700754 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800755 }
756 }
757
Adam Powell24428412015-04-01 17:19:56 -0700758 if (target != null) {
759 safelyStartActivity(target);
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700760 }
Adam Powell2ed547e2015-04-29 18:45:04 -0700761 return true;
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700762 }
763
Adam Powell23882512016-01-29 10:21:00 -0800764 public void safelyStartActivity(TargetInfo cti) {
Jeff Sharkey2b9eb892016-02-16 09:21:51 -0700765 // We're dispatching intents that might be coming from legacy apps, so
766 // don't kill ourselves.
767 StrictMode.disableDeathOnFileUriExposure();
768 try {
769 safelyStartActivityInternal(cti);
770 } finally {
771 StrictMode.enableDeathOnFileUriExposure();
772 }
773 }
774
775 private void safelyStartActivityInternal(TargetInfo cti) {
Sander Alewijnsef6545332014-10-31 12:39:02 +0000776 // If needed, show that intent is forwarded
777 // from managed profile to owner or other way around.
778 if (mProfileSwitchMessageId != -1) {
779 Toast.makeText(this, getString(mProfileSwitchMessageId), Toast.LENGTH_LONG).show();
780 }
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700781 if (!mSafeForwardingMode) {
Adam Powell24428412015-04-01 17:19:56 -0700782 if (cti.start(this, null)) {
783 onActivityStarted(cti);
784 }
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700785 return;
786 }
787 try {
Adam Powell24428412015-04-01 17:19:56 -0700788 if (cti.startAsCaller(this, null, UserHandle.USER_NULL)) {
789 onActivityStarted(cti);
790 }
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700791 } catch (RuntimeException e) {
792 String launchedFromPackage;
793 try {
794 launchedFromPackage = ActivityManagerNative.getDefault().getLaunchedFromPackage(
795 getActivityToken());
796 } catch (RemoteException e2) {
797 launchedFromPackage = "??";
798 }
799 Slog.wtf(TAG, "Unable to launch as uid " + mLaunchedFromUid
800 + " package " + launchedFromPackage + ", while running in "
801 + ActivityThread.currentProcessName(), e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800802 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800803 }
804
Adam Powell23882512016-01-29 10:21:00 -0800805 public void onActivityStarted(TargetInfo cti) {
Adam Powell0b3c1122014-10-09 12:50:14 -0700806 // Do nothing
807 }
808
Adam Powell23882512016-01-29 10:21:00 -0800809 public boolean shouldGetActivityMetadata() {
Adam Powell24428412015-04-01 17:19:56 -0700810 return false;
811 }
812
Adam Powell23882512016-01-29 10:21:00 -0800813 public boolean shouldAutoLaunchSingleChoice(TargetInfo target) {
Adam Powell39e94eb2015-09-08 17:01:49 -0700814 return true;
815 }
816
Adam Powell23882512016-01-29 10:21:00 -0800817 public void showTargetDetails(ResolveInfo ri) {
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700818 Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
Adam Powell0fc5b2b2012-07-18 18:20:29 -0700819 .setData(Uri.fromParts("package", ri.activityInfo.packageName, null))
Adam Powell24428412015-04-01 17:19:56 -0700820 .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
Amith Yamasani203a2f42012-10-03 20:16:40 -0700821 startActivity(in);
Adam Powellc5878612012-05-04 18:42:38 -0700822 }
823
Adam Powell23882512016-01-29 10:21:00 -0800824 public ResolveListAdapter createAdapter(Context context, List<Intent> payloadIntents,
Adam Powell7d758002015-05-06 17:49:36 -0700825 Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid,
826 boolean filterLastUsed) {
827 return new ResolveListAdapter(context, payloadIntents, initialIntents, rList,
828 launchedFromUid, filterLastUsed);
Adam Powell88831a22014-11-20 18:17:00 -0800829 }
830
Adam Powell39e94eb2015-09-08 17:01:49 -0700831 /**
832 * Returns true if the activity is finishing and creation should halt
833 */
Adam Powell23882512016-01-29 10:21:00 -0800834 public boolean configureContentView(List<Intent> payloadIntents, Intent[] initialIntents,
Adam Powell7d758002015-05-06 17:49:36 -0700835 List<ResolveInfo> rList, boolean alwaysUseOption) {
Dianne Hackborn57dd7372015-07-27 18:11:14 -0700836 // The last argument of createAdapter is whether to do special handling
837 // of the last used choice to highlight it in the list. We need to always
838 // turn this off when running under voice interaction, since it results in
839 // a more complicated UI that the current voice interaction flow is not able
840 // to handle.
Adam Powell7d758002015-05-06 17:49:36 -0700841 mAdapter = createAdapter(this, payloadIntents, initialIntents, rList,
Dianne Hackborn57dd7372015-07-27 18:11:14 -0700842 mLaunchedFromUid, alwaysUseOption && !isVoiceInteraction());
Adam Powell7d758002015-05-06 17:49:36 -0700843
844 final int layoutId;
845 if (mAdapter.hasFilteredItem()) {
846 layoutId = R.layout.resolver_list_with_default;
847 alwaysUseOption = false;
848 } else {
849 layoutId = getLayoutResource();
850 }
851 mAlwaysUseOption = alwaysUseOption;
852
Adam Powell50077352015-05-26 18:01:55 -0700853 int count = mAdapter.getUnfilteredCount();
Jeff Sharkeycc2ae6b42015-09-29 13:04:46 -0700854 if (count == 1 && mAdapter.getOtherProfile() == null) {
855 // Only one target, so we're a candidate to auto-launch!
856 final TargetInfo target = mAdapter.targetInfoForPosition(0, false);
857 if (shouldAutoLaunchSingleChoice(target)) {
858 safelyStartActivity(target);
859 mPackageMonitor.unregister();
860 mRegistered = false;
861 finish();
862 return true;
863 }
864 }
865 if (count > 0) {
Adam Powell7d758002015-05-06 17:49:36 -0700866 setContentView(layoutId);
867 mAdapterView = (AbsListView) findViewById(R.id.resolver_list);
868 onPrepareAdapterView(mAdapterView, mAdapter, alwaysUseOption);
Adam Powell7d758002015-05-06 17:49:36 -0700869 } else {
870 setContentView(R.layout.resolver_list);
871
872 final TextView empty = (TextView) findViewById(R.id.empty);
873 empty.setVisibility(View.VISIBLE);
874
875 mAdapterView = (AbsListView) findViewById(R.id.resolver_list);
876 mAdapterView.setVisibility(View.GONE);
877 }
Adam Powell39e94eb2015-09-08 17:01:49 -0700878 return false;
Adam Powell7d758002015-05-06 17:49:36 -0700879 }
880
Adam Powell23882512016-01-29 10:21:00 -0800881 public void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter,
Adam Powell7d758002015-05-06 17:49:36 -0700882 boolean alwaysUseOption) {
883 final boolean useHeader = adapter.hasFilteredItem();
884 final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null;
885
886 adapterView.setAdapter(mAdapter);
887
888 final ItemClickListener listener = new ItemClickListener();
889 adapterView.setOnItemClickListener(listener);
890 adapterView.setOnItemLongClickListener(listener);
891
892 if (alwaysUseOption) {
893 listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
894 }
895
896 if (useHeader && listView != null) {
897 listView.addHeaderView(LayoutInflater.from(this).inflate(
898 R.layout.resolver_different_item_header, listView, false));
899 }
Adam Powell24428412015-04-01 17:19:56 -0700900 }
901
Adam Powellc412be62015-06-24 13:54:10 -0700902 /**
903 * Check a simple match for the component of two ResolveInfos.
904 */
905 static boolean resolveInfoMatch(ResolveInfo lhs, ResolveInfo rhs) {
906 return lhs == null ? rhs == null
907 : lhs.activityInfo == null ? rhs.activityInfo == null
908 : Objects.equals(lhs.activityInfo.name, rhs.activityInfo.name)
909 && Objects.equals(lhs.activityInfo.packageName, rhs.activityInfo.packageName);
910 }
911
Adam Powell23882512016-01-29 10:21:00 -0800912 public final class DisplayResolveInfo implements TargetInfo {
Adam Powell24428412015-04-01 17:19:56 -0700913 private final ResolveInfo mResolveInfo;
914 private final CharSequence mDisplayLabel;
915 private Drawable mDisplayIcon;
Adam Powell00f4aad2015-09-17 13:38:16 -0700916 private Drawable mBadge;
Adam Powell24428412015-04-01 17:19:56 -0700917 private final CharSequence mExtendedInfo;
918 private final Intent mResolvedIntent;
Adam Powell2ed547e2015-04-29 18:45:04 -0700919 private final List<Intent> mSourceIntents = new ArrayList<>();
Adam Powell23882512016-01-29 10:21:00 -0800920 private boolean mPinned;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800921
Adam Powell23882512016-01-29 10:21:00 -0800922 public DisplayResolveInfo(Intent originalIntent, ResolveInfo pri, CharSequence pLabel,
Dianne Hackborneb034652009-09-07 00:49:58 -0700923 CharSequence pInfo, Intent pOrigIntent) {
Adam Powell2ed547e2015-04-29 18:45:04 -0700924 mSourceIntents.add(originalIntent);
Adam Powell24428412015-04-01 17:19:56 -0700925 mResolveInfo = pri;
926 mDisplayLabel = pLabel;
927 mExtendedInfo = pInfo;
928
929 final Intent intent = new Intent(pOrigIntent != null ? pOrigIntent :
Adam Powell2ed547e2015-04-29 18:45:04 -0700930 getReplacementIntent(pri.activityInfo, getTargetIntent()));
Adam Powell24428412015-04-01 17:19:56 -0700931 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
932 | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
933 final ActivityInfo ai = mResolveInfo.activityInfo;
934 intent.setComponent(new ComponentName(ai.applicationInfo.packageName, ai.name));
935
936 mResolvedIntent = intent;
937 }
938
Adam Powell2ed547e2015-04-29 18:45:04 -0700939 private DisplayResolveInfo(DisplayResolveInfo other, Intent fillInIntent, int flags) {
940 mSourceIntents.addAll(other.getAllSourceIntents());
941 mResolveInfo = other.mResolveInfo;
942 mDisplayLabel = other.mDisplayLabel;
943 mDisplayIcon = other.mDisplayIcon;
944 mExtendedInfo = other.mExtendedInfo;
945 mResolvedIntent = new Intent(other.mResolvedIntent);
946 mResolvedIntent.fillIn(fillInIntent, flags);
Adam Powell23882512016-01-29 10:21:00 -0800947 mPinned = other.mPinned;
Adam Powell2ed547e2015-04-29 18:45:04 -0700948 }
949
Adam Powell24428412015-04-01 17:19:56 -0700950 public ResolveInfo getResolveInfo() {
951 return mResolveInfo;
952 }
953
954 public CharSequence getDisplayLabel() {
955 return mDisplayLabel;
956 }
957
958 public Drawable getDisplayIcon() {
959 return mDisplayIcon;
960 }
961
Adam Powell7d758002015-05-06 17:49:36 -0700962 public Drawable getBadgeIcon() {
Adam Powell00f4aad2015-09-17 13:38:16 -0700963 // We only expose a badge if we have extended info.
964 // The badge is a higher-priority disambiguation signal
965 // but we don't need one if we wouldn't show extended info at all.
966 if (TextUtils.isEmpty(getExtendedInfo())) {
967 return null;
968 }
969
970 if (mBadge == null && mResolveInfo != null && mResolveInfo.activityInfo != null
971 && mResolveInfo.activityInfo.applicationInfo != null) {
972 if (mResolveInfo.activityInfo.icon == 0 || mResolveInfo.activityInfo.icon
973 == mResolveInfo.activityInfo.applicationInfo.icon) {
974 // Badging an icon with exactly the same icon is silly.
975 // If the activityInfo icon resid is 0 it will fall back
976 // to the application's icon, making it a match.
977 return null;
978 }
979 mBadge = mResolveInfo.activityInfo.applicationInfo.loadIcon(mPm);
980 }
981 return mBadge;
Adam Powell7d758002015-05-06 17:49:36 -0700982 }
983
Adam Powell2ed547e2015-04-29 18:45:04 -0700984 @Override
Alan Viverettece5d92c2015-07-31 16:46:56 -0400985 public CharSequence getBadgeContentDescription() {
986 return null;
987 }
988
989 @Override
Adam Powell2ed547e2015-04-29 18:45:04 -0700990 public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) {
991 return new DisplayResolveInfo(this, fillInIntent, flags);
992 }
993
994 @Override
995 public List<Intent> getAllSourceIntents() {
996 return mSourceIntents;
997 }
998
999 public void addAlternateSourceIntent(Intent alt) {
1000 mSourceIntents.add(alt);
1001 }
1002
Adam Powell24428412015-04-01 17:19:56 -07001003 public void setDisplayIcon(Drawable icon) {
1004 mDisplayIcon = icon;
1005 }
1006
1007 public boolean hasDisplayIcon() {
1008 return mDisplayIcon != null;
1009 }
1010
1011 public CharSequence getExtendedInfo() {
1012 return mExtendedInfo;
1013 }
1014
1015 public Intent getResolvedIntent() {
1016 return mResolvedIntent;
1017 }
1018
1019 @Override
1020 public ComponentName getResolvedComponentName() {
1021 return new ComponentName(mResolveInfo.activityInfo.packageName,
1022 mResolveInfo.activityInfo.name);
1023 }
1024
1025 @Override
1026 public boolean start(Activity activity, Bundle options) {
1027 activity.startActivity(mResolvedIntent, options);
1028 return true;
1029 }
1030
1031 @Override
1032 public boolean startAsCaller(Activity activity, Bundle options, int userId) {
Dianne Hackborna7cfbe02015-07-16 10:52:52 -07001033 activity.startActivityAsCaller(mResolvedIntent, options, false, userId);
Adam Powell24428412015-04-01 17:19:56 -07001034 return true;
1035 }
1036
1037 @Override
1038 public boolean startAsUser(Activity activity, Bundle options, UserHandle user) {
1039 activity.startActivityAsUser(mResolvedIntent, options, user);
1040 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001041 }
Adam Powell23882512016-01-29 10:21:00 -08001042
1043 @Override
1044 public boolean isPinned() {
1045 return mPinned;
1046 }
1047
1048 public void setPinned(boolean pinned) {
1049 mPinned = pinned;
1050 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001051 }
1052
Adam Powell24428412015-04-01 17:19:56 -07001053 /**
1054 * A single target as represented in the chooser.
1055 */
1056 public interface TargetInfo {
1057 /**
1058 * Get the resolved intent that represents this target. Note that this may not be the
1059 * intent that will be launched by calling one of the <code>start</code> methods provided;
1060 * this is the intent that will be credited with the launch.
1061 *
1062 * @return the resolved intent for this target
1063 */
Adam Powell23882512016-01-29 10:21:00 -08001064 Intent getResolvedIntent();
Adam Powell24428412015-04-01 17:19:56 -07001065
1066 /**
1067 * Get the resolved component name that represents this target. Note that this may not
1068 * be the component that will be directly launched by calling one of the <code>start</code>
1069 * methods provided; this is the component that will be credited with the launch.
1070 *
1071 * @return the resolved ComponentName for this target
1072 */
Adam Powell23882512016-01-29 10:21:00 -08001073 ComponentName getResolvedComponentName();
Adam Powell24428412015-04-01 17:19:56 -07001074
1075 /**
1076 * Start the activity referenced by this target.
1077 *
1078 * @param activity calling Activity performing the launch
1079 * @param options ActivityOptions bundle
1080 * @return true if the start completed successfully
1081 */
Adam Powell23882512016-01-29 10:21:00 -08001082 boolean start(Activity activity, Bundle options);
Adam Powell24428412015-04-01 17:19:56 -07001083
1084 /**
1085 * Start the activity referenced by this target as if the ResolverActivity's caller
1086 * was performing the start operation.
1087 *
1088 * @param activity calling Activity (actually) performing the launch
1089 * @param options ActivityOptions bundle
1090 * @param userId userId to start as or {@link UserHandle#USER_NULL} for activity's caller
1091 * @return true if the start completed successfully
1092 */
Adam Powell23882512016-01-29 10:21:00 -08001093 boolean startAsCaller(Activity activity, Bundle options, int userId);
Adam Powell24428412015-04-01 17:19:56 -07001094
1095 /**
1096 * Start the activity referenced by this target as a given user.
1097 *
1098 * @param activity calling activity performing the launch
1099 * @param options ActivityOptions bundle
1100 * @param user handle for the user to start the activity as
1101 * @return true if the start completed successfully
1102 */
Adam Powell23882512016-01-29 10:21:00 -08001103 boolean startAsUser(Activity activity, Bundle options, UserHandle user);
Adam Powell24428412015-04-01 17:19:56 -07001104
1105 /**
1106 * Return the ResolveInfo about how and why this target matched the original query
1107 * for available targets.
1108 *
1109 * @return ResolveInfo representing this target's match
1110 */
Adam Powell23882512016-01-29 10:21:00 -08001111 ResolveInfo getResolveInfo();
Adam Powell24428412015-04-01 17:19:56 -07001112
1113 /**
1114 * Return the human-readable text label for this target.
1115 *
1116 * @return user-visible target label
1117 */
Adam Powell23882512016-01-29 10:21:00 -08001118 CharSequence getDisplayLabel();
Adam Powell24428412015-04-01 17:19:56 -07001119
1120 /**
1121 * Return any extended info for this target. This may be used to disambiguate
1122 * otherwise identical targets.
1123 *
1124 * @return human-readable disambig string or null if none present
1125 */
Adam Powell23882512016-01-29 10:21:00 -08001126 CharSequence getExtendedInfo();
Adam Powell24428412015-04-01 17:19:56 -07001127
1128 /**
1129 * @return The drawable that should be used to represent this target
1130 */
Adam Powell23882512016-01-29 10:21:00 -08001131 Drawable getDisplayIcon();
Adam Powell2ed547e2015-04-29 18:45:04 -07001132
1133 /**
Adam Powell7d758002015-05-06 17:49:36 -07001134 * @return The (small) icon to badge the target with
1135 */
Adam Powell23882512016-01-29 10:21:00 -08001136 Drawable getBadgeIcon();
Adam Powell7d758002015-05-06 17:49:36 -07001137
1138 /**
Alan Viverettece5d92c2015-07-31 16:46:56 -04001139 * @return The content description for the badge icon
1140 */
Adam Powell23882512016-01-29 10:21:00 -08001141 CharSequence getBadgeContentDescription();
Alan Viverettece5d92c2015-07-31 16:46:56 -04001142
1143 /**
Adam Powell2ed547e2015-04-29 18:45:04 -07001144 * Clone this target with the given fill-in information.
1145 */
Adam Powell23882512016-01-29 10:21:00 -08001146 TargetInfo cloneFilledIn(Intent fillInIntent, int flags);
Adam Powell2ed547e2015-04-29 18:45:04 -07001147
1148 /**
1149 * @return the list of supported source intents deduped against this single target
1150 */
Adam Powell23882512016-01-29 10:21:00 -08001151 List<Intent> getAllSourceIntents();
1152
1153 /**
1154 * @return true if this target should be pinned to the front by the request of the user
1155 */
1156 boolean isPinned();
Adam Powell24428412015-04-01 17:19:56 -07001157 }
1158
Adam Powell23882512016-01-29 10:21:00 -08001159 public class ResolveListAdapter extends BaseAdapter {
Adam Powell7d758002015-05-06 17:49:36 -07001160 private final List<Intent> mIntents;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001161 private final Intent[] mInitialIntents;
1162 private final List<ResolveInfo> mBaseResolveList;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -07001163 private ResolveInfo mLastChosen;
Adam Powell88831a22014-11-20 18:17:00 -08001164 private DisplayResolveInfo mOtherProfile;
Dianne Hackborn5320eb82012-05-18 12:05:04 -07001165 private final int mLaunchedFromUid;
Adam Powell24428412015-04-01 17:19:56 -07001166 private boolean mHasExtendedInfo;
1167
1168 protected final LayoutInflater mInflater;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001169
Adam Powell2ed547e2015-04-29 18:45:04 -07001170 List<DisplayResolveInfo> mDisplayList;
1171 List<ResolvedComponentInfo> mOrigResolveList;
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -07001172
Adam Powell278902c2014-07-12 18:33:22 -07001173 private int mLastChosenPosition = -1;
1174 private boolean mFilterLastUsed;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001175
Adam Powell7d758002015-05-06 17:49:36 -07001176 public ResolveListAdapter(Context context, List<Intent> payloadIntents,
1177 Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid,
1178 boolean filterLastUsed) {
1179 mIntents = payloadIntents;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001180 mInitialIntents = initialIntents;
1181 mBaseResolveList = rList;
Dianne Hackborn5320eb82012-05-18 12:05:04 -07001182 mLaunchedFromUid = launchedFromUid;
Adam Powelle9414d92014-07-05 17:44:18 -07001183 mInflater = LayoutInflater.from(context);
Adam Powell2ed547e2015-04-29 18:45:04 -07001184 mDisplayList = new ArrayList<>();
Adam Powell278902c2014-07-12 18:33:22 -07001185 mFilterLastUsed = filterLastUsed;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001186 rebuildList();
1187 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001188
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001189 public void handlePackagesChanged() {
1190 rebuildList();
1191 notifyDataSetChanged();
Esteban Talavera6de72662014-12-11 17:54:07 +00001192 if (getCount() == 0) {
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001193 // We no longer have any items... just finish the activity.
1194 finish();
Adam Powellc5878612012-05-04 18:42:38 -07001195 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001196 }
1197
Adam Powell278902c2014-07-12 18:33:22 -07001198 public DisplayResolveInfo getFilteredItem() {
1199 if (mFilterLastUsed && mLastChosenPosition >= 0) {
1200 // Not using getItem since it offsets to dodge this position for the list
Adam Powell2ed547e2015-04-29 18:45:04 -07001201 return mDisplayList.get(mLastChosenPosition);
Adam Powell278902c2014-07-12 18:33:22 -07001202 }
1203 return null;
1204 }
1205
Adam Powell88831a22014-11-20 18:17:00 -08001206 public DisplayResolveInfo getOtherProfile() {
1207 return mOtherProfile;
1208 }
1209
Adam Powell278902c2014-07-12 18:33:22 -07001210 public int getFilteredPosition() {
1211 if (mFilterLastUsed && mLastChosenPosition >= 0) {
1212 return mLastChosenPosition;
1213 }
1214 return AbsListView.INVALID_POSITION;
1215 }
1216
1217 public boolean hasFilteredItem() {
1218 return mFilterLastUsed && mLastChosenPosition >= 0;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -07001219 }
1220
Adam Powella182e452015-07-06 16:57:56 -07001221 public float getScore(DisplayResolveInfo target) {
1222 return mResolverComparator.getScore(target.getResolvedComponentName());
1223 }
1224
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001225 private void rebuildList() {
Adam Powell2ed547e2015-04-29 18:45:04 -07001226 List<ResolvedComponentInfo> currentResolveList = null;
You Kim43a5070e2012-11-21 23:23:45 +09001227
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -07001228 try {
Adam Powell2ed547e2015-04-29 18:45:04 -07001229 final Intent primaryIntent = getTargetIntent();
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -07001230 mLastChosen = AppGlobals.getPackageManager().getLastChosenActivity(
Adam Powell2ed547e2015-04-29 18:45:04 -07001231 primaryIntent, primaryIntent.resolveTypeIfNeeded(getContentResolver()),
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -07001232 PackageManager.MATCH_DEFAULT_ONLY);
1233 } catch (RemoteException re) {
1234 Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
1235 }
1236
Sudheer Shanka7e64e102015-01-23 10:37:45 +00001237 // Clear the value of mOtherProfile from previous call.
1238 mOtherProfile = null;
Adam Powell2ed547e2015-04-29 18:45:04 -07001239 mDisplayList.clear();
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001240 if (mBaseResolveList != null) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001241 currentResolveList = mOrigResolveList = new ArrayList<>();
1242 addResolveListDedupe(currentResolveList, getTargetIntent(), mBaseResolveList);
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001243 } else {
Adam Powell2ed547e2015-04-29 18:45:04 -07001244 final boolean shouldGetResolvedFilter = shouldGetResolvedFilter();
1245 final boolean shouldGetActivityMetadata = shouldGetActivityMetadata();
1246 for (int i = 0, N = mIntents.size(); i < N; i++) {
1247 final Intent intent = mIntents.get(i);
1248 final List<ResolveInfo> infos = mPm.queryIntentActivities(intent,
1249 PackageManager.MATCH_DEFAULT_ONLY
1250 | (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0)
1251 | (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0));
1252 if (infos != null) {
1253 if (currentResolveList == null) {
1254 currentResolveList = mOrigResolveList = new ArrayList<>();
1255 }
1256 addResolveListDedupe(currentResolveList, intent, infos);
1257 }
1258 }
1259
Dianne Hackborn5320eb82012-05-18 12:05:04 -07001260 // Filter out any activities that the launched uid does not
Nicolas Prevot697397a2016-01-11 11:14:32 +00001261 // have permission for.
1262 // Also filter out those that are suspended because they couldn't
1263 // be started. We don't do this when we have an explicit
Dianne Hackborn5320eb82012-05-18 12:05:04 -07001264 // list of resolved activities, because that only happens when
1265 // we are being subclassed, so we can safely launch whatever
1266 // they gave us.
You Kim43a5070e2012-11-21 23:23:45 +09001267 if (currentResolveList != null) {
1268 for (int i=currentResolveList.size()-1; i >= 0; i--) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001269 ActivityInfo ai = currentResolveList.get(i)
1270 .getResolveInfoAt(0).activityInfo;
Dianne Hackborn5320eb82012-05-18 12:05:04 -07001271 int granted = ActivityManager.checkComponentPermission(
1272 ai.permission, mLaunchedFromUid,
1273 ai.applicationInfo.uid, ai.exported);
Nicolas Prevot697397a2016-01-11 11:14:32 +00001274 boolean suspended = (ai.applicationInfo.flags
1275 & ApplicationInfo.FLAG_SUSPENDED) != 0;
1276 if (granted != PackageManager.PERMISSION_GRANTED || suspended) {
Dianne Hackborn5320eb82012-05-18 12:05:04 -07001277 // Access not allowed!
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -07001278 if (mOrigResolveList == currentResolveList) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001279 mOrigResolveList = new ArrayList<>(mOrigResolveList);
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -07001280 }
You Kim43a5070e2012-11-21 23:23:45 +09001281 currentResolveList.remove(i);
Dianne Hackborn5320eb82012-05-18 12:05:04 -07001282 }
1283 }
1284 }
Jeff Hamiltond88e9aa2011-01-24 14:53:00 -06001285 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001286 int N;
You Kim43a5070e2012-11-21 23:23:45 +09001287 if ((currentResolveList != null) && ((N = currentResolveList.size()) > 0)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001288 // Only display the first matches that are either of equal
1289 // priority or have asked to be default options.
Adam Powell2ed547e2015-04-29 18:45:04 -07001290 ResolvedComponentInfo rci0 = currentResolveList.get(0);
1291 ResolveInfo r0 = rci0.getResolveInfoAt(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001292 for (int i=1; i<N; i++) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001293 ResolveInfo ri = currentResolveList.get(i).getResolveInfoAt(0);
You Kim43a5070e2012-11-21 23:23:45 +09001294 if (DEBUG) Log.v(
Adam Powell09a65602014-07-20 16:23:14 -07001295 TAG,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001296 r0.activityInfo.name + "=" +
1297 r0.priority + "/" + r0.isDefault + " vs " +
1298 ri.activityInfo.name + "=" +
1299 ri.priority + "/" + ri.isDefault);
You Kim43a5070e2012-11-21 23:23:45 +09001300 if (r0.priority != ri.priority ||
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001301 r0.isDefault != ri.isDefault) {
1302 while (i < N) {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -07001303 if (mOrigResolveList == currentResolveList) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001304 mOrigResolveList = new ArrayList<>(mOrigResolveList);
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -07001305 }
You Kim43a5070e2012-11-21 23:23:45 +09001306 currentResolveList.remove(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001307 N--;
1308 }
1309 }
1310 }
1311 if (N > 1) {
Adam Powelld25267c2015-06-05 18:02:21 -07001312 mResolverComparator.compute(currentResolveList);
1313 Collections.sort(currentResolveList, mResolverComparator);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001314 }
Dianne Hackborneb034652009-09-07 00:49:58 -07001315 // First put the initial items at the top.
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001316 if (mInitialIntents != null) {
1317 for (int i=0; i<mInitialIntents.length; i++) {
1318 Intent ii = mInitialIntents[i];
Dianne Hackborneb034652009-09-07 00:49:58 -07001319 if (ii == null) {
1320 continue;
1321 }
1322 ActivityInfo ai = ii.resolveActivityInfo(
1323 getPackageManager(), 0);
1324 if (ai == null) {
Adam Powell09a65602014-07-20 16:23:14 -07001325 Log.w(TAG, "No activity found for " + ii);
Dianne Hackborneb034652009-09-07 00:49:58 -07001326 continue;
1327 }
1328 ResolveInfo ri = new ResolveInfo();
1329 ri.activityInfo = ai;
Nicolas Prevot1a815922014-10-10 16:22:38 +01001330 UserManager userManager =
1331 (UserManager) getSystemService(Context.USER_SERVICE);
Dianne Hackborneb034652009-09-07 00:49:58 -07001332 if (ii instanceof LabeledIntent) {
1333 LabeledIntent li = (LabeledIntent)ii;
1334 ri.resolvePackageName = li.getSourcePackage();
1335 ri.labelRes = li.getLabelResource();
1336 ri.nonLocalizedLabel = li.getNonLocalizedLabel();
1337 ri.icon = li.getIconResource();
Sudheer Shanka9ded7602015-05-19 21:17:25 +01001338 ri.iconResourceId = ri.icon;
1339 }
1340 if (userManager.isManagedProfile()) {
1341 ri.noResourceId = true;
1342 ri.icon = 0;
Dianne Hackborneb034652009-09-07 00:49:58 -07001343 }
Adam Powell2ed547e2015-04-29 18:45:04 -07001344 addResolveInfo(new DisplayResolveInfo(ii, ri,
Dianne Hackborneb034652009-09-07 00:49:58 -07001345 ri.loadLabel(getPackageManager()), null, ii));
1346 }
1347 }
You Kim43a5070e2012-11-21 23:23:45 +09001348
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001349 // Check for applications with same name and use application name or
1350 // package name if necessary
Adam Powell2ed547e2015-04-29 18:45:04 -07001351 rci0 = currentResolveList.get(0);
1352 r0 = rci0.getResolveInfoAt(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001353 int start = 0;
1354 CharSequence r0Label = r0.loadLabel(mPm);
Adam Powell24428412015-04-01 17:19:56 -07001355 mHasExtendedInfo = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001356 for (int i = 1; i < N; i++) {
1357 if (r0Label == null) {
1358 r0Label = r0.activityInfo.packageName;
1359 }
Adam Powell2ed547e2015-04-29 18:45:04 -07001360 ResolvedComponentInfo rci = currentResolveList.get(i);
1361 ResolveInfo ri = rci.getResolveInfoAt(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001362 CharSequence riLabel = ri.loadLabel(mPm);
1363 if (riLabel == null) {
1364 riLabel = ri.activityInfo.packageName;
1365 }
1366 if (riLabel.equals(r0Label)) {
1367 continue;
1368 }
Adam Powell2ed547e2015-04-29 18:45:04 -07001369 processGroup(currentResolveList, start, (i-1), rci0, r0Label);
1370 rci0 = rci;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001371 r0 = ri;
1372 r0Label = riLabel;
1373 start = i;
1374 }
1375 // Process last group
Adam Powell2ed547e2015-04-29 18:45:04 -07001376 processGroup(currentResolveList, start, (N-1), rci0, r0Label);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001377 }
Kenny Guyb8a2df12014-12-12 16:56:08 +00001378
1379 // Layout doesn't handle both profile button and last chosen
1380 // so disable last chosen if profile button is present.
1381 if (mOtherProfile != null && mLastChosenPosition >= 0) {
1382 mLastChosenPosition = -1;
1383 mFilterLastUsed = false;
1384 }
Adam Powell24428412015-04-01 17:19:56 -07001385
1386 onListRebuilt();
1387 }
1388
Adam Powell2ed547e2015-04-29 18:45:04 -07001389 private void addResolveListDedupe(List<ResolvedComponentInfo> into, Intent intent,
1390 List<ResolveInfo> from) {
1391 final int fromCount = from.size();
1392 final int intoCount = into.size();
1393 for (int i = 0; i < fromCount; i++) {
1394 final ResolveInfo newInfo = from.get(i);
1395 boolean found = false;
1396 // Only loop to the end of into as it was before we started; no dupes in from.
1397 for (int j = 0; j < intoCount; j++) {
1398 final ResolvedComponentInfo rci = into.get(i);
1399 if (isSameResolvedComponent(newInfo, rci)) {
1400 found = true;
1401 rci.add(intent, newInfo);
1402 break;
1403 }
1404 }
1405 if (!found) {
Adam Powell23882512016-01-29 10:21:00 -08001406 final ComponentName name = new ComponentName(
1407 newInfo.activityInfo.packageName, newInfo.activityInfo.name);
1408 final ResolvedComponentInfo rci = new ResolvedComponentInfo(name,
1409 intent, newInfo);
1410 rci.setPinned(isComponentPinned(name));
1411 into.add(rci);
Adam Powell2ed547e2015-04-29 18:45:04 -07001412 }
1413 }
1414 }
1415
1416 private boolean isSameResolvedComponent(ResolveInfo a, ResolvedComponentInfo b) {
1417 final ActivityInfo ai = a.activityInfo;
1418 return ai.packageName.equals(b.name.getPackageName())
1419 && ai.name.equals(b.name.getClassName());
1420 }
1421
Adam Powell24428412015-04-01 17:19:56 -07001422 public void onListRebuilt() {
1423 // This space for rent
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001424 }
1425
Adam Powellc6d5e3a2015-04-23 12:22:20 -07001426 public boolean shouldGetResolvedFilter() {
1427 return mFilterLastUsed;
1428 }
1429
Adam Powell2ed547e2015-04-29 18:45:04 -07001430 private void processGroup(List<ResolvedComponentInfo> rList, int start, int end,
1431 ResolvedComponentInfo ro, CharSequence roLabel) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001432 // Process labels from start to i
1433 int num = end - start+1;
1434 if (num == 1) {
1435 // No duplicate labels. Use label for entry at start
Adam Powell2ed547e2015-04-29 18:45:04 -07001436 addResolveInfoWithAlternates(ro, null, roLabel);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001437 } else {
Adam Powell24428412015-04-01 17:19:56 -07001438 mHasExtendedInfo = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001439 boolean usePkg = false;
Adam Powell00f4aad2015-09-17 13:38:16 -07001440 final ApplicationInfo ai = ro.getResolveInfoAt(0).activityInfo.applicationInfo;
1441 final CharSequence startApp = ai.loadLabel(mPm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001442 if (startApp == null) {
1443 usePkg = true;
1444 }
1445 if (!usePkg) {
1446 // Use HashSet to track duplicates
1447 HashSet<CharSequence> duplicates =
1448 new HashSet<CharSequence>();
1449 duplicates.add(startApp);
1450 for (int j = start+1; j <= end ; j++) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001451 ResolveInfo jRi = rList.get(j).getResolveInfoAt(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001452 CharSequence jApp = jRi.activityInfo.applicationInfo.loadLabel(mPm);
1453 if ( (jApp == null) || (duplicates.contains(jApp))) {
1454 usePkg = true;
1455 break;
1456 } else {
1457 duplicates.add(jApp);
1458 }
1459 }
1460 // Clear HashSet for later use
1461 duplicates.clear();
1462 }
1463 for (int k = start; k <= end; k++) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001464 final ResolvedComponentInfo rci = rList.get(k);
1465 final ResolveInfo add = rci.getResolveInfoAt(0);
1466 final CharSequence extraInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001467 if (usePkg) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001468 // Use package name for all entries from start to end-1
Adam Powell2ed547e2015-04-29 18:45:04 -07001469 extraInfo = add.activityInfo.packageName;
1470 } else {
1471 // Use application name for all entries from start to end-1
1472 extraInfo = add.activityInfo.applicationInfo.loadLabel(mPm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001473 }
Adam Powell2ed547e2015-04-29 18:45:04 -07001474 addResolveInfoWithAlternates(rci, extraInfo, roLabel);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001475 }
1476 }
1477 }
1478
Adam Powell2ed547e2015-04-29 18:45:04 -07001479 private void addResolveInfoWithAlternates(ResolvedComponentInfo rci,
1480 CharSequence extraInfo, CharSequence roLabel) {
1481 final int count = rci.getCount();
1482 final Intent intent = rci.getIntentAt(0);
1483 final ResolveInfo add = rci.getResolveInfoAt(0);
1484 final Intent replaceIntent = getReplacementIntent(add.activityInfo, intent);
1485 final DisplayResolveInfo dri = new DisplayResolveInfo(intent, add, roLabel,
1486 extraInfo, replaceIntent);
Adam Powell23882512016-01-29 10:21:00 -08001487 dri.setPinned(rci.isPinned());
Adam Powell2ed547e2015-04-29 18:45:04 -07001488 addResolveInfo(dri);
1489 if (replaceIntent == intent) {
1490 // Only add alternates if we didn't get a specific replacement from
1491 // the caller. If we have one it trumps potential alternates.
1492 for (int i = 1, N = count; i < N; i++) {
1493 final Intent altIntent = rci.getIntentAt(i);
1494 dri.addAlternateSourceIntent(altIntent);
1495 }
1496 }
1497 updateLastChosenPosition(add);
1498 }
1499
Esteban Talavera6de72662014-12-11 17:54:07 +00001500 private void updateLastChosenPosition(ResolveInfo info) {
1501 if (mLastChosen != null
1502 && mLastChosen.activityInfo.packageName.equals(info.activityInfo.packageName)
1503 && mLastChosen.activityInfo.name.equals(info.activityInfo.name)) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001504 mLastChosenPosition = mDisplayList.size() - 1;
Esteban Talavera6de72662014-12-11 17:54:07 +00001505 }
1506 }
1507
Adam Powell88831a22014-11-20 18:17:00 -08001508 private void addResolveInfo(DisplayResolveInfo dri) {
Adam Powell24428412015-04-01 17:19:56 -07001509 if (dri.mResolveInfo.targetUserId != UserHandle.USER_CURRENT && mOtherProfile == null) {
Adam Powell88831a22014-11-20 18:17:00 -08001510 // So far we only support a single other profile at a time.
1511 // The first one we see gets special treatment.
1512 mOtherProfile = dri;
1513 } else {
Adam Powell2ed547e2015-04-29 18:45:04 -07001514 mDisplayList.add(dri);
Adam Powell88831a22014-11-20 18:17:00 -08001515 }
1516 }
1517
Adam Powell278902c2014-07-12 18:33:22 -07001518 public ResolveInfo resolveInfoForPosition(int position, boolean filtered) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001519 return (filtered ? getItem(position) : mDisplayList.get(position))
1520 .getResolveInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001521 }
1522
Adam Powell24428412015-04-01 17:19:56 -07001523 public TargetInfo targetInfoForPosition(int position, boolean filtered) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001524 return filtered ? getItem(position) : mDisplayList.get(position);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001525 }
1526
1527 public int getCount() {
Adam Powell2ed547e2015-04-29 18:45:04 -07001528 int result = mDisplayList.size();
Adam Powell278902c2014-07-12 18:33:22 -07001529 if (mFilterLastUsed && mLastChosenPosition >= 0) {
1530 result--;
1531 }
1532 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001533 }
1534
Adam Powell50077352015-05-26 18:01:55 -07001535 public int getUnfilteredCount() {
1536 return mDisplayList.size();
1537 }
1538
1539 public int getDisplayInfoCount() {
1540 return mDisplayList.size();
1541 }
1542
1543 public DisplayResolveInfo getDisplayInfoAt(int index) {
1544 return mDisplayList.get(index);
1545 }
1546
Adam Powell24428412015-04-01 17:19:56 -07001547 public TargetInfo getItem(int position) {
Adam Powell278902c2014-07-12 18:33:22 -07001548 if (mFilterLastUsed && mLastChosenPosition >= 0 && position >= mLastChosenPosition) {
1549 position++;
1550 }
Adam Powell2ed547e2015-04-29 18:45:04 -07001551 return mDisplayList.get(position);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001552 }
1553
1554 public long getItemId(int position) {
1555 return position;
1556 }
1557
Adam Powell24428412015-04-01 17:19:56 -07001558 public boolean hasExtendedInfo() {
1559 return mHasExtendedInfo;
1560 }
1561
1562 public boolean hasResolvedTarget(ResolveInfo info) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001563 for (int i = 0, N = mDisplayList.size(); i < N; i++) {
Adam Powellc412be62015-06-24 13:54:10 -07001564 if (resolveInfoMatch(info, mDisplayList.get(i).getResolveInfo())) {
Adam Powell24428412015-04-01 17:19:56 -07001565 return true;
1566 }
1567 }
1568 return false;
1569 }
1570
Adam Powell23882512016-01-29 10:21:00 -08001571 public int getDisplayResolveInfoCount() {
Adam Powell2ed547e2015-04-29 18:45:04 -07001572 return mDisplayList.size();
Adam Powell24428412015-04-01 17:19:56 -07001573 }
1574
Adam Powell23882512016-01-29 10:21:00 -08001575 public DisplayResolveInfo getDisplayResolveInfo(int index) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001576 // Used to query services. We only query services for primary targets, not alternates.
1577 return mDisplayList.get(index);
Adam Powell24428412015-04-01 17:19:56 -07001578 }
1579
1580 public final View getView(int position, View convertView, ViewGroup parent) {
Adam Powellfd1e93d2014-09-07 16:52:22 -07001581 View view = convertView;
1582 if (view == null) {
Adam Powell24428412015-04-01 17:19:56 -07001583 view = createView(parent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001584 }
Adam Powell7d758002015-05-06 17:49:36 -07001585 onBindView(view, getItem(position));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001586 return view;
1587 }
1588
Adam Powell7d758002015-05-06 17:49:36 -07001589 public final View createView(ViewGroup parent) {
1590 final View view = onCreateView(parent);
1591 final ViewHolder holder = new ViewHolder(view);
1592 view.setTag(holder);
1593 return view;
1594 }
1595
1596 public View onCreateView(ViewGroup parent) {
Adam Powell24428412015-04-01 17:19:56 -07001597 return mInflater.inflate(
1598 com.android.internal.R.layout.resolve_list_item, parent, false);
1599 }
1600
1601 public boolean showsExtendedInfo(TargetInfo info) {
1602 return !TextUtils.isEmpty(info.getExtendedInfo());
1603 }
1604
Adam Powell23882512016-01-29 10:21:00 -08001605 public boolean isComponentPinned(ComponentName name) {
1606 return false;
1607 }
1608
Adam Powell7d758002015-05-06 17:49:36 -07001609 public final void bindView(int position, View view) {
1610 onBindView(view, getItem(position));
1611 }
1612
1613 private void onBindView(View view, TargetInfo info) {
Adam Powell0256c6f2013-05-29 16:42:33 -07001614 final ViewHolder holder = (ViewHolder) view.getTag();
Adam Powell63b31692015-09-28 10:45:00 -07001615 final CharSequence label = info.getDisplayLabel();
1616 if (!TextUtils.equals(holder.text.getText(), label)) {
1617 holder.text.setText(info.getDisplayLabel());
1618 }
Adam Powell24428412015-04-01 17:19:56 -07001619 if (showsExtendedInfo(info)) {
Adam Powell0256c6f2013-05-29 16:42:33 -07001620 holder.text2.setVisibility(View.VISIBLE);
Adam Powell24428412015-04-01 17:19:56 -07001621 holder.text2.setText(info.getExtendedInfo());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001622 } else {
Adam Powell0256c6f2013-05-29 16:42:33 -07001623 holder.text2.setVisibility(View.GONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001624 }
Adam Powell24428412015-04-01 17:19:56 -07001625 if (info instanceof DisplayResolveInfo
1626 && !((DisplayResolveInfo) info).hasDisplayIcon()) {
1627 new LoadAdapterIconTask((DisplayResolveInfo) info).execute();
Dianne Hackborneb034652009-09-07 00:49:58 -07001628 }
Adam Powell24428412015-04-01 17:19:56 -07001629 holder.icon.setImageDrawable(info.getDisplayIcon());
Adam Powell7d758002015-05-06 17:49:36 -07001630 if (holder.badge != null) {
1631 final Drawable badge = info.getBadgeIcon();
1632 if (badge != null) {
1633 holder.badge.setImageDrawable(badge);
Alan Viverettece5d92c2015-07-31 16:46:56 -04001634 holder.badge.setContentDescription(info.getBadgeContentDescription());
Adam Powell7d758002015-05-06 17:49:36 -07001635 holder.badge.setVisibility(View.VISIBLE);
1636 } else {
1637 holder.badge.setVisibility(View.GONE);
1638 }
1639 }
Adam Powell0256c6f2013-05-29 16:42:33 -07001640 }
1641 }
1642
Adam Powell2ed547e2015-04-29 18:45:04 -07001643 static final class ResolvedComponentInfo {
1644 public final ComponentName name;
Adam Powell23882512016-01-29 10:21:00 -08001645 private boolean mPinned;
Adam Powell2ed547e2015-04-29 18:45:04 -07001646 private final List<Intent> mIntents = new ArrayList<>();
1647 private final List<ResolveInfo> mResolveInfos = new ArrayList<>();
1648
1649 public ResolvedComponentInfo(ComponentName name, Intent intent, ResolveInfo info) {
1650 this.name = name;
1651 add(intent, info);
1652 }
1653
1654 public void add(Intent intent, ResolveInfo info) {
1655 mIntents.add(intent);
1656 mResolveInfos.add(info);
1657 }
1658
1659 public int getCount() {
1660 return mIntents.size();
1661 }
1662
1663 public Intent getIntentAt(int index) {
1664 return index >= 0 ? mIntents.get(index) : null;
1665 }
1666
1667 public ResolveInfo getResolveInfoAt(int index) {
1668 return index >= 0 ? mResolveInfos.get(index) : null;
1669 }
1670
1671 public int findIntent(Intent intent) {
1672 for (int i = 0, N = mIntents.size(); i < N; i++) {
1673 if (intent.equals(mIntents.get(i))) {
1674 return i;
1675 }
1676 }
1677 return -1;
1678 }
1679
1680 public int findResolveInfo(ResolveInfo info) {
1681 for (int i = 0, N = mResolveInfos.size(); i < N; i++) {
1682 if (info.equals(mResolveInfos.get(i))) {
1683 return i;
1684 }
1685 }
1686 return -1;
1687 }
Adam Powell23882512016-01-29 10:21:00 -08001688
1689 public boolean isPinned() {
1690 return mPinned;
1691 }
1692
1693 public void setPinned(boolean pinned) {
1694 mPinned = pinned;
1695 }
Adam Powell2ed547e2015-04-29 18:45:04 -07001696 }
1697
Adam Powell0256c6f2013-05-29 16:42:33 -07001698 static class ViewHolder {
1699 public TextView text;
1700 public TextView text2;
1701 public ImageView icon;
Adam Powell7d758002015-05-06 17:49:36 -07001702 public ImageView badge;
Adam Powell0256c6f2013-05-29 16:42:33 -07001703
1704 public ViewHolder(View view) {
1705 text = (TextView) view.findViewById(com.android.internal.R.id.text1);
1706 text2 = (TextView) view.findViewById(com.android.internal.R.id.text2);
1707 icon = (ImageView) view.findViewById(R.id.icon);
Adam Powell7d758002015-05-06 17:49:36 -07001708 badge = (ImageView) view.findViewById(R.id.target_badge);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001709 }
1710 }
1711
Adam Powell7d758002015-05-06 17:49:36 -07001712 class ItemClickListener implements AdapterView.OnItemClickListener,
1713 AdapterView.OnItemLongClickListener {
1714 @Override
1715 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
1716 final ListView listView = parent instanceof ListView ? (ListView) parent : null;
1717 if (listView != null) {
1718 position -= listView.getHeaderViewsCount();
1719 }
1720 if (position < 0) {
1721 // Header views don't count.
1722 return;
1723 }
1724 final int checkedPos = mAdapterView.getCheckedItemPosition();
1725 final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
1726 if (mAlwaysUseOption && (!hasValidSelection || mLastSelected != checkedPos)) {
1727 setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
1728 mOnceButton.setEnabled(hasValidSelection);
1729 if (hasValidSelection) {
1730 mAdapterView.smoothScrollToPosition(checkedPos);
1731 }
1732 mLastSelected = checkedPos;
1733 } else {
1734 startSelected(position, false, true);
1735 }
1736 }
Adam Powell2d809622012-03-22 15:24:43 -07001737
1738 @Override
1739 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Adam Powell7d758002015-05-06 17:49:36 -07001740 final ListView listView = parent instanceof ListView ? (ListView) parent : null;
1741 if (listView != null) {
1742 position -= listView.getHeaderViewsCount();
Adam Powell24428412015-04-01 17:19:56 -07001743 }
Adam Powellfd1e93d2014-09-07 16:52:22 -07001744 if (position < 0) {
1745 // Header views don't count.
1746 return false;
1747 }
Adam Powell278902c2014-07-12 18:33:22 -07001748 ResolveInfo ri = mAdapter.resolveInfoForPosition(position, true);
Adam Powell23882512016-01-29 10:21:00 -08001749 showTargetDetails(ri);
Adam Powell2d809622012-03-22 15:24:43 -07001750 return true;
1751 }
1752
1753 }
Adam Powell0256c6f2013-05-29 16:42:33 -07001754
Adam Powell24428412015-04-01 17:19:56 -07001755 abstract class LoadIconTask extends AsyncTask<Void, Void, Drawable> {
1756 protected final DisplayResolveInfo mDisplayResolveInfo;
1757 private final ResolveInfo mResolveInfo;
1758
1759 public LoadIconTask(DisplayResolveInfo dri) {
1760 mDisplayResolveInfo = dri;
1761 mResolveInfo = dri.getResolveInfo();
Adam Powell0256c6f2013-05-29 16:42:33 -07001762 }
1763
1764 @Override
Adam Powell24428412015-04-01 17:19:56 -07001765 protected Drawable doInBackground(Void... params) {
1766 return loadIconForResolveInfo(mResolveInfo);
1767 }
1768
1769 @Override
1770 protected void onPostExecute(Drawable d) {
1771 mDisplayResolveInfo.setDisplayIcon(d);
1772 }
1773 }
1774
1775 class LoadAdapterIconTask extends LoadIconTask {
1776 public LoadAdapterIconTask(DisplayResolveInfo dri) {
1777 super(dri);
1778 }
1779
1780 @Override
1781 protected void onPostExecute(Drawable d) {
1782 super.onPostExecute(d);
1783 if (mProfileView != null && mAdapter.getOtherProfile() == mDisplayResolveInfo) {
Adam Powell88831a22014-11-20 18:17:00 -08001784 bindProfileView();
1785 }
Adam Powell0256c6f2013-05-29 16:42:33 -07001786 mAdapter.notifyDataSetChanged();
1787 }
1788 }
Adam Powell278902c2014-07-12 18:33:22 -07001789
Adam Powell24428412015-04-01 17:19:56 -07001790 class LoadIconIntoViewTask extends LoadIconTask {
1791 private final ImageView mTargetView;
Adam Powell278902c2014-07-12 18:33:22 -07001792
Adam Powell24428412015-04-01 17:19:56 -07001793 public LoadIconIntoViewTask(DisplayResolveInfo dri, ImageView target) {
1794 super(dri);
Adam Powell278902c2014-07-12 18:33:22 -07001795 mTargetView = target;
1796 }
1797
1798 @Override
Adam Powell24428412015-04-01 17:19:56 -07001799 protected void onPostExecute(Drawable d) {
1800 super.onPostExecute(d);
1801 mTargetView.setImageDrawable(d);
Adam Powell278902c2014-07-12 18:33:22 -07001802 }
1803 }
Adam Powell09a65602014-07-20 16:23:14 -07001804
Dianne Hackbornec452d92014-11-11 17:16:56 -08001805 static final boolean isSpecificUriMatch(int match) {
1806 match = match&IntentFilter.MATCH_CATEGORY_MASK;
1807 return match >= IntentFilter.MATCH_CATEGORY_HOST
1808 && match <= IntentFilter.MATCH_CATEGORY_PATH;
1809 }
1810
Adam Powell4c470d62015-06-19 17:46:17 -07001811 static class PickTargetOptionRequest extends PickOptionRequest {
1812 public PickTargetOptionRequest(@Nullable Prompt prompt, Option[] options,
1813 @Nullable Bundle extras) {
1814 super(prompt, options, extras);
1815 }
1816
1817 @Override
1818 public void onCancel() {
1819 super.onCancel();
1820 final ResolverActivity ra = (ResolverActivity) getActivity();
1821 if (ra != null) {
1822 ra.mPickOptionRequest = null;
1823 ra.finish();
1824 }
1825 }
1826
1827 @Override
1828 public void onPickOptionResult(boolean finished, Option[] selections, Bundle result) {
1829 super.onPickOptionResult(finished, selections, result);
1830 if (selections.length != 1) {
1831 // TODO In a better world we would filter the UI presented here and let the
1832 // user refine. Maybe later.
1833 return;
1834 }
1835
1836 final ResolverActivity ra = (ResolverActivity) getActivity();
1837 if (ra != null) {
1838 final TargetInfo ti = ra.mAdapter.getItem(selections[0].getIndex());
1839 if (ra.onTargetSelected(ti, false)) {
1840 ra.mPickOptionRequest = null;
1841 ra.finish();
1842 }
1843 }
1844 }
1845 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001846}