blob: 591267e51871fddac1510ebe735cff2f34dc236d [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 Powell0256c6f2013-05-29 16:42:33 -070019import android.os.AsyncTask;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import com.android.internal.R;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -080021import com.android.internal.content.PackageMonitor;
22
Adam Powellc5878612012-05-04 18:42:38 -070023import android.app.ActivityManager;
Dianne Hackborn5320eb82012-05-18 12:05:04 -070024import android.app.ActivityManagerNative;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -070025import android.app.AppGlobals;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.content.ComponentName;
27import android.content.Context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.content.Intent;
29import android.content.IntentFilter;
30import android.content.pm.ActivityInfo;
Dianne Hackborneb034652009-09-07 00:49:58 -070031import android.content.pm.LabeledIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.content.pm.PackageManager;
Adam Powellc5878612012-05-04 18:42:38 -070033import android.content.pm.PackageManager.NameNotFoundException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.content.pm.ResolveInfo;
Adam Powellc5878612012-05-04 18:42:38 -070035import android.content.res.Resources;
Dianne Hackborneb034652009-09-07 00:49:58 -070036import android.graphics.drawable.Drawable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.net.Uri;
38import android.os.Bundle;
39import android.os.PatternMatcher;
Dianne Hackborn5320eb82012-05-18 12:05:04 -070040import android.os.RemoteException;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070041import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.util.Log;
Adam Powell2d809622012-03-22 15:24:43 -070043import android.view.LayoutInflater;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import android.view.View;
45import android.view.ViewGroup;
Adam Powell2d809622012-03-22 15:24:43 -070046import android.widget.AdapterView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.widget.BaseAdapter;
Adam Powellc5878612012-05-04 18:42:38 -070048import android.widget.Button;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.widget.ImageView;
Adam Powell2d809622012-03-22 15:24:43 -070050import android.widget.ListView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.widget.TextView;
Adam Powell2d809622012-03-22 15:24:43 -070052
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import java.util.ArrayList;
54import java.util.Collections;
55import java.util.HashSet;
56import java.util.Iterator;
57import java.util.List;
58import java.util.Set;
59
60/**
61 * This activity is displayed when the system attempts to start an Intent for
62 * which there is more than one matching activity, allowing the user to decide
63 * which to go to. It is not normally used directly by application developers.
64 */
Adam Powellc5878612012-05-04 18:42:38 -070065public class ResolverActivity extends AlertActivity implements AdapterView.OnItemClickListener {
66 private static final String TAG = "ResolverActivity";
You Kim43a5070e2012-11-21 23:23:45 +090067 private static final boolean DEBUG = false;
Adam Powellc5878612012-05-04 18:42:38 -070068
Dianne Hackborn5320eb82012-05-18 12:05:04 -070069 private int mLaunchedFromUid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 private ResolveListAdapter mAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071 private PackageManager mPm;
Adam Powellc5878612012-05-04 18:42:38 -070072 private boolean mAlwaysUseOption;
73 private boolean mShowExtended;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -070074 private ListView mListView;
Adam Powellc5878612012-05-04 18:42:38 -070075 private Button mAlwaysButton;
76 private Button mOnceButton;
77 private int mIconDpi;
78 private int mIconSize;
Adam Powell589e6f92012-05-06 20:52:38 -070079 private int mMaxColumns;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -070080 private int mLastSelected = ListView.INVALID_POSITION;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081
Dianne Hackbornd44713a2012-04-30 16:34:46 -070082 private boolean mRegistered;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -080083 private final PackageMonitor mPackageMonitor = new PackageMonitor() {
84 @Override public void onSomePackagesChanged() {
85 mAdapter.handlePackagesChanged();
86 }
87 };
88
Dianne Hackborn905577f2011-09-07 18:31:28 -070089 private Intent makeMyIntent() {
90 Intent intent = new Intent(getIntent());
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -070091 intent.setComponent(null);
Dianne Hackborn905577f2011-09-07 18:31:28 -070092 // The resolver activity is set to be hidden from recent tasks.
93 // we don't want this attribute to be propagated to the next activity
94 // being launched. Note that if the original Intent also had this
95 // flag set, we are now losing it. That should be a very rare case
96 // and we can live with this.
97 intent.setFlags(intent.getFlags()&~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
98 return intent;
99 }
100
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 @Override
102 protected void onCreate(Bundle savedInstanceState) {
Christopher Tateb72b3632013-09-30 17:50:32 -0700103 // Use a specialized prompt when we're handling the 'Home' app startActivity()
104 final int titleResource;
105 final Intent intent = makeMyIntent();
106 final Set<String> categories = intent.getCategories();
107 if (Intent.ACTION_MAIN.equals(intent.getAction())
108 && categories != null
109 && categories.size() == 1
110 && categories.contains(Intent.CATEGORY_HOME)) {
111 titleResource = com.android.internal.R.string.whichHomeApplication;
112 } else {
113 titleResource = com.android.internal.R.string.whichApplication;
114 }
115
116 onCreate(savedInstanceState, intent, getResources().getText(titleResource),
Jeff Hamiltond88e9aa2011-01-24 14:53:00 -0600117 null, null, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118 }
119
120 protected void onCreate(Bundle savedInstanceState, Intent intent,
Jeff Hamiltond88e9aa2011-01-24 14:53:00 -0600121 CharSequence title, Intent[] initialIntents, List<ResolveInfo> rList,
Mike Lockwoodbce6f8f2011-03-13 17:26:52 -0400122 boolean alwaysUseOption) {
Adam Powellc5878612012-05-04 18:42:38 -0700123 setTheme(R.style.Theme_DeviceDefault_Light_Dialog_Alert);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 super.onCreate(savedInstanceState);
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700125 try {
126 mLaunchedFromUid = ActivityManagerNative.getDefault().getLaunchedFromUid(
127 getActivityToken());
128 } catch (RemoteException e) {
129 mLaunchedFromUid = -1;
130 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131 mPm = getPackageManager();
Adam Powellc5878612012-05-04 18:42:38 -0700132 mAlwaysUseOption = alwaysUseOption;
Adam Powell589e6f92012-05-06 20:52:38 -0700133 mMaxColumns = getResources().getInteger(R.integer.config_maxResolverActivityColumns);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134
135 AlertController.AlertParams ap = mAlertParams;
136
137 ap.mTitle = title;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138
Dianne Hackbornd0d75032012-04-19 23:12:09 -0700139 mPackageMonitor.register(this, getMainLooper(), false);
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700140 mRegistered = true;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800141
Adam Powellc5878612012-05-04 18:42:38 -0700142 final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
143 mIconDpi = am.getLauncherLargeIconDensity();
144 mIconSize = am.getLauncherLargeIconSize();
145
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700146 mAdapter = new ResolveListAdapter(this, intent, initialIntents, rList,
147 mLaunchedFromUid);
Mike Lockwood02eb8742011-02-27 09:10:37 -0800148 int count = mAdapter.getCount();
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700149 if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) {
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700150 // Gulp!
151 finish();
152 return;
153 } else if (count > 1) {
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700154 ap.mView = getLayoutInflater().inflate(R.layout.resolver_list, null);
155 mListView = (ListView) ap.mView.findViewById(R.id.resolver_list);
156 mListView.setAdapter(mAdapter);
157 mListView.setOnItemClickListener(this);
158 mListView.setOnItemLongClickListener(new ItemLongClickListener());
Adam Powellc5878612012-05-04 18:42:38 -0700159
160 if (alwaysUseOption) {
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700161 mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
Adam Powellc5878612012-05-04 18:42:38 -0700162 }
Mike Lockwood02eb8742011-02-27 09:10:37 -0800163 } else if (count == 1) {
Amith Yamasani203a2f42012-10-03 20:16:40 -0700164 startActivity(mAdapter.intentForPosition(0));
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700165 mPackageMonitor.unregister();
166 mRegistered = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 finish();
168 return;
169 } else {
Adam Powellc5878612012-05-04 18:42:38 -0700170 ap.mMessage = getResources().getText(R.string.noApplications);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 }
172
173 setupAlert();
Adam Powell2d809622012-03-22 15:24:43 -0700174
Adam Powellc5878612012-05-04 18:42:38 -0700175 if (alwaysUseOption) {
176 final ViewGroup buttonLayout = (ViewGroup) findViewById(R.id.button_bar);
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700177 if (buttonLayout != null) {
178 buttonLayout.setVisibility(View.VISIBLE);
179 mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
180 mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
181 } else {
182 mAlwaysUseOption = false;
183 }
Amith Yamasani34d41e92013-10-23 16:05:29 -0700184 // Set the initial highlight if there was a preferred or last used choice
185 final int initialHighlight = mAdapter.getInitialHighlight();
186 if (initialHighlight >= 0) {
187 mListView.setItemChecked(initialHighlight, true);
188 onItemClick(null, null, initialHighlight, 0); // Other entries are not used
189 }
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700190 }
Adam Powellc5878612012-05-04 18:42:38 -0700191 }
192
193 Drawable getIcon(Resources res, int resId) {
194 Drawable result;
195 try {
196 result = res.getDrawableForDensity(resId, mIconDpi);
197 } catch (Resources.NotFoundException e) {
198 result = null;
199 }
200
201 return result;
202 }
203
204 Drawable loadIconForResolveInfo(ResolveInfo ri) {
205 Drawable dr;
206 try {
207 if (ri.resolvePackageName != null && ri.icon != 0) {
208 dr = getIcon(mPm.getResourcesForApplication(ri.resolvePackageName), ri.icon);
209 if (dr != null) {
210 return dr;
211 }
212 }
213 final int iconRes = ri.getIconResource();
214 if (iconRes != 0) {
215 dr = getIcon(mPm.getResourcesForApplication(ri.activityInfo.packageName), iconRes);
216 if (dr != null) {
217 return dr;
218 }
219 }
220 } catch (NameNotFoundException e) {
221 Log.e(TAG, "Couldn't find resources for package", e);
222 }
223 return ri.loadIcon(mPm);
224 }
225
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800226 @Override
227 protected void onRestart() {
228 super.onRestart();
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700229 if (!mRegistered) {
230 mPackageMonitor.register(this, getMainLooper(), false);
231 mRegistered = true;
232 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800233 mAdapter.handlePackagesChanged();
234 }
235
236 @Override
237 protected void onStop() {
238 super.onStop();
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700239 if (mRegistered) {
240 mPackageMonitor.unregister();
241 mRegistered = false;
242 }
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700243 if ((getIntent().getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
244 // This resolver is in the unusual situation where it has been
245 // launched at the top of a new task. We don't let it be added
246 // to the recent tasks shown to the user, and we need to make sure
247 // that each time we are launched we get the correct launching
248 // uid (not re-using the same resolver from an old launching uid),
249 // so we will now finish ourself since being no longer visible,
250 // the user probably can't get back to us.
251 if (!isChangingConfigurations()) {
252 finish();
253 }
254 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800255 }
256
Adam Powellc5878612012-05-04 18:42:38 -0700257 @Override
Adam Powell9bee4662012-05-08 11:07:23 -0700258 protected void onRestoreInstanceState(Bundle savedInstanceState) {
259 super.onRestoreInstanceState(savedInstanceState);
260 if (mAlwaysUseOption) {
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700261 final int checkedPos = mListView.getCheckedItemPosition();
262 final boolean enabled = checkedPos != ListView.INVALID_POSITION;
Adam Powelld81cc4a2012-07-19 13:51:39 -0700263 mLastSelected = checkedPos;
Adam Powell9bee4662012-05-08 11:07:23 -0700264 mAlwaysButton.setEnabled(enabled);
265 mOnceButton.setEnabled(enabled);
266 if (enabled) {
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700267 mListView.setSelection(checkedPos);
Adam Powell9bee4662012-05-08 11:07:23 -0700268 }
269 }
270 }
271
272 @Override
Adam Powellc5878612012-05-04 18:42:38 -0700273 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700274 final int checkedPos = mListView.getCheckedItemPosition();
275 final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
Adam Powellbdda4e72012-07-20 11:22:03 -0700276 if (mAlwaysUseOption && (!hasValidSelection || mLastSelected != checkedPos)) {
Adam Powelld81cc4a2012-07-19 13:51:39 -0700277 mAlwaysButton.setEnabled(hasValidSelection);
278 mOnceButton.setEnabled(hasValidSelection);
279 if (hasValidSelection) {
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700280 mListView.smoothScrollToPosition(checkedPos);
Adam Powell9bee4662012-05-08 11:07:23 -0700281 }
Adam Powelld81cc4a2012-07-19 13:51:39 -0700282 mLastSelected = checkedPos;
Adam Powellc5878612012-05-04 18:42:38 -0700283 } else {
284 startSelected(position, false);
285 }
286 }
287
288 public void onButtonClick(View v) {
289 final int id = v.getId();
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700290 startSelected(mListView.getCheckedItemPosition(), id == R.id.button_always);
Adam Powellc5878612012-05-04 18:42:38 -0700291 dismiss();
292 }
293
294 void startSelected(int which, boolean always) {
Amith Yamasani07cd3512013-09-18 13:16:00 -0700295 if (isFinishing()) {
296 return;
297 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 ResolveInfo ri = mAdapter.resolveInfoForPosition(which);
299 Intent intent = mAdapter.intentForPosition(which);
Adam Powellc5878612012-05-04 18:42:38 -0700300 onIntentSelected(ri, intent, always);
Mike Lockwood02eb8742011-02-27 09:10:37 -0800301 finish();
302 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303
Mike Lockwood02eb8742011-02-27 09:10:37 -0800304 protected void onIntentSelected(ResolveInfo ri, Intent intent, boolean alwaysCheck) {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700305 if (mAlwaysUseOption && mAdapter.mOrigResolveList != null) {
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700306 // Build a reasonable intent filter, based on what matched.
307 IntentFilter filter = new IntentFilter();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700309 if (intent.getAction() != null) {
310 filter.addAction(intent.getAction());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700312 Set<String> categories = intent.getCategories();
313 if (categories != null) {
314 for (String cat : categories) {
315 filter.addCategory(cat);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 }
317 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700318 filter.addCategory(Intent.CATEGORY_DEFAULT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700320 int cat = ri.match&IntentFilter.MATCH_CATEGORY_MASK;
321 Uri data = intent.getData();
322 if (cat == IntentFilter.MATCH_CATEGORY_TYPE) {
323 String mimeType = intent.resolveType(this);
324 if (mimeType != null) {
325 try {
326 filter.addDataType(mimeType);
327 } catch (IntentFilter.MalformedMimeTypeException e) {
328 Log.w("ResolverActivity", e);
329 filter = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 }
331 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700332 }
333 if (data != null && data.getScheme() != null) {
334 // We need the data specification if there was no type,
335 // OR if the scheme is not one of our magical "file:"
336 // or "content:" schemes (see IntentFilter for the reason).
337 if (cat != IntentFilter.MATCH_CATEGORY_TYPE
338 || (!"file".equals(data.getScheme())
339 && !"content".equals(data.getScheme()))) {
340 filter.addDataScheme(data.getScheme());
341
342 // Look through the resolved filter to determine which part
343 // of it matched the original Intent.
344 Iterator<PatternMatcher> pIt = ri.filter.schemeSpecificPartsIterator();
345 if (pIt != null) {
346 String ssp = data.getSchemeSpecificPart();
347 while (ssp != null && pIt.hasNext()) {
348 PatternMatcher p = pIt.next();
349 if (p.match(ssp)) {
350 filter.addDataSchemeSpecificPart(p.getPath(), p.getType());
351 break;
352 }
Dianne Hackborndf1c0bf2013-06-12 16:21:38 -0700353 }
354 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700355 Iterator<IntentFilter.AuthorityEntry> aIt = ri.filter.authoritiesIterator();
356 if (aIt != null) {
357 while (aIt.hasNext()) {
358 IntentFilter.AuthorityEntry a = aIt.next();
359 if (a.match(data) >= 0) {
360 int port = a.getPort();
361 filter.addDataAuthority(a.getHost(),
362 port >= 0 ? Integer.toString(port) : null);
363 break;
364 }
365 }
366 }
367 pIt = ri.filter.pathsIterator();
368 if (pIt != null) {
369 String path = data.getPath();
370 while (path != null && pIt.hasNext()) {
371 PatternMatcher p = pIt.next();
372 if (p.match(path)) {
373 filter.addDataPath(p.getPath(), p.getType());
374 break;
375 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376 }
377 }
378 }
379 }
380
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700381 if (filter != null) {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700382 final int N = mAdapter.mOrigResolveList.size();
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700383 ComponentName[] set = new ComponentName[N];
384 int bestMatch = 0;
385 for (int i=0; i<N; i++) {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700386 ResolveInfo r = mAdapter.mOrigResolveList.get(i);
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700387 set[i] = new ComponentName(r.activityInfo.packageName,
388 r.activityInfo.name);
389 if (r.match > bestMatch) bestMatch = r.match;
390 }
391 if (alwaysCheck) {
392 getPackageManager().addPreferredActivity(filter, bestMatch, set,
393 intent.getComponent());
394 } else {
395 try {
396 AppGlobals.getPackageManager().setLastChosenActivity(intent,
397 intent.resolveTypeIfNeeded(getContentResolver()),
398 PackageManager.MATCH_DEFAULT_ONLY,
399 filter, bestMatch, intent.getComponent());
400 } catch (RemoteException re) {
401 Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
402 }
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700403 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 }
405 }
406
407 if (intent != null) {
Amith Yamasani203a2f42012-10-03 20:16:40 -0700408 startActivity(intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410 }
411
Adam Powellc5878612012-05-04 18:42:38 -0700412 void showAppDetails(ResolveInfo ri) {
413 Intent in = new Intent().setAction("android.settings.APPLICATION_DETAILS_SETTINGS")
Adam Powell0fc5b2b2012-07-18 18:20:29 -0700414 .setData(Uri.fromParts("package", ri.activityInfo.packageName, null))
415 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
Amith Yamasani203a2f42012-10-03 20:16:40 -0700416 startActivity(in);
Adam Powellc5878612012-05-04 18:42:38 -0700417 }
418
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 private final class DisplayResolveInfo {
420 ResolveInfo ri;
421 CharSequence displayLabel;
Dianne Hackborneb034652009-09-07 00:49:58 -0700422 Drawable displayIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 CharSequence extendedInfo;
Dianne Hackborneb034652009-09-07 00:49:58 -0700424 Intent origIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425
Dianne Hackborneb034652009-09-07 00:49:58 -0700426 DisplayResolveInfo(ResolveInfo pri, CharSequence pLabel,
427 CharSequence pInfo, Intent pOrigIntent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 ri = pri;
429 displayLabel = pLabel;
430 extendedInfo = pInfo;
Dianne Hackborneb034652009-09-07 00:49:58 -0700431 origIntent = pOrigIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 }
433 }
434
435 private final class ResolveListAdapter extends BaseAdapter {
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800436 private final Intent[] mInitialIntents;
437 private final List<ResolveInfo> mBaseResolveList;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700438 private ResolveInfo mLastChosen;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800439 private final Intent mIntent;
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700440 private final int mLaunchedFromUid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800441 private final LayoutInflater mInflater;
442
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700443 List<DisplayResolveInfo> mList;
444 List<ResolveInfo> mOrigResolveList;
445
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700446 private int mInitialHighlight = -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447
Dianne Hackborneb034652009-09-07 00:49:58 -0700448 public ResolveListAdapter(Context context, Intent intent,
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700449 Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450 mIntent = new Intent(intent);
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800451 mInitialIntents = initialIntents;
452 mBaseResolveList = rList;
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700453 mLaunchedFromUid = launchedFromUid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
You Kim43a5070e2012-11-21 23:23:45 +0900455 mList = new ArrayList<DisplayResolveInfo>();
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800456 rebuildList();
457 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800458
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800459 public void handlePackagesChanged() {
Adam Powellc5878612012-05-04 18:42:38 -0700460 final int oldItemCount = getCount();
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800461 rebuildList();
462 notifyDataSetChanged();
You Kim43a5070e2012-11-21 23:23:45 +0900463 final int newItemCount = getCount();
464 if (newItemCount == 0) {
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800465 // We no longer have any items... just finish the activity.
466 finish();
Adam Powellc5878612012-05-04 18:42:38 -0700467 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800468 }
469
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700470 public int getInitialHighlight() {
471 return mInitialHighlight;
472 }
473
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800474 private void rebuildList() {
You Kim43a5070e2012-11-21 23:23:45 +0900475 List<ResolveInfo> currentResolveList;
476
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700477 try {
478 mLastChosen = AppGlobals.getPackageManager().getLastChosenActivity(
479 mIntent, mIntent.resolveTypeIfNeeded(getContentResolver()),
480 PackageManager.MATCH_DEFAULT_ONLY);
481 } catch (RemoteException re) {
482 Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
483 }
484
You Kim43a5070e2012-11-21 23:23:45 +0900485 mList.clear();
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800486 if (mBaseResolveList != null) {
You Kim43a5070e2012-11-21 23:23:45 +0900487 currentResolveList = mBaseResolveList;
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700488 mOrigResolveList = null;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800489 } else {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700490 currentResolveList = mOrigResolveList = mPm.queryIntentActivities(
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800491 mIntent, PackageManager.MATCH_DEFAULT_ONLY
Amith Yamasani203a2f42012-10-03 20:16:40 -0700492 | (mAlwaysUseOption ? PackageManager.GET_RESOLVED_FILTER : 0));
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700493 // Filter out any activities that the launched uid does not
494 // have permission for. We don't do this when we have an explicit
495 // list of resolved activities, because that only happens when
496 // we are being subclassed, so we can safely launch whatever
497 // they gave us.
You Kim43a5070e2012-11-21 23:23:45 +0900498 if (currentResolveList != null) {
499 for (int i=currentResolveList.size()-1; i >= 0; i--) {
500 ActivityInfo ai = currentResolveList.get(i).activityInfo;
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700501 int granted = ActivityManager.checkComponentPermission(
502 ai.permission, mLaunchedFromUid,
503 ai.applicationInfo.uid, ai.exported);
504 if (granted != PackageManager.PERMISSION_GRANTED) {
505 // Access not allowed!
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700506 if (mOrigResolveList == currentResolveList) {
507 mOrigResolveList = new ArrayList<ResolveInfo>(mOrigResolveList);
508 }
You Kim43a5070e2012-11-21 23:23:45 +0900509 currentResolveList.remove(i);
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700510 }
511 }
512 }
Jeff Hamiltond88e9aa2011-01-24 14:53:00 -0600513 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 int N;
You Kim43a5070e2012-11-21 23:23:45 +0900515 if ((currentResolveList != null) && ((N = currentResolveList.size()) > 0)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 // Only display the first matches that are either of equal
517 // priority or have asked to be default options.
You Kim43a5070e2012-11-21 23:23:45 +0900518 ResolveInfo r0 = currentResolveList.get(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800519 for (int i=1; i<N; i++) {
You Kim43a5070e2012-11-21 23:23:45 +0900520 ResolveInfo ri = currentResolveList.get(i);
521 if (DEBUG) Log.v(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800522 "ResolveListActivity",
523 r0.activityInfo.name + "=" +
524 r0.priority + "/" + r0.isDefault + " vs " +
525 ri.activityInfo.name + "=" +
526 ri.priority + "/" + ri.isDefault);
You Kim43a5070e2012-11-21 23:23:45 +0900527 if (r0.priority != ri.priority ||
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528 r0.isDefault != ri.isDefault) {
529 while (i < N) {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700530 if (mOrigResolveList == currentResolveList) {
531 mOrigResolveList = new ArrayList<ResolveInfo>(mOrigResolveList);
532 }
You Kim43a5070e2012-11-21 23:23:45 +0900533 currentResolveList.remove(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800534 N--;
535 }
536 }
537 }
538 if (N > 1) {
539 ResolveInfo.DisplayNameComparator rComparator =
540 new ResolveInfo.DisplayNameComparator(mPm);
You Kim43a5070e2012-11-21 23:23:45 +0900541 Collections.sort(currentResolveList, rComparator);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800542 }
Dianne Hackborneb034652009-09-07 00:49:58 -0700543 // First put the initial items at the top.
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800544 if (mInitialIntents != null) {
545 for (int i=0; i<mInitialIntents.length; i++) {
546 Intent ii = mInitialIntents[i];
Dianne Hackborneb034652009-09-07 00:49:58 -0700547 if (ii == null) {
548 continue;
549 }
550 ActivityInfo ai = ii.resolveActivityInfo(
551 getPackageManager(), 0);
552 if (ai == null) {
553 Log.w("ResolverActivity", "No activity found for "
554 + ii);
555 continue;
556 }
557 ResolveInfo ri = new ResolveInfo();
558 ri.activityInfo = ai;
559 if (ii instanceof LabeledIntent) {
560 LabeledIntent li = (LabeledIntent)ii;
561 ri.resolvePackageName = li.getSourcePackage();
562 ri.labelRes = li.getLabelResource();
563 ri.nonLocalizedLabel = li.getNonLocalizedLabel();
564 ri.icon = li.getIconResource();
565 }
566 mList.add(new DisplayResolveInfo(ri,
567 ri.loadLabel(getPackageManager()), null, ii));
568 }
569 }
You Kim43a5070e2012-11-21 23:23:45 +0900570
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571 // Check for applications with same name and use application name or
572 // package name if necessary
You Kim43a5070e2012-11-21 23:23:45 +0900573 r0 = currentResolveList.get(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800574 int start = 0;
575 CharSequence r0Label = r0.loadLabel(mPm);
Adam Powellc5878612012-05-04 18:42:38 -0700576 mShowExtended = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800577 for (int i = 1; i < N; i++) {
578 if (r0Label == null) {
579 r0Label = r0.activityInfo.packageName;
580 }
You Kim43a5070e2012-11-21 23:23:45 +0900581 ResolveInfo ri = currentResolveList.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800582 CharSequence riLabel = ri.loadLabel(mPm);
583 if (riLabel == null) {
584 riLabel = ri.activityInfo.packageName;
585 }
586 if (riLabel.equals(r0Label)) {
587 continue;
588 }
You Kim43a5070e2012-11-21 23:23:45 +0900589 processGroup(currentResolveList, start, (i-1), r0, r0Label);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590 r0 = ri;
591 r0Label = riLabel;
592 start = i;
593 }
594 // Process last group
You Kim43a5070e2012-11-21 23:23:45 +0900595 processGroup(currentResolveList, start, (N-1), r0, r0Label);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800596 }
597 }
598
599 private void processGroup(List<ResolveInfo> rList, int start, int end, ResolveInfo ro,
600 CharSequence roLabel) {
601 // Process labels from start to i
602 int num = end - start+1;
603 if (num == 1) {
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700604 if (mLastChosen != null
605 && mLastChosen.activityInfo.packageName.equals(
606 ro.activityInfo.packageName)
607 && mLastChosen.activityInfo.name.equals(ro.activityInfo.name)) {
608 mInitialHighlight = mList.size();
609 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610 // No duplicate labels. Use label for entry at start
Dianne Hackborneb034652009-09-07 00:49:58 -0700611 mList.add(new DisplayResolveInfo(ro, roLabel, null, null));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612 } else {
Adam Powellc5878612012-05-04 18:42:38 -0700613 mShowExtended = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800614 boolean usePkg = false;
615 CharSequence startApp = ro.activityInfo.applicationInfo.loadLabel(mPm);
616 if (startApp == null) {
617 usePkg = true;
618 }
619 if (!usePkg) {
620 // Use HashSet to track duplicates
621 HashSet<CharSequence> duplicates =
622 new HashSet<CharSequence>();
623 duplicates.add(startApp);
624 for (int j = start+1; j <= end ; j++) {
625 ResolveInfo jRi = rList.get(j);
626 CharSequence jApp = jRi.activityInfo.applicationInfo.loadLabel(mPm);
627 if ( (jApp == null) || (duplicates.contains(jApp))) {
628 usePkg = true;
629 break;
630 } else {
631 duplicates.add(jApp);
632 }
633 }
634 // Clear HashSet for later use
635 duplicates.clear();
636 }
637 for (int k = start; k <= end; k++) {
638 ResolveInfo add = rList.get(k);
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700639 if (mLastChosen != null
640 && mLastChosen.activityInfo.packageName.equals(
641 add.activityInfo.packageName)
642 && mLastChosen.activityInfo.name.equals(add.activityInfo.name)) {
643 mInitialHighlight = mList.size();
644 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800645 if (usePkg) {
646 // Use application name for all entries from start to end-1
647 mList.add(new DisplayResolveInfo(add, roLabel,
Dianne Hackborneb034652009-09-07 00:49:58 -0700648 add.activityInfo.packageName, null));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800649 } else {
650 // Use package name for all entries from start to end-1
651 mList.add(new DisplayResolveInfo(add, roLabel,
Dianne Hackborneb034652009-09-07 00:49:58 -0700652 add.activityInfo.applicationInfo.loadLabel(mPm), null));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800653 }
654 }
655 }
656 }
657
658 public ResolveInfo resolveInfoForPosition(int position) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800659 return mList.get(position).ri;
660 }
661
662 public Intent intentForPosition(int position) {
Dianne Hackborneb034652009-09-07 00:49:58 -0700663 DisplayResolveInfo dri = mList.get(position);
664
665 Intent intent = new Intent(dri.origIntent != null
666 ? dri.origIntent : mIntent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800667 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
668 |Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
Dianne Hackborneb034652009-09-07 00:49:58 -0700669 ActivityInfo ai = dri.ri.activityInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800670 intent.setComponent(new ComponentName(
671 ai.applicationInfo.packageName, ai.name));
672 return intent;
673 }
674
675 public int getCount() {
You Kim43a5070e2012-11-21 23:23:45 +0900676 return mList.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800677 }
678
679 public Object getItem(int position) {
You Kim43a5070e2012-11-21 23:23:45 +0900680 return mList.get(position);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800681 }
682
683 public long getItemId(int position) {
684 return position;
685 }
686
687 public View getView(int position, View convertView, ViewGroup parent) {
688 View view;
689 if (convertView == null) {
690 view = mInflater.inflate(
691 com.android.internal.R.layout.resolve_list_item, parent, false);
Adam Powellc5878612012-05-04 18:42:38 -0700692
Adam Powell0256c6f2013-05-29 16:42:33 -0700693 final ViewHolder holder = new ViewHolder(view);
694 view.setTag(holder);
695
Adam Powellc5878612012-05-04 18:42:38 -0700696 // Fix the icon size even if we have different sized resources
Adam Powell0256c6f2013-05-29 16:42:33 -0700697 ViewGroup.LayoutParams lp = holder.icon.getLayoutParams();
Adam Powellc5878612012-05-04 18:42:38 -0700698 lp.width = lp.height = mIconSize;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699 } else {
700 view = convertView;
701 }
702 bindView(view, mList.get(position));
703 return view;
704 }
705
706 private final void bindView(View view, DisplayResolveInfo info) {
Adam Powell0256c6f2013-05-29 16:42:33 -0700707 final ViewHolder holder = (ViewHolder) view.getTag();
708 holder.text.setText(info.displayLabel);
Adam Powellc5878612012-05-04 18:42:38 -0700709 if (mShowExtended) {
Adam Powell0256c6f2013-05-29 16:42:33 -0700710 holder.text2.setVisibility(View.VISIBLE);
711 holder.text2.setText(info.extendedInfo);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800712 } else {
Adam Powell0256c6f2013-05-29 16:42:33 -0700713 holder.text2.setVisibility(View.GONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800714 }
Dianne Hackborneb034652009-09-07 00:49:58 -0700715 if (info.displayIcon == null) {
Adam Powell0256c6f2013-05-29 16:42:33 -0700716 new LoadIconTask().execute(info);
Dianne Hackborneb034652009-09-07 00:49:58 -0700717 }
Adam Powell0256c6f2013-05-29 16:42:33 -0700718 holder.icon.setImageDrawable(info.displayIcon);
719 }
720 }
721
722 static class ViewHolder {
723 public TextView text;
724 public TextView text2;
725 public ImageView icon;
726
727 public ViewHolder(View view) {
728 text = (TextView) view.findViewById(com.android.internal.R.id.text1);
729 text2 = (TextView) view.findViewById(com.android.internal.R.id.text2);
730 icon = (ImageView) view.findViewById(R.id.icon);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800731 }
732 }
733
Adam Powell2d809622012-03-22 15:24:43 -0700734 class ItemLongClickListener implements AdapterView.OnItemLongClickListener {
735
736 @Override
737 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
738 ResolveInfo ri = mAdapter.resolveInfoForPosition(position);
Adam Powellc5878612012-05-04 18:42:38 -0700739 showAppDetails(ri);
Adam Powell2d809622012-03-22 15:24:43 -0700740 return true;
741 }
742
743 }
Adam Powell0256c6f2013-05-29 16:42:33 -0700744
745 class LoadIconTask extends AsyncTask<DisplayResolveInfo, Void, DisplayResolveInfo> {
746 @Override
747 protected DisplayResolveInfo doInBackground(DisplayResolveInfo... params) {
748 final DisplayResolveInfo info = params[0];
749 if (info.displayIcon == null) {
750 info.displayIcon = loadIconForResolveInfo(info.ri);
751 }
752 return info;
753 }
754
755 @Override
756 protected void onPostExecute(DisplayResolveInfo info) {
757 mAdapter.notifyDataSetChanged();
758 }
759 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760}
761