blob: 622b70843cc2a5a4cbf76ccbfe0d00f338bd5368 [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;
Philip P. Moltmannf8173ca2016-04-12 15:11:23 -070020import android.annotation.StringRes;
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -080021import android.annotation.UiThread;
Adam Powelle9414d92014-07-05 17:44:18 -070022import android.app.Activity;
Hakan Seyalioglu13405c52017-01-31 19:01:31 -080023import android.app.ActivityManager;
Dianne Hackborn028ceeb2014-08-17 17:45:48 -070024import android.app.ActivityThread;
Adam Powell4c470d62015-06-19 17:46:17 -070025import android.app.VoiceInteractor.PickOptionRequest;
26import android.app.VoiceInteractor.PickOptionRequest.Option;
27import android.app.VoiceInteractor.Prompt;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.content.ComponentName;
29import android.content.Context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.content.Intent;
31import android.content.IntentFilter;
32import android.content.pm.ActivityInfo;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010033import android.content.pm.ApplicationInfo;
Dianne Hackborneb034652009-09-07 00:49:58 -070034import android.content.pm.LabeledIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.content.pm.PackageManager;
Adam Powellc5878612012-05-04 18:42:38 -070036import android.content.pm.PackageManager.NameNotFoundException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.content.pm.ResolveInfo;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010038import android.content.pm.UserInfo;
Adam Powellc5878612012-05-04 18:42:38 -070039import android.content.res.Resources;
Dianne Hackborneb034652009-09-07 00:49:58 -070040import android.graphics.drawable.Drawable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.net.Uri;
Hakan Seyalioglu13405c52017-01-31 19:01:31 -080042import android.os.AsyncTask;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010043import android.os.Build;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import android.os.Bundle;
45import android.os.PatternMatcher;
Hakan Seyalioglu13405c52017-01-31 19:01:31 -080046import android.os.RemoteException;
Jeff Sharkey37355a92016-02-05 16:19:10 -070047import android.os.StrictMode;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070048import android.os.UserHandle;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010049import android.os.UserManager;
Hakan Seyalioglu13405c52017-01-31 19:01:31 -080050import android.provider.MediaStore;
51import android.provider.Settings;
52import android.text.TextUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.util.Log;
Hakan Seyalioglu13405c52017-01-31 19:01:31 -080054import android.util.Slog;
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;
Hakan Seyalioglu13405c52017-01-31 19:01:31 -080058import android.widget.AbsListView;
Adam Powell2d809622012-03-22 15:24:43 -070059import android.widget.AdapterView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060import android.widget.BaseAdapter;
Adam Powellc5878612012-05-04 18:42:38 -070061import android.widget.Button;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062import android.widget.ImageView;
Adam Powell2d809622012-03-22 15:24:43 -070063import android.widget.ListView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064import android.widget.TextView;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010065import android.widget.Toast;
Hakan Seyalioglu13405c52017-01-31 19:01:31 -080066import com.android.internal.R;
67import com.android.internal.annotations.VisibleForTesting;
68import com.android.internal.content.PackageMonitor;
Clara Bayarrifa902aa2016-04-13 14:45:08 +010069import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010070import com.android.internal.logging.nano.MetricsProto;
Adam Powell4f6c2052014-07-07 18:49:10 -070071import com.android.internal.widget.ResolverDrawerLayout;
Adam Powell2d809622012-03-22 15:24:43 -070072
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073import java.util.ArrayList;
Clara Bayarrifa902aa2016-04-13 14:45:08 +010074import java.util.Arrays;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075import java.util.HashSet;
76import java.util.Iterator;
77import java.util.List;
Adam Powellc412be62015-06-24 13:54:10 -070078import java.util.Objects;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079import java.util.Set;
80
Wale Ogunwale9014e662016-03-19 14:55:46 -070081import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
Adrian Roos27c3ab62014-10-15 17:21:31 +020082
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 */
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -080088@UiThread
Adam Powell7d758002015-05-06 17:49:36 -070089public class ResolverActivity extends Activity {
Adam Powellc5878612012-05-04 18:42:38 -070090
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -080091 protected ResolveListAdapter mAdapter;
Dianne Hackborn028ceeb2014-08-17 17:45:48 -070092 private boolean mSafeForwardingMode;
Adam Powell24428412015-04-01 17:19:56 -070093 private AbsListView mAdapterView;
Adam Powellc5878612012-05-04 18:42:38 -070094 private Button mAlwaysButton;
95 private Button mOnceButton;
Adam Powell88831a22014-11-20 18:17:00 -080096 private View mProfileView;
Adam Powellc5878612012-05-04 18:42:38 -070097 private int mIconDpi;
Adam Powell24428412015-04-01 17:19:56 -070098 private int mLastSelected = AbsListView.INVALID_POSITION;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010099 private boolean mResolvingHome = false;
Sander Alewijnsef6545332014-10-31 12:39:02 +0000100 private int mProfileSwitchMessageId = -1;
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800101 private int mLayoutId;
Adam Powell2ed547e2015-04-29 18:45:04 -0700102 private final ArrayList<Intent> mIntents = new ArrayList<>();
Adam Powell4c470d62015-06-19 17:46:17 -0700103 private PickTargetOptionRequest mPickOptionRequest;
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800104 private String mReferrerPackage;
Hakan Seyalioglu33550122017-01-06 19:54:43 -0800105 private CharSequence mTitle;
106 private int mDefaultTitleResId;
Adam Powell09a65602014-07-20 16:23:14 -0700107
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800108 // Whether or not this activity supports choosing a default handler for the intent.
109 private boolean mSupportsAlwaysUseOption;
Adam Powell63b31692015-09-28 10:45:00 -0700110 protected ResolverDrawerLayout mResolverDrawerLayout;
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800111 protected PackageManager mPm;
112 protected int mLaunchedFromUid;
113
114 private static final String TAG = "ResolverActivity";
115 private static final boolean DEBUG = false;
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800116 private Runnable mPostListReadyRunnable;
Adam Powell63b31692015-09-28 10:45:00 -0700117
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700118 private boolean mRegistered;
Jorim Jaggif631ef72017-02-24 13:49:47 +0100119
120 /** See {@link #setRetainInOnStop}. */
121 private boolean mRetainInOnStop;
122
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800123 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
124 @Override public void onSomePackagesChanged() {
125 mAdapter.handlePackagesChanged();
Adam Powell88831a22014-11-20 18:17:00 -0800126 if (mProfileView != null) {
127 bindProfileView();
128 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800129 }
Xiaohui Chen393c8012017-02-14 14:55:07 -0800130
131 @Override
132 public boolean onPackageChanged(String packageName, int uid, String[] components) {
133 // We care about all package changes, not just the whole package itself which is
134 // default behavior.
135 return true;
136 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800137 };
138
Philip P. Moltmannf8173ca2016-04-12 15:11:23 -0700139 /**
140 * Get the string resource to be used as a label for the link to the resolver activity for an
141 * action.
142 *
143 * @param action The action to resolve
144 *
145 * @return The string resource to be used as a label
146 */
147 public static @StringRes int getLabelRes(String action) {
148 return ActionTitle.forAction(action).labelRes;
149 }
150
Adam Powell278902c2014-07-12 18:33:22 -0700151 private enum ActionTitle {
152 VIEW(Intent.ACTION_VIEW,
153 com.android.internal.R.string.whichViewApplication,
Philip P. Moltmannf8173ca2016-04-12 15:11:23 -0700154 com.android.internal.R.string.whichViewApplicationNamed,
155 com.android.internal.R.string.whichViewApplicationLabel),
Adam Powell278902c2014-07-12 18:33:22 -0700156 EDIT(Intent.ACTION_EDIT,
157 com.android.internal.R.string.whichEditApplication,
Philip P. Moltmannf8173ca2016-04-12 15:11:23 -0700158 com.android.internal.R.string.whichEditApplicationNamed,
159 com.android.internal.R.string.whichEditApplicationLabel),
Adam Powell278902c2014-07-12 18:33:22 -0700160 SEND(Intent.ACTION_SEND,
161 com.android.internal.R.string.whichSendApplication,
Philip P. Moltmannf8173ca2016-04-12 15:11:23 -0700162 com.android.internal.R.string.whichSendApplicationNamed,
163 com.android.internal.R.string.whichSendApplicationLabel),
Adam Powell278902c2014-07-12 18:33:22 -0700164 SENDTO(Intent.ACTION_SENDTO,
Adam Powell13ea8f42016-03-18 09:39:41 -0700165 com.android.internal.R.string.whichSendToApplication,
Philip P. Moltmannf8173ca2016-04-12 15:11:23 -0700166 com.android.internal.R.string.whichSendToApplicationNamed,
167 com.android.internal.R.string.whichSendToApplicationLabel),
Adam Powell278902c2014-07-12 18:33:22 -0700168 SEND_MULTIPLE(Intent.ACTION_SEND_MULTIPLE,
169 com.android.internal.R.string.whichSendApplication,
Philip P. Moltmannf8173ca2016-04-12 15:11:23 -0700170 com.android.internal.R.string.whichSendApplicationNamed,
171 com.android.internal.R.string.whichSendApplicationLabel),
172 CAPTURE_IMAGE(MediaStore.ACTION_IMAGE_CAPTURE,
173 com.android.internal.R.string.whichImageCaptureApplication,
174 com.android.internal.R.string.whichImageCaptureApplicationNamed,
175 com.android.internal.R.string.whichImageCaptureApplicationLabel),
Adam Powell278902c2014-07-12 18:33:22 -0700176 DEFAULT(null,
177 com.android.internal.R.string.whichApplication,
Philip P. Moltmannf8173ca2016-04-12 15:11:23 -0700178 com.android.internal.R.string.whichApplicationNamed,
179 com.android.internal.R.string.whichApplicationLabel),
Adam Powella35c77a2014-09-25 16:46:36 -0700180 HOME(Intent.ACTION_MAIN,
181 com.android.internal.R.string.whichHomeApplication,
Philip P. Moltmannf8173ca2016-04-12 15:11:23 -0700182 com.android.internal.R.string.whichHomeApplicationNamed,
183 com.android.internal.R.string.whichHomeApplicationLabel);
Adam Powell278902c2014-07-12 18:33:22 -0700184
185 public final String action;
186 public final int titleRes;
187 public final int namedTitleRes;
Philip P. Moltmannf8173ca2016-04-12 15:11:23 -0700188 public final @StringRes int labelRes;
Adam Powell278902c2014-07-12 18:33:22 -0700189
Philip P. Moltmannf8173ca2016-04-12 15:11:23 -0700190 ActionTitle(String action, int titleRes, int namedTitleRes, @StringRes int labelRes) {
Adam Powell278902c2014-07-12 18:33:22 -0700191 this.action = action;
192 this.titleRes = titleRes;
193 this.namedTitleRes = namedTitleRes;
Philip P. Moltmannf8173ca2016-04-12 15:11:23 -0700194 this.labelRes = labelRes;
Adam Powell278902c2014-07-12 18:33:22 -0700195 }
196
197 public static ActionTitle forAction(String action) {
198 for (ActionTitle title : values()) {
Adam Powella35c77a2014-09-25 16:46:36 -0700199 if (title != HOME && action != null && action.equals(title.action)) {
Adam Powell278902c2014-07-12 18:33:22 -0700200 return title;
201 }
202 }
203 return DEFAULT;
204 }
205 }
206
Dianne Hackborn905577f2011-09-07 18:31:28 -0700207 private Intent makeMyIntent() {
208 Intent intent = new Intent(getIntent());
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700209 intent.setComponent(null);
Dianne Hackborn905577f2011-09-07 18:31:28 -0700210 // The resolver activity is set to be hidden from recent tasks.
211 // we don't want this attribute to be propagated to the next activity
212 // being launched. Note that if the original Intent also had this
213 // flag set, we are now losing it. That should be a very rare case
214 // and we can live with this.
215 intent.setFlags(intent.getFlags()&~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
216 return intent;
217 }
218
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 @Override
220 protected void onCreate(Bundle savedInstanceState) {
Christopher Tateb72b3632013-09-30 17:50:32 -0700221 // Use a specialized prompt when we're handling the 'Home' app startActivity()
Christopher Tateb72b3632013-09-30 17:50:32 -0700222 final Intent intent = makeMyIntent();
223 final Set<String> categories = intent.getCategories();
224 if (Intent.ACTION_MAIN.equals(intent.getAction())
225 && categories != null
226 && categories.size() == 1
227 && categories.contains(Intent.CATEGORY_HOME)) {
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100228 // Note: this field is not set to true in the compatibility version.
229 mResolvingHome = true;
Christopher Tateb72b3632013-09-30 17:50:32 -0700230 }
231
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700232 setSafeForwardingMode(true);
233
Adam Powella35c77a2014-09-25 16:46:36 -0700234 onCreate(savedInstanceState, intent, null, 0, null, null, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235 }
236
Adam Powell278902c2014-07-12 18:33:22 -0700237 /**
Adam Powell24428412015-04-01 17:19:56 -0700238 * Compatibility version for other bundled services that use this overload without
Adam Powell278902c2014-07-12 18:33:22 -0700239 * a default title resource
240 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 protected void onCreate(Bundle savedInstanceState, Intent intent,
Adam Powell278902c2014-07-12 18:33:22 -0700242 CharSequence title, Intent[] initialIntents,
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800243 List<ResolveInfo> rList, boolean supportsAlwaysUseOption) {
244 onCreate(savedInstanceState, intent, title, 0, initialIntents, rList,
245 supportsAlwaysUseOption);
Adam Powell278902c2014-07-12 18:33:22 -0700246 }
247
248 protected void onCreate(Bundle savedInstanceState, Intent intent,
249 CharSequence title, int defaultTitleRes, Intent[] initialIntents,
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800250 List<ResolveInfo> rList, boolean supportsAlwaysUseOption) {
Adam Powelle9414d92014-07-05 17:44:18 -0700251 setTheme(R.style.Theme_DeviceDefault_Resolver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 super.onCreate(savedInstanceState);
Sander Alewijnsef6545332014-10-31 12:39:02 +0000253
254 // Determine whether we should show that intent is forwarded
255 // from managed profile to owner or other way around.
256 setProfileSwitchMessageId(intent.getContentUserHint());
257
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700258 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -0800259 mLaunchedFromUid = ActivityManager.getService().getLaunchedFromUid(
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700260 getActivityToken());
261 } catch (RemoteException e) {
262 mLaunchedFromUid = -1;
263 }
Adam Powell7d758002015-05-06 17:49:36 -0700264
265 if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) {
266 // Gulp!
267 finish();
268 return;
269 }
270
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800271 mPm = getPackageManager();
Adam Powell09a65602014-07-20 16:23:14 -0700272
Dianne Hackbornd0d75032012-04-19 23:12:09 -0700273 mPackageMonitor.register(this, getMainLooper(), false);
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700274 mRegistered = true;
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800275 mReferrerPackage = getReferrerPackageName();
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800276 mSupportsAlwaysUseOption = supportsAlwaysUseOption;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800277
Adam Powellc5878612012-05-04 18:42:38 -0700278 final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
279 mIconDpi = am.getLauncherLargeIconDensity();
Adam Powellc5878612012-05-04 18:42:38 -0700280
Adam Powell7d758002015-05-06 17:49:36 -0700281 // Add our initial intent as the first item, regardless of what else has already been added.
Adam Powell2ed547e2015-04-29 18:45:04 -0700282 mIntents.add(0, new Intent(intent));
Hakan Seyalioglu33550122017-01-06 19:54:43 -0800283 mTitle = title;
284 mDefaultTitleResId = defaultTitleRes;
Adam Powell278902c2014-07-12 18:33:22 -0700285
Hakan Seyalioglu33550122017-01-06 19:54:43 -0800286 if (configureContentView(mIntents, initialIntents, rList)) {
Adam Powell39e94eb2015-09-08 17:01:49 -0700287 return;
288 }
Adam Powell278902c2014-07-12 18:33:22 -0700289
Adam Powell4f6c2052014-07-07 18:49:10 -0700290 final ResolverDrawerLayout rdl = (ResolverDrawerLayout) findViewById(R.id.contentPanel);
291 if (rdl != null) {
Adam Powell5dd072d2014-10-30 19:51:41 -0700292 rdl.setOnDismissedListener(new ResolverDrawerLayout.OnDismissedListener() {
Adam Powell4f6c2052014-07-07 18:49:10 -0700293 @Override
Adam Powell5dd072d2014-10-30 19:51:41 -0700294 public void onDismissed() {
Adam Powell4f6c2052014-07-07 18:49:10 -0700295 finish();
296 }
297 });
Adam Powell4c470d62015-06-19 17:46:17 -0700298 if (isVoiceInteraction()) {
299 rdl.setCollapsed(false);
300 }
Adam Powell63b31692015-09-28 10:45:00 -0700301 mResolverDrawerLayout = rdl;
Adam Powell4f6c2052014-07-07 18:49:10 -0700302 }
303
Adam Powell88831a22014-11-20 18:17:00 -0800304 mProfileView = findViewById(R.id.profile_button);
305 if (mProfileView != null) {
306 mProfileView.setOnClickListener(new View.OnClickListener() {
307 @Override
308 public void onClick(View v) {
309 final DisplayResolveInfo dri = mAdapter.getOtherProfile();
310 if (dri == null) {
311 return;
312 }
313
Sander Alewijnse053d3dd2015-03-09 15:31:10 +0000314 // Do not show the profile switch message anymore.
315 mProfileSwitchMessageId = -1;
316
Adam Powell24428412015-04-01 17:19:56 -0700317 onTargetSelected(dri, false);
Adam Powell88831a22014-11-20 18:17:00 -0800318 finish();
319 }
320 });
321 bindProfileView();
322 }
Adam Powell4c470d62015-06-19 17:46:17 -0700323
324 if (isVoiceInteraction()) {
325 onSetupVoiceInteraction();
326 }
Clara Bayarri9550f5d2016-05-11 11:20:13 +0100327 final Set<String> categories = intent.getCategories();
Clara Bayarrifa902aa2016-04-13 14:45:08 +0100328 MetricsLogger.action(this, mAdapter.hasFilteredItem()
329 ? MetricsProto.MetricsEvent.ACTION_SHOW_APP_DISAMBIG_APP_FEATURED
330 : MetricsProto.MetricsEvent.ACTION_SHOW_APP_DISAMBIG_NONE_FEATURED,
331 intent.getAction() + ":" + intent.getType() + ":"
Clara Bayarri9550f5d2016-05-11 11:20:13 +0100332 + (categories != null ? Arrays.toString(categories.toArray()) : ""));
Adam Powell4c470d62015-06-19 17:46:17 -0700333 }
334
335 /**
336 * Perform any initialization needed for voice interaction.
337 */
Adam Powell23882512016-01-29 10:21:00 -0800338 public void onSetupVoiceInteraction() {
Adam Powell4c470d62015-06-19 17:46:17 -0700339 // Do it right now. Subclasses may delay this and send it later.
340 sendVoiceChoicesIfNeeded();
341 }
342
Adam Powell23882512016-01-29 10:21:00 -0800343 public void sendVoiceChoicesIfNeeded() {
Adam Powell4c470d62015-06-19 17:46:17 -0700344 if (!isVoiceInteraction()) {
345 // Clearly not needed.
346 return;
347 }
348
349
350 final Option[] options = new Option[mAdapter.getCount()];
351 for (int i = 0, N = options.length; i < N; i++) {
352 options[i] = optionForChooserTarget(mAdapter.getItem(i), i);
353 }
354
355 mPickOptionRequest = new PickTargetOptionRequest(
356 new Prompt(getTitle()), options, null);
357 getVoiceInteractor().submitRequest(mPickOptionRequest);
358 }
359
360 Option optionForChooserTarget(TargetInfo target, int index) {
361 return new Option(target.getDisplayLabel(), index);
Adam Powell88831a22014-11-20 18:17:00 -0800362 }
363
Adam Powell2ed547e2015-04-29 18:45:04 -0700364 protected final void setAdditionalTargets(Intent[] intents) {
365 if (intents != null) {
366 for (Intent intent : intents) {
367 mIntents.add(intent);
368 }
369 }
370 }
371
Adam Powell0ccc0e92015-04-23 17:19:37 -0700372 public Intent getTargetIntent() {
Adam Powell2ed547e2015-04-29 18:45:04 -0700373 return mIntents.isEmpty() ? null : mIntents.get(0);
Adam Powell0ccc0e92015-04-23 17:19:37 -0700374 }
375
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800376 protected String getReferrerPackageName() {
Adam Powell24428412015-04-01 17:19:56 -0700377 final Uri referrer = getReferrer();
378 if (referrer != null && "android-app".equals(referrer.getScheme())) {
379 return referrer.getHost();
380 }
381 return null;
382 }
383
Adam Powell23882512016-01-29 10:21:00 -0800384 public int getLayoutResource() {
Adam Powell24428412015-04-01 17:19:56 -0700385 return R.layout.resolver_list;
386 }
387
Adam Powell88831a22014-11-20 18:17:00 -0800388 void bindProfileView() {
389 final DisplayResolveInfo dri = mAdapter.getOtherProfile();
390 if (dri != null) {
391 mProfileView.setVisibility(View.VISIBLE);
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800392 View text = mProfileView.findViewById(R.id.profile_button);
393 if (!(text instanceof TextView)) {
394 text = mProfileView.findViewById(R.id.text1);
395 }
396 ((TextView) text).setText(dri.getDisplayLabel());
Adam Powell88831a22014-11-20 18:17:00 -0800397 } else {
398 mProfileView.setVisibility(View.GONE);
399 }
Adam Powell278902c2014-07-12 18:33:22 -0700400 }
401
Sander Alewijnsef6545332014-10-31 12:39:02 +0000402 private void setProfileSwitchMessageId(int contentUserHint) {
403 if (contentUserHint != UserHandle.USER_CURRENT &&
404 contentUserHint != UserHandle.myUserId()) {
405 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
406 UserInfo originUserInfo = userManager.getUserInfo(contentUserHint);
407 boolean originIsManaged = originUserInfo != null ? originUserInfo.isManagedProfile()
408 : false;
409 boolean targetIsManaged = userManager.isManagedProfile();
410 if (originIsManaged && !targetIsManaged) {
411 mProfileSwitchMessageId = com.android.internal.R.string.forward_intent_to_owner;
412 } else if (!originIsManaged && targetIsManaged) {
413 mProfileSwitchMessageId = com.android.internal.R.string.forward_intent_to_work;
414 }
415 }
416 }
417
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700418 /**
419 * Turn on launch mode that is safe to use when forwarding intents received from
420 * applications and running in system processes. This mode uses Activity.startActivityAsCaller
421 * instead of the normal Activity.startActivity for launching the activity selected
422 * by the user.
423 *
424 * <p>This mode is set to true by default if the activity is initialized through
425 * {@link #onCreate(android.os.Bundle)}. If a subclass calls one of the other onCreate
426 * methods, it is set to false by default. You must set it before calling one of the
427 * more detailed onCreate methods, so that it will be set correctly in the case where
428 * there is only one intent to resolve and it is thus started immediately.</p>
429 */
430 public void setSafeForwardingMode(boolean safeForwarding) {
431 mSafeForwardingMode = safeForwarding;
432 }
433
Adam Powell278902c2014-07-12 18:33:22 -0700434 protected CharSequence getTitleForAction(String action, int defaultTitleRes) {
Adam Powella35c77a2014-09-25 16:46:36 -0700435 final ActionTitle title = mResolvingHome ? ActionTitle.HOME : ActionTitle.forAction(action);
Hakan Seyalioglu23f34652017-02-03 09:38:35 -0800436 // While there may already be a filtered item, we can only use it in the title if the list
437 // is already sorted and all information relevant to it is already in the list.
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800438 final boolean named = mAdapter.getFilteredPosition() >= 0;
Adam Powell278902c2014-07-12 18:33:22 -0700439 if (title == ActionTitle.DEFAULT && defaultTitleRes != 0) {
440 return getString(defaultTitleRes);
441 } else {
Adam Powell24428412015-04-01 17:19:56 -0700442 return named
443 ? getString(title.namedTitleRes, mAdapter.getFilteredItem().getDisplayLabel())
444 : getString(title.titleRes);
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700445 }
Adam Powellc5878612012-05-04 18:42:38 -0700446 }
447
Adam Powelle9414d92014-07-05 17:44:18 -0700448 void dismiss() {
449 if (!isFinishing()) {
450 finish();
451 }
452 }
453
Adam Powellc5878612012-05-04 18:42:38 -0700454 Drawable getIcon(Resources res, int resId) {
455 Drawable result;
456 try {
457 result = res.getDrawableForDensity(resId, mIconDpi);
458 } catch (Resources.NotFoundException e) {
459 result = null;
460 }
461
462 return result;
463 }
464
465 Drawable loadIconForResolveInfo(ResolveInfo ri) {
466 Drawable dr;
467 try {
468 if (ri.resolvePackageName != null && ri.icon != 0) {
469 dr = getIcon(mPm.getResourcesForApplication(ri.resolvePackageName), ri.icon);
470 if (dr != null) {
471 return dr;
472 }
473 }
474 final int iconRes = ri.getIconResource();
475 if (iconRes != 0) {
476 dr = getIcon(mPm.getResourcesForApplication(ri.activityInfo.packageName), iconRes);
477 if (dr != null) {
478 return dr;
479 }
480 }
481 } catch (NameNotFoundException e) {
482 Log.e(TAG, "Couldn't find resources for package", e);
483 }
484 return ri.loadIcon(mPm);
485 }
486
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800487 @Override
488 protected void onRestart() {
489 super.onRestart();
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700490 if (!mRegistered) {
491 mPackageMonitor.register(this, getMainLooper(), false);
492 mRegistered = true;
493 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800494 mAdapter.handlePackagesChanged();
Adam Powell88831a22014-11-20 18:17:00 -0800495 if (mProfileView != null) {
496 bindProfileView();
497 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800498 }
499
500 @Override
501 protected void onStop() {
502 super.onStop();
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700503 if (mRegistered) {
504 mPackageMonitor.unregister();
505 mRegistered = false;
506 }
Wale Ogunwale9014e662016-03-19 14:55:46 -0700507 final Intent intent = getIntent();
508 if ((intent.getFlags() & FLAG_ACTIVITY_NEW_TASK) != 0 && !isVoiceInteraction()
Jorim Jaggif631ef72017-02-24 13:49:47 +0100509 && !mResolvingHome && !mRetainInOnStop) {
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700510 // This resolver is in the unusual situation where it has been
511 // launched at the top of a new task. We don't let it be added
512 // to the recent tasks shown to the user, and we need to make sure
513 // that each time we are launched we get the correct launching
514 // uid (not re-using the same resolver from an old launching uid),
515 // so we will now finish ourself since being no longer visible,
516 // the user probably can't get back to us.
517 if (!isChangingConfigurations()) {
518 finish();
519 }
520 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800521 }
522
Adam Powellc5878612012-05-04 18:42:38 -0700523 @Override
Adam Powell4c470d62015-06-19 17:46:17 -0700524 protected void onDestroy() {
525 super.onDestroy();
526 if (!isChangingConfigurations() && mPickOptionRequest != null) {
527 mPickOptionRequest.cancel();
528 }
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800529 if (mPostListReadyRunnable != null) {
530 getMainThreadHandler().removeCallbacks(mPostListReadyRunnable);
531 mPostListReadyRunnable = null;
Hakan Seyalioglu23f34652017-02-03 09:38:35 -0800532 }
Kang Li38a6da642017-04-05 12:30:55 -0700533 if (mAdapter != null && mAdapter.mResolverListController != null) {
534 mAdapter.mResolverListController.destroy();
535 }
Adam Powell4c470d62015-06-19 17:46:17 -0700536 }
537
538 @Override
Adam Powell9bee4662012-05-08 11:07:23 -0700539 protected void onRestoreInstanceState(Bundle savedInstanceState) {
540 super.onRestoreInstanceState(savedInstanceState);
Hakan Seyalioglu5dbc8192017-02-24 16:16:37 -0800541 resetAlwaysOrOnceButtonBar();
Adam Powell9bee4662012-05-08 11:07:23 -0700542 }
543
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100544 private boolean hasManagedProfile() {
545 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
546 if (userManager == null) {
547 return false;
548 }
549
550 try {
551 List<UserInfo> profiles = userManager.getProfiles(getUserId());
552 for (UserInfo userInfo : profiles) {
553 if (userInfo != null && userInfo.isManagedProfile()) {
554 return true;
555 }
556 }
557 } catch (SecurityException e) {
558 return false;
559 }
560 return false;
561 }
562
563 private boolean supportsManagedProfiles(ResolveInfo resolveInfo) {
564 try {
565 ApplicationInfo appInfo = getPackageManager().getApplicationInfo(
566 resolveInfo.activityInfo.packageName, 0 /* default flags */);
Adam Powell4c470d62015-06-19 17:46:17 -0700567 return appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100568 } catch (NameNotFoundException e) {
569 return false;
570 }
571 }
572
Adam Powell278902c2014-07-12 18:33:22 -0700573 private void setAlwaysButtonEnabled(boolean hasValidSelection, int checkedPos,
574 boolean filtered) {
Nicolas Prevot50449882014-06-23 12:42:37 +0100575 boolean enabled = false;
576 if (hasValidSelection) {
Adam Powell278902c2014-07-12 18:33:22 -0700577 ResolveInfo ri = mAdapter.resolveInfoForPosition(checkedPos, filtered);
Hakan Seyalioglu5dbc8192017-02-24 16:16:37 -0800578 if (ri == null) {
579 Log.e(TAG, "Invalid position supplied to setAlwaysButtonEnabled");
580 return;
581 } else if (ri.targetUserId != UserHandle.USER_CURRENT) {
582 Log.e(TAG, "Attempted to set selection to resolve info for another user");
583 return;
584 } else {
Nicolas Prevot50449882014-06-23 12:42:37 +0100585 enabled = true;
586 }
587 }
588 mAlwaysButton.setEnabled(enabled);
589 }
590
Adam Powellc5878612012-05-04 18:42:38 -0700591 public void onButtonClick(View v) {
592 final int id = v.getId();
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800593 startSelected(mAdapter.hasFilteredItem() ?
594 mAdapter.getFilteredPosition():
595 mAdapterView.getCheckedItemPosition(),
Adam Powell278902c2014-07-12 18:33:22 -0700596 id == R.id.button_always,
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800597 !mAdapter.hasFilteredItem());
Adam Powellc5878612012-05-04 18:42:38 -0700598 }
599
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800600 public void startSelected(int which, boolean always, boolean hasIndexBeenFiltered) {
Amith Yamasani07cd3512013-09-18 13:16:00 -0700601 if (isFinishing()) {
602 return;
603 }
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800604 ResolveInfo ri = mAdapter.resolveInfoForPosition(which, hasIndexBeenFiltered);
Sander Alewijnse86d35ba2015-02-04 15:14:53 +0000605 if (mResolvingHome && hasManagedProfile() && !supportsManagedProfiles(ri)) {
606 Toast.makeText(this, String.format(getResources().getString(
607 com.android.internal.R.string.activity_resolver_work_profiles_support),
608 ri.activityInfo.loadLabel(getPackageManager()).toString()),
609 Toast.LENGTH_LONG).show();
610 return;
611 }
612
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800613 TargetInfo target = mAdapter.targetInfoForPosition(which, hasIndexBeenFiltered);
Hakan Seyalioglu23f34652017-02-03 09:38:35 -0800614 if (target == null) {
615 return;
616 }
Adam Powell2ed547e2015-04-29 18:45:04 -0700617 if (onTargetSelected(target, always)) {
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800618 if (always && mSupportsAlwaysUseOption) {
Clara Bayarrifa902aa2016-04-13 14:45:08 +0100619 MetricsLogger.action(
620 this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_ALWAYS);
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800621 } else if (mSupportsAlwaysUseOption) {
Clara Bayarrifa902aa2016-04-13 14:45:08 +0100622 MetricsLogger.action(
623 this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_JUST_ONCE);
624 } else {
625 MetricsLogger.action(
626 this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_TAP);
627 }
628 MetricsLogger.action(this, mAdapter.hasFilteredItem()
629 ? MetricsProto.MetricsEvent.ACTION_HIDE_APP_DISAMBIG_APP_FEATURED
630 : MetricsProto.MetricsEvent.ACTION_HIDE_APP_DISAMBIG_NONE_FEATURED);
Adam Powell2ed547e2015-04-29 18:45:04 -0700631 finish();
632 }
Mike Lockwood02eb8742011-02-27 09:10:37 -0800633 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800634
Adam Powelle49d9392014-07-17 18:45:19 -0700635 /**
636 * Replace me in subclasses!
637 */
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000638 public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) {
Adam Powelle49d9392014-07-17 18:45:19 -0700639 return defIntent;
640 }
641
Adam Powell2ed547e2015-04-29 18:45:04 -0700642 protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) {
Adam Powell24428412015-04-01 17:19:56 -0700643 final ResolveInfo ri = target.getResolveInfo();
644 final Intent intent = target != null ? target.getResolvedIntent() : null;
645
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800646 if (intent != null && (mSupportsAlwaysUseOption || mAdapter.hasFilteredItem())
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800647 && mAdapter.mUnfilteredResolveList != null) {
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700648 // Build a reasonable intent filter, based on what matched.
649 IntentFilter filter = new IntentFilter();
Henrik Engström3277cf12014-07-17 12:18:29 +0200650 Intent filterIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800651
Henrik Engström3277cf12014-07-17 12:18:29 +0200652 if (intent.getSelector() != null) {
653 filterIntent = intent.getSelector();
654 } else {
655 filterIntent = intent;
656 }
657
658 String action = filterIntent.getAction();
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800659 if (action != null) {
660 filter.addAction(action);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800661 }
Henrik Engström3277cf12014-07-17 12:18:29 +0200662 Set<String> categories = filterIntent.getCategories();
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700663 if (categories != null) {
664 for (String cat : categories) {
665 filter.addCategory(cat);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800666 }
667 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700668 filter.addCategory(Intent.CATEGORY_DEFAULT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800669
Adam Powell24428412015-04-01 17:19:56 -0700670 int cat = ri.match & IntentFilter.MATCH_CATEGORY_MASK;
Henrik Engström3277cf12014-07-17 12:18:29 +0200671 Uri data = filterIntent.getData();
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700672 if (cat == IntentFilter.MATCH_CATEGORY_TYPE) {
Henrik Engström3277cf12014-07-17 12:18:29 +0200673 String mimeType = filterIntent.resolveType(this);
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700674 if (mimeType != null) {
675 try {
676 filter.addDataType(mimeType);
677 } catch (IntentFilter.MalformedMimeTypeException e) {
678 Log.w("ResolverActivity", e);
679 filter = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800680 }
681 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700682 }
683 if (data != null && data.getScheme() != null) {
684 // We need the data specification if there was no type,
685 // OR if the scheme is not one of our magical "file:"
686 // or "content:" schemes (see IntentFilter for the reason).
687 if (cat != IntentFilter.MATCH_CATEGORY_TYPE
688 || (!"file".equals(data.getScheme())
689 && !"content".equals(data.getScheme()))) {
690 filter.addDataScheme(data.getScheme());
691
692 // Look through the resolved filter to determine which part
693 // of it matched the original Intent.
694 Iterator<PatternMatcher> pIt = ri.filter.schemeSpecificPartsIterator();
695 if (pIt != null) {
696 String ssp = data.getSchemeSpecificPart();
697 while (ssp != null && pIt.hasNext()) {
698 PatternMatcher p = pIt.next();
699 if (p.match(ssp)) {
700 filter.addDataSchemeSpecificPart(p.getPath(), p.getType());
701 break;
702 }
Dianne Hackborndf1c0bf2013-06-12 16:21:38 -0700703 }
704 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700705 Iterator<IntentFilter.AuthorityEntry> aIt = ri.filter.authoritiesIterator();
706 if (aIt != null) {
707 while (aIt.hasNext()) {
708 IntentFilter.AuthorityEntry a = aIt.next();
709 if (a.match(data) >= 0) {
710 int port = a.getPort();
711 filter.addDataAuthority(a.getHost(),
712 port >= 0 ? Integer.toString(port) : null);
713 break;
714 }
715 }
716 }
717 pIt = ri.filter.pathsIterator();
718 if (pIt != null) {
719 String path = data.getPath();
720 while (path != null && pIt.hasNext()) {
721 PatternMatcher p = pIt.next();
722 if (p.match(path)) {
723 filter.addDataPath(p.getPath(), p.getType());
724 break;
725 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800726 }
727 }
728 }
729 }
730
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700731 if (filter != null) {
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800732 final int N = mAdapter.mUnfilteredResolveList.size();
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800733 ComponentName[] set;
734 // If we don't add back in the component for forwarding the intent to a managed
735 // profile, the preferred activity may not be updated correctly (as the set of
736 // components we tell it we knew about will have changed).
737 final boolean needToAddBackProfileForwardingComponent
738 = mAdapter.mOtherProfile != null;
739 if (!needToAddBackProfileForwardingComponent) {
740 set = new ComponentName[N];
741 } else {
742 set = new ComponentName[N + 1];
743 }
744
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700745 int bestMatch = 0;
746 for (int i=0; i<N; i++) {
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800747 ResolveInfo r = mAdapter.mUnfilteredResolveList.get(i).getResolveInfoAt(0);
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700748 set[i] = new ComponentName(r.activityInfo.packageName,
749 r.activityInfo.name);
750 if (r.match > bestMatch) bestMatch = r.match;
751 }
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800752
753 if (needToAddBackProfileForwardingComponent) {
754 set[N] = mAdapter.mOtherProfile.getResolvedComponentName();
755 final int otherProfileMatch = mAdapter.mOtherProfile.getResolveInfo().match;
756 if (otherProfileMatch > bestMatch) bestMatch = otherProfileMatch;
757 }
758
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700759 if (alwaysCheck) {
Fabrice Di Meglio3453e082015-05-11 17:46:23 -0700760 final int userId = getUserId();
761 final PackageManager pm = getPackageManager();
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800762
763 // Set the preferred Activity
764 pm.addPreferredActivity(filter, bestMatch, set, intent.getComponent());
765
Fabrice Di Meglio3453e082015-05-11 17:46:23 -0700766 if (ri.handleAllWebDataURI) {
767 // Set default Browser if needed
Jeff Sharkeye06b4d12016-01-06 14:51:50 -0700768 final String packageName = pm.getDefaultBrowserPackageNameAsUser(userId);
Fabrice Di Meglio3453e082015-05-11 17:46:23 -0700769 if (TextUtils.isEmpty(packageName)) {
Jeff Sharkeye06b4d12016-01-06 14:51:50 -0700770 pm.setDefaultBrowserPackageNameAsUser(ri.activityInfo.packageName, userId);
Fabrice Di Meglio3453e082015-05-11 17:46:23 -0700771 }
772 } else {
773 // Update Domain Verification status
774 ComponentName cn = intent.getComponent();
775 String packageName = cn.getPackageName();
776 String dataScheme = (data != null) ? data.getScheme() : null;
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800777
Fabrice Di Meglio3453e082015-05-11 17:46:23 -0700778 boolean isHttpOrHttps = (dataScheme != null) &&
779 (dataScheme.equals(IntentFilter.SCHEME_HTTP) ||
780 dataScheme.equals(IntentFilter.SCHEME_HTTPS));
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800781
Fabrice Di Meglio3453e082015-05-11 17:46:23 -0700782 boolean isViewAction = (action != null) && action.equals(Intent.ACTION_VIEW);
783 boolean hasCategoryBrowsable = (categories != null) &&
784 categories.contains(Intent.CATEGORY_BROWSABLE);
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800785
Fabrice Di Meglio3453e082015-05-11 17:46:23 -0700786 if (isHttpOrHttps && isViewAction && hasCategoryBrowsable) {
Jeff Sharkeye06b4d12016-01-06 14:51:50 -0700787 pm.updateIntentVerificationStatusAsUser(packageName,
Fabrice Di Meglio3453e082015-05-11 17:46:23 -0700788 PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS,
789 userId);
790 }
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800791 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700792 } else {
793 try {
Hakan Seyalioglu9149dca2017-01-17 12:20:01 -0800794 mAdapter.mResolverListController.setLastChosen(intent, filter, bestMatch);
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700795 } catch (RemoteException re) {
796 Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
797 }
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700798 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800799 }
800 }
801
Adam Powell24428412015-04-01 17:19:56 -0700802 if (target != null) {
803 safelyStartActivity(target);
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700804 }
Adam Powell2ed547e2015-04-29 18:45:04 -0700805 return true;
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700806 }
807
Adam Powell23882512016-01-29 10:21:00 -0800808 public void safelyStartActivity(TargetInfo cti) {
Jeff Sharkey2b9eb892016-02-16 09:21:51 -0700809 // We're dispatching intents that might be coming from legacy apps, so
810 // don't kill ourselves.
811 StrictMode.disableDeathOnFileUriExposure();
812 try {
813 safelyStartActivityInternal(cti);
814 } finally {
815 StrictMode.enableDeathOnFileUriExposure();
816 }
817 }
818
819 private void safelyStartActivityInternal(TargetInfo cti) {
Sander Alewijnsef6545332014-10-31 12:39:02 +0000820 // If needed, show that intent is forwarded
821 // from managed profile to owner or other way around.
822 if (mProfileSwitchMessageId != -1) {
823 Toast.makeText(this, getString(mProfileSwitchMessageId), Toast.LENGTH_LONG).show();
824 }
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700825 if (!mSafeForwardingMode) {
Adam Powell24428412015-04-01 17:19:56 -0700826 if (cti.start(this, null)) {
827 onActivityStarted(cti);
828 }
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700829 return;
830 }
831 try {
Adam Powell24428412015-04-01 17:19:56 -0700832 if (cti.startAsCaller(this, null, UserHandle.USER_NULL)) {
833 onActivityStarted(cti);
834 }
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700835 } catch (RuntimeException e) {
836 String launchedFromPackage;
837 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -0800838 launchedFromPackage = ActivityManager.getService().getLaunchedFromPackage(
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700839 getActivityToken());
840 } catch (RemoteException e2) {
841 launchedFromPackage = "??";
842 }
843 Slog.wtf(TAG, "Unable to launch as uid " + mLaunchedFromUid
844 + " package " + launchedFromPackage + ", while running in "
845 + ActivityThread.currentProcessName(), e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800846 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800847 }
848
Adam Powell23882512016-01-29 10:21:00 -0800849 public void onActivityStarted(TargetInfo cti) {
Adam Powell0b3c1122014-10-09 12:50:14 -0700850 // Do nothing
851 }
852
Adam Powell23882512016-01-29 10:21:00 -0800853 public boolean shouldGetActivityMetadata() {
Adam Powell24428412015-04-01 17:19:56 -0700854 return false;
855 }
856
Adam Powell23882512016-01-29 10:21:00 -0800857 public boolean shouldAutoLaunchSingleChoice(TargetInfo target) {
Adam Powell39e94eb2015-09-08 17:01:49 -0700858 return true;
859 }
860
Adam Powell23882512016-01-29 10:21:00 -0800861 public void showTargetDetails(ResolveInfo ri) {
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700862 Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
Adam Powell0fc5b2b2012-07-18 18:20:29 -0700863 .setData(Uri.fromParts("package", ri.activityInfo.packageName, null))
Adam Powell24428412015-04-01 17:19:56 -0700864 .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
Amith Yamasani203a2f42012-10-03 20:16:40 -0700865 startActivity(in);
Adam Powellc5878612012-05-04 18:42:38 -0700866 }
867
Adam Powell23882512016-01-29 10:21:00 -0800868 public ResolveListAdapter createAdapter(Context context, List<Intent> payloadIntents,
Adam Powell7d758002015-05-06 17:49:36 -0700869 Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid,
870 boolean filterLastUsed) {
871 return new ResolveListAdapter(context, payloadIntents, initialIntents, rList,
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800872 launchedFromUid, filterLastUsed, createListController());
873 }
874
875 @VisibleForTesting
876 protected ResolverListController createListController() {
877 return new ResolverListController(
878 this,
879 mPm,
880 getTargetIntent(),
881 getReferrerPackageName(),
882 mLaunchedFromUid);
Adam Powell88831a22014-11-20 18:17:00 -0800883 }
884
Adam Powell39e94eb2015-09-08 17:01:49 -0700885 /**
886 * Returns true if the activity is finishing and creation should halt
887 */
Adam Powell23882512016-01-29 10:21:00 -0800888 public boolean configureContentView(List<Intent> payloadIntents, Intent[] initialIntents,
Hakan Seyalioglu33550122017-01-06 19:54:43 -0800889 List<ResolveInfo> rList) {
Dianne Hackborn57dd7372015-07-27 18:11:14 -0700890 // The last argument of createAdapter is whether to do special handling
891 // of the last used choice to highlight it in the list. We need to always
892 // turn this off when running under voice interaction, since it results in
893 // a more complicated UI that the current voice interaction flow is not able
894 // to handle.
Adam Powell7d758002015-05-06 17:49:36 -0700895 mAdapter = createAdapter(this, payloadIntents, initialIntents, rList,
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800896 mLaunchedFromUid, mSupportsAlwaysUseOption && !isVoiceInteraction());
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800897 boolean rebuildCompleted = mAdapter.rebuildList();
Adam Powell7d758002015-05-06 17:49:36 -0700898
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800899 if (useLayoutWithDefault()) {
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800900 mLayoutId = R.layout.resolver_list_with_default;
Adam Powell7d758002015-05-06 17:49:36 -0700901 } else {
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800902 mLayoutId = getLayoutResource();
Adam Powell7d758002015-05-06 17:49:36 -0700903 }
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800904 setContentView(mLayoutId);
Adam Powell7d758002015-05-06 17:49:36 -0700905
Adam Powell50077352015-05-26 18:01:55 -0700906 int count = mAdapter.getUnfilteredCount();
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800907
908 // We only rebuild asynchronously when we have multiple elements to sort. In the case where
909 // we're already done, we can check if we should auto-launch immediately.
910 if (rebuildCompleted) {
911 if (count == 1 && mAdapter.getOtherProfile() == null) {
912 // Only one target, so we're a candidate to auto-launch!
913 final TargetInfo target = mAdapter.targetInfoForPosition(0, false);
914 if (shouldAutoLaunchSingleChoice(target)) {
915 safelyStartActivity(target);
916 mPackageMonitor.unregister();
917 mRegistered = false;
918 finish();
919 return true;
920 }
Jeff Sharkeycc2ae6b42015-09-29 13:04:46 -0700921 }
922 }
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800923
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800924
Hakan Seyalioglu23f34652017-02-03 09:38:35 -0800925 mAdapterView = (AbsListView) findViewById(R.id.resolver_list);
926
927 if (count == 0 && mAdapter.mPlaceholderCount == 0) {
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800928 final TextView emptyView = (TextView) findViewById(R.id.empty);
929 emptyView.setVisibility(View.VISIBLE);
Adam Powell7d758002015-05-06 17:49:36 -0700930 mAdapterView.setVisibility(View.GONE);
Hakan Seyalioglu23f34652017-02-03 09:38:35 -0800931 } else {
932 mAdapterView.setVisibility(View.VISIBLE);
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800933 onPrepareAdapterView(mAdapterView, mAdapter);
Adam Powell7d758002015-05-06 17:49:36 -0700934 }
Adam Powell39e94eb2015-09-08 17:01:49 -0700935 return false;
Adam Powell7d758002015-05-06 17:49:36 -0700936 }
937
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800938 public void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter) {
Adam Powell7d758002015-05-06 17:49:36 -0700939 final boolean useHeader = adapter.hasFilteredItem();
940 final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null;
941
942 adapterView.setAdapter(mAdapter);
943
944 final ItemClickListener listener = new ItemClickListener();
945 adapterView.setOnItemClickListener(listener);
946 adapterView.setOnItemLongClickListener(listener);
947
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800948 if (mSupportsAlwaysUseOption) {
Adam Powell7d758002015-05-06 17:49:36 -0700949 listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
950 }
951
Hakan Seyalioglu33550122017-01-06 19:54:43 -0800952 // In case this method is called again (due to activity recreation), avoid adding a new
953 // header if one is already present.
954 if (useHeader && listView != null && listView.getHeaderViewsCount() == 0) {
Adam Powell7d758002015-05-06 17:49:36 -0700955 listView.addHeaderView(LayoutInflater.from(this).inflate(
956 R.layout.resolver_different_item_header, listView, false));
957 }
Adam Powell24428412015-04-01 17:19:56 -0700958 }
959
Hakan Seyalioglu33550122017-01-06 19:54:43 -0800960 public void setTitleAndIcon() {
Hakan Seyalioglu23f34652017-02-03 09:38:35 -0800961 if (mAdapter.getCount() == 0 && mAdapter.mPlaceholderCount == 0) {
Hakan Seyalioglu33550122017-01-06 19:54:43 -0800962 final TextView titleView = (TextView) findViewById(R.id.title);
963 if (titleView != null) {
Hakan Seyalioglu23f34652017-02-03 09:38:35 -0800964 titleView.setVisibility(View.GONE);
Hakan Seyalioglu33550122017-01-06 19:54:43 -0800965 }
Hakan Seyalioglu23f34652017-02-03 09:38:35 -0800966 }
967
968 CharSequence title = mTitle != null
969 ? mTitle
970 : getTitleForAction(getTargetIntent().getAction(), mDefaultTitleResId);
971
972 if (!TextUtils.isEmpty(title)) {
973 final TextView titleView = (TextView) findViewById(R.id.title);
974 if (titleView != null) {
975 titleView.setText(title);
976 }
977 setTitle(title);
Hakan Seyalioglu33550122017-01-06 19:54:43 -0800978
979 // Try to initialize the title icon if we have a view for it and a title to match
980 final ImageView titleIcon = (ImageView) findViewById(R.id.title_icon);
981 if (titleIcon != null) {
982 ApplicationInfo ai = null;
983 try {
984 if (!TextUtils.isEmpty(mReferrerPackage)) {
985 ai = mPm.getApplicationInfo(mReferrerPackage, 0);
986 }
987 } catch (NameNotFoundException e) {
988 Log.e(TAG, "Could not find referrer package " + mReferrerPackage);
989 }
990
991 if (ai != null) {
992 titleIcon.setImageDrawable(ai.loadIcon(mPm));
993 }
994 }
995 }
996
997 final ImageView iconView = (ImageView) findViewById(R.id.icon);
998 final DisplayResolveInfo iconInfo = mAdapter.getFilteredItem();
999 if (iconView != null && iconInfo != null) {
1000 new LoadIconIntoViewTask(iconInfo, iconView).execute();
1001 }
1002 }
1003
1004 public void resetAlwaysOrOnceButtonBar() {
Hakan Seyalioglu13405c52017-01-31 19:01:31 -08001005 if (mSupportsAlwaysUseOption) {
Hakan Seyalioglu33550122017-01-06 19:54:43 -08001006 final ViewGroup buttonLayout = (ViewGroup) findViewById(R.id.button_bar);
1007 if (buttonLayout != null) {
1008 buttonLayout.setVisibility(View.VISIBLE);
1009 mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
1010 mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
Hakan Seyalioglu13405c52017-01-31 19:01:31 -08001011 } else {
1012 Log.e(TAG, "Layout unexpectedly does not have a button bar");
Hakan Seyalioglu33550122017-01-06 19:54:43 -08001013 }
1014 }
1015
Hakan Seyalioglu13405c52017-01-31 19:01:31 -08001016 if (useLayoutWithDefault()
1017 && mAdapter.getFilteredPosition() != ListView.INVALID_POSITION) {
Hakan Seyalioglu33550122017-01-06 19:54:43 -08001018 setAlwaysButtonEnabled(true, mAdapter.getFilteredPosition(), false);
1019 mOnceButton.setEnabled(true);
Hakan Seyalioglu23f34652017-02-03 09:38:35 -08001020 return;
1021 }
1022
1023 // When the items load in, if an item was already selected, enable the buttons
1024 if (mAdapterView != null
1025 && mAdapterView.getCheckedItemPosition() != ListView.INVALID_POSITION) {
1026 setAlwaysButtonEnabled(true, mAdapterView.getCheckedItemPosition(), true);
1027 mOnceButton.setEnabled(true);
Hakan Seyalioglu33550122017-01-06 19:54:43 -08001028 }
1029 }
1030
Hakan Seyalioglu13405c52017-01-31 19:01:31 -08001031 private boolean useLayoutWithDefault() {
1032 return mSupportsAlwaysUseOption && mAdapter.hasFilteredItem();
1033 }
1034
Adam Powellc412be62015-06-24 13:54:10 -07001035 /**
Jorim Jaggif631ef72017-02-24 13:49:47 +01001036 * If {@code retainInOnStop} is set to true, we will not finish ourselves when onStop gets
1037 * called and we are launched in a new task.
1038 */
1039 protected void setRetainInOnStop(boolean retainInOnStop) {
1040 mRetainInOnStop = retainInOnStop;
1041 }
1042
1043 /**
Adam Powellc412be62015-06-24 13:54:10 -07001044 * Check a simple match for the component of two ResolveInfos.
1045 */
1046 static boolean resolveInfoMatch(ResolveInfo lhs, ResolveInfo rhs) {
1047 return lhs == null ? rhs == null
1048 : lhs.activityInfo == null ? rhs.activityInfo == null
1049 : Objects.equals(lhs.activityInfo.name, rhs.activityInfo.name)
1050 && Objects.equals(lhs.activityInfo.packageName, rhs.activityInfo.packageName);
1051 }
1052
Adam Powell23882512016-01-29 10:21:00 -08001053 public final class DisplayResolveInfo implements TargetInfo {
Adam Powell24428412015-04-01 17:19:56 -07001054 private final ResolveInfo mResolveInfo;
1055 private final CharSequence mDisplayLabel;
1056 private Drawable mDisplayIcon;
Adam Powell00f4aad2015-09-17 13:38:16 -07001057 private Drawable mBadge;
Adam Powell24428412015-04-01 17:19:56 -07001058 private final CharSequence mExtendedInfo;
1059 private final Intent mResolvedIntent;
Adam Powell2ed547e2015-04-29 18:45:04 -07001060 private final List<Intent> mSourceIntents = new ArrayList<>();
Adam Powell23882512016-01-29 10:21:00 -08001061 private boolean mPinned;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001062
Adam Powell23882512016-01-29 10:21:00 -08001063 public DisplayResolveInfo(Intent originalIntent, ResolveInfo pri, CharSequence pLabel,
Dianne Hackborneb034652009-09-07 00:49:58 -07001064 CharSequence pInfo, Intent pOrigIntent) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001065 mSourceIntents.add(originalIntent);
Adam Powell24428412015-04-01 17:19:56 -07001066 mResolveInfo = pri;
1067 mDisplayLabel = pLabel;
1068 mExtendedInfo = pInfo;
1069
1070 final Intent intent = new Intent(pOrigIntent != null ? pOrigIntent :
Adam Powell2ed547e2015-04-29 18:45:04 -07001071 getReplacementIntent(pri.activityInfo, getTargetIntent()));
Adam Powell24428412015-04-01 17:19:56 -07001072 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
1073 | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
1074 final ActivityInfo ai = mResolveInfo.activityInfo;
1075 intent.setComponent(new ComponentName(ai.applicationInfo.packageName, ai.name));
1076
1077 mResolvedIntent = intent;
1078 }
1079
Adam Powell2ed547e2015-04-29 18:45:04 -07001080 private DisplayResolveInfo(DisplayResolveInfo other, Intent fillInIntent, int flags) {
1081 mSourceIntents.addAll(other.getAllSourceIntents());
1082 mResolveInfo = other.mResolveInfo;
1083 mDisplayLabel = other.mDisplayLabel;
1084 mDisplayIcon = other.mDisplayIcon;
1085 mExtendedInfo = other.mExtendedInfo;
1086 mResolvedIntent = new Intent(other.mResolvedIntent);
1087 mResolvedIntent.fillIn(fillInIntent, flags);
Adam Powell23882512016-01-29 10:21:00 -08001088 mPinned = other.mPinned;
Adam Powell2ed547e2015-04-29 18:45:04 -07001089 }
1090
Adam Powell24428412015-04-01 17:19:56 -07001091 public ResolveInfo getResolveInfo() {
1092 return mResolveInfo;
1093 }
1094
1095 public CharSequence getDisplayLabel() {
1096 return mDisplayLabel;
1097 }
1098
1099 public Drawable getDisplayIcon() {
1100 return mDisplayIcon;
1101 }
1102
Adam Powell7d758002015-05-06 17:49:36 -07001103 public Drawable getBadgeIcon() {
Adam Powell00f4aad2015-09-17 13:38:16 -07001104 // We only expose a badge if we have extended info.
1105 // The badge is a higher-priority disambiguation signal
1106 // but we don't need one if we wouldn't show extended info at all.
1107 if (TextUtils.isEmpty(getExtendedInfo())) {
1108 return null;
1109 }
1110
1111 if (mBadge == null && mResolveInfo != null && mResolveInfo.activityInfo != null
1112 && mResolveInfo.activityInfo.applicationInfo != null) {
1113 if (mResolveInfo.activityInfo.icon == 0 || mResolveInfo.activityInfo.icon
1114 == mResolveInfo.activityInfo.applicationInfo.icon) {
1115 // Badging an icon with exactly the same icon is silly.
1116 // If the activityInfo icon resid is 0 it will fall back
1117 // to the application's icon, making it a match.
1118 return null;
1119 }
1120 mBadge = mResolveInfo.activityInfo.applicationInfo.loadIcon(mPm);
1121 }
1122 return mBadge;
Adam Powell7d758002015-05-06 17:49:36 -07001123 }
1124
Adam Powell2ed547e2015-04-29 18:45:04 -07001125 @Override
Alan Viverettece5d92c2015-07-31 16:46:56 -04001126 public CharSequence getBadgeContentDescription() {
1127 return null;
1128 }
1129
1130 @Override
Adam Powell2ed547e2015-04-29 18:45:04 -07001131 public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) {
1132 return new DisplayResolveInfo(this, fillInIntent, flags);
1133 }
1134
1135 @Override
1136 public List<Intent> getAllSourceIntents() {
1137 return mSourceIntents;
1138 }
1139
1140 public void addAlternateSourceIntent(Intent alt) {
1141 mSourceIntents.add(alt);
1142 }
1143
Adam Powell24428412015-04-01 17:19:56 -07001144 public void setDisplayIcon(Drawable icon) {
1145 mDisplayIcon = icon;
1146 }
1147
1148 public boolean hasDisplayIcon() {
1149 return mDisplayIcon != null;
1150 }
1151
1152 public CharSequence getExtendedInfo() {
1153 return mExtendedInfo;
1154 }
1155
1156 public Intent getResolvedIntent() {
1157 return mResolvedIntent;
1158 }
1159
1160 @Override
1161 public ComponentName getResolvedComponentName() {
1162 return new ComponentName(mResolveInfo.activityInfo.packageName,
1163 mResolveInfo.activityInfo.name);
1164 }
1165
1166 @Override
1167 public boolean start(Activity activity, Bundle options) {
1168 activity.startActivity(mResolvedIntent, options);
1169 return true;
1170 }
1171
1172 @Override
1173 public boolean startAsCaller(Activity activity, Bundle options, int userId) {
Dianne Hackborna7cfbe02015-07-16 10:52:52 -07001174 activity.startActivityAsCaller(mResolvedIntent, options, false, userId);
Adam Powell24428412015-04-01 17:19:56 -07001175 return true;
1176 }
1177
1178 @Override
1179 public boolean startAsUser(Activity activity, Bundle options, UserHandle user) {
1180 activity.startActivityAsUser(mResolvedIntent, options, user);
1181 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001182 }
Adam Powell23882512016-01-29 10:21:00 -08001183
1184 @Override
1185 public boolean isPinned() {
1186 return mPinned;
1187 }
1188
1189 public void setPinned(boolean pinned) {
1190 mPinned = pinned;
1191 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001192 }
1193
Adam Powell24428412015-04-01 17:19:56 -07001194 /**
1195 * A single target as represented in the chooser.
1196 */
1197 public interface TargetInfo {
1198 /**
1199 * Get the resolved intent that represents this target. Note that this may not be the
1200 * intent that will be launched by calling one of the <code>start</code> methods provided;
1201 * this is the intent that will be credited with the launch.
1202 *
1203 * @return the resolved intent for this target
1204 */
Adam Powell23882512016-01-29 10:21:00 -08001205 Intent getResolvedIntent();
Adam Powell24428412015-04-01 17:19:56 -07001206
1207 /**
1208 * Get the resolved component name that represents this target. Note that this may not
1209 * be the component that will be directly launched by calling one of the <code>start</code>
1210 * methods provided; this is the component that will be credited with the launch.
1211 *
1212 * @return the resolved ComponentName for this target
1213 */
Adam Powell23882512016-01-29 10:21:00 -08001214 ComponentName getResolvedComponentName();
Adam Powell24428412015-04-01 17:19:56 -07001215
1216 /**
1217 * Start the activity referenced by this target.
1218 *
1219 * @param activity calling Activity performing the launch
1220 * @param options ActivityOptions bundle
1221 * @return true if the start completed successfully
1222 */
Adam Powell23882512016-01-29 10:21:00 -08001223 boolean start(Activity activity, Bundle options);
Adam Powell24428412015-04-01 17:19:56 -07001224
1225 /**
1226 * Start the activity referenced by this target as if the ResolverActivity's caller
1227 * was performing the start operation.
1228 *
1229 * @param activity calling Activity (actually) performing the launch
1230 * @param options ActivityOptions bundle
1231 * @param userId userId to start as or {@link UserHandle#USER_NULL} for activity's caller
1232 * @return true if the start completed successfully
1233 */
Adam Powell23882512016-01-29 10:21:00 -08001234 boolean startAsCaller(Activity activity, Bundle options, int userId);
Adam Powell24428412015-04-01 17:19:56 -07001235
1236 /**
1237 * Start the activity referenced by this target as a given user.
1238 *
1239 * @param activity calling activity performing the launch
1240 * @param options ActivityOptions bundle
1241 * @param user handle for the user to start the activity as
1242 * @return true if the start completed successfully
1243 */
Adam Powell23882512016-01-29 10:21:00 -08001244 boolean startAsUser(Activity activity, Bundle options, UserHandle user);
Adam Powell24428412015-04-01 17:19:56 -07001245
1246 /**
1247 * Return the ResolveInfo about how and why this target matched the original query
1248 * for available targets.
1249 *
1250 * @return ResolveInfo representing this target's match
1251 */
Adam Powell23882512016-01-29 10:21:00 -08001252 ResolveInfo getResolveInfo();
Adam Powell24428412015-04-01 17:19:56 -07001253
1254 /**
1255 * Return the human-readable text label for this target.
1256 *
1257 * @return user-visible target label
1258 */
Adam Powell23882512016-01-29 10:21:00 -08001259 CharSequence getDisplayLabel();
Adam Powell24428412015-04-01 17:19:56 -07001260
1261 /**
1262 * Return any extended info for this target. This may be used to disambiguate
1263 * otherwise identical targets.
1264 *
1265 * @return human-readable disambig string or null if none present
1266 */
Adam Powell23882512016-01-29 10:21:00 -08001267 CharSequence getExtendedInfo();
Adam Powell24428412015-04-01 17:19:56 -07001268
1269 /**
1270 * @return The drawable that should be used to represent this target
1271 */
Adam Powell23882512016-01-29 10:21:00 -08001272 Drawable getDisplayIcon();
Adam Powell2ed547e2015-04-29 18:45:04 -07001273
1274 /**
Adam Powell7d758002015-05-06 17:49:36 -07001275 * @return The (small) icon to badge the target with
1276 */
Adam Powell23882512016-01-29 10:21:00 -08001277 Drawable getBadgeIcon();
Adam Powell7d758002015-05-06 17:49:36 -07001278
1279 /**
Alan Viverettece5d92c2015-07-31 16:46:56 -04001280 * @return The content description for the badge icon
1281 */
Adam Powell23882512016-01-29 10:21:00 -08001282 CharSequence getBadgeContentDescription();
Alan Viverettece5d92c2015-07-31 16:46:56 -04001283
1284 /**
Adam Powell2ed547e2015-04-29 18:45:04 -07001285 * Clone this target with the given fill-in information.
1286 */
Adam Powell23882512016-01-29 10:21:00 -08001287 TargetInfo cloneFilledIn(Intent fillInIntent, int flags);
Adam Powell2ed547e2015-04-29 18:45:04 -07001288
1289 /**
1290 * @return the list of supported source intents deduped against this single target
1291 */
Adam Powell23882512016-01-29 10:21:00 -08001292 List<Intent> getAllSourceIntents();
1293
1294 /**
1295 * @return true if this target should be pinned to the front by the request of the user
1296 */
1297 boolean isPinned();
Adam Powell24428412015-04-01 17:19:56 -07001298 }
1299
Adam Powell23882512016-01-29 10:21:00 -08001300 public class ResolveListAdapter extends BaseAdapter {
Adam Powell7d758002015-05-06 17:49:36 -07001301 private final List<Intent> mIntents;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001302 private final Intent[] mInitialIntents;
1303 private final List<ResolveInfo> mBaseResolveList;
Hakan Seyalioglu33550122017-01-06 19:54:43 -08001304 protected ResolveInfo mLastChosen;
Adam Powell88831a22014-11-20 18:17:00 -08001305 private DisplayResolveInfo mOtherProfile;
Adam Powell24428412015-04-01 17:19:56 -07001306 private boolean mHasExtendedInfo;
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001307 private ResolverListController mResolverListController;
Hakan Seyalioglu23f34652017-02-03 09:38:35 -08001308 private int mPlaceholderCount;
Adam Powell24428412015-04-01 17:19:56 -07001309
1310 protected final LayoutInflater mInflater;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001311
Adam Powell2ed547e2015-04-29 18:45:04 -07001312 List<DisplayResolveInfo> mDisplayList;
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001313 List<ResolvedComponentInfo> mUnfilteredResolveList;
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -07001314
Adam Powell278902c2014-07-12 18:33:22 -07001315 private int mLastChosenPosition = -1;
1316 private boolean mFilterLastUsed;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001317
Adam Powell7d758002015-05-06 17:49:36 -07001318 public ResolveListAdapter(Context context, List<Intent> payloadIntents,
1319 Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid,
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001320 boolean filterLastUsed,
1321 ResolverListController resolverListController) {
Adam Powell7d758002015-05-06 17:49:36 -07001322 mIntents = payloadIntents;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001323 mInitialIntents = initialIntents;
1324 mBaseResolveList = rList;
Dianne Hackborn5320eb82012-05-18 12:05:04 -07001325 mLaunchedFromUid = launchedFromUid;
Adam Powelle9414d92014-07-05 17:44:18 -07001326 mInflater = LayoutInflater.from(context);
Adam Powell2ed547e2015-04-29 18:45:04 -07001327 mDisplayList = new ArrayList<>();
Adam Powell278902c2014-07-12 18:33:22 -07001328 mFilterLastUsed = filterLastUsed;
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001329 mResolverListController = resolverListController;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001330 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001331
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001332 public void handlePackagesChanged() {
1333 rebuildList();
Esteban Talavera6de72662014-12-11 17:54:07 +00001334 if (getCount() == 0) {
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001335 // We no longer have any items... just finish the activity.
1336 finish();
Adam Powellc5878612012-05-04 18:42:38 -07001337 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001338 }
1339
Hakan Seyalioglu23f34652017-02-03 09:38:35 -08001340 public void setPlaceholderCount(int count) {
1341 mPlaceholderCount = count;
1342 }
1343
Hakan Seyalioglu13405c52017-01-31 19:01:31 -08001344 @Nullable
Adam Powell278902c2014-07-12 18:33:22 -07001345 public DisplayResolveInfo getFilteredItem() {
1346 if (mFilterLastUsed && mLastChosenPosition >= 0) {
1347 // Not using getItem since it offsets to dodge this position for the list
Adam Powell2ed547e2015-04-29 18:45:04 -07001348 return mDisplayList.get(mLastChosenPosition);
Adam Powell278902c2014-07-12 18:33:22 -07001349 }
1350 return null;
1351 }
1352
Adam Powell88831a22014-11-20 18:17:00 -08001353 public DisplayResolveInfo getOtherProfile() {
1354 return mOtherProfile;
1355 }
1356
Adam Powell278902c2014-07-12 18:33:22 -07001357 public int getFilteredPosition() {
1358 if (mFilterLastUsed && mLastChosenPosition >= 0) {
1359 return mLastChosenPosition;
1360 }
1361 return AbsListView.INVALID_POSITION;
1362 }
1363
1364 public boolean hasFilteredItem() {
Hakan Seyalioglu33550122017-01-06 19:54:43 -08001365 return mFilterLastUsed && mLastChosen != null;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -07001366 }
1367
Adam Powella182e452015-07-06 16:57:56 -07001368 public float getScore(DisplayResolveInfo target) {
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001369 return mResolverListController.getScore(target);
Adam Powella182e452015-07-06 16:57:56 -07001370 }
1371
Kang Li0cef910d2017-01-05 09:14:36 -08001372 public void updateModel(ComponentName componentName) {
1373 mResolverListController.updateModel(componentName);
1374 }
1375
Kang Li9fa2a2c2017-01-06 13:33:24 -08001376 public void updateChooserCounts(String packageName, int userId, String action) {
1377 mResolverListController.updateChooserCounts(packageName, userId, action);
1378 }
1379
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001380 /**
1381 * Rebuild the list of resolvers. In some cases some parts will need some asynchronous work
1382 * to complete.
1383 *
1384 * @return Whether or not the list building is completed.
1385 */
1386 protected boolean rebuildList() {
Adam Powell2ed547e2015-04-29 18:45:04 -07001387 List<ResolvedComponentInfo> currentResolveList = null;
Sudheer Shanka7e64e102015-01-23 10:37:45 +00001388 // Clear the value of mOtherProfile from previous call.
1389 mOtherProfile = null;
Hakan Seyalioglu13405c52017-01-31 19:01:31 -08001390 mLastChosen = null;
1391 mLastChosenPosition = -1;
Adam Powell2ed547e2015-04-29 18:45:04 -07001392 mDisplayList.clear();
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001393 if (mBaseResolveList != null) {
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001394 currentResolveList = mUnfilteredResolveList = new ArrayList<>();
1395 mResolverListController.addResolveListDedupe(currentResolveList,
1396 getTargetIntent(),
1397 mBaseResolveList);
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001398 } else {
Hakan Seyalioglu33550122017-01-06 19:54:43 -08001399 currentResolveList = mUnfilteredResolveList =
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001400 mResolverListController.getResolversForIntent(shouldGetResolvedFilter(),
1401 shouldGetActivityMetadata(),
1402 mIntents);
1403 if (currentResolveList == null) {
1404 processSortedList(currentResolveList);
1405 return true;
Adam Powell2ed547e2015-04-29 18:45:04 -07001406 }
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001407 List<ResolvedComponentInfo> originalList =
1408 mResolverListController.filterIneligibleActivities(currentResolveList,
1409 true);
1410 if (originalList != null) {
1411 mUnfilteredResolveList = originalList;
Dianne Hackborn5320eb82012-05-18 12:05:04 -07001412 }
Jeff Hamiltond88e9aa2011-01-24 14:53:00 -06001413 }
Hakan Seyalioglu13405c52017-01-31 19:01:31 -08001414
1415 // So far we only support a single other profile at a time.
1416 // The first one we see gets special treatment.
1417 for (ResolvedComponentInfo info : currentResolveList) {
1418 if (info.getResolveInfoAt(0).targetUserId != UserHandle.USER_CURRENT) {
1419 mOtherProfile = new DisplayResolveInfo(info.getIntentAt(0),
1420 info.getResolveInfoAt(0),
1421 info.getResolveInfoAt(0).loadLabel(mPm),
1422 info.getResolveInfoAt(0).loadLabel(mPm),
1423 getReplacementIntent(info.getResolveInfoAt(0).activityInfo,
1424 info.getIntentAt(0)));
1425 currentResolveList.remove(info);
1426 break;
1427 }
1428 }
1429
1430 if (mOtherProfile == null) {
1431 try {
1432 mLastChosen = mResolverListController.getLastChosen();
1433 } catch (RemoteException re) {
1434 Log.d(TAG, "Error calling getLastChosenActivity\n" + re);
1435 }
1436 }
1437
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001438 int N;
You Kim43a5070e2012-11-21 23:23:45 +09001439 if ((currentResolveList != null) && ((N = currentResolveList.size()) > 0)) {
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001440 // We only care about fixing the unfilteredList if the current resolve list and
1441 // current resolve list are currently the same.
1442 List<ResolvedComponentInfo> originalList =
1443 mResolverListController.filterLowPriority(currentResolveList,
1444 mUnfilteredResolveList == currentResolveList);
1445 if (originalList != null) {
1446 mUnfilteredResolveList = originalList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001447 }
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001448
Hakan Seyalioglu873cbfd2017-02-21 19:23:43 -08001449 if (currentResolveList.size() > 1) {
Hakan Seyalioglu23f34652017-02-03 09:38:35 -08001450 setPlaceholderCount(currentResolveList.size());
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001451 AsyncTask<List<ResolvedComponentInfo>,
1452 Void,
1453 List<ResolvedComponentInfo>> sortingTask =
1454 new AsyncTask<List<ResolvedComponentInfo>,
1455 Void,
1456 List<ResolvedComponentInfo>>() {
1457 @Override
1458 protected List<ResolvedComponentInfo> doInBackground(
1459 List<ResolvedComponentInfo>... params) {
1460 mResolverListController.sort(params[0]);
1461 return params[0];
1462 }
1463
1464 @Override
1465 protected void onPostExecute(List<ResolvedComponentInfo> sortedComponents) {
1466 processSortedList(sortedComponents);
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001467 if (mProfileView != null) {
1468 bindProfileView();
1469 }
Hakan Seyalioglu23f34652017-02-03 09:38:35 -08001470 notifyDataSetChanged();
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001471 }
1472 };
1473 sortingTask.execute(currentResolveList);
Hakan Seyalioglu13405c52017-01-31 19:01:31 -08001474 postListReadyRunnable();
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001475 return false;
1476 } else {
1477 processSortedList(currentResolveList);
1478 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001479 }
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001480 } else {
1481 processSortedList(currentResolveList);
1482 return true;
1483 }
1484 }
1485
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001486 private void processSortedList(List<ResolvedComponentInfo> sortedComponents) {
1487 int N;
1488 if (sortedComponents != null && (N = sortedComponents.size()) != 0) {
Dianne Hackborneb034652009-09-07 00:49:58 -07001489 // First put the initial items at the top.
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001490 if (mInitialIntents != null) {
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001491 for (int i = 0; i < mInitialIntents.length; i++) {
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001492 Intent ii = mInitialIntents[i];
Dianne Hackborneb034652009-09-07 00:49:58 -07001493 if (ii == null) {
1494 continue;
1495 }
1496 ActivityInfo ai = ii.resolveActivityInfo(
1497 getPackageManager(), 0);
1498 if (ai == null) {
Adam Powell09a65602014-07-20 16:23:14 -07001499 Log.w(TAG, "No activity found for " + ii);
Dianne Hackborneb034652009-09-07 00:49:58 -07001500 continue;
1501 }
1502 ResolveInfo ri = new ResolveInfo();
1503 ri.activityInfo = ai;
Nicolas Prevot1a815922014-10-10 16:22:38 +01001504 UserManager userManager =
1505 (UserManager) getSystemService(Context.USER_SERVICE);
Dianne Hackborneb034652009-09-07 00:49:58 -07001506 if (ii instanceof LabeledIntent) {
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001507 LabeledIntent li = (LabeledIntent) ii;
Dianne Hackborneb034652009-09-07 00:49:58 -07001508 ri.resolvePackageName = li.getSourcePackage();
1509 ri.labelRes = li.getLabelResource();
1510 ri.nonLocalizedLabel = li.getNonLocalizedLabel();
1511 ri.icon = li.getIconResource();
Sudheer Shanka9ded7602015-05-19 21:17:25 +01001512 ri.iconResourceId = ri.icon;
1513 }
1514 if (userManager.isManagedProfile()) {
1515 ri.noResourceId = true;
1516 ri.icon = 0;
Dianne Hackborneb034652009-09-07 00:49:58 -07001517 }
Adam Powell2ed547e2015-04-29 18:45:04 -07001518 addResolveInfo(new DisplayResolveInfo(ii, ri,
Dianne Hackborneb034652009-09-07 00:49:58 -07001519 ri.loadLabel(getPackageManager()), null, ii));
1520 }
1521 }
You Kim43a5070e2012-11-21 23:23:45 +09001522
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001523 // Check for applications with same name and use application name or
1524 // package name if necessary
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001525 ResolvedComponentInfo rci0 = sortedComponents.get(0);
1526 ResolveInfo r0 = rci0.getResolveInfoAt(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001527 int start = 0;
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001528 CharSequence r0Label = r0.loadLabel(mPm);
Adam Powell24428412015-04-01 17:19:56 -07001529 mHasExtendedInfo = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001530 for (int i = 1; i < N; i++) {
1531 if (r0Label == null) {
1532 r0Label = r0.activityInfo.packageName;
1533 }
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001534 ResolvedComponentInfo rci = sortedComponents.get(i);
Adam Powell2ed547e2015-04-29 18:45:04 -07001535 ResolveInfo ri = rci.getResolveInfoAt(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001536 CharSequence riLabel = ri.loadLabel(mPm);
1537 if (riLabel == null) {
1538 riLabel = ri.activityInfo.packageName;
1539 }
1540 if (riLabel.equals(r0Label)) {
1541 continue;
1542 }
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001543 processGroup(sortedComponents, start, (i - 1), rci0, r0Label);
Adam Powell2ed547e2015-04-29 18:45:04 -07001544 rci0 = rci;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001545 r0 = ri;
1546 r0Label = riLabel;
1547 start = i;
1548 }
1549 // Process last group
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001550 processGroup(sortedComponents, start, (N - 1), rci0, r0Label);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001551 }
Hakan Seyalioglu33550122017-01-06 19:54:43 -08001552
Hakan Seyalioglu13405c52017-01-31 19:01:31 -08001553 postListReadyRunnable();
1554 }
1555
1556 /**
1557 * Some necessary methods for creating the list are initiated in onCreate and will also
1558 * determine the layout known. We therefore can't update the UI inline and post to the
1559 * handler thread to update after the current task is finished.
1560 */
1561 private void postListReadyRunnable() {
1562 if (mPostListReadyRunnable == null) {
1563 mPostListReadyRunnable = new Runnable() {
1564 @Override
1565 public void run() {
1566 setTitleAndIcon();
1567 resetAlwaysOrOnceButtonBar();
1568 onListRebuilt();
1569 mPostListReadyRunnable = null;
1570 }
1571 };
1572 getMainThreadHandler().post(mPostListReadyRunnable);
1573 }
Adam Powell24428412015-04-01 17:19:56 -07001574 }
1575
1576 public void onListRebuilt() {
Xiaohui Chen393c8012017-02-14 14:55:07 -08001577 int count = getUnfilteredCount();
1578 if (count == 1 && getOtherProfile() == null) {
1579 // Only one target, so we're a candidate to auto-launch!
1580 final TargetInfo target = targetInfoForPosition(0, false);
1581 if (shouldAutoLaunchSingleChoice(target)) {
1582 safelyStartActivity(target);
1583 finish();
1584 }
1585 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001586 }
1587
Adam Powellc6d5e3a2015-04-23 12:22:20 -07001588 public boolean shouldGetResolvedFilter() {
1589 return mFilterLastUsed;
1590 }
1591
Adam Powell2ed547e2015-04-29 18:45:04 -07001592 private void processGroup(List<ResolvedComponentInfo> rList, int start, int end,
1593 ResolvedComponentInfo ro, CharSequence roLabel) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001594 // Process labels from start to i
1595 int num = end - start+1;
1596 if (num == 1) {
1597 // No duplicate labels. Use label for entry at start
Adam Powell2ed547e2015-04-29 18:45:04 -07001598 addResolveInfoWithAlternates(ro, null, roLabel);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001599 } else {
Adam Powell24428412015-04-01 17:19:56 -07001600 mHasExtendedInfo = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001601 boolean usePkg = false;
Adam Powell00f4aad2015-09-17 13:38:16 -07001602 final ApplicationInfo ai = ro.getResolveInfoAt(0).activityInfo.applicationInfo;
1603 final CharSequence startApp = ai.loadLabel(mPm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001604 if (startApp == null) {
1605 usePkg = true;
1606 }
1607 if (!usePkg) {
1608 // Use HashSet to track duplicates
1609 HashSet<CharSequence> duplicates =
1610 new HashSet<CharSequence>();
1611 duplicates.add(startApp);
1612 for (int j = start+1; j <= end ; j++) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001613 ResolveInfo jRi = rList.get(j).getResolveInfoAt(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001614 CharSequence jApp = jRi.activityInfo.applicationInfo.loadLabel(mPm);
1615 if ( (jApp == null) || (duplicates.contains(jApp))) {
1616 usePkg = true;
1617 break;
1618 } else {
1619 duplicates.add(jApp);
1620 }
1621 }
1622 // Clear HashSet for later use
1623 duplicates.clear();
1624 }
1625 for (int k = start; k <= end; k++) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001626 final ResolvedComponentInfo rci = rList.get(k);
1627 final ResolveInfo add = rci.getResolveInfoAt(0);
1628 final CharSequence extraInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001629 if (usePkg) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001630 // Use package name for all entries from start to end-1
Adam Powell2ed547e2015-04-29 18:45:04 -07001631 extraInfo = add.activityInfo.packageName;
1632 } else {
1633 // Use application name for all entries from start to end-1
1634 extraInfo = add.activityInfo.applicationInfo.loadLabel(mPm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001635 }
Adam Powell2ed547e2015-04-29 18:45:04 -07001636 addResolveInfoWithAlternates(rci, extraInfo, roLabel);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001637 }
1638 }
1639 }
1640
Adam Powell2ed547e2015-04-29 18:45:04 -07001641 private void addResolveInfoWithAlternates(ResolvedComponentInfo rci,
1642 CharSequence extraInfo, CharSequence roLabel) {
1643 final int count = rci.getCount();
1644 final Intent intent = rci.getIntentAt(0);
1645 final ResolveInfo add = rci.getResolveInfoAt(0);
1646 final Intent replaceIntent = getReplacementIntent(add.activityInfo, intent);
1647 final DisplayResolveInfo dri = new DisplayResolveInfo(intent, add, roLabel,
1648 extraInfo, replaceIntent);
Adam Powell23882512016-01-29 10:21:00 -08001649 dri.setPinned(rci.isPinned());
Adam Powell2ed547e2015-04-29 18:45:04 -07001650 addResolveInfo(dri);
1651 if (replaceIntent == intent) {
1652 // Only add alternates if we didn't get a specific replacement from
1653 // the caller. If we have one it trumps potential alternates.
1654 for (int i = 1, N = count; i < N; i++) {
1655 final Intent altIntent = rci.getIntentAt(i);
1656 dri.addAlternateSourceIntent(altIntent);
1657 }
1658 }
1659 updateLastChosenPosition(add);
1660 }
1661
Esteban Talavera6de72662014-12-11 17:54:07 +00001662 private void updateLastChosenPosition(ResolveInfo info) {
Hakan Seyalioglu13405c52017-01-31 19:01:31 -08001663 // If another profile is present, ignore the last chosen entry.
1664 if (mOtherProfile != null) {
1665 mLastChosenPosition = -1;
1666 return;
1667 }
Esteban Talavera6de72662014-12-11 17:54:07 +00001668 if (mLastChosen != null
1669 && mLastChosen.activityInfo.packageName.equals(info.activityInfo.packageName)
1670 && mLastChosen.activityInfo.name.equals(info.activityInfo.name)) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001671 mLastChosenPosition = mDisplayList.size() - 1;
Esteban Talavera6de72662014-12-11 17:54:07 +00001672 }
1673 }
1674
Hakan Seyalioglu13405c52017-01-31 19:01:31 -08001675 // We assume that at this point we've already filtered out the only intent for a different
1676 // targetUserId which we're going to use.
Adam Powell88831a22014-11-20 18:17:00 -08001677 private void addResolveInfo(DisplayResolveInfo dri) {
Hakan Seyalioglu13405c52017-01-31 19:01:31 -08001678 if (dri.mResolveInfo.targetUserId == UserHandle.USER_CURRENT) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001679 mDisplayList.add(dri);
Adam Powell88831a22014-11-20 18:17:00 -08001680 }
1681 }
1682
Hakan Seyalioglu23f34652017-02-03 09:38:35 -08001683 @Nullable
Adam Powell278902c2014-07-12 18:33:22 -07001684 public ResolveInfo resolveInfoForPosition(int position, boolean filtered) {
Hakan Seyalioglu23f34652017-02-03 09:38:35 -08001685 TargetInfo target = targetInfoForPosition(position, filtered);
1686 if (target != null) {
1687 return target.getResolveInfo();
1688 }
1689 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001690 }
1691
Hakan Seyalioglu23f34652017-02-03 09:38:35 -08001692 @Nullable
Adam Powell24428412015-04-01 17:19:56 -07001693 public TargetInfo targetInfoForPosition(int position, boolean filtered) {
Hakan Seyalioglu23f34652017-02-03 09:38:35 -08001694 if (filtered) {
1695 return getItem(position);
1696 }
1697 if (mDisplayList.size() > position) {
1698 return mDisplayList.get(position);
1699 }
1700 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001701 }
1702
1703 public int getCount() {
Hakan Seyalioglu23f34652017-02-03 09:38:35 -08001704 int totalSize = mDisplayList == null || mDisplayList.isEmpty() ? mPlaceholderCount :
1705 mDisplayList.size();
Adam Powell278902c2014-07-12 18:33:22 -07001706 if (mFilterLastUsed && mLastChosenPosition >= 0) {
Hakan Seyalioglu23f34652017-02-03 09:38:35 -08001707 totalSize--;
Adam Powell278902c2014-07-12 18:33:22 -07001708 }
Hakan Seyalioglu23f34652017-02-03 09:38:35 -08001709 return totalSize;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001710 }
1711
Adam Powell50077352015-05-26 18:01:55 -07001712 public int getUnfilteredCount() {
1713 return mDisplayList.size();
1714 }
1715
1716 public int getDisplayInfoCount() {
1717 return mDisplayList.size();
1718 }
1719
1720 public DisplayResolveInfo getDisplayInfoAt(int index) {
1721 return mDisplayList.get(index);
1722 }
1723
Hakan Seyalioglu23f34652017-02-03 09:38:35 -08001724 @Nullable
Adam Powell24428412015-04-01 17:19:56 -07001725 public TargetInfo getItem(int position) {
Adam Powell278902c2014-07-12 18:33:22 -07001726 if (mFilterLastUsed && mLastChosenPosition >= 0 && position >= mLastChosenPosition) {
1727 position++;
1728 }
Hakan Seyalioglu23f34652017-02-03 09:38:35 -08001729 if (mDisplayList.size() > position) {
1730 return mDisplayList.get(position);
1731 } else {
1732 return null;
1733 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001734 }
1735
1736 public long getItemId(int position) {
1737 return position;
1738 }
1739
Adam Powell24428412015-04-01 17:19:56 -07001740 public boolean hasExtendedInfo() {
1741 return mHasExtendedInfo;
1742 }
1743
1744 public boolean hasResolvedTarget(ResolveInfo info) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001745 for (int i = 0, N = mDisplayList.size(); i < N; i++) {
Adam Powellc412be62015-06-24 13:54:10 -07001746 if (resolveInfoMatch(info, mDisplayList.get(i).getResolveInfo())) {
Adam Powell24428412015-04-01 17:19:56 -07001747 return true;
1748 }
1749 }
1750 return false;
1751 }
1752
Adam Powell23882512016-01-29 10:21:00 -08001753 public int getDisplayResolveInfoCount() {
Adam Powell2ed547e2015-04-29 18:45:04 -07001754 return mDisplayList.size();
Adam Powell24428412015-04-01 17:19:56 -07001755 }
1756
Adam Powell23882512016-01-29 10:21:00 -08001757 public DisplayResolveInfo getDisplayResolveInfo(int index) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001758 // Used to query services. We only query services for primary targets, not alternates.
1759 return mDisplayList.get(index);
Adam Powell24428412015-04-01 17:19:56 -07001760 }
1761
1762 public final View getView(int position, View convertView, ViewGroup parent) {
Adam Powellfd1e93d2014-09-07 16:52:22 -07001763 View view = convertView;
1764 if (view == null) {
Adam Powell24428412015-04-01 17:19:56 -07001765 view = createView(parent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001766 }
Adam Powell7d758002015-05-06 17:49:36 -07001767 onBindView(view, getItem(position));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001768 return view;
1769 }
1770
Adam Powell7d758002015-05-06 17:49:36 -07001771 public final View createView(ViewGroup parent) {
1772 final View view = onCreateView(parent);
1773 final ViewHolder holder = new ViewHolder(view);
1774 view.setTag(holder);
1775 return view;
1776 }
1777
1778 public View onCreateView(ViewGroup parent) {
Adam Powell24428412015-04-01 17:19:56 -07001779 return mInflater.inflate(
1780 com.android.internal.R.layout.resolve_list_item, parent, false);
1781 }
1782
1783 public boolean showsExtendedInfo(TargetInfo info) {
1784 return !TextUtils.isEmpty(info.getExtendedInfo());
1785 }
1786
Adam Powell23882512016-01-29 10:21:00 -08001787 public boolean isComponentPinned(ComponentName name) {
1788 return false;
1789 }
1790
Adam Powell7d758002015-05-06 17:49:36 -07001791 public final void bindView(int position, View view) {
1792 onBindView(view, getItem(position));
1793 }
1794
1795 private void onBindView(View view, TargetInfo info) {
Adam Powell0256c6f2013-05-29 16:42:33 -07001796 final ViewHolder holder = (ViewHolder) view.getTag();
Hakan Seyalioglu23f34652017-02-03 09:38:35 -08001797 if (info == null) {
1798 holder.icon.setImageDrawable(
1799 getDrawable(R.drawable.resolver_icon_placeholder));
1800 return;
1801 }
Adam Powell63b31692015-09-28 10:45:00 -07001802 final CharSequence label = info.getDisplayLabel();
1803 if (!TextUtils.equals(holder.text.getText(), label)) {
1804 holder.text.setText(info.getDisplayLabel());
1805 }
Adam Powell24428412015-04-01 17:19:56 -07001806 if (showsExtendedInfo(info)) {
Adam Powell0256c6f2013-05-29 16:42:33 -07001807 holder.text2.setVisibility(View.VISIBLE);
Adam Powell24428412015-04-01 17:19:56 -07001808 holder.text2.setText(info.getExtendedInfo());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001809 } else {
Adam Powell0256c6f2013-05-29 16:42:33 -07001810 holder.text2.setVisibility(View.GONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001811 }
Adam Powell24428412015-04-01 17:19:56 -07001812 if (info instanceof DisplayResolveInfo
1813 && !((DisplayResolveInfo) info).hasDisplayIcon()) {
1814 new LoadAdapterIconTask((DisplayResolveInfo) info).execute();
Dianne Hackborneb034652009-09-07 00:49:58 -07001815 }
Adam Powell24428412015-04-01 17:19:56 -07001816 holder.icon.setImageDrawable(info.getDisplayIcon());
Adam Powell7d758002015-05-06 17:49:36 -07001817 if (holder.badge != null) {
1818 final Drawable badge = info.getBadgeIcon();
1819 if (badge != null) {
1820 holder.badge.setImageDrawable(badge);
Alan Viverettece5d92c2015-07-31 16:46:56 -04001821 holder.badge.setContentDescription(info.getBadgeContentDescription());
Adam Powell7d758002015-05-06 17:49:36 -07001822 holder.badge.setVisibility(View.VISIBLE);
1823 } else {
1824 holder.badge.setVisibility(View.GONE);
1825 }
1826 }
Adam Powell0256c6f2013-05-29 16:42:33 -07001827 }
1828 }
1829
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001830 @VisibleForTesting
1831 public static final class ResolvedComponentInfo {
Adam Powell2ed547e2015-04-29 18:45:04 -07001832 public final ComponentName name;
Adam Powell23882512016-01-29 10:21:00 -08001833 private boolean mPinned;
Adam Powell2ed547e2015-04-29 18:45:04 -07001834 private final List<Intent> mIntents = new ArrayList<>();
1835 private final List<ResolveInfo> mResolveInfos = new ArrayList<>();
1836
1837 public ResolvedComponentInfo(ComponentName name, Intent intent, ResolveInfo info) {
1838 this.name = name;
1839 add(intent, info);
1840 }
1841
1842 public void add(Intent intent, ResolveInfo info) {
1843 mIntents.add(intent);
1844 mResolveInfos.add(info);
1845 }
1846
1847 public int getCount() {
1848 return mIntents.size();
1849 }
1850
1851 public Intent getIntentAt(int index) {
1852 return index >= 0 ? mIntents.get(index) : null;
1853 }
1854
1855 public ResolveInfo getResolveInfoAt(int index) {
1856 return index >= 0 ? mResolveInfos.get(index) : null;
1857 }
1858
1859 public int findIntent(Intent intent) {
1860 for (int i = 0, N = mIntents.size(); i < N; i++) {
1861 if (intent.equals(mIntents.get(i))) {
1862 return i;
1863 }
1864 }
1865 return -1;
1866 }
1867
1868 public int findResolveInfo(ResolveInfo info) {
1869 for (int i = 0, N = mResolveInfos.size(); i < N; i++) {
1870 if (info.equals(mResolveInfos.get(i))) {
1871 return i;
1872 }
1873 }
1874 return -1;
1875 }
Adam Powell23882512016-01-29 10:21:00 -08001876
1877 public boolean isPinned() {
1878 return mPinned;
1879 }
1880
1881 public void setPinned(boolean pinned) {
1882 mPinned = pinned;
1883 }
Adam Powell2ed547e2015-04-29 18:45:04 -07001884 }
1885
Adam Powell0256c6f2013-05-29 16:42:33 -07001886 static class ViewHolder {
1887 public TextView text;
1888 public TextView text2;
1889 public ImageView icon;
Adam Powell7d758002015-05-06 17:49:36 -07001890 public ImageView badge;
Adam Powell0256c6f2013-05-29 16:42:33 -07001891
1892 public ViewHolder(View view) {
1893 text = (TextView) view.findViewById(com.android.internal.R.id.text1);
1894 text2 = (TextView) view.findViewById(com.android.internal.R.id.text2);
1895 icon = (ImageView) view.findViewById(R.id.icon);
Adam Powell7d758002015-05-06 17:49:36 -07001896 badge = (ImageView) view.findViewById(R.id.target_badge);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001897 }
1898 }
1899
Adam Powell7d758002015-05-06 17:49:36 -07001900 class ItemClickListener implements AdapterView.OnItemClickListener,
1901 AdapterView.OnItemLongClickListener {
1902 @Override
1903 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
1904 final ListView listView = parent instanceof ListView ? (ListView) parent : null;
1905 if (listView != null) {
1906 position -= listView.getHeaderViewsCount();
1907 }
1908 if (position < 0) {
1909 // Header views don't count.
1910 return;
1911 }
Hakan Seyalioglu23f34652017-02-03 09:38:35 -08001912 // If we're still loading, we can't yet enable the buttons.
1913 if (mAdapter.resolveInfoForPosition(position, true) == null) {
1914 return;
1915 }
1916
Adam Powell7d758002015-05-06 17:49:36 -07001917 final int checkedPos = mAdapterView.getCheckedItemPosition();
1918 final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
Hakan Seyalioglu13405c52017-01-31 19:01:31 -08001919 if (!useLayoutWithDefault()
1920 && (!hasValidSelection || mLastSelected != checkedPos)) {
Adam Powell7d758002015-05-06 17:49:36 -07001921 setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
1922 mOnceButton.setEnabled(hasValidSelection);
1923 if (hasValidSelection) {
1924 mAdapterView.smoothScrollToPosition(checkedPos);
1925 }
1926 mLastSelected = checkedPos;
1927 } else {
1928 startSelected(position, false, true);
1929 }
1930 }
Adam Powell2d809622012-03-22 15:24:43 -07001931
1932 @Override
1933 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Adam Powell7d758002015-05-06 17:49:36 -07001934 final ListView listView = parent instanceof ListView ? (ListView) parent : null;
1935 if (listView != null) {
1936 position -= listView.getHeaderViewsCount();
Adam Powell24428412015-04-01 17:19:56 -07001937 }
Adam Powellfd1e93d2014-09-07 16:52:22 -07001938 if (position < 0) {
1939 // Header views don't count.
1940 return false;
1941 }
Adam Powell278902c2014-07-12 18:33:22 -07001942 ResolveInfo ri = mAdapter.resolveInfoForPosition(position, true);
Adam Powell23882512016-01-29 10:21:00 -08001943 showTargetDetails(ri);
Adam Powell2d809622012-03-22 15:24:43 -07001944 return true;
1945 }
1946
1947 }
Adam Powell0256c6f2013-05-29 16:42:33 -07001948
Adam Powell24428412015-04-01 17:19:56 -07001949 abstract class LoadIconTask extends AsyncTask<Void, Void, Drawable> {
1950 protected final DisplayResolveInfo mDisplayResolveInfo;
1951 private final ResolveInfo mResolveInfo;
1952
1953 public LoadIconTask(DisplayResolveInfo dri) {
1954 mDisplayResolveInfo = dri;
1955 mResolveInfo = dri.getResolveInfo();
Adam Powell0256c6f2013-05-29 16:42:33 -07001956 }
1957
1958 @Override
Adam Powell24428412015-04-01 17:19:56 -07001959 protected Drawable doInBackground(Void... params) {
1960 return loadIconForResolveInfo(mResolveInfo);
1961 }
1962
1963 @Override
1964 protected void onPostExecute(Drawable d) {
1965 mDisplayResolveInfo.setDisplayIcon(d);
1966 }
1967 }
1968
1969 class LoadAdapterIconTask extends LoadIconTask {
1970 public LoadAdapterIconTask(DisplayResolveInfo dri) {
1971 super(dri);
1972 }
1973
1974 @Override
1975 protected void onPostExecute(Drawable d) {
1976 super.onPostExecute(d);
1977 if (mProfileView != null && mAdapter.getOtherProfile() == mDisplayResolveInfo) {
Adam Powell88831a22014-11-20 18:17:00 -08001978 bindProfileView();
1979 }
Adam Powell0256c6f2013-05-29 16:42:33 -07001980 mAdapter.notifyDataSetChanged();
1981 }
1982 }
Adam Powell278902c2014-07-12 18:33:22 -07001983
Adam Powell24428412015-04-01 17:19:56 -07001984 class LoadIconIntoViewTask extends LoadIconTask {
1985 private final ImageView mTargetView;
Adam Powell278902c2014-07-12 18:33:22 -07001986
Adam Powell24428412015-04-01 17:19:56 -07001987 public LoadIconIntoViewTask(DisplayResolveInfo dri, ImageView target) {
1988 super(dri);
Adam Powell278902c2014-07-12 18:33:22 -07001989 mTargetView = target;
1990 }
1991
1992 @Override
Adam Powell24428412015-04-01 17:19:56 -07001993 protected void onPostExecute(Drawable d) {
1994 super.onPostExecute(d);
1995 mTargetView.setImageDrawable(d);
Adam Powell278902c2014-07-12 18:33:22 -07001996 }
1997 }
Adam Powell09a65602014-07-20 16:23:14 -07001998
Dianne Hackbornec452d92014-11-11 17:16:56 -08001999 static final boolean isSpecificUriMatch(int match) {
2000 match = match&IntentFilter.MATCH_CATEGORY_MASK;
2001 return match >= IntentFilter.MATCH_CATEGORY_HOST
2002 && match <= IntentFilter.MATCH_CATEGORY_PATH;
2003 }
2004
Adam Powell4c470d62015-06-19 17:46:17 -07002005 static class PickTargetOptionRequest extends PickOptionRequest {
2006 public PickTargetOptionRequest(@Nullable Prompt prompt, Option[] options,
2007 @Nullable Bundle extras) {
2008 super(prompt, options, extras);
2009 }
2010
2011 @Override
2012 public void onCancel() {
2013 super.onCancel();
2014 final ResolverActivity ra = (ResolverActivity) getActivity();
2015 if (ra != null) {
2016 ra.mPickOptionRequest = null;
2017 ra.finish();
2018 }
2019 }
2020
2021 @Override
2022 public void onPickOptionResult(boolean finished, Option[] selections, Bundle result) {
2023 super.onPickOptionResult(finished, selections, result);
2024 if (selections.length != 1) {
2025 // TODO In a better world we would filter the UI presented here and let the
2026 // user refine. Maybe later.
2027 return;
2028 }
2029
2030 final ResolverActivity ra = (ResolverActivity) getActivity();
2031 if (ra != null) {
2032 final TargetInfo ti = ra.mAdapter.getItem(selections[0].getIndex());
2033 if (ra.onTargetSelected(ti, false)) {
2034 ra.mPickOptionRequest = null;
2035 ra.finish();
2036 }
2037 }
2038 }
2039 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002040}