blob: aa947287891d7b1e1d75cab62174caec9ed37c84 [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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.content.ComponentName;
26import android.content.Context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.pm.ActivityInfo;
Dianne Hackborneb034652009-09-07 00:49:58 -070030import android.content.pm.LabeledIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.content.pm.PackageManager;
Adam Powellc5878612012-05-04 18:42:38 -070032import android.content.pm.PackageManager.NameNotFoundException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.content.pm.ResolveInfo;
Adam Powellc5878612012-05-04 18:42:38 -070034import android.content.res.Resources;
Dianne Hackborneb034652009-09-07 00:49:58 -070035import android.graphics.drawable.Drawable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.net.Uri;
37import android.os.Bundle;
38import android.os.PatternMatcher;
Dianne Hackborn5320eb82012-05-18 12:05:04 -070039import android.os.RemoteException;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070040import android.os.UserHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.util.Log;
Adam Powell2d809622012-03-22 15:24:43 -070042import android.view.LayoutInflater;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.view.View;
44import android.view.ViewGroup;
Adam Powell2d809622012-03-22 15:24:43 -070045import android.widget.AdapterView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.widget.BaseAdapter;
Adam Powellc5878612012-05-04 18:42:38 -070047import android.widget.Button;
48import android.widget.GridView;
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;
74 private GridView mGrid;
75 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;
Adam Powelld81cc4a2012-07-19 13:51:39 -070080 private int mLastSelected = GridView.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());
91 // The resolver activity is set to be hidden from recent tasks.
92 // we don't want this attribute to be propagated to the next activity
93 // being launched. Note that if the original Intent also had this
94 // flag set, we are now losing it. That should be a very rare case
95 // and we can live with this.
96 intent.setFlags(intent.getFlags()&~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
97 return intent;
98 }
99
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100 @Override
101 protected void onCreate(Bundle savedInstanceState) {
Dianne Hackborn905577f2011-09-07 18:31:28 -0700102 onCreate(savedInstanceState, makeMyIntent(),
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 getResources().getText(com.android.internal.R.string.whichApplication),
Jeff Hamiltond88e9aa2011-01-24 14:53:00 -0600104 null, null, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 }
106
107 protected void onCreate(Bundle savedInstanceState, Intent intent,
Jeff Hamiltond88e9aa2011-01-24 14:53:00 -0600108 CharSequence title, Intent[] initialIntents, List<ResolveInfo> rList,
Mike Lockwoodbce6f8f2011-03-13 17:26:52 -0400109 boolean alwaysUseOption) {
Adam Powellc5878612012-05-04 18:42:38 -0700110 setTheme(R.style.Theme_DeviceDefault_Light_Dialog_Alert);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 super.onCreate(savedInstanceState);
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700112 try {
113 mLaunchedFromUid = ActivityManagerNative.getDefault().getLaunchedFromUid(
114 getActivityToken());
115 } catch (RemoteException e) {
116 mLaunchedFromUid = -1;
117 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800118 mPm = getPackageManager();
Adam Powellc5878612012-05-04 18:42:38 -0700119 mAlwaysUseOption = alwaysUseOption;
Adam Powell589e6f92012-05-06 20:52:38 -0700120 mMaxColumns = getResources().getInteger(R.integer.config_maxResolverActivityColumns);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 intent.setComponent(null);
122
123 AlertController.AlertParams ap = mAlertParams;
124
125 ap.mTitle = title;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126
Dianne Hackbornd0d75032012-04-19 23:12:09 -0700127 mPackageMonitor.register(this, getMainLooper(), false);
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700128 mRegistered = true;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800129
Adam Powellc5878612012-05-04 18:42:38 -0700130 final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
131 mIconDpi = am.getLauncherLargeIconDensity();
132 mIconSize = am.getLauncherLargeIconSize();
133
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700134 mAdapter = new ResolveListAdapter(this, intent, initialIntents, rList,
135 mLaunchedFromUid);
Mike Lockwood02eb8742011-02-27 09:10:37 -0800136 int count = mAdapter.getCount();
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700137 if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) {
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700138 // Gulp!
139 finish();
140 return;
141 } else if (count > 1) {
Adam Powellc5878612012-05-04 18:42:38 -0700142 ap.mView = getLayoutInflater().inflate(R.layout.resolver_grid, null);
143 mGrid = (GridView) ap.mView.findViewById(R.id.resolver_grid);
144 mGrid.setAdapter(mAdapter);
145 mGrid.setOnItemClickListener(this);
146 mGrid.setOnItemLongClickListener(new ItemLongClickListener());
147
148 if (alwaysUseOption) {
149 mGrid.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
150 }
151
152 resizeGrid();
Mike Lockwood02eb8742011-02-27 09:10:37 -0800153 } else if (count == 1) {
Amith Yamasani203a2f42012-10-03 20:16:40 -0700154 startActivity(mAdapter.intentForPosition(0));
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700155 mPackageMonitor.unregister();
156 mRegistered = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157 finish();
158 return;
159 } else {
Adam Powellc5878612012-05-04 18:42:38 -0700160 ap.mMessage = getResources().getText(R.string.noApplications);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161 }
162
163 setupAlert();
Adam Powell2d809622012-03-22 15:24:43 -0700164
Adam Powellc5878612012-05-04 18:42:38 -0700165 if (alwaysUseOption) {
166 final ViewGroup buttonLayout = (ViewGroup) findViewById(R.id.button_bar);
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700167 if (buttonLayout != null) {
168 buttonLayout.setVisibility(View.VISIBLE);
169 mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
170 mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
171 } else {
172 mAlwaysUseOption = false;
173 }
Adam Powell2d809622012-03-22 15:24:43 -0700174 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175 }
176
Adam Powellc5878612012-05-04 18:42:38 -0700177 void resizeGrid() {
178 final int itemCount = mAdapter.getCount();
Adam Powell589e6f92012-05-06 20:52:38 -0700179 mGrid.setNumColumns(Math.min(itemCount, mMaxColumns));
Adam Powellc5878612012-05-04 18:42:38 -0700180 }
181
182 Drawable getIcon(Resources res, int resId) {
183 Drawable result;
184 try {
185 result = res.getDrawableForDensity(resId, mIconDpi);
186 } catch (Resources.NotFoundException e) {
187 result = null;
188 }
189
190 return result;
191 }
192
193 Drawable loadIconForResolveInfo(ResolveInfo ri) {
194 Drawable dr;
195 try {
196 if (ri.resolvePackageName != null && ri.icon != 0) {
197 dr = getIcon(mPm.getResourcesForApplication(ri.resolvePackageName), ri.icon);
198 if (dr != null) {
199 return dr;
200 }
201 }
202 final int iconRes = ri.getIconResource();
203 if (iconRes != 0) {
204 dr = getIcon(mPm.getResourcesForApplication(ri.activityInfo.packageName), iconRes);
205 if (dr != null) {
206 return dr;
207 }
208 }
209 } catch (NameNotFoundException e) {
210 Log.e(TAG, "Couldn't find resources for package", e);
211 }
212 return ri.loadIcon(mPm);
213 }
214
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800215 @Override
216 protected void onRestart() {
217 super.onRestart();
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700218 if (!mRegistered) {
219 mPackageMonitor.register(this, getMainLooper(), false);
220 mRegistered = true;
221 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800222 mAdapter.handlePackagesChanged();
223 }
224
225 @Override
226 protected void onStop() {
227 super.onStop();
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700228 if (mRegistered) {
229 mPackageMonitor.unregister();
230 mRegistered = false;
231 }
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700232 if ((getIntent().getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
233 // This resolver is in the unusual situation where it has been
234 // launched at the top of a new task. We don't let it be added
235 // to the recent tasks shown to the user, and we need to make sure
236 // that each time we are launched we get the correct launching
237 // uid (not re-using the same resolver from an old launching uid),
238 // so we will now finish ourself since being no longer visible,
239 // the user probably can't get back to us.
240 if (!isChangingConfigurations()) {
241 finish();
242 }
243 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800244 }
245
Adam Powellc5878612012-05-04 18:42:38 -0700246 @Override
Adam Powell9bee4662012-05-08 11:07:23 -0700247 protected void onRestoreInstanceState(Bundle savedInstanceState) {
248 super.onRestoreInstanceState(savedInstanceState);
249 if (mAlwaysUseOption) {
250 final int checkedPos = mGrid.getCheckedItemPosition();
251 final boolean enabled = checkedPos != GridView.INVALID_POSITION;
Adam Powelld81cc4a2012-07-19 13:51:39 -0700252 mLastSelected = checkedPos;
Adam Powell9bee4662012-05-08 11:07:23 -0700253 mAlwaysButton.setEnabled(enabled);
254 mOnceButton.setEnabled(enabled);
255 if (enabled) {
256 mGrid.setSelection(checkedPos);
257 }
258 }
259 }
260
261 @Override
Adam Powellc5878612012-05-04 18:42:38 -0700262 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Adam Powelld81cc4a2012-07-19 13:51:39 -0700263 final int checkedPos = mGrid.getCheckedItemPosition();
264 final boolean hasValidSelection = checkedPos != GridView.INVALID_POSITION;
Adam Powellbdda4e72012-07-20 11:22:03 -0700265 if (mAlwaysUseOption && (!hasValidSelection || mLastSelected != checkedPos)) {
Adam Powelld81cc4a2012-07-19 13:51:39 -0700266 mAlwaysButton.setEnabled(hasValidSelection);
267 mOnceButton.setEnabled(hasValidSelection);
268 if (hasValidSelection) {
Adam Powell9bee4662012-05-08 11:07:23 -0700269 mGrid.smoothScrollToPosition(checkedPos);
270 }
Adam Powelld81cc4a2012-07-19 13:51:39 -0700271 mLastSelected = checkedPos;
Adam Powellc5878612012-05-04 18:42:38 -0700272 } else {
273 startSelected(position, false);
274 }
275 }
276
277 public void onButtonClick(View v) {
278 final int id = v.getId();
279 startSelected(mGrid.getCheckedItemPosition(), id == R.id.button_always);
280 dismiss();
281 }
282
283 void startSelected(int which, boolean always) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 ResolveInfo ri = mAdapter.resolveInfoForPosition(which);
285 Intent intent = mAdapter.intentForPosition(which);
Adam Powellc5878612012-05-04 18:42:38 -0700286 onIntentSelected(ri, intent, always);
Mike Lockwood02eb8742011-02-27 09:10:37 -0800287 finish();
288 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289
Mike Lockwood02eb8742011-02-27 09:10:37 -0800290 protected void onIntentSelected(ResolveInfo ri, Intent intent, boolean alwaysCheck) {
291 if (alwaysCheck) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 // Build a reasonable intent filter, based on what matched.
293 IntentFilter filter = new IntentFilter();
294
295 if (intent.getAction() != null) {
296 filter.addAction(intent.getAction());
297 }
298 Set<String> categories = intent.getCategories();
299 if (categories != null) {
300 for (String cat : categories) {
301 filter.addCategory(cat);
302 }
303 }
304 filter.addCategory(Intent.CATEGORY_DEFAULT);
305
306 int cat = ri.match&IntentFilter.MATCH_CATEGORY_MASK;
307 Uri data = intent.getData();
308 if (cat == IntentFilter.MATCH_CATEGORY_TYPE) {
309 String mimeType = intent.resolveType(this);
310 if (mimeType != null) {
311 try {
312 filter.addDataType(mimeType);
313 } catch (IntentFilter.MalformedMimeTypeException e) {
314 Log.w("ResolverActivity", e);
315 filter = null;
316 }
317 }
Dianne Hackborn5ef402b2010-03-26 17:17:25 -0700318 }
319 if (data != null && data.getScheme() != null) {
320 // We need the data specification if there was no type,
321 // OR if the scheme is not one of our magical "file:"
322 // or "content:" schemes (see IntentFilter for the reason).
323 if (cat != IntentFilter.MATCH_CATEGORY_TYPE
324 || (!"file".equals(data.getScheme())
325 && !"content".equals(data.getScheme()))) {
326 filter.addDataScheme(data.getScheme());
You Kim43a5070e2012-11-21 23:23:45 +0900327
Dianne Hackborn5ef402b2010-03-26 17:17:25 -0700328 // Look through the resolved filter to determine which part
329 // of it matched the original Intent.
Dianne Hackborndf1c0bf2013-06-12 16:21:38 -0700330 Iterator<PatternMatcher> pIt = ri.filter.schemeSpecificPartsIterator();
331 if (pIt != null) {
332 String ssp = data.getSchemeSpecificPart();
333 while (ssp != null && pIt.hasNext()) {
334 PatternMatcher p = pIt.next();
335 if (p.match(ssp)) {
336 filter.addDataSchemeSpecificPart(p.getPath(), p.getType());
337 break;
338 }
339 }
340 }
Dianne Hackborn5ef402b2010-03-26 17:17:25 -0700341 Iterator<IntentFilter.AuthorityEntry> aIt = ri.filter.authoritiesIterator();
342 if (aIt != null) {
343 while (aIt.hasNext()) {
344 IntentFilter.AuthorityEntry a = aIt.next();
345 if (a.match(data) >= 0) {
346 int port = a.getPort();
347 filter.addDataAuthority(a.getHost(),
348 port >= 0 ? Integer.toString(port) : null);
349 break;
350 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 }
352 }
Dianne Hackborndf1c0bf2013-06-12 16:21:38 -0700353 pIt = ri.filter.pathsIterator();
Dianne Hackborn5ef402b2010-03-26 17:17:25 -0700354 if (pIt != null) {
355 String path = data.getPath();
356 while (path != null && pIt.hasNext()) {
357 PatternMatcher p = pIt.next();
358 if (p.match(path)) {
359 filter.addDataPath(p.getPath(), p.getType());
360 break;
361 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 }
363 }
364 }
365 }
366
367 if (filter != null) {
368 final int N = mAdapter.mList.size();
369 ComponentName[] set = new ComponentName[N];
370 int bestMatch = 0;
371 for (int i=0; i<N; i++) {
372 ResolveInfo r = mAdapter.mList.get(i).ri;
373 set[i] = new ComponentName(r.activityInfo.packageName,
374 r.activityInfo.name);
375 if (r.match > bestMatch) bestMatch = r.match;
376 }
377 getPackageManager().addPreferredActivity(filter, bestMatch, set,
Amith Yamasani203a2f42012-10-03 20:16:40 -0700378 intent.getComponent());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379 }
380 }
381
382 if (intent != null) {
Amith Yamasani203a2f42012-10-03 20:16:40 -0700383 startActivity(intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 }
386
Adam Powellc5878612012-05-04 18:42:38 -0700387 void showAppDetails(ResolveInfo ri) {
388 Intent in = new Intent().setAction("android.settings.APPLICATION_DETAILS_SETTINGS")
Adam Powell0fc5b2b2012-07-18 18:20:29 -0700389 .setData(Uri.fromParts("package", ri.activityInfo.packageName, null))
390 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
Amith Yamasani203a2f42012-10-03 20:16:40 -0700391 startActivity(in);
Adam Powellc5878612012-05-04 18:42:38 -0700392 }
393
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 private final class DisplayResolveInfo {
395 ResolveInfo ri;
396 CharSequence displayLabel;
Dianne Hackborneb034652009-09-07 00:49:58 -0700397 Drawable displayIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 CharSequence extendedInfo;
Dianne Hackborneb034652009-09-07 00:49:58 -0700399 Intent origIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400
Dianne Hackborneb034652009-09-07 00:49:58 -0700401 DisplayResolveInfo(ResolveInfo pri, CharSequence pLabel,
402 CharSequence pInfo, Intent pOrigIntent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800403 ri = pri;
404 displayLabel = pLabel;
405 extendedInfo = pInfo;
Dianne Hackborneb034652009-09-07 00:49:58 -0700406 origIntent = pOrigIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407 }
408 }
409
410 private final class ResolveListAdapter extends BaseAdapter {
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800411 private final Intent[] mInitialIntents;
412 private final List<ResolveInfo> mBaseResolveList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 private final Intent mIntent;
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700414 private final int mLaunchedFromUid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 private final LayoutInflater mInflater;
416
417 private List<DisplayResolveInfo> mList;
418
Dianne Hackborneb034652009-09-07 00:49:58 -0700419 public ResolveListAdapter(Context context, Intent intent,
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700420 Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 mIntent = new Intent(intent);
422 mIntent.setComponent(null);
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800423 mInitialIntents = initialIntents;
424 mBaseResolveList = rList;
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700425 mLaunchedFromUid = launchedFromUid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800426 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
You Kim43a5070e2012-11-21 23:23:45 +0900427 mList = new ArrayList<DisplayResolveInfo>();
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800428 rebuildList();
429 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800431 public void handlePackagesChanged() {
Adam Powellc5878612012-05-04 18:42:38 -0700432 final int oldItemCount = getCount();
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800433 rebuildList();
434 notifyDataSetChanged();
You Kim43a5070e2012-11-21 23:23:45 +0900435 final int newItemCount = getCount();
436 if (newItemCount == 0) {
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800437 // We no longer have any items... just finish the activity.
438 finish();
You Kim43a5070e2012-11-21 23:23:45 +0900439 } else if (newItemCount != oldItemCount) {
Adam Powellc5878612012-05-04 18:42:38 -0700440 resizeGrid();
441 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800442 }
443
444 private void rebuildList() {
You Kim43a5070e2012-11-21 23:23:45 +0900445 List<ResolveInfo> currentResolveList;
446
447 mList.clear();
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800448 if (mBaseResolveList != null) {
You Kim43a5070e2012-11-21 23:23:45 +0900449 currentResolveList = mBaseResolveList;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800450 } else {
You Kim43a5070e2012-11-21 23:23:45 +0900451 currentResolveList = mPm.queryIntentActivities(
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800452 mIntent, PackageManager.MATCH_DEFAULT_ONLY
Amith Yamasani203a2f42012-10-03 20:16:40 -0700453 | (mAlwaysUseOption ? PackageManager.GET_RESOLVED_FILTER : 0));
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700454 // Filter out any activities that the launched uid does not
455 // have permission for. We don't do this when we have an explicit
456 // list of resolved activities, because that only happens when
457 // we are being subclassed, so we can safely launch whatever
458 // they gave us.
You Kim43a5070e2012-11-21 23:23:45 +0900459 if (currentResolveList != null) {
460 for (int i=currentResolveList.size()-1; i >= 0; i--) {
461 ActivityInfo ai = currentResolveList.get(i).activityInfo;
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700462 int granted = ActivityManager.checkComponentPermission(
463 ai.permission, mLaunchedFromUid,
464 ai.applicationInfo.uid, ai.exported);
465 if (granted != PackageManager.PERMISSION_GRANTED) {
466 // Access not allowed!
You Kim43a5070e2012-11-21 23:23:45 +0900467 currentResolveList.remove(i);
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700468 }
469 }
470 }
Jeff Hamiltond88e9aa2011-01-24 14:53:00 -0600471 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472 int N;
You Kim43a5070e2012-11-21 23:23:45 +0900473 if ((currentResolveList != null) && ((N = currentResolveList.size()) > 0)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 // Only display the first matches that are either of equal
475 // priority or have asked to be default options.
You Kim43a5070e2012-11-21 23:23:45 +0900476 ResolveInfo r0 = currentResolveList.get(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800477 for (int i=1; i<N; i++) {
You Kim43a5070e2012-11-21 23:23:45 +0900478 ResolveInfo ri = currentResolveList.get(i);
479 if (DEBUG) Log.v(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800480 "ResolveListActivity",
481 r0.activityInfo.name + "=" +
482 r0.priority + "/" + r0.isDefault + " vs " +
483 ri.activityInfo.name + "=" +
484 ri.priority + "/" + ri.isDefault);
You Kim43a5070e2012-11-21 23:23:45 +0900485 if (r0.priority != ri.priority ||
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486 r0.isDefault != ri.isDefault) {
487 while (i < N) {
You Kim43a5070e2012-11-21 23:23:45 +0900488 currentResolveList.remove(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489 N--;
490 }
491 }
492 }
493 if (N > 1) {
494 ResolveInfo.DisplayNameComparator rComparator =
495 new ResolveInfo.DisplayNameComparator(mPm);
You Kim43a5070e2012-11-21 23:23:45 +0900496 Collections.sort(currentResolveList, rComparator);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497 }
Dianne Hackborneb034652009-09-07 00:49:58 -0700498 // First put the initial items at the top.
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800499 if (mInitialIntents != null) {
500 for (int i=0; i<mInitialIntents.length; i++) {
501 Intent ii = mInitialIntents[i];
Dianne Hackborneb034652009-09-07 00:49:58 -0700502 if (ii == null) {
503 continue;
504 }
505 ActivityInfo ai = ii.resolveActivityInfo(
506 getPackageManager(), 0);
507 if (ai == null) {
508 Log.w("ResolverActivity", "No activity found for "
509 + ii);
510 continue;
511 }
512 ResolveInfo ri = new ResolveInfo();
513 ri.activityInfo = ai;
514 if (ii instanceof LabeledIntent) {
515 LabeledIntent li = (LabeledIntent)ii;
516 ri.resolvePackageName = li.getSourcePackage();
517 ri.labelRes = li.getLabelResource();
518 ri.nonLocalizedLabel = li.getNonLocalizedLabel();
519 ri.icon = li.getIconResource();
520 }
521 mList.add(new DisplayResolveInfo(ri,
522 ri.loadLabel(getPackageManager()), null, ii));
523 }
524 }
You Kim43a5070e2012-11-21 23:23:45 +0900525
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800526 // Check for applications with same name and use application name or
527 // package name if necessary
You Kim43a5070e2012-11-21 23:23:45 +0900528 r0 = currentResolveList.get(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 int start = 0;
530 CharSequence r0Label = r0.loadLabel(mPm);
Adam Powellc5878612012-05-04 18:42:38 -0700531 mShowExtended = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800532 for (int i = 1; i < N; i++) {
533 if (r0Label == null) {
534 r0Label = r0.activityInfo.packageName;
535 }
You Kim43a5070e2012-11-21 23:23:45 +0900536 ResolveInfo ri = currentResolveList.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537 CharSequence riLabel = ri.loadLabel(mPm);
538 if (riLabel == null) {
539 riLabel = ri.activityInfo.packageName;
540 }
541 if (riLabel.equals(r0Label)) {
542 continue;
543 }
You Kim43a5070e2012-11-21 23:23:45 +0900544 processGroup(currentResolveList, start, (i-1), r0, r0Label);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800545 r0 = ri;
546 r0Label = riLabel;
547 start = i;
548 }
549 // Process last group
You Kim43a5070e2012-11-21 23:23:45 +0900550 processGroup(currentResolveList, start, (N-1), r0, r0Label);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800551 }
552 }
553
554 private void processGroup(List<ResolveInfo> rList, int start, int end, ResolveInfo ro,
555 CharSequence roLabel) {
556 // Process labels from start to i
557 int num = end - start+1;
558 if (num == 1) {
559 // No duplicate labels. Use label for entry at start
Dianne Hackborneb034652009-09-07 00:49:58 -0700560 mList.add(new DisplayResolveInfo(ro, roLabel, null, null));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800561 } else {
Adam Powellc5878612012-05-04 18:42:38 -0700562 mShowExtended = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800563 boolean usePkg = false;
564 CharSequence startApp = ro.activityInfo.applicationInfo.loadLabel(mPm);
565 if (startApp == null) {
566 usePkg = true;
567 }
568 if (!usePkg) {
569 // Use HashSet to track duplicates
570 HashSet<CharSequence> duplicates =
571 new HashSet<CharSequence>();
572 duplicates.add(startApp);
573 for (int j = start+1; j <= end ; j++) {
574 ResolveInfo jRi = rList.get(j);
575 CharSequence jApp = jRi.activityInfo.applicationInfo.loadLabel(mPm);
576 if ( (jApp == null) || (duplicates.contains(jApp))) {
577 usePkg = true;
578 break;
579 } else {
580 duplicates.add(jApp);
581 }
582 }
583 // Clear HashSet for later use
584 duplicates.clear();
585 }
586 for (int k = start; k <= end; k++) {
587 ResolveInfo add = rList.get(k);
588 if (usePkg) {
589 // Use application name for all entries from start to end-1
590 mList.add(new DisplayResolveInfo(add, roLabel,
Dianne Hackborneb034652009-09-07 00:49:58 -0700591 add.activityInfo.packageName, null));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800592 } else {
593 // Use package name for all entries from start to end-1
594 mList.add(new DisplayResolveInfo(add, roLabel,
Dianne Hackborneb034652009-09-07 00:49:58 -0700595 add.activityInfo.applicationInfo.loadLabel(mPm), null));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800596 }
597 }
598 }
599 }
600
601 public ResolveInfo resolveInfoForPosition(int position) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 return mList.get(position).ri;
603 }
604
605 public Intent intentForPosition(int position) {
Dianne Hackborneb034652009-09-07 00:49:58 -0700606 DisplayResolveInfo dri = mList.get(position);
607
608 Intent intent = new Intent(dri.origIntent != null
609 ? dri.origIntent : mIntent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
611 |Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
Dianne Hackborneb034652009-09-07 00:49:58 -0700612 ActivityInfo ai = dri.ri.activityInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800613 intent.setComponent(new ComponentName(
614 ai.applicationInfo.packageName, ai.name));
615 return intent;
616 }
617
618 public int getCount() {
You Kim43a5070e2012-11-21 23:23:45 +0900619 return mList.size();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620 }
621
622 public Object getItem(int position) {
You Kim43a5070e2012-11-21 23:23:45 +0900623 return mList.get(position);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800624 }
625
626 public long getItemId(int position) {
627 return position;
628 }
629
630 public View getView(int position, View convertView, ViewGroup parent) {
631 View view;
632 if (convertView == null) {
633 view = mInflater.inflate(
634 com.android.internal.R.layout.resolve_list_item, parent, false);
Adam Powellc5878612012-05-04 18:42:38 -0700635
Adam Powell0256c6f2013-05-29 16:42:33 -0700636 final ViewHolder holder = new ViewHolder(view);
637 view.setTag(holder);
638
Adam Powellc5878612012-05-04 18:42:38 -0700639 // Fix the icon size even if we have different sized resources
Adam Powell0256c6f2013-05-29 16:42:33 -0700640 ViewGroup.LayoutParams lp = holder.icon.getLayoutParams();
Adam Powellc5878612012-05-04 18:42:38 -0700641 lp.width = lp.height = mIconSize;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800642 } else {
643 view = convertView;
644 }
645 bindView(view, mList.get(position));
646 return view;
647 }
648
649 private final void bindView(View view, DisplayResolveInfo info) {
Adam Powell0256c6f2013-05-29 16:42:33 -0700650 final ViewHolder holder = (ViewHolder) view.getTag();
651 holder.text.setText(info.displayLabel);
Adam Powellc5878612012-05-04 18:42:38 -0700652 if (mShowExtended) {
Adam Powell0256c6f2013-05-29 16:42:33 -0700653 holder.text2.setVisibility(View.VISIBLE);
654 holder.text2.setText(info.extendedInfo);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800655 } else {
Adam Powell0256c6f2013-05-29 16:42:33 -0700656 holder.text2.setVisibility(View.GONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800657 }
Dianne Hackborneb034652009-09-07 00:49:58 -0700658 if (info.displayIcon == null) {
Adam Powell0256c6f2013-05-29 16:42:33 -0700659 new LoadIconTask().execute(info);
Dianne Hackborneb034652009-09-07 00:49:58 -0700660 }
Adam Powell0256c6f2013-05-29 16:42:33 -0700661 holder.icon.setImageDrawable(info.displayIcon);
662 }
663 }
664
665 static class ViewHolder {
666 public TextView text;
667 public TextView text2;
668 public ImageView icon;
669
670 public ViewHolder(View view) {
671 text = (TextView) view.findViewById(com.android.internal.R.id.text1);
672 text2 = (TextView) view.findViewById(com.android.internal.R.id.text2);
673 icon = (ImageView) view.findViewById(R.id.icon);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800674 }
675 }
676
Adam Powell2d809622012-03-22 15:24:43 -0700677 class ItemLongClickListener implements AdapterView.OnItemLongClickListener {
678
679 @Override
680 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
681 ResolveInfo ri = mAdapter.resolveInfoForPosition(position);
Adam Powellc5878612012-05-04 18:42:38 -0700682 showAppDetails(ri);
Adam Powell2d809622012-03-22 15:24:43 -0700683 return true;
684 }
685
686 }
Adam Powell0256c6f2013-05-29 16:42:33 -0700687
688 class LoadIconTask extends AsyncTask<DisplayResolveInfo, Void, DisplayResolveInfo> {
689 @Override
690 protected DisplayResolveInfo doInBackground(DisplayResolveInfo... params) {
691 final DisplayResolveInfo info = params[0];
692 if (info.displayIcon == null) {
693 info.displayIcon = loadIconForResolveInfo(info.ri);
694 }
695 return info;
696 }
697
698 @Override
699 protected void onPostExecute(DisplayResolveInfo info) {
700 mAdapter.notifyDataSetChanged();
701 }
702 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800703}
704