blob: 661acbeda4bfe0a7a0d6fed4f223f832cbdea3d4 [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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import com.android.internal.R;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -080029import com.android.internal.content.PackageMonitor;
30
Adam Powellc5878612012-05-04 18:42:38 -070031import android.app.ActivityManager;
Dianne Hackborn5320eb82012-05-18 12:05:04 -070032import android.app.ActivityManagerNative;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -070033import android.app.AppGlobals;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.content.ComponentName;
35import android.content.Context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.content.Intent;
37import android.content.IntentFilter;
38import android.content.pm.ActivityInfo;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010039import android.content.pm.ApplicationInfo;
Dianne Hackborneb034652009-09-07 00:49:58 -070040import android.content.pm.LabeledIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.content.pm.PackageManager;
Adam Powellc5878612012-05-04 18:42:38 -070042import android.content.pm.PackageManager.NameNotFoundException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.content.pm.ResolveInfo;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010044import android.content.pm.UserInfo;
Adam Powellc5878612012-05-04 18:42:38 -070045import android.content.res.Resources;
Dianne Hackborneb034652009-09-07 00:49:58 -070046import android.graphics.drawable.Drawable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.net.Uri;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010048import android.os.Build;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.os.Bundle;
50import android.os.PatternMatcher;
Dianne Hackborn5320eb82012-05-18 12:05:04 -070051import android.os.RemoteException;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070052import android.os.UserHandle;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010053import android.os.UserManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import android.util.Log;
Adam Powell2d809622012-03-22 15:24:43 -070055import android.view.LayoutInflater;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.view.View;
57import android.view.ViewGroup;
Adam Powell2d809622012-03-22 15:24:43 -070058import android.widget.AdapterView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059import android.widget.BaseAdapter;
Adam Powellc5878612012-05-04 18:42:38 -070060import android.widget.Button;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061import android.widget.ImageView;
Adam Powell2d809622012-03-22 15:24:43 -070062import android.widget.ListView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063import android.widget.TextView;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +010064import android.widget.Toast;
Adam Powell4f6c2052014-07-07 18:49:10 -070065import com.android.internal.widget.ResolverDrawerLayout;
Adam Powell2d809622012-03-22 15:24:43 -070066
Adam Powell09a65602014-07-20 16:23:14 -070067import java.text.Collator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068import java.util.ArrayList;
69import java.util.Collections;
Adam Powell09a65602014-07-20 16:23:14 -070070import java.util.Comparator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071import java.util.HashSet;
72import java.util.Iterator;
73import java.util.List;
Adam Lesinskicc562a82014-08-27 11:52:52 -070074import java.util.Map;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075import java.util.Set;
76
Adrian Roos27c3ab62014-10-15 17:21:31 +020077import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
78import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
79
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080/**
81 * This activity is displayed when the system attempts to start an Intent for
82 * which there is more than one matching activity, allowing the user to decide
83 * which to go to. It is not normally used directly by application developers.
84 */
Adam Powelle9414d92014-07-05 17:44:18 -070085public class ResolverActivity extends Activity implements AdapterView.OnItemClickListener {
Adam Powellc5878612012-05-04 18:42:38 -070086 private static final String TAG = "ResolverActivity";
Adam Powell09a65602014-07-20 16:23:14 -070087 private static final boolean DEBUG = false;
Adam Powellc5878612012-05-04 18:42:38 -070088
Dianne Hackborn5320eb82012-05-18 12:05:04 -070089 private int mLaunchedFromUid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090 private ResolveListAdapter mAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091 private PackageManager mPm;
Dianne Hackborn028ceeb2014-08-17 17:45:48 -070092 private boolean mSafeForwardingMode;
Adam Powellc5878612012-05-04 18:42:38 -070093 private boolean mAlwaysUseOption;
94 private boolean mShowExtended;
Adam Powellfd1e93d2014-09-07 16:52:22 -070095 private ListView mListView;
Adam Powellc5878612012-05-04 18:42:38 -070096 private Button mAlwaysButton;
97 private Button mOnceButton;
Adam Powell88831a22014-11-20 18:17:00 -080098 private View mProfileView;
Adam Powellc5878612012-05-04 18:42:38 -070099 private int mIconDpi;
100 private int mIconSize;
Adam Powell589e6f92012-05-06 20:52:38 -0700101 private int mMaxColumns;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700102 private int mLastSelected = ListView.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 Powell88831a22014-11-20 18:17:00 -0800105 private Intent mIntent;
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 /**
195 * Compatibility version for other bundled services that use this ocerload without
196 * 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
Adam Powell589e6f92012-05-06 20:52:38 -0700226 mMaxColumns = getResources().getInteger(R.integer.config_maxResolverActivityColumns);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227
Dianne Hackbornd0d75032012-04-19 23:12:09 -0700228 mPackageMonitor.register(this, getMainLooper(), false);
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700229 mRegistered = true;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800230
Adam Powellc5878612012-05-04 18:42:38 -0700231 final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
232 mIconDpi = am.getLauncherLargeIconDensity();
233 mIconSize = am.getLauncherLargeIconSize();
234
Adam Powell88831a22014-11-20 18:17:00 -0800235 mIntent = new Intent(intent);
236 mAdapter = new ResolveListAdapter(this, initialIntents, rList,
Adam Powell278902c2014-07-12 18:33:22 -0700237 mLaunchedFromUid, alwaysUseOption);
238
239 final int layoutId;
Adam Powellfd1e93d2014-09-07 16:52:22 -0700240 final boolean useHeader;
Adam Powell278902c2014-07-12 18:33:22 -0700241 if (mAdapter.hasFilteredItem()) {
242 layoutId = R.layout.resolver_list_with_default;
243 alwaysUseOption = false;
Adam Powellfd1e93d2014-09-07 16:52:22 -0700244 useHeader = true;
Adam Powell278902c2014-07-12 18:33:22 -0700245 } else {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700246 useHeader = false;
Adam Powell278902c2014-07-12 18:33:22 -0700247 layoutId = R.layout.resolver_list;
248 }
249 mAlwaysUseOption = alwaysUseOption;
250
Dianne Hackbornf02b60a2012-08-16 10:48:27 -0700251 if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) {
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700252 // Gulp!
253 finish();
254 return;
Esteban Talavera6de72662014-12-11 17:54:07 +0000255 }
256
257 int count = mAdapter.mList.size();
258 if (count > 1 || (count == 1 && mAdapter.getOtherProfile() != null)) {
Adam Powell278902c2014-07-12 18:33:22 -0700259 setContentView(layoutId);
Adam Powellfd1e93d2014-09-07 16:52:22 -0700260 mListView = (ListView) findViewById(R.id.resolver_list);
261 mListView.setAdapter(mAdapter);
262 mListView.setOnItemClickListener(this);
263 mListView.setOnItemLongClickListener(new ItemLongClickListener());
Adam Powellc5878612012-05-04 18:42:38 -0700264
265 if (alwaysUseOption) {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700266 mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
Adam Powellc5878612012-05-04 18:42:38 -0700267 }
Adam Powelle9414d92014-07-05 17:44:18 -0700268
Adam Powellfd1e93d2014-09-07 16:52:22 -0700269 if (useHeader) {
270 mListView.addHeaderView(LayoutInflater.from(this).inflate(
271 R.layout.resolver_different_item_header, mListView, false));
272 }
Mike Lockwood02eb8742011-02-27 09:10:37 -0800273 } else if (count == 1) {
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700274 safelyStartActivity(mAdapter.intentForPosition(0, false));
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700275 mPackageMonitor.unregister();
276 mRegistered = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 finish();
278 return;
279 } else {
Adam Powelle9414d92014-07-05 17:44:18 -0700280 setContentView(R.layout.resolver_list);
281
282 final TextView empty = (TextView) findViewById(R.id.empty);
283 empty.setVisibility(View.VISIBLE);
284
Adam Powellfd1e93d2014-09-07 16:52:22 -0700285 mListView = (ListView) findViewById(R.id.resolver_list);
286 mListView.setVisibility(View.GONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 }
Adrian Roos27c3ab62014-10-15 17:21:31 +0200288 // Prevent the Resolver window from becoming the top fullscreen window and thus from taking
289 // control of the system bars.
290 getWindow().clearFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291
Adam Powell4f6c2052014-07-07 18:49:10 -0700292 final ResolverDrawerLayout rdl = (ResolverDrawerLayout) findViewById(R.id.contentPanel);
293 if (rdl != null) {
Adam Powell5dd072d2014-10-30 19:51:41 -0700294 rdl.setOnDismissedListener(new ResolverDrawerLayout.OnDismissedListener() {
Adam Powell4f6c2052014-07-07 18:49:10 -0700295 @Override
Adam Powell5dd072d2014-10-30 19:51:41 -0700296 public void onDismissed() {
Adam Powell4f6c2052014-07-07 18:49:10 -0700297 finish();
298 }
299 });
300 }
301
Adam Powell11f59a02014-08-20 13:22:16 -0700302 if (title == null) {
303 title = getTitleForAction(intent.getAction(), defaultTitleRes);
304 }
305 if (!TextUtils.isEmpty(title)) {
306 final TextView titleView = (TextView) findViewById(R.id.title);
307 if (titleView != null) {
308 titleView.setText(title);
Adam Powell278902c2014-07-12 18:33:22 -0700309 }
Adam Powell11f59a02014-08-20 13:22:16 -0700310 setTitle(title);
Adam Powelle9414d92014-07-05 17:44:18 -0700311 }
Adam Powell2d809622012-03-22 15:24:43 -0700312
Adam Powell278902c2014-07-12 18:33:22 -0700313 final ImageView iconView = (ImageView) findViewById(R.id.icon);
314 final DisplayResolveInfo iconInfo = mAdapter.getFilteredItem();
315 if (iconView != null && iconInfo != null) {
316 new LoadIconIntoViewTask(iconView).execute(iconInfo);
317 }
318
319 if (alwaysUseOption || mAdapter.hasFilteredItem()) {
Adam Powellc5878612012-05-04 18:42:38 -0700320 final ViewGroup buttonLayout = (ViewGroup) findViewById(R.id.button_bar);
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700321 if (buttonLayout != null) {
322 buttonLayout.setVisibility(View.VISIBLE);
323 mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
324 mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
325 } else {
326 mAlwaysUseOption = false;
327 }
Adam Powell278902c2014-07-12 18:33:22 -0700328 }
329
330 if (mAdapter.hasFilteredItem()) {
331 setAlwaysButtonEnabled(true, mAdapter.getFilteredPosition(), false);
332 mOnceButton.setEnabled(true);
333 }
Adam Powell88831a22014-11-20 18:17:00 -0800334
335 mProfileView = findViewById(R.id.profile_button);
336 if (mProfileView != null) {
337 mProfileView.setOnClickListener(new View.OnClickListener() {
338 @Override
339 public void onClick(View v) {
340 final DisplayResolveInfo dri = mAdapter.getOtherProfile();
341 if (dri == null) {
342 return;
343 }
344
345 final Intent intent = intentForDisplayResolveInfo(dri);
346 onIntentSelected(dri.ri, intent, mAlwaysUseOption);
347 finish();
348 }
349 });
350 bindProfileView();
351 }
352 }
353
354 void bindProfileView() {
355 final DisplayResolveInfo dri = mAdapter.getOtherProfile();
356 if (dri != null) {
357 mProfileView.setVisibility(View.VISIBLE);
358 final ImageView icon = (ImageView) mProfileView.findViewById(R.id.icon);
359 final TextView text = (TextView) mProfileView.findViewById(R.id.text1);
360 if (dri.displayIcon == null) {
361 new LoadIconTask().execute(dri);
362 }
363 icon.setImageDrawable(dri.displayIcon);
364 text.setText(dri.displayLabel);
365 } else {
366 mProfileView.setVisibility(View.GONE);
367 }
Adam Powell278902c2014-07-12 18:33:22 -0700368 }
369
Sander Alewijnsef6545332014-10-31 12:39:02 +0000370 private void setProfileSwitchMessageId(int contentUserHint) {
371 if (contentUserHint != UserHandle.USER_CURRENT &&
372 contentUserHint != UserHandle.myUserId()) {
373 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
374 UserInfo originUserInfo = userManager.getUserInfo(contentUserHint);
375 boolean originIsManaged = originUserInfo != null ? originUserInfo.isManagedProfile()
376 : false;
377 boolean targetIsManaged = userManager.isManagedProfile();
378 if (originIsManaged && !targetIsManaged) {
379 mProfileSwitchMessageId = com.android.internal.R.string.forward_intent_to_owner;
380 } else if (!originIsManaged && targetIsManaged) {
381 mProfileSwitchMessageId = com.android.internal.R.string.forward_intent_to_work;
382 }
383 }
384 }
385
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700386 /**
387 * Turn on launch mode that is safe to use when forwarding intents received from
388 * applications and running in system processes. This mode uses Activity.startActivityAsCaller
389 * instead of the normal Activity.startActivity for launching the activity selected
390 * by the user.
391 *
392 * <p>This mode is set to true by default if the activity is initialized through
393 * {@link #onCreate(android.os.Bundle)}. If a subclass calls one of the other onCreate
394 * methods, it is set to false by default. You must set it before calling one of the
395 * more detailed onCreate methods, so that it will be set correctly in the case where
396 * there is only one intent to resolve and it is thus started immediately.</p>
397 */
398 public void setSafeForwardingMode(boolean safeForwarding) {
399 mSafeForwardingMode = safeForwarding;
400 }
401
Adam Powell278902c2014-07-12 18:33:22 -0700402 protected CharSequence getTitleForAction(String action, int defaultTitleRes) {
Adam Powella35c77a2014-09-25 16:46:36 -0700403 final ActionTitle title = mResolvingHome ? ActionTitle.HOME : ActionTitle.forAction(action);
Adam Powell278902c2014-07-12 18:33:22 -0700404 final boolean named = mAdapter.hasFilteredItem();
405 if (title == ActionTitle.DEFAULT && defaultTitleRes != 0) {
406 return getString(defaultTitleRes);
407 } else {
408 return named ? getString(title.namedTitleRes, mAdapter.getFilteredItem().displayLabel) :
409 getString(title.titleRes);
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700410 }
Adam Powellc5878612012-05-04 18:42:38 -0700411 }
412
Adam Powelle9414d92014-07-05 17:44:18 -0700413 void dismiss() {
414 if (!isFinishing()) {
415 finish();
416 }
417 }
418
Adam Powellc5878612012-05-04 18:42:38 -0700419 Drawable getIcon(Resources res, int resId) {
420 Drawable result;
421 try {
422 result = res.getDrawableForDensity(resId, mIconDpi);
423 } catch (Resources.NotFoundException e) {
424 result = null;
425 }
426
427 return result;
428 }
429
430 Drawable loadIconForResolveInfo(ResolveInfo ri) {
431 Drawable dr;
432 try {
433 if (ri.resolvePackageName != null && ri.icon != 0) {
434 dr = getIcon(mPm.getResourcesForApplication(ri.resolvePackageName), ri.icon);
435 if (dr != null) {
436 return dr;
437 }
438 }
439 final int iconRes = ri.getIconResource();
440 if (iconRes != 0) {
441 dr = getIcon(mPm.getResourcesForApplication(ri.activityInfo.packageName), iconRes);
442 if (dr != null) {
443 return dr;
444 }
445 }
446 } catch (NameNotFoundException e) {
447 Log.e(TAG, "Couldn't find resources for package", e);
448 }
449 return ri.loadIcon(mPm);
450 }
451
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800452 @Override
453 protected void onRestart() {
454 super.onRestart();
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700455 if (!mRegistered) {
456 mPackageMonitor.register(this, getMainLooper(), false);
457 mRegistered = true;
458 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800459 mAdapter.handlePackagesChanged();
Adam Powell88831a22014-11-20 18:17:00 -0800460 if (mProfileView != null) {
461 bindProfileView();
462 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800463 }
464
465 @Override
466 protected void onStop() {
467 super.onStop();
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700468 if (mRegistered) {
469 mPackageMonitor.unregister();
470 mRegistered = false;
471 }
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700472 if ((getIntent().getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
473 // This resolver is in the unusual situation where it has been
474 // launched at the top of a new task. We don't let it be added
475 // to the recent tasks shown to the user, and we need to make sure
476 // that each time we are launched we get the correct launching
477 // uid (not re-using the same resolver from an old launching uid),
478 // so we will now finish ourself since being no longer visible,
479 // the user probably can't get back to us.
480 if (!isChangingConfigurations()) {
481 finish();
482 }
483 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800484 }
485
Adam Powellc5878612012-05-04 18:42:38 -0700486 @Override
Adam Powell9bee4662012-05-08 11:07:23 -0700487 protected void onRestoreInstanceState(Bundle savedInstanceState) {
488 super.onRestoreInstanceState(savedInstanceState);
489 if (mAlwaysUseOption) {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700490 final int checkedPos = mListView.getCheckedItemPosition();
Nicolas Prevot50449882014-06-23 12:42:37 +0100491 final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
Adam Powelld81cc4a2012-07-19 13:51:39 -0700492 mLastSelected = checkedPos;
Adam Powell278902c2014-07-12 18:33:22 -0700493 setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
Nicolas Prevot50449882014-06-23 12:42:37 +0100494 mOnceButton.setEnabled(hasValidSelection);
495 if (hasValidSelection) {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700496 mListView.setSelection(checkedPos);
Adam Powell9bee4662012-05-08 11:07:23 -0700497 }
498 }
499 }
500
501 @Override
Adam Powellc5878612012-05-04 18:42:38 -0700502 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700503 position -= mListView.getHeaderViewsCount();
504 if (position < 0) {
505 // Header views don't count.
506 return;
507 }
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100508 ResolveInfo resolveInfo = mAdapter.resolveInfoForPosition(position, true);
509 if (mResolvingHome && hasManagedProfile()
510 && !supportsManagedProfiles(resolveInfo)) {
511 Toast.makeText(this, String.format(getResources().getString(
512 com.android.internal.R.string.activity_resolver_work_profiles_support),
513 resolveInfo.activityInfo.loadLabel(getPackageManager()).toString()),
514 Toast.LENGTH_LONG).show();
515 return;
516 }
Adam Powellfd1e93d2014-09-07 16:52:22 -0700517 final int checkedPos = mListView.getCheckedItemPosition();
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700518 final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
Adam Powellbdda4e72012-07-20 11:22:03 -0700519 if (mAlwaysUseOption && (!hasValidSelection || mLastSelected != checkedPos)) {
Adam Powell278902c2014-07-12 18:33:22 -0700520 setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
Adam Powelld81cc4a2012-07-19 13:51:39 -0700521 mOnceButton.setEnabled(hasValidSelection);
522 if (hasValidSelection) {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700523 mListView.smoothScrollToPosition(checkedPos);
Adam Powell9bee4662012-05-08 11:07:23 -0700524 }
Adam Powelld81cc4a2012-07-19 13:51:39 -0700525 mLastSelected = checkedPos;
Adam Powellc5878612012-05-04 18:42:38 -0700526 } else {
Adam Powell278902c2014-07-12 18:33:22 -0700527 startSelected(position, false, true);
Adam Powellc5878612012-05-04 18:42:38 -0700528 }
529 }
530
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100531 private boolean hasManagedProfile() {
532 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
533 if (userManager == null) {
534 return false;
535 }
536
537 try {
538 List<UserInfo> profiles = userManager.getProfiles(getUserId());
539 for (UserInfo userInfo : profiles) {
540 if (userInfo != null && userInfo.isManagedProfile()) {
541 return true;
542 }
543 }
544 } catch (SecurityException e) {
545 return false;
546 }
547 return false;
548 }
549
550 private boolean supportsManagedProfiles(ResolveInfo resolveInfo) {
551 try {
552 ApplicationInfo appInfo = getPackageManager().getApplicationInfo(
553 resolveInfo.activityInfo.packageName, 0 /* default flags */);
554 return versionNumberAtLeastL(appInfo.targetSdkVersion);
555 } catch (NameNotFoundException e) {
556 return false;
557 }
558 }
559
560 private boolean versionNumberAtLeastL(int versionNumber) {
Dianne Hackborn955d8d62014-10-07 20:17:19 -0700561 return versionNumber >= Build.VERSION_CODES.LOLLIPOP;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100562 }
563
Adam Powell278902c2014-07-12 18:33:22 -0700564 private void setAlwaysButtonEnabled(boolean hasValidSelection, int checkedPos,
565 boolean filtered) {
Nicolas Prevot50449882014-06-23 12:42:37 +0100566 boolean enabled = false;
567 if (hasValidSelection) {
Adam Powell278902c2014-07-12 18:33:22 -0700568 ResolveInfo ri = mAdapter.resolveInfoForPosition(checkedPos, filtered);
Nicolas Prevot50449882014-06-23 12:42:37 +0100569 if (ri.targetUserId == UserHandle.USER_CURRENT) {
570 enabled = true;
571 }
572 }
573 mAlwaysButton.setEnabled(enabled);
574 }
575
Adam Powellc5878612012-05-04 18:42:38 -0700576 public void onButtonClick(View v) {
577 final int id = v.getId();
Adam Powell278902c2014-07-12 18:33:22 -0700578 startSelected(mAlwaysUseOption ?
Adam Powellfd1e93d2014-09-07 16:52:22 -0700579 mListView.getCheckedItemPosition() : mAdapter.getFilteredPosition(),
Adam Powell278902c2014-07-12 18:33:22 -0700580 id == R.id.button_always,
581 mAlwaysUseOption);
Adam Powellc5878612012-05-04 18:42:38 -0700582 dismiss();
583 }
584
Adam Powell278902c2014-07-12 18:33:22 -0700585 void startSelected(int which, boolean always, boolean filtered) {
Amith Yamasani07cd3512013-09-18 13:16:00 -0700586 if (isFinishing()) {
587 return;
588 }
Adam Powell278902c2014-07-12 18:33:22 -0700589 ResolveInfo ri = mAdapter.resolveInfoForPosition(which, filtered);
590 Intent intent = mAdapter.intentForPosition(which, filtered);
Adam Powellc5878612012-05-04 18:42:38 -0700591 onIntentSelected(ri, intent, always);
Mike Lockwood02eb8742011-02-27 09:10:37 -0800592 finish();
593 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800594
Adam Powelle49d9392014-07-17 18:45:19 -0700595 /**
596 * Replace me in subclasses!
597 */
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000598 public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) {
Adam Powelle49d9392014-07-17 18:45:19 -0700599 return defIntent;
600 }
601
Mike Lockwood02eb8742011-02-27 09:10:37 -0800602 protected void onIntentSelected(ResolveInfo ri, Intent intent, boolean alwaysCheck) {
Adam Powell278902c2014-07-12 18:33:22 -0700603 if ((mAlwaysUseOption || mAdapter.hasFilteredItem()) && mAdapter.mOrigResolveList != null) {
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700604 // Build a reasonable intent filter, based on what matched.
605 IntentFilter filter = new IntentFilter();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800606
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700607 if (intent.getAction() != null) {
608 filter.addAction(intent.getAction());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700610 Set<String> categories = intent.getCategories();
611 if (categories != null) {
612 for (String cat : categories) {
613 filter.addCategory(cat);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800614 }
615 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700616 filter.addCategory(Intent.CATEGORY_DEFAULT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700618 int cat = ri.match&IntentFilter.MATCH_CATEGORY_MASK;
619 Uri data = intent.getData();
620 if (cat == IntentFilter.MATCH_CATEGORY_TYPE) {
621 String mimeType = intent.resolveType(this);
622 if (mimeType != null) {
623 try {
624 filter.addDataType(mimeType);
625 } catch (IntentFilter.MalformedMimeTypeException e) {
626 Log.w("ResolverActivity", e);
627 filter = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800628 }
629 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700630 }
631 if (data != null && data.getScheme() != null) {
632 // We need the data specification if there was no type,
633 // OR if the scheme is not one of our magical "file:"
634 // or "content:" schemes (see IntentFilter for the reason).
635 if (cat != IntentFilter.MATCH_CATEGORY_TYPE
636 || (!"file".equals(data.getScheme())
637 && !"content".equals(data.getScheme()))) {
638 filter.addDataScheme(data.getScheme());
639
640 // Look through the resolved filter to determine which part
641 // of it matched the original Intent.
642 Iterator<PatternMatcher> pIt = ri.filter.schemeSpecificPartsIterator();
643 if (pIt != null) {
644 String ssp = data.getSchemeSpecificPart();
645 while (ssp != null && pIt.hasNext()) {
646 PatternMatcher p = pIt.next();
647 if (p.match(ssp)) {
648 filter.addDataSchemeSpecificPart(p.getPath(), p.getType());
649 break;
650 }
Dianne Hackborndf1c0bf2013-06-12 16:21:38 -0700651 }
652 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700653 Iterator<IntentFilter.AuthorityEntry> aIt = ri.filter.authoritiesIterator();
654 if (aIt != null) {
655 while (aIt.hasNext()) {
656 IntentFilter.AuthorityEntry a = aIt.next();
657 if (a.match(data) >= 0) {
658 int port = a.getPort();
659 filter.addDataAuthority(a.getHost(),
660 port >= 0 ? Integer.toString(port) : null);
661 break;
662 }
663 }
664 }
665 pIt = ri.filter.pathsIterator();
666 if (pIt != null) {
667 String path = data.getPath();
668 while (path != null && pIt.hasNext()) {
669 PatternMatcher p = pIt.next();
670 if (p.match(path)) {
671 filter.addDataPath(p.getPath(), p.getType());
672 break;
673 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800674 }
675 }
676 }
677 }
678
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700679 if (filter != null) {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700680 final int N = mAdapter.mOrigResolveList.size();
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700681 ComponentName[] set = new ComponentName[N];
682 int bestMatch = 0;
683 for (int i=0; i<N; i++) {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700684 ResolveInfo r = mAdapter.mOrigResolveList.get(i);
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700685 set[i] = new ComponentName(r.activityInfo.packageName,
686 r.activityInfo.name);
687 if (r.match > bestMatch) bestMatch = r.match;
688 }
689 if (alwaysCheck) {
690 getPackageManager().addPreferredActivity(filter, bestMatch, set,
691 intent.getComponent());
692 } else {
693 try {
694 AppGlobals.getPackageManager().setLastChosenActivity(intent,
695 intent.resolveTypeIfNeeded(getContentResolver()),
696 PackageManager.MATCH_DEFAULT_ONLY,
697 filter, bestMatch, intent.getComponent());
698 } catch (RemoteException re) {
699 Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
700 }
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700701 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800702 }
703 }
704
705 if (intent != null) {
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700706 safelyStartActivity(intent);
707 }
708 }
709
710 public void safelyStartActivity(Intent intent) {
Sander Alewijnsef6545332014-10-31 12:39:02 +0000711 // If needed, show that intent is forwarded
712 // from managed profile to owner or other way around.
713 if (mProfileSwitchMessageId != -1) {
714 Toast.makeText(this, getString(mProfileSwitchMessageId), Toast.LENGTH_LONG).show();
715 }
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700716 if (!mSafeForwardingMode) {
Amith Yamasani203a2f42012-10-03 20:16:40 -0700717 startActivity(intent);
Adam Powell0b3c1122014-10-09 12:50:14 -0700718 onActivityStarted(intent);
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700719 return;
720 }
721 try {
Jeff Sharkey97978802014-10-14 10:48:18 -0700722 startActivityAsCaller(intent, null, UserHandle.USER_NULL);
Adam Powell0b3c1122014-10-09 12:50:14 -0700723 onActivityStarted(intent);
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700724 } catch (RuntimeException e) {
725 String launchedFromPackage;
726 try {
727 launchedFromPackage = ActivityManagerNative.getDefault().getLaunchedFromPackage(
728 getActivityToken());
729 } catch (RemoteException e2) {
730 launchedFromPackage = "??";
731 }
732 Slog.wtf(TAG, "Unable to launch as uid " + mLaunchedFromUid
733 + " package " + launchedFromPackage + ", while running in "
734 + ActivityThread.currentProcessName(), e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800735 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800736 }
737
Adam Powell0b3c1122014-10-09 12:50:14 -0700738 public void onActivityStarted(Intent intent) {
739 // Do nothing
740 }
741
Adam Powellc5878612012-05-04 18:42:38 -0700742 void showAppDetails(ResolveInfo ri) {
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700743 Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
Adam Powell0fc5b2b2012-07-18 18:20:29 -0700744 .setData(Uri.fromParts("package", ri.activityInfo.packageName, null))
745 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
Amith Yamasani203a2f42012-10-03 20:16:40 -0700746 startActivity(in);
Adam Powellc5878612012-05-04 18:42:38 -0700747 }
748
Adam Powell88831a22014-11-20 18:17:00 -0800749 Intent intentForDisplayResolveInfo(DisplayResolveInfo dri) {
750 Intent intent = new Intent(dri.origIntent != null ? dri.origIntent :
751 getReplacementIntent(dri.ri.activityInfo, mIntent));
752 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
753 |Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
754 ActivityInfo ai = dri.ri.activityInfo;
755 intent.setComponent(new ComponentName(
756 ai.applicationInfo.packageName, ai.name));
757 return intent;
758 }
759
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760 private final class DisplayResolveInfo {
761 ResolveInfo ri;
762 CharSequence displayLabel;
Dianne Hackborneb034652009-09-07 00:49:58 -0700763 Drawable displayIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800764 CharSequence extendedInfo;
Dianne Hackborneb034652009-09-07 00:49:58 -0700765 Intent origIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800766
Dianne Hackborneb034652009-09-07 00:49:58 -0700767 DisplayResolveInfo(ResolveInfo pri, CharSequence pLabel,
768 CharSequence pInfo, Intent pOrigIntent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800769 ri = pri;
770 displayLabel = pLabel;
771 extendedInfo = pInfo;
Dianne Hackborneb034652009-09-07 00:49:58 -0700772 origIntent = pOrigIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800773 }
774 }
775
776 private final class ResolveListAdapter extends BaseAdapter {
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800777 private final Intent[] mInitialIntents;
778 private final List<ResolveInfo> mBaseResolveList;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700779 private ResolveInfo mLastChosen;
Adam Powell88831a22014-11-20 18:17:00 -0800780 private DisplayResolveInfo mOtherProfile;
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700781 private final int mLaunchedFromUid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782 private final LayoutInflater mInflater;
783
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700784 List<DisplayResolveInfo> mList;
785 List<ResolveInfo> mOrigResolveList;
786
Adam Powell278902c2014-07-12 18:33:22 -0700787 private int mLastChosenPosition = -1;
788 private boolean mFilterLastUsed;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789
Adam Powell88831a22014-11-20 18:17:00 -0800790 public ResolveListAdapter(Context context, Intent[] initialIntents,
791 List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) {
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800792 mInitialIntents = initialIntents;
793 mBaseResolveList = rList;
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700794 mLaunchedFromUid = launchedFromUid;
Adam Powelle9414d92014-07-05 17:44:18 -0700795 mInflater = LayoutInflater.from(context);
You Kim43a5070e2012-11-21 23:23:45 +0900796 mList = new ArrayList<DisplayResolveInfo>();
Adam Powell278902c2014-07-12 18:33:22 -0700797 mFilterLastUsed = filterLastUsed;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800798 rebuildList();
799 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800800
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800801 public void handlePackagesChanged() {
802 rebuildList();
803 notifyDataSetChanged();
Esteban Talavera6de72662014-12-11 17:54:07 +0000804 if (getCount() == 0) {
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800805 // We no longer have any items... just finish the activity.
806 finish();
Adam Powellc5878612012-05-04 18:42:38 -0700807 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800808 }
809
Adam Powell278902c2014-07-12 18:33:22 -0700810 public DisplayResolveInfo getFilteredItem() {
811 if (mFilterLastUsed && mLastChosenPosition >= 0) {
812 // Not using getItem since it offsets to dodge this position for the list
813 return mList.get(mLastChosenPosition);
814 }
815 return null;
816 }
817
Adam Powell88831a22014-11-20 18:17:00 -0800818 public DisplayResolveInfo getOtherProfile() {
819 return mOtherProfile;
820 }
821
Adam Powell278902c2014-07-12 18:33:22 -0700822 public int getFilteredPosition() {
823 if (mFilterLastUsed && mLastChosenPosition >= 0) {
824 return mLastChosenPosition;
825 }
826 return AbsListView.INVALID_POSITION;
827 }
828
829 public boolean hasFilteredItem() {
830 return mFilterLastUsed && mLastChosenPosition >= 0;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700831 }
832
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800833 private void rebuildList() {
You Kim43a5070e2012-11-21 23:23:45 +0900834 List<ResolveInfo> currentResolveList;
835
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700836 try {
837 mLastChosen = AppGlobals.getPackageManager().getLastChosenActivity(
838 mIntent, mIntent.resolveTypeIfNeeded(getContentResolver()),
839 PackageManager.MATCH_DEFAULT_ONLY);
840 } catch (RemoteException re) {
841 Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
842 }
843
You Kim43a5070e2012-11-21 23:23:45 +0900844 mList.clear();
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800845 if (mBaseResolveList != null) {
Xiong Lie88b0422014-04-10 15:25:45 +0800846 currentResolveList = mOrigResolveList = mBaseResolveList;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800847 } else {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700848 currentResolveList = mOrigResolveList = mPm.queryIntentActivities(
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800849 mIntent, PackageManager.MATCH_DEFAULT_ONLY
Adam Powell278902c2014-07-12 18:33:22 -0700850 | (mFilterLastUsed ? PackageManager.GET_RESOLVED_FILTER : 0));
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700851 // Filter out any activities that the launched uid does not
852 // have permission for. We don't do this when we have an explicit
853 // list of resolved activities, because that only happens when
854 // we are being subclassed, so we can safely launch whatever
855 // they gave us.
You Kim43a5070e2012-11-21 23:23:45 +0900856 if (currentResolveList != null) {
857 for (int i=currentResolveList.size()-1; i >= 0; i--) {
858 ActivityInfo ai = currentResolveList.get(i).activityInfo;
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700859 int granted = ActivityManager.checkComponentPermission(
860 ai.permission, mLaunchedFromUid,
861 ai.applicationInfo.uid, ai.exported);
862 if (granted != PackageManager.PERMISSION_GRANTED) {
863 // Access not allowed!
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700864 if (mOrigResolveList == currentResolveList) {
865 mOrigResolveList = new ArrayList<ResolveInfo>(mOrigResolveList);
866 }
You Kim43a5070e2012-11-21 23:23:45 +0900867 currentResolveList.remove(i);
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700868 }
869 }
870 }
Jeff Hamiltond88e9aa2011-01-24 14:53:00 -0600871 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800872 int N;
You Kim43a5070e2012-11-21 23:23:45 +0900873 if ((currentResolveList != null) && ((N = currentResolveList.size()) > 0)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800874 // Only display the first matches that are either of equal
875 // priority or have asked to be default options.
You Kim43a5070e2012-11-21 23:23:45 +0900876 ResolveInfo r0 = currentResolveList.get(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800877 for (int i=1; i<N; i++) {
You Kim43a5070e2012-11-21 23:23:45 +0900878 ResolveInfo ri = currentResolveList.get(i);
879 if (DEBUG) Log.v(
Adam Powell09a65602014-07-20 16:23:14 -0700880 TAG,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800881 r0.activityInfo.name + "=" +
882 r0.priority + "/" + r0.isDefault + " vs " +
883 ri.activityInfo.name + "=" +
884 ri.priority + "/" + ri.isDefault);
You Kim43a5070e2012-11-21 23:23:45 +0900885 if (r0.priority != ri.priority ||
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800886 r0.isDefault != ri.isDefault) {
887 while (i < N) {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700888 if (mOrigResolveList == currentResolveList) {
889 mOrigResolveList = new ArrayList<ResolveInfo>(mOrigResolveList);
890 }
You Kim43a5070e2012-11-21 23:23:45 +0900891 currentResolveList.remove(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800892 N--;
893 }
894 }
895 }
896 if (N > 1) {
Adam Powell09a65602014-07-20 16:23:14 -0700897 Comparator<ResolveInfo> rComparator =
Dianne Hackbornec452d92014-11-11 17:16:56 -0800898 new ResolverComparator(ResolverActivity.this, mIntent);
You Kim43a5070e2012-11-21 23:23:45 +0900899 Collections.sort(currentResolveList, rComparator);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800900 }
Dianne Hackborneb034652009-09-07 00:49:58 -0700901 // First put the initial items at the top.
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800902 if (mInitialIntents != null) {
903 for (int i=0; i<mInitialIntents.length; i++) {
904 Intent ii = mInitialIntents[i];
Dianne Hackborneb034652009-09-07 00:49:58 -0700905 if (ii == null) {
906 continue;
907 }
908 ActivityInfo ai = ii.resolveActivityInfo(
909 getPackageManager(), 0);
910 if (ai == null) {
Adam Powell09a65602014-07-20 16:23:14 -0700911 Log.w(TAG, "No activity found for " + ii);
Dianne Hackborneb034652009-09-07 00:49:58 -0700912 continue;
913 }
914 ResolveInfo ri = new ResolveInfo();
915 ri.activityInfo = ai;
Nicolas Prevot1a815922014-10-10 16:22:38 +0100916 UserManager userManager =
917 (UserManager) getSystemService(Context.USER_SERVICE);
918 if (userManager.isManagedProfile()) {
919 ri.noResourceId = true;
920 }
Dianne Hackborneb034652009-09-07 00:49:58 -0700921 if (ii instanceof LabeledIntent) {
922 LabeledIntent li = (LabeledIntent)ii;
923 ri.resolvePackageName = li.getSourcePackage();
924 ri.labelRes = li.getLabelResource();
925 ri.nonLocalizedLabel = li.getNonLocalizedLabel();
926 ri.icon = li.getIconResource();
927 }
Adam Powell88831a22014-11-20 18:17:00 -0800928 addResolveInfo(new DisplayResolveInfo(ri,
Dianne Hackborneb034652009-09-07 00:49:58 -0700929 ri.loadLabel(getPackageManager()), null, ii));
930 }
931 }
You Kim43a5070e2012-11-21 23:23:45 +0900932
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800933 // Check for applications with same name and use application name or
934 // package name if necessary
You Kim43a5070e2012-11-21 23:23:45 +0900935 r0 = currentResolveList.get(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800936 int start = 0;
937 CharSequence r0Label = r0.loadLabel(mPm);
Adam Powellc5878612012-05-04 18:42:38 -0700938 mShowExtended = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800939 for (int i = 1; i < N; i++) {
940 if (r0Label == null) {
941 r0Label = r0.activityInfo.packageName;
942 }
You Kim43a5070e2012-11-21 23:23:45 +0900943 ResolveInfo ri = currentResolveList.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800944 CharSequence riLabel = ri.loadLabel(mPm);
945 if (riLabel == null) {
946 riLabel = ri.activityInfo.packageName;
947 }
948 if (riLabel.equals(r0Label)) {
949 continue;
950 }
You Kim43a5070e2012-11-21 23:23:45 +0900951 processGroup(currentResolveList, start, (i-1), r0, r0Label);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800952 r0 = ri;
953 r0Label = riLabel;
954 start = i;
955 }
956 // Process last group
You Kim43a5070e2012-11-21 23:23:45 +0900957 processGroup(currentResolveList, start, (N-1), r0, r0Label);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800958 }
Kenny Guyb8a2df12014-12-12 16:56:08 +0000959
960 // Layout doesn't handle both profile button and last chosen
961 // so disable last chosen if profile button is present.
962 if (mOtherProfile != null && mLastChosenPosition >= 0) {
963 mLastChosenPosition = -1;
964 mFilterLastUsed = false;
965 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800966 }
967
968 private void processGroup(List<ResolveInfo> rList, int start, int end, ResolveInfo ro,
969 CharSequence roLabel) {
970 // Process labels from start to i
971 int num = end - start+1;
972 if (num == 1) {
973 // No duplicate labels. Use label for entry at start
Adam Powell88831a22014-11-20 18:17:00 -0800974 addResolveInfo(new DisplayResolveInfo(ro, roLabel, null, null));
Esteban Talavera6de72662014-12-11 17:54:07 +0000975 updateLastChosenPosition(ro);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800976 } else {
Adam Powellc5878612012-05-04 18:42:38 -0700977 mShowExtended = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800978 boolean usePkg = false;
979 CharSequence startApp = ro.activityInfo.applicationInfo.loadLabel(mPm);
980 if (startApp == null) {
981 usePkg = true;
982 }
983 if (!usePkg) {
984 // Use HashSet to track duplicates
985 HashSet<CharSequence> duplicates =
986 new HashSet<CharSequence>();
987 duplicates.add(startApp);
988 for (int j = start+1; j <= end ; j++) {
989 ResolveInfo jRi = rList.get(j);
990 CharSequence jApp = jRi.activityInfo.applicationInfo.loadLabel(mPm);
991 if ( (jApp == null) || (duplicates.contains(jApp))) {
992 usePkg = true;
993 break;
994 } else {
995 duplicates.add(jApp);
996 }
997 }
998 // Clear HashSet for later use
999 duplicates.clear();
1000 }
1001 for (int k = start; k <= end; k++) {
1002 ResolveInfo add = rList.get(k);
1003 if (usePkg) {
1004 // Use application name for all entries from start to end-1
Adam Powell88831a22014-11-20 18:17:00 -08001005 addResolveInfo(new DisplayResolveInfo(add, roLabel,
Dianne Hackborneb034652009-09-07 00:49:58 -07001006 add.activityInfo.packageName, null));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001007 } else {
1008 // Use package name for all entries from start to end-1
Adam Powell88831a22014-11-20 18:17:00 -08001009 addResolveInfo(new DisplayResolveInfo(add, roLabel,
Dianne Hackborneb034652009-09-07 00:49:58 -07001010 add.activityInfo.applicationInfo.loadLabel(mPm), null));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001011 }
Esteban Talavera6de72662014-12-11 17:54:07 +00001012 updateLastChosenPosition(add);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001013 }
1014 }
1015 }
1016
Esteban Talavera6de72662014-12-11 17:54:07 +00001017 private void updateLastChosenPosition(ResolveInfo info) {
1018 if (mLastChosen != null
1019 && mLastChosen.activityInfo.packageName.equals(info.activityInfo.packageName)
1020 && mLastChosen.activityInfo.name.equals(info.activityInfo.name)) {
1021 mLastChosenPosition = mList.size() - 1;
1022 }
1023 }
1024
Adam Powell88831a22014-11-20 18:17:00 -08001025 private void addResolveInfo(DisplayResolveInfo dri) {
1026 if (dri.ri.targetUserId != UserHandle.USER_CURRENT && mOtherProfile == null) {
1027 // So far we only support a single other profile at a time.
1028 // The first one we see gets special treatment.
1029 mOtherProfile = dri;
1030 } else {
1031 mList.add(dri);
1032 }
1033 }
1034
Adam Powell278902c2014-07-12 18:33:22 -07001035 public ResolveInfo resolveInfoForPosition(int position, boolean filtered) {
1036 return (filtered ? getItem(position) : mList.get(position)).ri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001037 }
1038
Adam Powell278902c2014-07-12 18:33:22 -07001039 public Intent intentForPosition(int position, boolean filtered) {
1040 DisplayResolveInfo dri = filtered ? getItem(position) : mList.get(position);
Adam Powell88831a22014-11-20 18:17:00 -08001041 return intentForDisplayResolveInfo(dri);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001042 }
1043
1044 public int getCount() {
Adam Powell278902c2014-07-12 18:33:22 -07001045 int result = mList.size();
1046 if (mFilterLastUsed && mLastChosenPosition >= 0) {
1047 result--;
1048 }
1049 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001050 }
1051
Adam Powell278902c2014-07-12 18:33:22 -07001052 public DisplayResolveInfo getItem(int position) {
1053 if (mFilterLastUsed && mLastChosenPosition >= 0 && position >= mLastChosenPosition) {
1054 position++;
1055 }
You Kim43a5070e2012-11-21 23:23:45 +09001056 return mList.get(position);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001057 }
1058
1059 public long getItemId(int position) {
1060 return position;
1061 }
1062
1063 public View getView(int position, View convertView, ViewGroup parent) {
Adam Powellfd1e93d2014-09-07 16:52:22 -07001064 View view = convertView;
1065 if (view == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001066 view = mInflater.inflate(
1067 com.android.internal.R.layout.resolve_list_item, parent, false);
Adam Powellc5878612012-05-04 18:42:38 -07001068
Adam Powell0256c6f2013-05-29 16:42:33 -07001069 final ViewHolder holder = new ViewHolder(view);
1070 view.setTag(holder);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001071 }
Adam Powell278902c2014-07-12 18:33:22 -07001072 bindView(view, getItem(position));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001073 return view;
1074 }
1075
1076 private final void bindView(View view, DisplayResolveInfo info) {
Adam Powell0256c6f2013-05-29 16:42:33 -07001077 final ViewHolder holder = (ViewHolder) view.getTag();
1078 holder.text.setText(info.displayLabel);
Adam Powellc5878612012-05-04 18:42:38 -07001079 if (mShowExtended) {
Adam Powell0256c6f2013-05-29 16:42:33 -07001080 holder.text2.setVisibility(View.VISIBLE);
1081 holder.text2.setText(info.extendedInfo);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001082 } else {
Adam Powell0256c6f2013-05-29 16:42:33 -07001083 holder.text2.setVisibility(View.GONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001084 }
Dianne Hackborneb034652009-09-07 00:49:58 -07001085 if (info.displayIcon == null) {
Adam Powell0256c6f2013-05-29 16:42:33 -07001086 new LoadIconTask().execute(info);
Dianne Hackborneb034652009-09-07 00:49:58 -07001087 }
Adam Powell0256c6f2013-05-29 16:42:33 -07001088 holder.icon.setImageDrawable(info.displayIcon);
1089 }
1090 }
1091
1092 static class ViewHolder {
1093 public TextView text;
1094 public TextView text2;
1095 public ImageView icon;
1096
1097 public ViewHolder(View view) {
1098 text = (TextView) view.findViewById(com.android.internal.R.id.text1);
1099 text2 = (TextView) view.findViewById(com.android.internal.R.id.text2);
1100 icon = (ImageView) view.findViewById(R.id.icon);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001101 }
1102 }
1103
Adam Powell2d809622012-03-22 15:24:43 -07001104 class ItemLongClickListener implements AdapterView.OnItemLongClickListener {
1105
1106 @Override
1107 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Adam Powellfd1e93d2014-09-07 16:52:22 -07001108 position -= mListView.getHeaderViewsCount();
1109 if (position < 0) {
1110 // Header views don't count.
1111 return false;
1112 }
Adam Powell278902c2014-07-12 18:33:22 -07001113 ResolveInfo ri = mAdapter.resolveInfoForPosition(position, true);
Adam Powellc5878612012-05-04 18:42:38 -07001114 showAppDetails(ri);
Adam Powell2d809622012-03-22 15:24:43 -07001115 return true;
1116 }
1117
1118 }
Adam Powell0256c6f2013-05-29 16:42:33 -07001119
1120 class LoadIconTask extends AsyncTask<DisplayResolveInfo, Void, DisplayResolveInfo> {
1121 @Override
1122 protected DisplayResolveInfo doInBackground(DisplayResolveInfo... params) {
1123 final DisplayResolveInfo info = params[0];
1124 if (info.displayIcon == null) {
1125 info.displayIcon = loadIconForResolveInfo(info.ri);
1126 }
1127 return info;
1128 }
1129
1130 @Override
1131 protected void onPostExecute(DisplayResolveInfo info) {
Adam Powell88831a22014-11-20 18:17:00 -08001132 if (mProfileView != null && mAdapter.getOtherProfile() == info) {
1133 bindProfileView();
1134 }
Adam Powell0256c6f2013-05-29 16:42:33 -07001135 mAdapter.notifyDataSetChanged();
1136 }
1137 }
Adam Powell278902c2014-07-12 18:33:22 -07001138
1139 class LoadIconIntoViewTask extends AsyncTask<DisplayResolveInfo, Void, DisplayResolveInfo> {
1140 final ImageView mTargetView;
1141
1142 public LoadIconIntoViewTask(ImageView target) {
1143 mTargetView = target;
1144 }
1145
1146 @Override
1147 protected DisplayResolveInfo doInBackground(DisplayResolveInfo... params) {
1148 final DisplayResolveInfo info = params[0];
1149 if (info.displayIcon == null) {
1150 info.displayIcon = loadIconForResolveInfo(info.ri);
1151 }
1152 return info;
1153 }
1154
1155 @Override
1156 protected void onPostExecute(DisplayResolveInfo info) {
1157 mTargetView.setImageDrawable(info.displayIcon);
1158 }
1159 }
Adam Powell09a65602014-07-20 16:23:14 -07001160
Dianne Hackbornec452d92014-11-11 17:16:56 -08001161 static final boolean isSpecificUriMatch(int match) {
1162 match = match&IntentFilter.MATCH_CATEGORY_MASK;
1163 return match >= IntentFilter.MATCH_CATEGORY_HOST
1164 && match <= IntentFilter.MATCH_CATEGORY_PATH;
1165 }
1166
Adam Powell09a65602014-07-20 16:23:14 -07001167 class ResolverComparator implements Comparator<ResolveInfo> {
1168 private final Collator mCollator;
Dianne Hackbornec452d92014-11-11 17:16:56 -08001169 private final boolean mHttp;
Adam Powell09a65602014-07-20 16:23:14 -07001170
Dianne Hackbornec452d92014-11-11 17:16:56 -08001171 public ResolverComparator(Context context, Intent intent) {
Adam Powell09a65602014-07-20 16:23:14 -07001172 mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
Dianne Hackbornec452d92014-11-11 17:16:56 -08001173 String scheme = intent.getScheme();
1174 mHttp = "http".equals(scheme) || "https".equals(scheme);
Adam Powell09a65602014-07-20 16:23:14 -07001175 }
1176
1177 @Override
1178 public int compare(ResolveInfo lhs, ResolveInfo rhs) {
1179 // We want to put the one targeted to another user at the end of the dialog.
1180 if (lhs.targetUserId != UserHandle.USER_CURRENT) {
1181 return 1;
1182 }
Adam Powell09a65602014-07-20 16:23:14 -07001183
Dianne Hackbornec452d92014-11-11 17:16:56 -08001184 if (mHttp) {
1185 // Special case: we want filters that match URI paths/schemes to be
1186 // ordered before others. This is for the case when opening URIs,
1187 // to make native apps go above browsers.
1188 final boolean lhsSpecific = isSpecificUriMatch(lhs.match);
1189 final boolean rhsSpecific = isSpecificUriMatch(rhs.match);
1190 if (lhsSpecific != rhsSpecific) {
1191 return lhsSpecific ? -1 : 1;
1192 }
1193 }
1194
Adam Powell09a65602014-07-20 16:23:14 -07001195 if (mStats != null) {
1196 final long timeDiff =
1197 getPackageTimeSpent(rhs.activityInfo.packageName) -
1198 getPackageTimeSpent(lhs.activityInfo.packageName);
1199
1200 if (timeDiff != 0) {
1201 return timeDiff > 0 ? 1 : -1;
1202 }
1203 }
1204
1205 CharSequence sa = lhs.loadLabel(mPm);
1206 if (sa == null) sa = lhs.activityInfo.name;
1207 CharSequence sb = rhs.loadLabel(mPm);
1208 if (sb == null) sb = rhs.activityInfo.name;
1209
1210 return mCollator.compare(sa.toString(), sb.toString());
1211 }
1212
1213 private long getPackageTimeSpent(String packageName) {
1214 if (mStats != null) {
Adam Lesinski35168002014-07-21 15:25:30 -07001215 final UsageStats stats = mStats.get(packageName);
Adam Powell09a65602014-07-20 16:23:14 -07001216 if (stats != null) {
Adam Lesinski35168002014-07-21 15:25:30 -07001217 return stats.getTotalTimeInForeground();
Adam Powell09a65602014-07-20 16:23:14 -07001218 }
1219
1220 }
1221 return 0;
1222 }
1223 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001224}