blob: 2e80896190e8dcb6cccfad39bbc0527d5353fa5d [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.app;
18
Adam Powelle9414d92014-07-05 17:44:18 -070019import android.app.Activity;
Dianne Hackborn028ceeb2014-08-17 17:45:48 -070020import android.app.ActivityThread;
Adam Powell09a65602014-07-20 16:23:14 -070021import android.app.usage.UsageStats;
22import android.app.usage.UsageStatsManager;
Adam Powell0256c6f2013-05-29 16:42:33 -070023import android.os.AsyncTask;
Dianne Hackborn028ceeb2014-08-17 17:45:48 -070024import android.provider.Settings;
Adam Powell11f59a02014-08-20 13:22:16 -070025import android.text.TextUtils;
Dianne Hackborn028ceeb2014-08-17 17:45:48 -070026import android.util.Slog;
Adam Powell278902c2014-07-12 18:33:22 -070027import android.widget.AbsListView;
Adam Powell24428412015-04-01 17:19:56 -070028import android.widget.GridView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import com.android.internal.R;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -080030import com.android.internal.content.PackageMonitor;
31
Adam Powellc5878612012-05-04 18:42:38 -070032import android.app.ActivityManager;
Dianne Hackborn5320eb82012-05-18 12:05:04 -070033import android.app.ActivityManagerNative;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -070034import android.app.AppGlobals;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.content.ComponentName;
36import android.content.Context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.content.Intent;
38import android.content.IntentFilter;
39import android.content.pm.ActivityInfo;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010040import android.content.pm.ApplicationInfo;
Dianne Hackborneb034652009-09-07 00:49:58 -070041import android.content.pm.LabeledIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.content.pm.PackageManager;
Adam Powellc5878612012-05-04 18:42:38 -070043import android.content.pm.PackageManager.NameNotFoundException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import android.content.pm.ResolveInfo;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010045import android.content.pm.UserInfo;
Adam Powellc5878612012-05-04 18:42:38 -070046import android.content.res.Resources;
Dianne Hackborneb034652009-09-07 00:49:58 -070047import android.graphics.drawable.Drawable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.net.Uri;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010049import android.os.Build;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.os.Bundle;
51import android.os.PatternMatcher;
Dianne Hackborn5320eb82012-05-18 12:05:04 -070052import android.os.RemoteException;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070053import android.os.UserHandle;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010054import android.os.UserManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.util.Log;
Adam Powell2d809622012-03-22 15:24:43 -070056import android.view.LayoutInflater;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057import android.view.View;
58import android.view.ViewGroup;
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;
Adam Powell4f6c2052014-07-07 18:49:10 -070066import com.android.internal.widget.ResolverDrawerLayout;
Adam Powell2d809622012-03-22 15:24:43 -070067
Adam Powell09a65602014-07-20 16:23:14 -070068import java.text.Collator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069import java.util.ArrayList;
70import java.util.Collections;
Adam Powell09a65602014-07-20 16:23:14 -070071import java.util.Comparator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072import java.util.HashSet;
73import java.util.Iterator;
74import java.util.List;
Adam Lesinskicc562a82014-08-27 11:52:52 -070075import java.util.Map;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076import java.util.Set;
77
Adrian Roos27c3ab62014-10-15 17:21:31 +020078import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
79import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
80
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081/**
82 * This activity is displayed when the system attempts to start an Intent for
83 * which there is more than one matching activity, allowing the user to decide
84 * which to go to. It is not normally used directly by application developers.
85 */
Adam Powelle9414d92014-07-05 17:44:18 -070086public class ResolverActivity extends Activity implements AdapterView.OnItemClickListener {
Adam Powellc5878612012-05-04 18:42:38 -070087 private static final String TAG = "ResolverActivity";
Adam Powell09a65602014-07-20 16:23:14 -070088 private static final boolean DEBUG = false;
Adam Powellc5878612012-05-04 18:42:38 -070089
Dianne Hackborn5320eb82012-05-18 12:05:04 -070090 private int mLaunchedFromUid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091 private ResolveListAdapter mAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092 private PackageManager mPm;
Dianne Hackborn028ceeb2014-08-17 17:45:48 -070093 private boolean mSafeForwardingMode;
Adam Powellc5878612012-05-04 18:42:38 -070094 private boolean mAlwaysUseOption;
Adam Powell24428412015-04-01 17:19:56 -070095 private AbsListView mAdapterView;
Adam Powellfd1e93d2014-09-07 16:52:22 -070096 private ListView mListView;
Adam Powell24428412015-04-01 17:19:56 -070097 private GridView mGridView;
Adam Powellc5878612012-05-04 18:42:38 -070098 private Button mAlwaysButton;
99 private Button mOnceButton;
Adam Powell88831a22014-11-20 18:17:00 -0800100 private View mProfileView;
Adam Powellc5878612012-05-04 18:42:38 -0700101 private int mIconDpi;
Adam Powell24428412015-04-01 17:19:56 -0700102 private int mLastSelected = AbsListView.INVALID_POSITION;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100103 private boolean mResolvingHome = false;
Sander Alewijnsef6545332014-10-31 12:39:02 +0000104 private int mProfileSwitchMessageId = -1;
Adam Powell2ed547e2015-04-29 18:45:04 -0700105 private final ArrayList<Intent> mIntents = new ArrayList<>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106
Adam Powell09a65602014-07-20 16:23:14 -0700107 private UsageStatsManager mUsm;
Adam Lesinskicc562a82014-08-27 11:52:52 -0700108 private Map<String, UsageStats> mStats;
Adam Powell09a65602014-07-20 16:23:14 -0700109 private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 14;
110
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700111 private boolean mRegistered;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800112 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
113 @Override public void onSomePackagesChanged() {
114 mAdapter.handlePackagesChanged();
Adam Powell88831a22014-11-20 18:17:00 -0800115 if (mProfileView != null) {
116 bindProfileView();
117 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800118 }
119 };
120
Adam Powell278902c2014-07-12 18:33:22 -0700121 private enum ActionTitle {
122 VIEW(Intent.ACTION_VIEW,
123 com.android.internal.R.string.whichViewApplication,
124 com.android.internal.R.string.whichViewApplicationNamed),
125 EDIT(Intent.ACTION_EDIT,
126 com.android.internal.R.string.whichEditApplication,
127 com.android.internal.R.string.whichEditApplicationNamed),
128 SEND(Intent.ACTION_SEND,
129 com.android.internal.R.string.whichSendApplication,
130 com.android.internal.R.string.whichSendApplicationNamed),
131 SENDTO(Intent.ACTION_SENDTO,
132 com.android.internal.R.string.whichSendApplication,
133 com.android.internal.R.string.whichSendApplicationNamed),
134 SEND_MULTIPLE(Intent.ACTION_SEND_MULTIPLE,
135 com.android.internal.R.string.whichSendApplication,
136 com.android.internal.R.string.whichSendApplicationNamed),
137 DEFAULT(null,
138 com.android.internal.R.string.whichApplication,
Adam Powella35c77a2014-09-25 16:46:36 -0700139 com.android.internal.R.string.whichApplicationNamed),
140 HOME(Intent.ACTION_MAIN,
141 com.android.internal.R.string.whichHomeApplication,
142 com.android.internal.R.string.whichHomeApplicationNamed);
Adam Powell278902c2014-07-12 18:33:22 -0700143
144 public final String action;
145 public final int titleRes;
146 public final int namedTitleRes;
147
148 ActionTitle(String action, int titleRes, int namedTitleRes) {
149 this.action = action;
150 this.titleRes = titleRes;
151 this.namedTitleRes = namedTitleRes;
152 }
153
154 public static ActionTitle forAction(String action) {
155 for (ActionTitle title : values()) {
Adam Powella35c77a2014-09-25 16:46:36 -0700156 if (title != HOME && action != null && action.equals(title.action)) {
Adam Powell278902c2014-07-12 18:33:22 -0700157 return title;
158 }
159 }
160 return DEFAULT;
161 }
162 }
163
Dianne Hackborn905577f2011-09-07 18:31:28 -0700164 private Intent makeMyIntent() {
165 Intent intent = new Intent(getIntent());
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700166 intent.setComponent(null);
Dianne Hackborn905577f2011-09-07 18:31:28 -0700167 // The resolver activity is set to be hidden from recent tasks.
168 // we don't want this attribute to be propagated to the next activity
169 // being launched. Note that if the original Intent also had this
170 // flag set, we are now losing it. That should be a very rare case
171 // and we can live with this.
172 intent.setFlags(intent.getFlags()&~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
173 return intent;
174 }
175
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176 @Override
177 protected void onCreate(Bundle savedInstanceState) {
Christopher Tateb72b3632013-09-30 17:50:32 -0700178 // Use a specialized prompt when we're handling the 'Home' app startActivity()
Christopher Tateb72b3632013-09-30 17:50:32 -0700179 final Intent intent = makeMyIntent();
180 final Set<String> categories = intent.getCategories();
181 if (Intent.ACTION_MAIN.equals(intent.getAction())
182 && categories != null
183 && categories.size() == 1
184 && categories.contains(Intent.CATEGORY_HOME)) {
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100185 // Note: this field is not set to true in the compatibility version.
186 mResolvingHome = true;
Christopher Tateb72b3632013-09-30 17:50:32 -0700187 }
188
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700189 setSafeForwardingMode(true);
190
Adam Powella35c77a2014-09-25 16:46:36 -0700191 onCreate(savedInstanceState, intent, null, 0, null, null, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 }
193
Adam Powell278902c2014-07-12 18:33:22 -0700194 /**
Adam Powell24428412015-04-01 17:19:56 -0700195 * Compatibility version for other bundled services that use this overload without
Adam Powell278902c2014-07-12 18:33:22 -0700196 * a default title resource
197 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 protected void onCreate(Bundle savedInstanceState, Intent intent,
Adam Powell278902c2014-07-12 18:33:22 -0700199 CharSequence title, Intent[] initialIntents,
200 List<ResolveInfo> rList, boolean alwaysUseOption) {
Adam Powell09a65602014-07-20 16:23:14 -0700201 onCreate(savedInstanceState, intent, title, 0, initialIntents, rList, alwaysUseOption);
Adam Powell278902c2014-07-12 18:33:22 -0700202 }
203
204 protected void onCreate(Bundle savedInstanceState, Intent intent,
205 CharSequence title, int defaultTitleRes, Intent[] initialIntents,
206 List<ResolveInfo> rList, boolean alwaysUseOption) {
Adam Powelle9414d92014-07-05 17:44:18 -0700207 setTheme(R.style.Theme_DeviceDefault_Resolver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800208 super.onCreate(savedInstanceState);
Sander Alewijnsef6545332014-10-31 12:39:02 +0000209
210 // Determine whether we should show that intent is forwarded
211 // from managed profile to owner or other way around.
212 setProfileSwitchMessageId(intent.getContentUserHint());
213
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700214 try {
215 mLaunchedFromUid = ActivityManagerNative.getDefault().getLaunchedFromUid(
216 getActivityToken());
217 } catch (RemoteException e) {
218 mLaunchedFromUid = -1;
219 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 mPm = getPackageManager();
Adam Powell09a65602014-07-20 16:23:14 -0700221 mUsm = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
222
223 final long sinceTime = System.currentTimeMillis() - USAGE_STATS_PERIOD;
Adam Lesinski35168002014-07-21 15:25:30 -0700224 mStats = mUsm.queryAndAggregateUsageStats(sinceTime, System.currentTimeMillis());
Adam Powell09a65602014-07-20 16:23:14 -0700225
Dianne Hackbornd0d75032012-04-19 23:12:09 -0700226 mPackageMonitor.register(this, getMainLooper(), false);
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700227 mRegistered = true;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800228
Adam Powellc5878612012-05-04 18:42:38 -0700229 final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
230 mIconDpi = am.getLauncherLargeIconDensity();
Adam Powellc5878612012-05-04 18:42:38 -0700231
Adam Powell2ed547e2015-04-29 18:45:04 -0700232 mIntents.add(0, new Intent(intent));
Adam Powell24428412015-04-01 17:19:56 -0700233 mAdapter = createAdapter(this, initialIntents, rList, mLaunchedFromUid, alwaysUseOption);
Adam Powell278902c2014-07-12 18:33:22 -0700234
235 final int layoutId;
Adam Powellfd1e93d2014-09-07 16:52:22 -0700236 final boolean useHeader;
Adam Powell278902c2014-07-12 18:33:22 -0700237 if (mAdapter.hasFilteredItem()) {
238 layoutId = R.layout.resolver_list_with_default;
239 alwaysUseOption = false;
Adam Powellfd1e93d2014-09-07 16:52:22 -0700240 useHeader = true;
Adam Powell278902c2014-07-12 18:33:22 -0700241 } else {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700242 useHeader = false;
Adam Powell24428412015-04-01 17:19:56 -0700243 layoutId = getLayoutResource();
Adam Powell278902c2014-07-12 18:33:22 -0700244 }
245 mAlwaysUseOption = alwaysUseOption;
246
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700247 if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) {
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700248 // Gulp!
249 finish();
250 return;
Esteban Talavera6de72662014-12-11 17:54:07 +0000251 }
252
Adam Powell2ed547e2015-04-29 18:45:04 -0700253 int count = mAdapter.mDisplayList.size();
Esteban Talavera6de72662014-12-11 17:54:07 +0000254 if (count > 1 || (count == 1 && mAdapter.getOtherProfile() != null)) {
Adam Powell278902c2014-07-12 18:33:22 -0700255 setContentView(layoutId);
Adam Powell24428412015-04-01 17:19:56 -0700256 mAdapterView = (AbsListView) findViewById(R.id.resolver_list);
257 mAdapterView.setAdapter(mAdapter);
258 mAdapterView.setOnItemClickListener(this);
259 mAdapterView.setOnItemLongClickListener(new ItemLongClickListener());
Adam Powellc5878612012-05-04 18:42:38 -0700260
Adam Powell24428412015-04-01 17:19:56 -0700261 // Initialize the different types of collection views we may have. Depending
262 // on which ones are initialized later we'll configure different properties.
263 if (mAdapterView instanceof ListView) {
264 mListView = (ListView) mAdapterView;
265 }
266 if (mAdapterView instanceof GridView) {
267 mGridView = (GridView) mAdapterView;
Adam Powellc5878612012-05-04 18:42:38 -0700268 }
Adam Powelle9414d92014-07-05 17:44:18 -0700269
Adam Powell24428412015-04-01 17:19:56 -0700270 if (alwaysUseOption) {
271 mAdapterView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
272 }
273
274 if (useHeader && mListView != null) {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700275 mListView.addHeaderView(LayoutInflater.from(this).inflate(
276 R.layout.resolver_different_item_header, mListView, false));
277 }
Mike Lockwood02eb8742011-02-27 09:10:37 -0800278 } else if (count == 1) {
Adam Powell24428412015-04-01 17:19:56 -0700279 safelyStartActivity(mAdapter.targetInfoForPosition(0, false));
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700280 mPackageMonitor.unregister();
281 mRegistered = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 finish();
283 return;
284 } else {
Adam Powelle9414d92014-07-05 17:44:18 -0700285 setContentView(R.layout.resolver_list);
286
287 final TextView empty = (TextView) findViewById(R.id.empty);
288 empty.setVisibility(View.VISIBLE);
289
Adam Powell24428412015-04-01 17:19:56 -0700290 mAdapterView = (AbsListView) findViewById(R.id.resolver_list);
291 mAdapterView.setVisibility(View.GONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 }
Adrian Roos27c3ab62014-10-15 17:21:31 +0200293 // Prevent the Resolver window from becoming the top fullscreen window and thus from taking
294 // control of the system bars.
295 getWindow().clearFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296
Adam Powell4f6c2052014-07-07 18:49:10 -0700297 final ResolverDrawerLayout rdl = (ResolverDrawerLayout) findViewById(R.id.contentPanel);
298 if (rdl != null) {
Adam Powell5dd072d2014-10-30 19:51:41 -0700299 rdl.setOnDismissedListener(new ResolverDrawerLayout.OnDismissedListener() {
Adam Powell4f6c2052014-07-07 18:49:10 -0700300 @Override
Adam Powell5dd072d2014-10-30 19:51:41 -0700301 public void onDismissed() {
Adam Powell4f6c2052014-07-07 18:49:10 -0700302 finish();
303 }
304 });
305 }
306
Adam Powell11f59a02014-08-20 13:22:16 -0700307 if (title == null) {
308 title = getTitleForAction(intent.getAction(), defaultTitleRes);
309 }
310 if (!TextUtils.isEmpty(title)) {
311 final TextView titleView = (TextView) findViewById(R.id.title);
312 if (titleView != null) {
313 titleView.setText(title);
Adam Powell278902c2014-07-12 18:33:22 -0700314 }
Adam Powell11f59a02014-08-20 13:22:16 -0700315 setTitle(title);
Adam Powell24428412015-04-01 17:19:56 -0700316
317 // Try to initialize the title icon if we have a view for it and a title to match
318 final ImageView titleIcon = (ImageView) findViewById(R.id.title_icon);
319 if (titleIcon != null) {
320 final String referrerPackage = getReferrerPackageName();
321 ApplicationInfo ai = null;
322 try {
323 if (!TextUtils.isEmpty(referrerPackage)) {
324 ai = mPm.getApplicationInfo(referrerPackage, 0);
325 }
326 } catch (NameNotFoundException e) {
327 Log.e(TAG, "Could not find referrer package " + referrerPackage);
328 }
329
330 if (ai != null) {
331 titleIcon.setImageDrawable(ai.loadIcon(mPm));
332 }
333 }
Adam Powelle9414d92014-07-05 17:44:18 -0700334 }
Adam Powell2d809622012-03-22 15:24:43 -0700335
Adam Powell278902c2014-07-12 18:33:22 -0700336 final ImageView iconView = (ImageView) findViewById(R.id.icon);
337 final DisplayResolveInfo iconInfo = mAdapter.getFilteredItem();
338 if (iconView != null && iconInfo != null) {
Adam Powell24428412015-04-01 17:19:56 -0700339 new LoadIconIntoViewTask(iconInfo, iconView).execute();
Adam Powell278902c2014-07-12 18:33:22 -0700340 }
341
342 if (alwaysUseOption || mAdapter.hasFilteredItem()) {
Adam Powellc5878612012-05-04 18:42:38 -0700343 final ViewGroup buttonLayout = (ViewGroup) findViewById(R.id.button_bar);
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700344 if (buttonLayout != null) {
345 buttonLayout.setVisibility(View.VISIBLE);
346 mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
347 mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
348 } else {
349 mAlwaysUseOption = false;
350 }
Adam Powell278902c2014-07-12 18:33:22 -0700351 }
352
353 if (mAdapter.hasFilteredItem()) {
354 setAlwaysButtonEnabled(true, mAdapter.getFilteredPosition(), false);
355 mOnceButton.setEnabled(true);
356 }
Adam Powell88831a22014-11-20 18:17:00 -0800357
358 mProfileView = findViewById(R.id.profile_button);
359 if (mProfileView != null) {
360 mProfileView.setOnClickListener(new View.OnClickListener() {
361 @Override
362 public void onClick(View v) {
363 final DisplayResolveInfo dri = mAdapter.getOtherProfile();
364 if (dri == null) {
365 return;
366 }
367
Sander Alewijnse053d3dd2015-03-09 15:31:10 +0000368 // Do not show the profile switch message anymore.
369 mProfileSwitchMessageId = -1;
370
Adam Powell24428412015-04-01 17:19:56 -0700371 onTargetSelected(dri, false);
Adam Powell88831a22014-11-20 18:17:00 -0800372 finish();
373 }
374 });
375 bindProfileView();
376 }
377 }
378
Adam Powell2ed547e2015-04-29 18:45:04 -0700379 protected final void setAdditionalTargets(Intent[] intents) {
380 if (intents != null) {
381 for (Intent intent : intents) {
382 mIntents.add(intent);
383 }
384 }
385 }
386
Adam Powell0ccc0e92015-04-23 17:19:37 -0700387 public Intent getTargetIntent() {
Adam Powell2ed547e2015-04-29 18:45:04 -0700388 return mIntents.isEmpty() ? null : mIntents.get(0);
Adam Powell0ccc0e92015-04-23 17:19:37 -0700389 }
390
Adam Powell24428412015-04-01 17:19:56 -0700391 private String getReferrerPackageName() {
392 final Uri referrer = getReferrer();
393 if (referrer != null && "android-app".equals(referrer.getScheme())) {
394 return referrer.getHost();
395 }
396 return null;
397 }
398
399 int getLayoutResource() {
400 return R.layout.resolver_list;
401 }
402
Adam Powell88831a22014-11-20 18:17:00 -0800403 void bindProfileView() {
404 final DisplayResolveInfo dri = mAdapter.getOtherProfile();
405 if (dri != null) {
406 mProfileView.setVisibility(View.VISIBLE);
407 final ImageView icon = (ImageView) mProfileView.findViewById(R.id.icon);
408 final TextView text = (TextView) mProfileView.findViewById(R.id.text1);
Adam Powell24428412015-04-01 17:19:56 -0700409 if (!dri.hasDisplayIcon()) {
410 new LoadIconIntoViewTask(dri, icon).execute();
Adam Powell88831a22014-11-20 18:17:00 -0800411 }
Adam Powell24428412015-04-01 17:19:56 -0700412 icon.setImageDrawable(dri.getDisplayIcon());
413 text.setText(dri.getDisplayLabel());
Adam Powell88831a22014-11-20 18:17:00 -0800414 } else {
415 mProfileView.setVisibility(View.GONE);
416 }
Adam Powell278902c2014-07-12 18:33:22 -0700417 }
418
Sander Alewijnsef6545332014-10-31 12:39:02 +0000419 private void setProfileSwitchMessageId(int contentUserHint) {
420 if (contentUserHint != UserHandle.USER_CURRENT &&
421 contentUserHint != UserHandle.myUserId()) {
422 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
423 UserInfo originUserInfo = userManager.getUserInfo(contentUserHint);
424 boolean originIsManaged = originUserInfo != null ? originUserInfo.isManagedProfile()
425 : false;
426 boolean targetIsManaged = userManager.isManagedProfile();
427 if (originIsManaged && !targetIsManaged) {
428 mProfileSwitchMessageId = com.android.internal.R.string.forward_intent_to_owner;
429 } else if (!originIsManaged && targetIsManaged) {
430 mProfileSwitchMessageId = com.android.internal.R.string.forward_intent_to_work;
431 }
432 }
433 }
434
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700435 /**
436 * Turn on launch mode that is safe to use when forwarding intents received from
437 * applications and running in system processes. This mode uses Activity.startActivityAsCaller
438 * instead of the normal Activity.startActivity for launching the activity selected
439 * by the user.
440 *
441 * <p>This mode is set to true by default if the activity is initialized through
442 * {@link #onCreate(android.os.Bundle)}. If a subclass calls one of the other onCreate
443 * methods, it is set to false by default. You must set it before calling one of the
444 * more detailed onCreate methods, so that it will be set correctly in the case where
445 * there is only one intent to resolve and it is thus started immediately.</p>
446 */
447 public void setSafeForwardingMode(boolean safeForwarding) {
448 mSafeForwardingMode = safeForwarding;
449 }
450
Adam Powell278902c2014-07-12 18:33:22 -0700451 protected CharSequence getTitleForAction(String action, int defaultTitleRes) {
Adam Powella35c77a2014-09-25 16:46:36 -0700452 final ActionTitle title = mResolvingHome ? ActionTitle.HOME : ActionTitle.forAction(action);
Adam Powell278902c2014-07-12 18:33:22 -0700453 final boolean named = mAdapter.hasFilteredItem();
454 if (title == ActionTitle.DEFAULT && defaultTitleRes != 0) {
455 return getString(defaultTitleRes);
456 } else {
Adam Powell24428412015-04-01 17:19:56 -0700457 return named
458 ? getString(title.namedTitleRes, mAdapter.getFilteredItem().getDisplayLabel())
459 : getString(title.titleRes);
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700460 }
Adam Powellc5878612012-05-04 18:42:38 -0700461 }
462
Adam Powelle9414d92014-07-05 17:44:18 -0700463 void dismiss() {
464 if (!isFinishing()) {
465 finish();
466 }
467 }
468
Adam Powellc5878612012-05-04 18:42:38 -0700469 Drawable getIcon(Resources res, int resId) {
470 Drawable result;
471 try {
472 result = res.getDrawableForDensity(resId, mIconDpi);
473 } catch (Resources.NotFoundException e) {
474 result = null;
475 }
476
477 return result;
478 }
479
480 Drawable loadIconForResolveInfo(ResolveInfo ri) {
481 Drawable dr;
482 try {
483 if (ri.resolvePackageName != null && ri.icon != 0) {
484 dr = getIcon(mPm.getResourcesForApplication(ri.resolvePackageName), ri.icon);
485 if (dr != null) {
486 return dr;
487 }
488 }
489 final int iconRes = ri.getIconResource();
490 if (iconRes != 0) {
491 dr = getIcon(mPm.getResourcesForApplication(ri.activityInfo.packageName), iconRes);
492 if (dr != null) {
493 return dr;
494 }
495 }
496 } catch (NameNotFoundException e) {
497 Log.e(TAG, "Couldn't find resources for package", e);
498 }
499 return ri.loadIcon(mPm);
500 }
501
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800502 @Override
503 protected void onRestart() {
504 super.onRestart();
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700505 if (!mRegistered) {
506 mPackageMonitor.register(this, getMainLooper(), false);
507 mRegistered = true;
508 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800509 mAdapter.handlePackagesChanged();
Adam Powell88831a22014-11-20 18:17:00 -0800510 if (mProfileView != null) {
511 bindProfileView();
512 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800513 }
514
515 @Override
516 protected void onStop() {
517 super.onStop();
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700518 if (mRegistered) {
519 mPackageMonitor.unregister();
520 mRegistered = false;
521 }
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700522 if ((getIntent().getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
523 // This resolver is in the unusual situation where it has been
524 // launched at the top of a new task. We don't let it be added
525 // to the recent tasks shown to the user, and we need to make sure
526 // that each time we are launched we get the correct launching
527 // uid (not re-using the same resolver from an old launching uid),
528 // so we will now finish ourself since being no longer visible,
529 // the user probably can't get back to us.
530 if (!isChangingConfigurations()) {
531 finish();
532 }
533 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800534 }
535
Adam Powellc5878612012-05-04 18:42:38 -0700536 @Override
Adam Powell9bee4662012-05-08 11:07:23 -0700537 protected void onRestoreInstanceState(Bundle savedInstanceState) {
538 super.onRestoreInstanceState(savedInstanceState);
539 if (mAlwaysUseOption) {
Adam Powell24428412015-04-01 17:19:56 -0700540 final int checkedPos = mAdapterView.getCheckedItemPosition();
Nicolas Prevot50449882014-06-23 12:42:37 +0100541 final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
Adam Powelld81cc4a2012-07-19 13:51:39 -0700542 mLastSelected = checkedPos;
Adam Powell278902c2014-07-12 18:33:22 -0700543 setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
Nicolas Prevot50449882014-06-23 12:42:37 +0100544 mOnceButton.setEnabled(hasValidSelection);
545 if (hasValidSelection) {
Adam Powell24428412015-04-01 17:19:56 -0700546 mAdapterView.setSelection(checkedPos);
Adam Powell9bee4662012-05-08 11:07:23 -0700547 }
548 }
549 }
550
551 @Override
Adam Powellc5878612012-05-04 18:42:38 -0700552 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Adam Powell24428412015-04-01 17:19:56 -0700553 if (mListView != null) {
554 position -= mListView.getHeaderViewsCount();
555 }
Adam Powellfd1e93d2014-09-07 16:52:22 -0700556 if (position < 0) {
557 // Header views don't count.
558 return;
559 }
Adam Powell24428412015-04-01 17:19:56 -0700560 final int checkedPos = mAdapterView.getCheckedItemPosition();
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700561 final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
Adam Powellbdda4e72012-07-20 11:22:03 -0700562 if (mAlwaysUseOption && (!hasValidSelection || mLastSelected != checkedPos)) {
Adam Powell278902c2014-07-12 18:33:22 -0700563 setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
Adam Powelld81cc4a2012-07-19 13:51:39 -0700564 mOnceButton.setEnabled(hasValidSelection);
565 if (hasValidSelection) {
Adam Powell24428412015-04-01 17:19:56 -0700566 mAdapterView.smoothScrollToPosition(checkedPos);
Adam Powell9bee4662012-05-08 11:07:23 -0700567 }
Adam Powelld81cc4a2012-07-19 13:51:39 -0700568 mLastSelected = checkedPos;
Adam Powellc5878612012-05-04 18:42:38 -0700569 } else {
Adam Powell278902c2014-07-12 18:33:22 -0700570 startSelected(position, false, true);
Adam Powellc5878612012-05-04 18:42:38 -0700571 }
572 }
573
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100574 private boolean hasManagedProfile() {
575 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
576 if (userManager == null) {
577 return false;
578 }
579
580 try {
581 List<UserInfo> profiles = userManager.getProfiles(getUserId());
582 for (UserInfo userInfo : profiles) {
583 if (userInfo != null && userInfo.isManagedProfile()) {
584 return true;
585 }
586 }
587 } catch (SecurityException e) {
588 return false;
589 }
590 return false;
591 }
592
593 private boolean supportsManagedProfiles(ResolveInfo resolveInfo) {
594 try {
595 ApplicationInfo appInfo = getPackageManager().getApplicationInfo(
596 resolveInfo.activityInfo.packageName, 0 /* default flags */);
597 return versionNumberAtLeastL(appInfo.targetSdkVersion);
598 } catch (NameNotFoundException e) {
599 return false;
600 }
601 }
602
603 private boolean versionNumberAtLeastL(int versionNumber) {
Dianne Hackborn955d8d62014-10-07 20:17:19 -0700604 return versionNumber >= Build.VERSION_CODES.LOLLIPOP;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100605 }
606
Adam Powell278902c2014-07-12 18:33:22 -0700607 private void setAlwaysButtonEnabled(boolean hasValidSelection, int checkedPos,
608 boolean filtered) {
Nicolas Prevot50449882014-06-23 12:42:37 +0100609 boolean enabled = false;
610 if (hasValidSelection) {
Adam Powell278902c2014-07-12 18:33:22 -0700611 ResolveInfo ri = mAdapter.resolveInfoForPosition(checkedPos, filtered);
Nicolas Prevot50449882014-06-23 12:42:37 +0100612 if (ri.targetUserId == UserHandle.USER_CURRENT) {
613 enabled = true;
614 }
615 }
616 mAlwaysButton.setEnabled(enabled);
617 }
618
Adam Powellc5878612012-05-04 18:42:38 -0700619 public void onButtonClick(View v) {
620 final int id = v.getId();
Adam Powell278902c2014-07-12 18:33:22 -0700621 startSelected(mAlwaysUseOption ?
Adam Powell24428412015-04-01 17:19:56 -0700622 mAdapterView.getCheckedItemPosition() : mAdapter.getFilteredPosition(),
Adam Powell278902c2014-07-12 18:33:22 -0700623 id == R.id.button_always,
624 mAlwaysUseOption);
Adam Powellc5878612012-05-04 18:42:38 -0700625 }
626
Adam Powell278902c2014-07-12 18:33:22 -0700627 void startSelected(int which, boolean always, boolean filtered) {
Amith Yamasani07cd3512013-09-18 13:16:00 -0700628 if (isFinishing()) {
629 return;
630 }
Adam Powell278902c2014-07-12 18:33:22 -0700631 ResolveInfo ri = mAdapter.resolveInfoForPosition(which, filtered);
Sander Alewijnse86d35ba2015-02-04 15:14:53 +0000632 if (mResolvingHome && hasManagedProfile() && !supportsManagedProfiles(ri)) {
633 Toast.makeText(this, String.format(getResources().getString(
634 com.android.internal.R.string.activity_resolver_work_profiles_support),
635 ri.activityInfo.loadLabel(getPackageManager()).toString()),
636 Toast.LENGTH_LONG).show();
637 return;
638 }
639
Adam Powell24428412015-04-01 17:19:56 -0700640 TargetInfo target = mAdapter.targetInfoForPosition(which, filtered);
Adam Powell2ed547e2015-04-29 18:45:04 -0700641 if (onTargetSelected(target, always)) {
642 finish();
643 }
Mike Lockwood02eb8742011-02-27 09:10:37 -0800644 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800645
Adam Powelle49d9392014-07-17 18:45:19 -0700646 /**
647 * Replace me in subclasses!
648 */
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000649 public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) {
Adam Powelle49d9392014-07-17 18:45:19 -0700650 return defIntent;
651 }
652
Adam Powell2ed547e2015-04-29 18:45:04 -0700653 protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) {
Adam Powell24428412015-04-01 17:19:56 -0700654 final ResolveInfo ri = target.getResolveInfo();
655 final Intent intent = target != null ? target.getResolvedIntent() : null;
656
657 if (intent != null && (mAlwaysUseOption || mAdapter.hasFilteredItem())
658 && mAdapter.mOrigResolveList != null) {
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700659 // Build a reasonable intent filter, based on what matched.
660 IntentFilter filter = new IntentFilter();
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800661 String action = intent.getAction();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800662
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800663 if (action != null) {
664 filter.addAction(action);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800665 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700666 Set<String> categories = intent.getCategories();
667 if (categories != null) {
668 for (String cat : categories) {
669 filter.addCategory(cat);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800670 }
671 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700672 filter.addCategory(Intent.CATEGORY_DEFAULT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673
Adam Powell24428412015-04-01 17:19:56 -0700674 int cat = ri.match & IntentFilter.MATCH_CATEGORY_MASK;
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700675 Uri data = intent.getData();
676 if (cat == IntentFilter.MATCH_CATEGORY_TYPE) {
677 String mimeType = intent.resolveType(this);
678 if (mimeType != null) {
679 try {
680 filter.addDataType(mimeType);
681 } catch (IntentFilter.MalformedMimeTypeException e) {
682 Log.w("ResolverActivity", e);
683 filter = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800684 }
685 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700686 }
687 if (data != null && data.getScheme() != null) {
688 // We need the data specification if there was no type,
689 // OR if the scheme is not one of our magical "file:"
690 // or "content:" schemes (see IntentFilter for the reason).
691 if (cat != IntentFilter.MATCH_CATEGORY_TYPE
692 || (!"file".equals(data.getScheme())
693 && !"content".equals(data.getScheme()))) {
694 filter.addDataScheme(data.getScheme());
695
696 // Look through the resolved filter to determine which part
697 // of it matched the original Intent.
698 Iterator<PatternMatcher> pIt = ri.filter.schemeSpecificPartsIterator();
699 if (pIt != null) {
700 String ssp = data.getSchemeSpecificPart();
701 while (ssp != null && pIt.hasNext()) {
702 PatternMatcher p = pIt.next();
703 if (p.match(ssp)) {
704 filter.addDataSchemeSpecificPart(p.getPath(), p.getType());
705 break;
706 }
Dianne Hackborndf1c0bf2013-06-12 16:21:38 -0700707 }
708 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700709 Iterator<IntentFilter.AuthorityEntry> aIt = ri.filter.authoritiesIterator();
710 if (aIt != null) {
711 while (aIt.hasNext()) {
712 IntentFilter.AuthorityEntry a = aIt.next();
713 if (a.match(data) >= 0) {
714 int port = a.getPort();
715 filter.addDataAuthority(a.getHost(),
716 port >= 0 ? Integer.toString(port) : null);
717 break;
718 }
719 }
720 }
721 pIt = ri.filter.pathsIterator();
722 if (pIt != null) {
723 String path = data.getPath();
724 while (path != null && pIt.hasNext()) {
725 PatternMatcher p = pIt.next();
726 if (p.match(path)) {
727 filter.addDataPath(p.getPath(), p.getType());
728 break;
729 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800730 }
731 }
732 }
733 }
734
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700735 if (filter != null) {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700736 final int N = mAdapter.mOrigResolveList.size();
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700737 ComponentName[] set = new ComponentName[N];
738 int bestMatch = 0;
739 for (int i=0; i<N; i++) {
Adam Powell2ed547e2015-04-29 18:45:04 -0700740 ResolveInfo r = mAdapter.mOrigResolveList.get(i).getResolveInfoAt(0);
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700741 set[i] = new ComponentName(r.activityInfo.packageName,
742 r.activityInfo.name);
743 if (r.match > bestMatch) bestMatch = r.match;
744 }
745 if (alwaysCheck) {
Fabrice Di Meglio3453e082015-05-11 17:46:23 -0700746 final int userId = getUserId();
747 final PackageManager pm = getPackageManager();
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800748
749 // Set the preferred Activity
750 pm.addPreferredActivity(filter, bestMatch, set, intent.getComponent());
751
Fabrice Di Meglio3453e082015-05-11 17:46:23 -0700752 if (ri.handleAllWebDataURI) {
753 // Set default Browser if needed
754 final String packageName = pm.getDefaultBrowserPackageName(userId);
755 if (TextUtils.isEmpty(packageName)) {
756 pm.setDefaultBrowserPackageName(ri.activityInfo.packageName, userId);
757 }
758 } else {
759 // Update Domain Verification status
760 ComponentName cn = intent.getComponent();
761 String packageName = cn.getPackageName();
762 String dataScheme = (data != null) ? data.getScheme() : null;
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800763
Fabrice Di Meglio3453e082015-05-11 17:46:23 -0700764 boolean isHttpOrHttps = (dataScheme != null) &&
765 (dataScheme.equals(IntentFilter.SCHEME_HTTP) ||
766 dataScheme.equals(IntentFilter.SCHEME_HTTPS));
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800767
Fabrice Di Meglio3453e082015-05-11 17:46:23 -0700768 boolean isViewAction = (action != null) && action.equals(Intent.ACTION_VIEW);
769 boolean hasCategoryBrowsable = (categories != null) &&
770 categories.contains(Intent.CATEGORY_BROWSABLE);
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800771
Fabrice Di Meglio3453e082015-05-11 17:46:23 -0700772 if (isHttpOrHttps && isViewAction && hasCategoryBrowsable) {
773 pm.updateIntentVerificationStatus(packageName,
774 PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS,
775 userId);
776 }
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800777 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700778 } else {
779 try {
780 AppGlobals.getPackageManager().setLastChosenActivity(intent,
781 intent.resolveTypeIfNeeded(getContentResolver()),
782 PackageManager.MATCH_DEFAULT_ONLY,
783 filter, bestMatch, intent.getComponent());
784 } catch (RemoteException re) {
785 Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
786 }
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700787 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800788 }
789 }
790
Adam Powell24428412015-04-01 17:19:56 -0700791 if (target != null) {
792 safelyStartActivity(target);
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700793 }
Adam Powell2ed547e2015-04-29 18:45:04 -0700794 return true;
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700795 }
796
Adam Powell24428412015-04-01 17:19:56 -0700797 void safelyStartActivity(TargetInfo cti) {
Sander Alewijnsef6545332014-10-31 12:39:02 +0000798 // If needed, show that intent is forwarded
799 // from managed profile to owner or other way around.
800 if (mProfileSwitchMessageId != -1) {
801 Toast.makeText(this, getString(mProfileSwitchMessageId), Toast.LENGTH_LONG).show();
802 }
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700803 if (!mSafeForwardingMode) {
Adam Powell24428412015-04-01 17:19:56 -0700804 if (cti.start(this, null)) {
805 onActivityStarted(cti);
806 }
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700807 return;
808 }
809 try {
Adam Powell24428412015-04-01 17:19:56 -0700810 if (cti.startAsCaller(this, null, UserHandle.USER_NULL)) {
811 onActivityStarted(cti);
812 }
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700813 } catch (RuntimeException e) {
814 String launchedFromPackage;
815 try {
816 launchedFromPackage = ActivityManagerNative.getDefault().getLaunchedFromPackage(
817 getActivityToken());
818 } catch (RemoteException e2) {
819 launchedFromPackage = "??";
820 }
821 Slog.wtf(TAG, "Unable to launch as uid " + mLaunchedFromUid
822 + " package " + launchedFromPackage + ", while running in "
823 + ActivityThread.currentProcessName(), e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800824 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800825 }
826
Adam Powell24428412015-04-01 17:19:56 -0700827 void onActivityStarted(TargetInfo cti) {
Adam Powell0b3c1122014-10-09 12:50:14 -0700828 // Do nothing
829 }
830
Adam Powell24428412015-04-01 17:19:56 -0700831 boolean shouldGetActivityMetadata() {
832 return false;
833 }
834
Adam Powellc5878612012-05-04 18:42:38 -0700835 void showAppDetails(ResolveInfo ri) {
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700836 Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
Adam Powell0fc5b2b2012-07-18 18:20:29 -0700837 .setData(Uri.fromParts("package", ri.activityInfo.packageName, null))
Adam Powell24428412015-04-01 17:19:56 -0700838 .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
Amith Yamasani203a2f42012-10-03 20:16:40 -0700839 startActivity(in);
Adam Powellc5878612012-05-04 18:42:38 -0700840 }
841
Adam Powell24428412015-04-01 17:19:56 -0700842 ResolveListAdapter createAdapter(Context context, Intent[] initialIntents,
843 List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) {
844 return new ResolveListAdapter(context, initialIntents, rList, launchedFromUid,
845 filterLastUsed);
Adam Powell88831a22014-11-20 18:17:00 -0800846 }
847
Adam Powell24428412015-04-01 17:19:56 -0700848 ResolveListAdapter getAdapter() {
849 return mAdapter;
850 }
851
852 final class DisplayResolveInfo implements TargetInfo {
853 private final ResolveInfo mResolveInfo;
854 private final CharSequence mDisplayLabel;
855 private Drawable mDisplayIcon;
856 private final CharSequence mExtendedInfo;
857 private final Intent mResolvedIntent;
Adam Powell2ed547e2015-04-29 18:45:04 -0700858 private final List<Intent> mSourceIntents = new ArrayList<>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859
Adam Powell2ed547e2015-04-29 18:45:04 -0700860 DisplayResolveInfo(Intent originalIntent, ResolveInfo pri, CharSequence pLabel,
Dianne Hackborneb034652009-09-07 00:49:58 -0700861 CharSequence pInfo, Intent pOrigIntent) {
Adam Powell2ed547e2015-04-29 18:45:04 -0700862 mSourceIntents.add(originalIntent);
Adam Powell24428412015-04-01 17:19:56 -0700863 mResolveInfo = pri;
864 mDisplayLabel = pLabel;
865 mExtendedInfo = pInfo;
866
867 final Intent intent = new Intent(pOrigIntent != null ? pOrigIntent :
Adam Powell2ed547e2015-04-29 18:45:04 -0700868 getReplacementIntent(pri.activityInfo, getTargetIntent()));
Adam Powell24428412015-04-01 17:19:56 -0700869 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
870 | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
871 final ActivityInfo ai = mResolveInfo.activityInfo;
872 intent.setComponent(new ComponentName(ai.applicationInfo.packageName, ai.name));
873
874 mResolvedIntent = intent;
875 }
876
Adam Powell2ed547e2015-04-29 18:45:04 -0700877 private DisplayResolveInfo(DisplayResolveInfo other, Intent fillInIntent, int flags) {
878 mSourceIntents.addAll(other.getAllSourceIntents());
879 mResolveInfo = other.mResolveInfo;
880 mDisplayLabel = other.mDisplayLabel;
881 mDisplayIcon = other.mDisplayIcon;
882 mExtendedInfo = other.mExtendedInfo;
883 mResolvedIntent = new Intent(other.mResolvedIntent);
884 mResolvedIntent.fillIn(fillInIntent, flags);
885 }
886
Adam Powell24428412015-04-01 17:19:56 -0700887 public ResolveInfo getResolveInfo() {
888 return mResolveInfo;
889 }
890
891 public CharSequence getDisplayLabel() {
892 return mDisplayLabel;
893 }
894
895 public Drawable getDisplayIcon() {
896 return mDisplayIcon;
897 }
898
Adam Powell2ed547e2015-04-29 18:45:04 -0700899 @Override
900 public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) {
901 return new DisplayResolveInfo(this, fillInIntent, flags);
902 }
903
904 @Override
905 public List<Intent> getAllSourceIntents() {
906 return mSourceIntents;
907 }
908
909 public void addAlternateSourceIntent(Intent alt) {
910 mSourceIntents.add(alt);
911 }
912
Adam Powell24428412015-04-01 17:19:56 -0700913 public void setDisplayIcon(Drawable icon) {
914 mDisplayIcon = icon;
915 }
916
917 public boolean hasDisplayIcon() {
918 return mDisplayIcon != null;
919 }
920
921 public CharSequence getExtendedInfo() {
922 return mExtendedInfo;
923 }
924
925 public Intent getResolvedIntent() {
926 return mResolvedIntent;
927 }
928
929 @Override
930 public ComponentName getResolvedComponentName() {
931 return new ComponentName(mResolveInfo.activityInfo.packageName,
932 mResolveInfo.activityInfo.name);
933 }
934
935 @Override
936 public boolean start(Activity activity, Bundle options) {
937 activity.startActivity(mResolvedIntent, options);
938 return true;
939 }
940
941 @Override
942 public boolean startAsCaller(Activity activity, Bundle options, int userId) {
943 activity.startActivityAsCaller(mResolvedIntent, options, userId);
944 return true;
945 }
946
947 @Override
948 public boolean startAsUser(Activity activity, Bundle options, UserHandle user) {
949 activity.startActivityAsUser(mResolvedIntent, options, user);
950 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800951 }
952 }
953
Adam Powell24428412015-04-01 17:19:56 -0700954 /**
955 * A single target as represented in the chooser.
956 */
957 public interface TargetInfo {
958 /**
959 * Get the resolved intent that represents this target. Note that this may not be the
960 * intent that will be launched by calling one of the <code>start</code> methods provided;
961 * this is the intent that will be credited with the launch.
962 *
963 * @return the resolved intent for this target
964 */
965 public Intent getResolvedIntent();
966
967 /**
968 * Get the resolved component name that represents this target. Note that this may not
969 * be the component that will be directly launched by calling one of the <code>start</code>
970 * methods provided; this is the component that will be credited with the launch.
971 *
972 * @return the resolved ComponentName for this target
973 */
974 public ComponentName getResolvedComponentName();
975
976 /**
977 * Start the activity referenced by this target.
978 *
979 * @param activity calling Activity performing the launch
980 * @param options ActivityOptions bundle
981 * @return true if the start completed successfully
982 */
983 public boolean start(Activity activity, Bundle options);
984
985 /**
986 * Start the activity referenced by this target as if the ResolverActivity's caller
987 * was performing the start operation.
988 *
989 * @param activity calling Activity (actually) performing the launch
990 * @param options ActivityOptions bundle
991 * @param userId userId to start as or {@link UserHandle#USER_NULL} for activity's caller
992 * @return true if the start completed successfully
993 */
994 public boolean startAsCaller(Activity activity, Bundle options, int userId);
995
996 /**
997 * Start the activity referenced by this target as a given user.
998 *
999 * @param activity calling activity performing the launch
1000 * @param options ActivityOptions bundle
1001 * @param user handle for the user to start the activity as
1002 * @return true if the start completed successfully
1003 */
1004 public boolean startAsUser(Activity activity, Bundle options, UserHandle user);
1005
1006 /**
1007 * Return the ResolveInfo about how and why this target matched the original query
1008 * for available targets.
1009 *
1010 * @return ResolveInfo representing this target's match
1011 */
1012 public ResolveInfo getResolveInfo();
1013
1014 /**
1015 * Return the human-readable text label for this target.
1016 *
1017 * @return user-visible target label
1018 */
1019 public CharSequence getDisplayLabel();
1020
1021 /**
1022 * Return any extended info for this target. This may be used to disambiguate
1023 * otherwise identical targets.
1024 *
1025 * @return human-readable disambig string or null if none present
1026 */
1027 public CharSequence getExtendedInfo();
1028
1029 /**
1030 * @return The drawable that should be used to represent this target
1031 */
1032 public Drawable getDisplayIcon();
Adam Powell2ed547e2015-04-29 18:45:04 -07001033
1034 /**
1035 * Clone this target with the given fill-in information.
1036 */
1037 public TargetInfo cloneFilledIn(Intent fillInIntent, int flags);
1038
1039 /**
1040 * @return the list of supported source intents deduped against this single target
1041 */
1042 public List<Intent> getAllSourceIntents();
Adam Powell24428412015-04-01 17:19:56 -07001043 }
1044
1045 class ResolveListAdapter extends BaseAdapter {
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001046 private final Intent[] mInitialIntents;
1047 private final List<ResolveInfo> mBaseResolveList;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -07001048 private ResolveInfo mLastChosen;
Adam Powell88831a22014-11-20 18:17:00 -08001049 private DisplayResolveInfo mOtherProfile;
Dianne Hackborn5320eb82012-05-18 12:05:04 -07001050 private final int mLaunchedFromUid;
Adam Powell24428412015-04-01 17:19:56 -07001051 private boolean mHasExtendedInfo;
1052
1053 protected final LayoutInflater mInflater;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001054
Adam Powell2ed547e2015-04-29 18:45:04 -07001055 List<DisplayResolveInfo> mDisplayList;
1056 List<ResolvedComponentInfo> mOrigResolveList;
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -07001057
Adam Powell278902c2014-07-12 18:33:22 -07001058 private int mLastChosenPosition = -1;
1059 private boolean mFilterLastUsed;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001060
Adam Powell88831a22014-11-20 18:17:00 -08001061 public ResolveListAdapter(Context context, Intent[] initialIntents,
1062 List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) {
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001063 mInitialIntents = initialIntents;
1064 mBaseResolveList = rList;
Dianne Hackborn5320eb82012-05-18 12:05:04 -07001065 mLaunchedFromUid = launchedFromUid;
Adam Powelle9414d92014-07-05 17:44:18 -07001066 mInflater = LayoutInflater.from(context);
Adam Powell2ed547e2015-04-29 18:45:04 -07001067 mDisplayList = new ArrayList<>();
Adam Powell278902c2014-07-12 18:33:22 -07001068 mFilterLastUsed = filterLastUsed;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001069 rebuildList();
1070 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001071
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001072 public void handlePackagesChanged() {
1073 rebuildList();
1074 notifyDataSetChanged();
Esteban Talavera6de72662014-12-11 17:54:07 +00001075 if (getCount() == 0) {
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001076 // We no longer have any items... just finish the activity.
1077 finish();
Adam Powellc5878612012-05-04 18:42:38 -07001078 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001079 }
1080
Adam Powell278902c2014-07-12 18:33:22 -07001081 public DisplayResolveInfo getFilteredItem() {
1082 if (mFilterLastUsed && mLastChosenPosition >= 0) {
1083 // Not using getItem since it offsets to dodge this position for the list
Adam Powell2ed547e2015-04-29 18:45:04 -07001084 return mDisplayList.get(mLastChosenPosition);
Adam Powell278902c2014-07-12 18:33:22 -07001085 }
1086 return null;
1087 }
1088
Adam Powell88831a22014-11-20 18:17:00 -08001089 public DisplayResolveInfo getOtherProfile() {
1090 return mOtherProfile;
1091 }
1092
Adam Powell278902c2014-07-12 18:33:22 -07001093 public int getFilteredPosition() {
1094 if (mFilterLastUsed && mLastChosenPosition >= 0) {
1095 return mLastChosenPosition;
1096 }
1097 return AbsListView.INVALID_POSITION;
1098 }
1099
1100 public boolean hasFilteredItem() {
1101 return mFilterLastUsed && mLastChosenPosition >= 0;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -07001102 }
1103
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001104 private void rebuildList() {
Adam Powell2ed547e2015-04-29 18:45:04 -07001105 List<ResolvedComponentInfo> currentResolveList = null;
You Kim43a5070e2012-11-21 23:23:45 +09001106
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -07001107 try {
Adam Powell2ed547e2015-04-29 18:45:04 -07001108 final Intent primaryIntent = getTargetIntent();
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -07001109 mLastChosen = AppGlobals.getPackageManager().getLastChosenActivity(
Adam Powell2ed547e2015-04-29 18:45:04 -07001110 primaryIntent, primaryIntent.resolveTypeIfNeeded(getContentResolver()),
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -07001111 PackageManager.MATCH_DEFAULT_ONLY);
1112 } catch (RemoteException re) {
1113 Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
1114 }
1115
Sudheer Shanka7e64e102015-01-23 10:37:45 +00001116 // Clear the value of mOtherProfile from previous call.
1117 mOtherProfile = null;
Adam Powell2ed547e2015-04-29 18:45:04 -07001118 mDisplayList.clear();
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001119 if (mBaseResolveList != null) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001120 currentResolveList = mOrigResolveList = new ArrayList<>();
1121 addResolveListDedupe(currentResolveList, getTargetIntent(), mBaseResolveList);
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001122 } else {
Adam Powell2ed547e2015-04-29 18:45:04 -07001123 final boolean shouldGetResolvedFilter = shouldGetResolvedFilter();
1124 final boolean shouldGetActivityMetadata = shouldGetActivityMetadata();
1125 for (int i = 0, N = mIntents.size(); i < N; i++) {
1126 final Intent intent = mIntents.get(i);
1127 final List<ResolveInfo> infos = mPm.queryIntentActivities(intent,
1128 PackageManager.MATCH_DEFAULT_ONLY
1129 | (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0)
1130 | (shouldGetActivityMetadata ? PackageManager.GET_META_DATA : 0));
1131 if (infos != null) {
1132 if (currentResolveList == null) {
1133 currentResolveList = mOrigResolveList = new ArrayList<>();
1134 }
1135 addResolveListDedupe(currentResolveList, intent, infos);
1136 }
1137 }
1138
Dianne Hackborn5320eb82012-05-18 12:05:04 -07001139 // Filter out any activities that the launched uid does not
1140 // have permission for. We don't do this when we have an explicit
1141 // list of resolved activities, because that only happens when
1142 // we are being subclassed, so we can safely launch whatever
1143 // they gave us.
You Kim43a5070e2012-11-21 23:23:45 +09001144 if (currentResolveList != null) {
1145 for (int i=currentResolveList.size()-1; i >= 0; i--) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001146 ActivityInfo ai = currentResolveList.get(i)
1147 .getResolveInfoAt(0).activityInfo;
Dianne Hackborn5320eb82012-05-18 12:05:04 -07001148 int granted = ActivityManager.checkComponentPermission(
1149 ai.permission, mLaunchedFromUid,
1150 ai.applicationInfo.uid, ai.exported);
1151 if (granted != PackageManager.PERMISSION_GRANTED) {
1152 // Access not allowed!
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -07001153 if (mOrigResolveList == currentResolveList) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001154 mOrigResolveList = new ArrayList<>(mOrigResolveList);
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -07001155 }
You Kim43a5070e2012-11-21 23:23:45 +09001156 currentResolveList.remove(i);
Dianne Hackborn5320eb82012-05-18 12:05:04 -07001157 }
1158 }
1159 }
Jeff Hamiltond88e9aa2011-01-24 14:53:00 -06001160 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001161 int N;
You Kim43a5070e2012-11-21 23:23:45 +09001162 if ((currentResolveList != null) && ((N = currentResolveList.size()) > 0)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001163 // Only display the first matches that are either of equal
1164 // priority or have asked to be default options.
Adam Powell2ed547e2015-04-29 18:45:04 -07001165 ResolvedComponentInfo rci0 = currentResolveList.get(0);
1166 ResolveInfo r0 = rci0.getResolveInfoAt(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001167 for (int i=1; i<N; i++) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001168 ResolveInfo ri = currentResolveList.get(i).getResolveInfoAt(0);
You Kim43a5070e2012-11-21 23:23:45 +09001169 if (DEBUG) Log.v(
Adam Powell09a65602014-07-20 16:23:14 -07001170 TAG,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001171 r0.activityInfo.name + "=" +
1172 r0.priority + "/" + r0.isDefault + " vs " +
1173 ri.activityInfo.name + "=" +
1174 ri.priority + "/" + ri.isDefault);
You Kim43a5070e2012-11-21 23:23:45 +09001175 if (r0.priority != ri.priority ||
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001176 r0.isDefault != ri.isDefault) {
1177 while (i < N) {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -07001178 if (mOrigResolveList == currentResolveList) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001179 mOrigResolveList = new ArrayList<>(mOrigResolveList);
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -07001180 }
You Kim43a5070e2012-11-21 23:23:45 +09001181 currentResolveList.remove(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001182 N--;
1183 }
1184 }
1185 }
1186 if (N > 1) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001187 Collections.sort(currentResolveList,
1188 new ResolverComparator(ResolverActivity.this, getTargetIntent()));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001189 }
Dianne Hackborneb034652009-09-07 00:49:58 -07001190 // First put the initial items at the top.
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -08001191 if (mInitialIntents != null) {
1192 for (int i=0; i<mInitialIntents.length; i++) {
1193 Intent ii = mInitialIntents[i];
Dianne Hackborneb034652009-09-07 00:49:58 -07001194 if (ii == null) {
1195 continue;
1196 }
1197 ActivityInfo ai = ii.resolveActivityInfo(
1198 getPackageManager(), 0);
1199 if (ai == null) {
Adam Powell09a65602014-07-20 16:23:14 -07001200 Log.w(TAG, "No activity found for " + ii);
Dianne Hackborneb034652009-09-07 00:49:58 -07001201 continue;
1202 }
1203 ResolveInfo ri = new ResolveInfo();
1204 ri.activityInfo = ai;
Nicolas Prevot1a815922014-10-10 16:22:38 +01001205 UserManager userManager =
1206 (UserManager) getSystemService(Context.USER_SERVICE);
1207 if (userManager.isManagedProfile()) {
1208 ri.noResourceId = true;
1209 }
Dianne Hackborneb034652009-09-07 00:49:58 -07001210 if (ii instanceof LabeledIntent) {
1211 LabeledIntent li = (LabeledIntent)ii;
1212 ri.resolvePackageName = li.getSourcePackage();
1213 ri.labelRes = li.getLabelResource();
1214 ri.nonLocalizedLabel = li.getNonLocalizedLabel();
1215 ri.icon = li.getIconResource();
1216 }
Adam Powell2ed547e2015-04-29 18:45:04 -07001217 addResolveInfo(new DisplayResolveInfo(ii, ri,
Dianne Hackborneb034652009-09-07 00:49:58 -07001218 ri.loadLabel(getPackageManager()), null, ii));
1219 }
1220 }
You Kim43a5070e2012-11-21 23:23:45 +09001221
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001222 // Check for applications with same name and use application name or
1223 // package name if necessary
Adam Powell2ed547e2015-04-29 18:45:04 -07001224 rci0 = currentResolveList.get(0);
1225 r0 = rci0.getResolveInfoAt(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001226 int start = 0;
1227 CharSequence r0Label = r0.loadLabel(mPm);
Adam Powell24428412015-04-01 17:19:56 -07001228 mHasExtendedInfo = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001229 for (int i = 1; i < N; i++) {
1230 if (r0Label == null) {
1231 r0Label = r0.activityInfo.packageName;
1232 }
Adam Powell2ed547e2015-04-29 18:45:04 -07001233 ResolvedComponentInfo rci = currentResolveList.get(i);
1234 ResolveInfo ri = rci.getResolveInfoAt(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001235 CharSequence riLabel = ri.loadLabel(mPm);
1236 if (riLabel == null) {
1237 riLabel = ri.activityInfo.packageName;
1238 }
1239 if (riLabel.equals(r0Label)) {
1240 continue;
1241 }
Adam Powell2ed547e2015-04-29 18:45:04 -07001242 processGroup(currentResolveList, start, (i-1), rci0, r0Label);
1243 rci0 = rci;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001244 r0 = ri;
1245 r0Label = riLabel;
1246 start = i;
1247 }
1248 // Process last group
Adam Powell2ed547e2015-04-29 18:45:04 -07001249 processGroup(currentResolveList, start, (N-1), rci0, r0Label);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001250 }
Kenny Guyb8a2df12014-12-12 16:56:08 +00001251
1252 // Layout doesn't handle both profile button and last chosen
1253 // so disable last chosen if profile button is present.
1254 if (mOtherProfile != null && mLastChosenPosition >= 0) {
1255 mLastChosenPosition = -1;
1256 mFilterLastUsed = false;
1257 }
Adam Powell24428412015-04-01 17:19:56 -07001258
1259 onListRebuilt();
1260 }
1261
Adam Powell2ed547e2015-04-29 18:45:04 -07001262 private void addResolveListDedupe(List<ResolvedComponentInfo> into, Intent intent,
1263 List<ResolveInfo> from) {
1264 final int fromCount = from.size();
1265 final int intoCount = into.size();
1266 for (int i = 0; i < fromCount; i++) {
1267 final ResolveInfo newInfo = from.get(i);
1268 boolean found = false;
1269 // Only loop to the end of into as it was before we started; no dupes in from.
1270 for (int j = 0; j < intoCount; j++) {
1271 final ResolvedComponentInfo rci = into.get(i);
1272 if (isSameResolvedComponent(newInfo, rci)) {
1273 found = true;
1274 rci.add(intent, newInfo);
1275 break;
1276 }
1277 }
1278 if (!found) {
1279 into.add(new ResolvedComponentInfo(new ComponentName(
1280 newInfo.activityInfo.packageName, newInfo.activityInfo.name),
1281 intent, newInfo));
1282 }
1283 }
1284 }
1285
1286 private boolean isSameResolvedComponent(ResolveInfo a, ResolvedComponentInfo b) {
1287 final ActivityInfo ai = a.activityInfo;
1288 return ai.packageName.equals(b.name.getPackageName())
1289 && ai.name.equals(b.name.getClassName());
1290 }
1291
Adam Powell24428412015-04-01 17:19:56 -07001292 public void onListRebuilt() {
1293 // This space for rent
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001294 }
1295
Adam Powellc6d5e3a2015-04-23 12:22:20 -07001296 public boolean shouldGetResolvedFilter() {
1297 return mFilterLastUsed;
1298 }
1299
Adam Powell2ed547e2015-04-29 18:45:04 -07001300 private void processGroup(List<ResolvedComponentInfo> rList, int start, int end,
1301 ResolvedComponentInfo ro, CharSequence roLabel) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001302 // Process labels from start to i
1303 int num = end - start+1;
1304 if (num == 1) {
1305 // No duplicate labels. Use label for entry at start
Adam Powell2ed547e2015-04-29 18:45:04 -07001306 addResolveInfoWithAlternates(ro, null, roLabel);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001307 } else {
Adam Powell24428412015-04-01 17:19:56 -07001308 mHasExtendedInfo = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001309 boolean usePkg = false;
Adam Powell2ed547e2015-04-29 18:45:04 -07001310 CharSequence startApp = ro.getResolveInfoAt(0).activityInfo.applicationInfo
1311 .loadLabel(mPm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001312 if (startApp == null) {
1313 usePkg = true;
1314 }
1315 if (!usePkg) {
1316 // Use HashSet to track duplicates
1317 HashSet<CharSequence> duplicates =
1318 new HashSet<CharSequence>();
1319 duplicates.add(startApp);
1320 for (int j = start+1; j <= end ; j++) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001321 ResolveInfo jRi = rList.get(j).getResolveInfoAt(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001322 CharSequence jApp = jRi.activityInfo.applicationInfo.loadLabel(mPm);
1323 if ( (jApp == null) || (duplicates.contains(jApp))) {
1324 usePkg = true;
1325 break;
1326 } else {
1327 duplicates.add(jApp);
1328 }
1329 }
1330 // Clear HashSet for later use
1331 duplicates.clear();
1332 }
1333 for (int k = start; k <= end; k++) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001334 final ResolvedComponentInfo rci = rList.get(k);
1335 final ResolveInfo add = rci.getResolveInfoAt(0);
1336 final CharSequence extraInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001337 if (usePkg) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001338 // Use package name for all entries from start to end-1
Adam Powell2ed547e2015-04-29 18:45:04 -07001339 extraInfo = add.activityInfo.packageName;
1340 } else {
1341 // Use application name for all entries from start to end-1
1342 extraInfo = add.activityInfo.applicationInfo.loadLabel(mPm);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001343 }
Adam Powell2ed547e2015-04-29 18:45:04 -07001344 addResolveInfoWithAlternates(rci, extraInfo, roLabel);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001345 }
1346 }
1347 }
1348
Adam Powell2ed547e2015-04-29 18:45:04 -07001349 private void addResolveInfoWithAlternates(ResolvedComponentInfo rci,
1350 CharSequence extraInfo, CharSequence roLabel) {
1351 final int count = rci.getCount();
1352 final Intent intent = rci.getIntentAt(0);
1353 final ResolveInfo add = rci.getResolveInfoAt(0);
1354 final Intent replaceIntent = getReplacementIntent(add.activityInfo, intent);
1355 final DisplayResolveInfo dri = new DisplayResolveInfo(intent, add, roLabel,
1356 extraInfo, replaceIntent);
1357 addResolveInfo(dri);
1358 if (replaceIntent == intent) {
1359 // Only add alternates if we didn't get a specific replacement from
1360 // the caller. If we have one it trumps potential alternates.
1361 for (int i = 1, N = count; i < N; i++) {
1362 final Intent altIntent = rci.getIntentAt(i);
1363 dri.addAlternateSourceIntent(altIntent);
1364 }
1365 }
1366 updateLastChosenPosition(add);
1367 }
1368
Esteban Talavera6de72662014-12-11 17:54:07 +00001369 private void updateLastChosenPosition(ResolveInfo info) {
1370 if (mLastChosen != null
1371 && mLastChosen.activityInfo.packageName.equals(info.activityInfo.packageName)
1372 && mLastChosen.activityInfo.name.equals(info.activityInfo.name)) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001373 mLastChosenPosition = mDisplayList.size() - 1;
Esteban Talavera6de72662014-12-11 17:54:07 +00001374 }
1375 }
1376
Adam Powell88831a22014-11-20 18:17:00 -08001377 private void addResolveInfo(DisplayResolveInfo dri) {
Adam Powell24428412015-04-01 17:19:56 -07001378 if (dri.mResolveInfo.targetUserId != UserHandle.USER_CURRENT && mOtherProfile == null) {
Adam Powell88831a22014-11-20 18:17:00 -08001379 // So far we only support a single other profile at a time.
1380 // The first one we see gets special treatment.
1381 mOtherProfile = dri;
1382 } else {
Adam Powell2ed547e2015-04-29 18:45:04 -07001383 mDisplayList.add(dri);
Adam Powell88831a22014-11-20 18:17:00 -08001384 }
1385 }
1386
Adam Powell278902c2014-07-12 18:33:22 -07001387 public ResolveInfo resolveInfoForPosition(int position, boolean filtered) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001388 return (filtered ? getItem(position) : mDisplayList.get(position))
1389 .getResolveInfo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001390 }
1391
Adam Powell24428412015-04-01 17:19:56 -07001392 public TargetInfo targetInfoForPosition(int position, boolean filtered) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001393 return filtered ? getItem(position) : mDisplayList.get(position);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001394 }
1395
1396 public int getCount() {
Adam Powell2ed547e2015-04-29 18:45:04 -07001397 int result = mDisplayList.size();
Adam Powell278902c2014-07-12 18:33:22 -07001398 if (mFilterLastUsed && mLastChosenPosition >= 0) {
1399 result--;
1400 }
1401 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001402 }
1403
Adam Powell24428412015-04-01 17:19:56 -07001404 public TargetInfo getItem(int position) {
Adam Powell278902c2014-07-12 18:33:22 -07001405 if (mFilterLastUsed && mLastChosenPosition >= 0 && position >= mLastChosenPosition) {
1406 position++;
1407 }
Adam Powell2ed547e2015-04-29 18:45:04 -07001408 return mDisplayList.get(position);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001409 }
1410
1411 public long getItemId(int position) {
1412 return position;
1413 }
1414
Adam Powell24428412015-04-01 17:19:56 -07001415 public boolean hasExtendedInfo() {
1416 return mHasExtendedInfo;
1417 }
1418
1419 public boolean hasResolvedTarget(ResolveInfo info) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001420 for (int i = 0, N = mDisplayList.size(); i < N; i++) {
1421 if (info.equals(mDisplayList.get(i).getResolveInfo())) {
Adam Powell24428412015-04-01 17:19:56 -07001422 return true;
1423 }
1424 }
1425 return false;
1426 }
1427
1428 protected int getDisplayResolveInfoCount() {
Adam Powell2ed547e2015-04-29 18:45:04 -07001429 return mDisplayList.size();
Adam Powell24428412015-04-01 17:19:56 -07001430 }
1431
1432 protected DisplayResolveInfo getDisplayResolveInfo(int index) {
Adam Powell2ed547e2015-04-29 18:45:04 -07001433 // Used to query services. We only query services for primary targets, not alternates.
1434 return mDisplayList.get(index);
Adam Powell24428412015-04-01 17:19:56 -07001435 }
1436
1437 public final View getView(int position, View convertView, ViewGroup parent) {
Adam Powellfd1e93d2014-09-07 16:52:22 -07001438 View view = convertView;
1439 if (view == null) {
Adam Powell24428412015-04-01 17:19:56 -07001440 view = createView(parent);
Adam Powellc5878612012-05-04 18:42:38 -07001441
Adam Powell0256c6f2013-05-29 16:42:33 -07001442 final ViewHolder holder = new ViewHolder(view);
1443 view.setTag(holder);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001444 }
Adam Powell278902c2014-07-12 18:33:22 -07001445 bindView(view, getItem(position));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001446 return view;
1447 }
1448
Adam Powell24428412015-04-01 17:19:56 -07001449 public View createView(ViewGroup parent) {
1450 return mInflater.inflate(
1451 com.android.internal.R.layout.resolve_list_item, parent, false);
1452 }
1453
1454 public boolean showsExtendedInfo(TargetInfo info) {
1455 return !TextUtils.isEmpty(info.getExtendedInfo());
1456 }
1457
1458 private final void bindView(View view, TargetInfo info) {
Adam Powell0256c6f2013-05-29 16:42:33 -07001459 final ViewHolder holder = (ViewHolder) view.getTag();
Adam Powell24428412015-04-01 17:19:56 -07001460 holder.text.setText(info.getDisplayLabel());
1461 if (showsExtendedInfo(info)) {
Adam Powell0256c6f2013-05-29 16:42:33 -07001462 holder.text2.setVisibility(View.VISIBLE);
Adam Powell24428412015-04-01 17:19:56 -07001463 holder.text2.setText(info.getExtendedInfo());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001464 } else {
Adam Powell0256c6f2013-05-29 16:42:33 -07001465 holder.text2.setVisibility(View.GONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001466 }
Adam Powell24428412015-04-01 17:19:56 -07001467 if (info instanceof DisplayResolveInfo
1468 && !((DisplayResolveInfo) info).hasDisplayIcon()) {
1469 new LoadAdapterIconTask((DisplayResolveInfo) info).execute();
Dianne Hackborneb034652009-09-07 00:49:58 -07001470 }
Adam Powell24428412015-04-01 17:19:56 -07001471 holder.icon.setImageDrawable(info.getDisplayIcon());
Adam Powell0256c6f2013-05-29 16:42:33 -07001472 }
1473 }
1474
Adam Powell2ed547e2015-04-29 18:45:04 -07001475 static final class ResolvedComponentInfo {
1476 public final ComponentName name;
1477 private final List<Intent> mIntents = new ArrayList<>();
1478 private final List<ResolveInfo> mResolveInfos = new ArrayList<>();
1479
1480 public ResolvedComponentInfo(ComponentName name, Intent intent, ResolveInfo info) {
1481 this.name = name;
1482 add(intent, info);
1483 }
1484
1485 public void add(Intent intent, ResolveInfo info) {
1486 mIntents.add(intent);
1487 mResolveInfos.add(info);
1488 }
1489
1490 public int getCount() {
1491 return mIntents.size();
1492 }
1493
1494 public Intent getIntentAt(int index) {
1495 return index >= 0 ? mIntents.get(index) : null;
1496 }
1497
1498 public ResolveInfo getResolveInfoAt(int index) {
1499 return index >= 0 ? mResolveInfos.get(index) : null;
1500 }
1501
1502 public int findIntent(Intent intent) {
1503 for (int i = 0, N = mIntents.size(); i < N; i++) {
1504 if (intent.equals(mIntents.get(i))) {
1505 return i;
1506 }
1507 }
1508 return -1;
1509 }
1510
1511 public int findResolveInfo(ResolveInfo info) {
1512 for (int i = 0, N = mResolveInfos.size(); i < N; i++) {
1513 if (info.equals(mResolveInfos.get(i))) {
1514 return i;
1515 }
1516 }
1517 return -1;
1518 }
1519 }
1520
Adam Powell0256c6f2013-05-29 16:42:33 -07001521 static class ViewHolder {
1522 public TextView text;
1523 public TextView text2;
1524 public ImageView icon;
1525
1526 public ViewHolder(View view) {
1527 text = (TextView) view.findViewById(com.android.internal.R.id.text1);
1528 text2 = (TextView) view.findViewById(com.android.internal.R.id.text2);
1529 icon = (ImageView) view.findViewById(R.id.icon);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001530 }
1531 }
1532
Adam Powell2d809622012-03-22 15:24:43 -07001533 class ItemLongClickListener implements AdapterView.OnItemLongClickListener {
1534
1535 @Override
1536 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Adam Powell24428412015-04-01 17:19:56 -07001537 if (mListView != null) {
1538 position -= mListView.getHeaderViewsCount();
1539 }
Adam Powellfd1e93d2014-09-07 16:52:22 -07001540 if (position < 0) {
1541 // Header views don't count.
1542 return false;
1543 }
Adam Powell278902c2014-07-12 18:33:22 -07001544 ResolveInfo ri = mAdapter.resolveInfoForPosition(position, true);
Adam Powellc5878612012-05-04 18:42:38 -07001545 showAppDetails(ri);
Adam Powell2d809622012-03-22 15:24:43 -07001546 return true;
1547 }
1548
1549 }
Adam Powell0256c6f2013-05-29 16:42:33 -07001550
Adam Powell24428412015-04-01 17:19:56 -07001551 abstract class LoadIconTask extends AsyncTask<Void, Void, Drawable> {
1552 protected final DisplayResolveInfo mDisplayResolveInfo;
1553 private final ResolveInfo mResolveInfo;
1554
1555 public LoadIconTask(DisplayResolveInfo dri) {
1556 mDisplayResolveInfo = dri;
1557 mResolveInfo = dri.getResolveInfo();
Adam Powell0256c6f2013-05-29 16:42:33 -07001558 }
1559
1560 @Override
Adam Powell24428412015-04-01 17:19:56 -07001561 protected Drawable doInBackground(Void... params) {
1562 return loadIconForResolveInfo(mResolveInfo);
1563 }
1564
1565 @Override
1566 protected void onPostExecute(Drawable d) {
1567 mDisplayResolveInfo.setDisplayIcon(d);
1568 }
1569 }
1570
1571 class LoadAdapterIconTask extends LoadIconTask {
1572 public LoadAdapterIconTask(DisplayResolveInfo dri) {
1573 super(dri);
1574 }
1575
1576 @Override
1577 protected void onPostExecute(Drawable d) {
1578 super.onPostExecute(d);
1579 if (mProfileView != null && mAdapter.getOtherProfile() == mDisplayResolveInfo) {
Adam Powell88831a22014-11-20 18:17:00 -08001580 bindProfileView();
1581 }
Adam Powell0256c6f2013-05-29 16:42:33 -07001582 mAdapter.notifyDataSetChanged();
1583 }
1584 }
Adam Powell278902c2014-07-12 18:33:22 -07001585
Adam Powell24428412015-04-01 17:19:56 -07001586 class LoadIconIntoViewTask extends LoadIconTask {
1587 private final ImageView mTargetView;
Adam Powell278902c2014-07-12 18:33:22 -07001588
Adam Powell24428412015-04-01 17:19:56 -07001589 public LoadIconIntoViewTask(DisplayResolveInfo dri, ImageView target) {
1590 super(dri);
Adam Powell278902c2014-07-12 18:33:22 -07001591 mTargetView = target;
1592 }
1593
1594 @Override
Adam Powell24428412015-04-01 17:19:56 -07001595 protected void onPostExecute(Drawable d) {
1596 super.onPostExecute(d);
1597 mTargetView.setImageDrawable(d);
Adam Powell278902c2014-07-12 18:33:22 -07001598 }
1599 }
Adam Powell09a65602014-07-20 16:23:14 -07001600
Dianne Hackbornec452d92014-11-11 17:16:56 -08001601 static final boolean isSpecificUriMatch(int match) {
1602 match = match&IntentFilter.MATCH_CATEGORY_MASK;
1603 return match >= IntentFilter.MATCH_CATEGORY_HOST
1604 && match <= IntentFilter.MATCH_CATEGORY_PATH;
1605 }
1606
Adam Powell2ed547e2015-04-29 18:45:04 -07001607 class ResolverComparator implements Comparator<ResolvedComponentInfo> {
Adam Powell09a65602014-07-20 16:23:14 -07001608 private final Collator mCollator;
Dianne Hackbornec452d92014-11-11 17:16:56 -08001609 private final boolean mHttp;
Adam Powell09a65602014-07-20 16:23:14 -07001610
Dianne Hackbornec452d92014-11-11 17:16:56 -08001611 public ResolverComparator(Context context, Intent intent) {
Adam Powell09a65602014-07-20 16:23:14 -07001612 mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
Dianne Hackbornec452d92014-11-11 17:16:56 -08001613 String scheme = intent.getScheme();
1614 mHttp = "http".equals(scheme) || "https".equals(scheme);
Adam Powell09a65602014-07-20 16:23:14 -07001615 }
1616
1617 @Override
Adam Powell2ed547e2015-04-29 18:45:04 -07001618 public int compare(ResolvedComponentInfo lhsp, ResolvedComponentInfo rhsp) {
1619 final ResolveInfo lhs = lhsp.getResolveInfoAt(0);
1620 final ResolveInfo rhs = rhsp.getResolveInfoAt(0);
1621
Adam Powell09a65602014-07-20 16:23:14 -07001622 // We want to put the one targeted to another user at the end of the dialog.
1623 if (lhs.targetUserId != UserHandle.USER_CURRENT) {
1624 return 1;
1625 }
Adam Powell09a65602014-07-20 16:23:14 -07001626
Dianne Hackbornec452d92014-11-11 17:16:56 -08001627 if (mHttp) {
1628 // Special case: we want filters that match URI paths/schemes to be
1629 // ordered before others. This is for the case when opening URIs,
1630 // to make native apps go above browsers.
1631 final boolean lhsSpecific = isSpecificUriMatch(lhs.match);
1632 final boolean rhsSpecific = isSpecificUriMatch(rhs.match);
1633 if (lhsSpecific != rhsSpecific) {
1634 return lhsSpecific ? -1 : 1;
1635 }
1636 }
1637
Adam Powell09a65602014-07-20 16:23:14 -07001638 if (mStats != null) {
1639 final long timeDiff =
1640 getPackageTimeSpent(rhs.activityInfo.packageName) -
1641 getPackageTimeSpent(lhs.activityInfo.packageName);
1642
1643 if (timeDiff != 0) {
1644 return timeDiff > 0 ? 1 : -1;
1645 }
1646 }
1647
1648 CharSequence sa = lhs.loadLabel(mPm);
1649 if (sa == null) sa = lhs.activityInfo.name;
1650 CharSequence sb = rhs.loadLabel(mPm);
1651 if (sb == null) sb = rhs.activityInfo.name;
1652
1653 return mCollator.compare(sa.toString(), sb.toString());
1654 }
1655
1656 private long getPackageTimeSpent(String packageName) {
1657 if (mStats != null) {
Adam Lesinski35168002014-07-21 15:25:30 -07001658 final UsageStats stats = mStats.get(packageName);
Adam Powell09a65602014-07-20 16:23:14 -07001659 if (stats != null) {
Adam Lesinski35168002014-07-21 15:25:30 -07001660 return stats.getTotalTimeInForeground();
Adam Powell09a65602014-07-20 16:23:14 -07001661 }
Adam Powell09a65602014-07-20 16:23:14 -07001662 }
1663 return 0;
1664 }
1665 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001666}