blob: 6b35f3f6edafbbf6bfb85cea0707fd84ec9283d3 [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
Sander Alewijnse053d3dd2015-03-09 15:31:10 +0000345 // Do not show the profile switch message anymore.
346 mProfileSwitchMessageId = -1;
347
Adam Powell88831a22014-11-20 18:17:00 -0800348 final Intent intent = intentForDisplayResolveInfo(dri);
Nicolas Prevotdaf92062015-01-21 17:50:23 +0000349 onIntentSelected(dri.ri, intent, false);
Adam Powell88831a22014-11-20 18:17:00 -0800350 finish();
351 }
352 });
353 bindProfileView();
354 }
355 }
356
357 void bindProfileView() {
358 final DisplayResolveInfo dri = mAdapter.getOtherProfile();
359 if (dri != null) {
360 mProfileView.setVisibility(View.VISIBLE);
361 final ImageView icon = (ImageView) mProfileView.findViewById(R.id.icon);
362 final TextView text = (TextView) mProfileView.findViewById(R.id.text1);
363 if (dri.displayIcon == null) {
364 new LoadIconTask().execute(dri);
365 }
366 icon.setImageDrawable(dri.displayIcon);
367 text.setText(dri.displayLabel);
368 } else {
369 mProfileView.setVisibility(View.GONE);
370 }
Adam Powell278902c2014-07-12 18:33:22 -0700371 }
372
Sander Alewijnsef6545332014-10-31 12:39:02 +0000373 private void setProfileSwitchMessageId(int contentUserHint) {
374 if (contentUserHint != UserHandle.USER_CURRENT &&
375 contentUserHint != UserHandle.myUserId()) {
376 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
377 UserInfo originUserInfo = userManager.getUserInfo(contentUserHint);
378 boolean originIsManaged = originUserInfo != null ? originUserInfo.isManagedProfile()
379 : false;
380 boolean targetIsManaged = userManager.isManagedProfile();
381 if (originIsManaged && !targetIsManaged) {
382 mProfileSwitchMessageId = com.android.internal.R.string.forward_intent_to_owner;
383 } else if (!originIsManaged && targetIsManaged) {
384 mProfileSwitchMessageId = com.android.internal.R.string.forward_intent_to_work;
385 }
386 }
387 }
388
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700389 /**
390 * Turn on launch mode that is safe to use when forwarding intents received from
391 * applications and running in system processes. This mode uses Activity.startActivityAsCaller
392 * instead of the normal Activity.startActivity for launching the activity selected
393 * by the user.
394 *
395 * <p>This mode is set to true by default if the activity is initialized through
396 * {@link #onCreate(android.os.Bundle)}. If a subclass calls one of the other onCreate
397 * methods, it is set to false by default. You must set it before calling one of the
398 * more detailed onCreate methods, so that it will be set correctly in the case where
399 * there is only one intent to resolve and it is thus started immediately.</p>
400 */
401 public void setSafeForwardingMode(boolean safeForwarding) {
402 mSafeForwardingMode = safeForwarding;
403 }
404
Adam Powell278902c2014-07-12 18:33:22 -0700405 protected CharSequence getTitleForAction(String action, int defaultTitleRes) {
Adam Powella35c77a2014-09-25 16:46:36 -0700406 final ActionTitle title = mResolvingHome ? ActionTitle.HOME : ActionTitle.forAction(action);
Adam Powell278902c2014-07-12 18:33:22 -0700407 final boolean named = mAdapter.hasFilteredItem();
408 if (title == ActionTitle.DEFAULT && defaultTitleRes != 0) {
409 return getString(defaultTitleRes);
410 } else {
411 return named ? getString(title.namedTitleRes, mAdapter.getFilteredItem().displayLabel) :
412 getString(title.titleRes);
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700413 }
Adam Powellc5878612012-05-04 18:42:38 -0700414 }
415
Adam Powelle9414d92014-07-05 17:44:18 -0700416 void dismiss() {
417 if (!isFinishing()) {
418 finish();
419 }
420 }
421
Adam Powellc5878612012-05-04 18:42:38 -0700422 Drawable getIcon(Resources res, int resId) {
423 Drawable result;
424 try {
425 result = res.getDrawableForDensity(resId, mIconDpi);
426 } catch (Resources.NotFoundException e) {
427 result = null;
428 }
429
430 return result;
431 }
432
433 Drawable loadIconForResolveInfo(ResolveInfo ri) {
434 Drawable dr;
435 try {
436 if (ri.resolvePackageName != null && ri.icon != 0) {
437 dr = getIcon(mPm.getResourcesForApplication(ri.resolvePackageName), ri.icon);
438 if (dr != null) {
439 return dr;
440 }
441 }
442 final int iconRes = ri.getIconResource();
443 if (iconRes != 0) {
444 dr = getIcon(mPm.getResourcesForApplication(ri.activityInfo.packageName), iconRes);
445 if (dr != null) {
446 return dr;
447 }
448 }
449 } catch (NameNotFoundException e) {
450 Log.e(TAG, "Couldn't find resources for package", e);
451 }
452 return ri.loadIcon(mPm);
453 }
454
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800455 @Override
456 protected void onRestart() {
457 super.onRestart();
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700458 if (!mRegistered) {
459 mPackageMonitor.register(this, getMainLooper(), false);
460 mRegistered = true;
461 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800462 mAdapter.handlePackagesChanged();
Adam Powell88831a22014-11-20 18:17:00 -0800463 if (mProfileView != null) {
464 bindProfileView();
465 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800466 }
467
468 @Override
469 protected void onStop() {
470 super.onStop();
Dianne Hackbornd44713a2012-04-30 16:34:46 -0700471 if (mRegistered) {
472 mPackageMonitor.unregister();
473 mRegistered = false;
474 }
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700475 if ((getIntent().getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
476 // This resolver is in the unusual situation where it has been
477 // launched at the top of a new task. We don't let it be added
478 // to the recent tasks shown to the user, and we need to make sure
479 // that each time we are launched we get the correct launching
480 // uid (not re-using the same resolver from an old launching uid),
481 // so we will now finish ourself since being no longer visible,
482 // the user probably can't get back to us.
483 if (!isChangingConfigurations()) {
484 finish();
485 }
486 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800487 }
488
Adam Powellc5878612012-05-04 18:42:38 -0700489 @Override
Adam Powell9bee4662012-05-08 11:07:23 -0700490 protected void onRestoreInstanceState(Bundle savedInstanceState) {
491 super.onRestoreInstanceState(savedInstanceState);
492 if (mAlwaysUseOption) {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700493 final int checkedPos = mListView.getCheckedItemPosition();
Nicolas Prevot50449882014-06-23 12:42:37 +0100494 final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
Adam Powelld81cc4a2012-07-19 13:51:39 -0700495 mLastSelected = checkedPos;
Adam Powell278902c2014-07-12 18:33:22 -0700496 setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
Nicolas Prevot50449882014-06-23 12:42:37 +0100497 mOnceButton.setEnabled(hasValidSelection);
498 if (hasValidSelection) {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700499 mListView.setSelection(checkedPos);
Adam Powell9bee4662012-05-08 11:07:23 -0700500 }
501 }
502 }
503
504 @Override
Adam Powellc5878612012-05-04 18:42:38 -0700505 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700506 position -= mListView.getHeaderViewsCount();
507 if (position < 0) {
508 // Header views don't count.
509 return;
510 }
Adam Powellfd1e93d2014-09-07 16:52:22 -0700511 final int checkedPos = mListView.getCheckedItemPosition();
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700512 final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
Adam Powellbdda4e72012-07-20 11:22:03 -0700513 if (mAlwaysUseOption && (!hasValidSelection || mLastSelected != checkedPos)) {
Adam Powell278902c2014-07-12 18:33:22 -0700514 setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
Adam Powelld81cc4a2012-07-19 13:51:39 -0700515 mOnceButton.setEnabled(hasValidSelection);
516 if (hasValidSelection) {
Adam Powellfd1e93d2014-09-07 16:52:22 -0700517 mListView.smoothScrollToPosition(checkedPos);
Adam Powell9bee4662012-05-08 11:07:23 -0700518 }
Adam Powelld81cc4a2012-07-19 13:51:39 -0700519 mLastSelected = checkedPos;
Adam Powellc5878612012-05-04 18:42:38 -0700520 } else {
Adam Powell278902c2014-07-12 18:33:22 -0700521 startSelected(position, false, true);
Adam Powellc5878612012-05-04 18:42:38 -0700522 }
523 }
524
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100525 private boolean hasManagedProfile() {
526 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
527 if (userManager == null) {
528 return false;
529 }
530
531 try {
532 List<UserInfo> profiles = userManager.getProfiles(getUserId());
533 for (UserInfo userInfo : profiles) {
534 if (userInfo != null && userInfo.isManagedProfile()) {
535 return true;
536 }
537 }
538 } catch (SecurityException e) {
539 return false;
540 }
541 return false;
542 }
543
544 private boolean supportsManagedProfiles(ResolveInfo resolveInfo) {
545 try {
546 ApplicationInfo appInfo = getPackageManager().getApplicationInfo(
547 resolveInfo.activityInfo.packageName, 0 /* default flags */);
548 return versionNumberAtLeastL(appInfo.targetSdkVersion);
549 } catch (NameNotFoundException e) {
550 return false;
551 }
552 }
553
554 private boolean versionNumberAtLeastL(int versionNumber) {
Dianne Hackborn955d8d62014-10-07 20:17:19 -0700555 return versionNumber >= Build.VERSION_CODES.LOLLIPOP;
Sander Alewijnse6c9eee82014-07-17 10:03:31 +0100556 }
557
Adam Powell278902c2014-07-12 18:33:22 -0700558 private void setAlwaysButtonEnabled(boolean hasValidSelection, int checkedPos,
559 boolean filtered) {
Nicolas Prevot50449882014-06-23 12:42:37 +0100560 boolean enabled = false;
561 if (hasValidSelection) {
Adam Powell278902c2014-07-12 18:33:22 -0700562 ResolveInfo ri = mAdapter.resolveInfoForPosition(checkedPos, filtered);
Nicolas Prevot50449882014-06-23 12:42:37 +0100563 if (ri.targetUserId == UserHandle.USER_CURRENT) {
564 enabled = true;
565 }
566 }
567 mAlwaysButton.setEnabled(enabled);
568 }
569
Adam Powellc5878612012-05-04 18:42:38 -0700570 public void onButtonClick(View v) {
571 final int id = v.getId();
Adam Powell278902c2014-07-12 18:33:22 -0700572 startSelected(mAlwaysUseOption ?
Adam Powellfd1e93d2014-09-07 16:52:22 -0700573 mListView.getCheckedItemPosition() : mAdapter.getFilteredPosition(),
Adam Powell278902c2014-07-12 18:33:22 -0700574 id == R.id.button_always,
575 mAlwaysUseOption);
Adam Powellc5878612012-05-04 18:42:38 -0700576 }
577
Adam Powell278902c2014-07-12 18:33:22 -0700578 void startSelected(int which, boolean always, boolean filtered) {
Amith Yamasani07cd3512013-09-18 13:16:00 -0700579 if (isFinishing()) {
580 return;
581 }
Adam Powell278902c2014-07-12 18:33:22 -0700582 ResolveInfo ri = mAdapter.resolveInfoForPosition(which, filtered);
Sander Alewijnse86d35ba2015-02-04 15:14:53 +0000583 if (mResolvingHome && hasManagedProfile() && !supportsManagedProfiles(ri)) {
584 Toast.makeText(this, String.format(getResources().getString(
585 com.android.internal.R.string.activity_resolver_work_profiles_support),
586 ri.activityInfo.loadLabel(getPackageManager()).toString()),
587 Toast.LENGTH_LONG).show();
588 return;
589 }
590
Adam Powell278902c2014-07-12 18:33:22 -0700591 Intent intent = mAdapter.intentForPosition(which, filtered);
Adam Powellc5878612012-05-04 18:42:38 -0700592 onIntentSelected(ri, intent, always);
Mike Lockwood02eb8742011-02-27 09:10:37 -0800593 finish();
594 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800595
Adam Powelle49d9392014-07-17 18:45:19 -0700596 /**
597 * Replace me in subclasses!
598 */
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000599 public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) {
Adam Powelle49d9392014-07-17 18:45:19 -0700600 return defIntent;
601 }
602
Mike Lockwood02eb8742011-02-27 09:10:37 -0800603 protected void onIntentSelected(ResolveInfo ri, Intent intent, boolean alwaysCheck) {
Adam Powell278902c2014-07-12 18:33:22 -0700604 if ((mAlwaysUseOption || mAdapter.hasFilteredItem()) && mAdapter.mOrigResolveList != null) {
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700605 // Build a reasonable intent filter, based on what matched.
606 IntentFilter filter = new IntentFilter();
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800607 String action = intent.getAction();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800609 if (action != null) {
610 filter.addAction(action);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800611 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700612 Set<String> categories = intent.getCategories();
613 if (categories != null) {
614 for (String cat : categories) {
615 filter.addCategory(cat);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800616 }
617 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700618 filter.addCategory(Intent.CATEGORY_DEFAULT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800619
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700620 int cat = ri.match&IntentFilter.MATCH_CATEGORY_MASK;
621 Uri data = intent.getData();
622 if (cat == IntentFilter.MATCH_CATEGORY_TYPE) {
623 String mimeType = intent.resolveType(this);
624 if (mimeType != null) {
625 try {
626 filter.addDataType(mimeType);
627 } catch (IntentFilter.MalformedMimeTypeException e) {
628 Log.w("ResolverActivity", e);
629 filter = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800630 }
631 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700632 }
633 if (data != null && data.getScheme() != null) {
634 // We need the data specification if there was no type,
635 // OR if the scheme is not one of our magical "file:"
636 // or "content:" schemes (see IntentFilter for the reason).
637 if (cat != IntentFilter.MATCH_CATEGORY_TYPE
638 || (!"file".equals(data.getScheme())
639 && !"content".equals(data.getScheme()))) {
640 filter.addDataScheme(data.getScheme());
641
642 // Look through the resolved filter to determine which part
643 // of it matched the original Intent.
644 Iterator<PatternMatcher> pIt = ri.filter.schemeSpecificPartsIterator();
645 if (pIt != null) {
646 String ssp = data.getSchemeSpecificPart();
647 while (ssp != null && pIt.hasNext()) {
648 PatternMatcher p = pIt.next();
649 if (p.match(ssp)) {
650 filter.addDataSchemeSpecificPart(p.getPath(), p.getType());
651 break;
652 }
Dianne Hackborndf1c0bf2013-06-12 16:21:38 -0700653 }
654 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700655 Iterator<IntentFilter.AuthorityEntry> aIt = ri.filter.authoritiesIterator();
656 if (aIt != null) {
657 while (aIt.hasNext()) {
658 IntentFilter.AuthorityEntry a = aIt.next();
659 if (a.match(data) >= 0) {
660 int port = a.getPort();
661 filter.addDataAuthority(a.getHost(),
662 port >= 0 ? Integer.toString(port) : null);
663 break;
664 }
665 }
666 }
667 pIt = ri.filter.pathsIterator();
668 if (pIt != null) {
669 String path = data.getPath();
670 while (path != null && pIt.hasNext()) {
671 PatternMatcher p = pIt.next();
672 if (p.match(path)) {
673 filter.addDataPath(p.getPath(), p.getType());
674 break;
675 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800676 }
677 }
678 }
679 }
680
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700681 if (filter != null) {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700682 final int N = mAdapter.mOrigResolveList.size();
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700683 ComponentName[] set = new ComponentName[N];
684 int bestMatch = 0;
685 for (int i=0; i<N; i++) {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700686 ResolveInfo r = mAdapter.mOrigResolveList.get(i);
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700687 set[i] = new ComponentName(r.activityInfo.packageName,
688 r.activityInfo.name);
689 if (r.match > bestMatch) bestMatch = r.match;
690 }
691 if (alwaysCheck) {
Fabrice Di Meglio1c1b4712014-11-19 17:12:32 -0800692 PackageManager pm = getPackageManager();
693
694 // Set the preferred Activity
695 pm.addPreferredActivity(filter, bestMatch, set, intent.getComponent());
696
697 // Update Domain Verification status
698 int userId = getUserId();
699 ComponentName cn = intent.getComponent();
700 String packageName = cn.getPackageName();
701 String dataScheme = (data != null) ? data.getScheme() : null;
702
703 boolean isHttpOrHttps = (dataScheme != null) &&
704 (dataScheme.equals(IntentFilter.SCHEME_HTTP) ||
705 dataScheme.equals(IntentFilter.SCHEME_HTTPS));
706
707 boolean isViewAction = (action != null) && action.equals(Intent.ACTION_VIEW);
708 boolean hasCategoryBrowsable = (categories != null) &&
709 categories.contains(Intent.CATEGORY_BROWSABLE);
710
711 if (isHttpOrHttps && isViewAction && hasCategoryBrowsable) {
712 pm.updateIntentVerificationStatus(packageName,
713 PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS,
714 userId);
715 }
Amith Yamasani588dd2a2013-09-03 12:30:44 -0700716 } else {
717 try {
718 AppGlobals.getPackageManager().setLastChosenActivity(intent,
719 intent.resolveTypeIfNeeded(getContentResolver()),
720 PackageManager.MATCH_DEFAULT_ONLY,
721 filter, bestMatch, intent.getComponent());
722 } catch (RemoteException re) {
723 Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
724 }
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700725 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800726 }
727 }
728
729 if (intent != null) {
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700730 safelyStartActivity(intent);
731 }
732 }
733
734 public void safelyStartActivity(Intent intent) {
Sander Alewijnsef6545332014-10-31 12:39:02 +0000735 // If needed, show that intent is forwarded
736 // from managed profile to owner or other way around.
737 if (mProfileSwitchMessageId != -1) {
738 Toast.makeText(this, getString(mProfileSwitchMessageId), Toast.LENGTH_LONG).show();
739 }
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700740 if (!mSafeForwardingMode) {
Amith Yamasani203a2f42012-10-03 20:16:40 -0700741 startActivity(intent);
Adam Powell0b3c1122014-10-09 12:50:14 -0700742 onActivityStarted(intent);
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700743 return;
744 }
745 try {
Jeff Sharkey97978802014-10-14 10:48:18 -0700746 startActivityAsCaller(intent, null, UserHandle.USER_NULL);
Adam Powell0b3c1122014-10-09 12:50:14 -0700747 onActivityStarted(intent);
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700748 } catch (RuntimeException e) {
749 String launchedFromPackage;
750 try {
751 launchedFromPackage = ActivityManagerNative.getDefault().getLaunchedFromPackage(
752 getActivityToken());
753 } catch (RemoteException e2) {
754 launchedFromPackage = "??";
755 }
756 Slog.wtf(TAG, "Unable to launch as uid " + mLaunchedFromUid
757 + " package " + launchedFromPackage + ", while running in "
758 + ActivityThread.currentProcessName(), e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760 }
761
Adam Powell0b3c1122014-10-09 12:50:14 -0700762 public void onActivityStarted(Intent intent) {
763 // Do nothing
764 }
765
Adam Powellc5878612012-05-04 18:42:38 -0700766 void showAppDetails(ResolveInfo ri) {
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700767 Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
Adam Powell0fc5b2b2012-07-18 18:20:29 -0700768 .setData(Uri.fromParts("package", ri.activityInfo.packageName, null))
769 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
Amith Yamasani203a2f42012-10-03 20:16:40 -0700770 startActivity(in);
Adam Powellc5878612012-05-04 18:42:38 -0700771 }
772
Adam Powell88831a22014-11-20 18:17:00 -0800773 Intent intentForDisplayResolveInfo(DisplayResolveInfo dri) {
774 Intent intent = new Intent(dri.origIntent != null ? dri.origIntent :
775 getReplacementIntent(dri.ri.activityInfo, mIntent));
776 intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
777 |Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
778 ActivityInfo ai = dri.ri.activityInfo;
779 intent.setComponent(new ComponentName(
780 ai.applicationInfo.packageName, ai.name));
781 return intent;
782 }
783
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800784 private final class DisplayResolveInfo {
785 ResolveInfo ri;
786 CharSequence displayLabel;
Dianne Hackborneb034652009-09-07 00:49:58 -0700787 Drawable displayIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800788 CharSequence extendedInfo;
Dianne Hackborneb034652009-09-07 00:49:58 -0700789 Intent origIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800790
Dianne Hackborneb034652009-09-07 00:49:58 -0700791 DisplayResolveInfo(ResolveInfo pri, CharSequence pLabel,
792 CharSequence pInfo, Intent pOrigIntent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793 ri = pri;
794 displayLabel = pLabel;
795 extendedInfo = pInfo;
Dianne Hackborneb034652009-09-07 00:49:58 -0700796 origIntent = pOrigIntent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800797 }
798 }
799
800 private final class ResolveListAdapter extends BaseAdapter {
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800801 private final Intent[] mInitialIntents;
802 private final List<ResolveInfo> mBaseResolveList;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700803 private ResolveInfo mLastChosen;
Adam Powell88831a22014-11-20 18:17:00 -0800804 private DisplayResolveInfo mOtherProfile;
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700805 private final int mLaunchedFromUid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800806 private final LayoutInflater mInflater;
807
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700808 List<DisplayResolveInfo> mList;
809 List<ResolveInfo> mOrigResolveList;
810
Adam Powell278902c2014-07-12 18:33:22 -0700811 private int mLastChosenPosition = -1;
812 private boolean mFilterLastUsed;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800813
Adam Powell88831a22014-11-20 18:17:00 -0800814 public ResolveListAdapter(Context context, Intent[] initialIntents,
815 List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) {
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800816 mInitialIntents = initialIntents;
817 mBaseResolveList = rList;
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700818 mLaunchedFromUid = launchedFromUid;
Adam Powelle9414d92014-07-05 17:44:18 -0700819 mInflater = LayoutInflater.from(context);
You Kim43a5070e2012-11-21 23:23:45 +0900820 mList = new ArrayList<DisplayResolveInfo>();
Adam Powell278902c2014-07-12 18:33:22 -0700821 mFilterLastUsed = filterLastUsed;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800822 rebuildList();
823 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800824
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800825 public void handlePackagesChanged() {
826 rebuildList();
827 notifyDataSetChanged();
Esteban Talavera6de72662014-12-11 17:54:07 +0000828 if (getCount() == 0) {
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800829 // We no longer have any items... just finish the activity.
830 finish();
Adam Powellc5878612012-05-04 18:42:38 -0700831 }
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800832 }
833
Adam Powell278902c2014-07-12 18:33:22 -0700834 public DisplayResolveInfo getFilteredItem() {
835 if (mFilterLastUsed && mLastChosenPosition >= 0) {
836 // Not using getItem since it offsets to dodge this position for the list
837 return mList.get(mLastChosenPosition);
838 }
839 return null;
840 }
841
Adam Powell88831a22014-11-20 18:17:00 -0800842 public DisplayResolveInfo getOtherProfile() {
843 return mOtherProfile;
844 }
845
Adam Powell278902c2014-07-12 18:33:22 -0700846 public int getFilteredPosition() {
847 if (mFilterLastUsed && mLastChosenPosition >= 0) {
848 return mLastChosenPosition;
849 }
850 return AbsListView.INVALID_POSITION;
851 }
852
853 public boolean hasFilteredItem() {
854 return mFilterLastUsed && mLastChosenPosition >= 0;
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700855 }
856
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800857 private void rebuildList() {
You Kim43a5070e2012-11-21 23:23:45 +0900858 List<ResolveInfo> currentResolveList;
859
Amith Yamasanie9ecc8b2013-08-22 11:16:27 -0700860 try {
861 mLastChosen = AppGlobals.getPackageManager().getLastChosenActivity(
862 mIntent, mIntent.resolveTypeIfNeeded(getContentResolver()),
863 PackageManager.MATCH_DEFAULT_ONLY);
864 } catch (RemoteException re) {
865 Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
866 }
867
Sudheer Shanka7e64e102015-01-23 10:37:45 +0000868 // Clear the value of mOtherProfile from previous call.
869 mOtherProfile = null;
You Kim43a5070e2012-11-21 23:23:45 +0900870 mList.clear();
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800871 if (mBaseResolveList != null) {
Xiong Lie88b0422014-04-10 15:25:45 +0800872 currentResolveList = mOrigResolveList = mBaseResolveList;
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800873 } else {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700874 currentResolveList = mOrigResolveList = mPm.queryIntentActivities(
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800875 mIntent, PackageManager.MATCH_DEFAULT_ONLY
Adam Powell278902c2014-07-12 18:33:22 -0700876 | (mFilterLastUsed ? PackageManager.GET_RESOLVED_FILTER : 0));
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700877 // Filter out any activities that the launched uid does not
878 // have permission for. We don't do this when we have an explicit
879 // list of resolved activities, because that only happens when
880 // we are being subclassed, so we can safely launch whatever
881 // they gave us.
You Kim43a5070e2012-11-21 23:23:45 +0900882 if (currentResolveList != null) {
883 for (int i=currentResolveList.size()-1; i >= 0; i--) {
884 ActivityInfo ai = currentResolveList.get(i).activityInfo;
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700885 int granted = ActivityManager.checkComponentPermission(
886 ai.permission, mLaunchedFromUid,
887 ai.applicationInfo.uid, ai.exported);
888 if (granted != PackageManager.PERMISSION_GRANTED) {
889 // Access not allowed!
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700890 if (mOrigResolveList == currentResolveList) {
891 mOrigResolveList = new ArrayList<ResolveInfo>(mOrigResolveList);
892 }
You Kim43a5070e2012-11-21 23:23:45 +0900893 currentResolveList.remove(i);
Dianne Hackborn5320eb82012-05-18 12:05:04 -0700894 }
895 }
896 }
Jeff Hamiltond88e9aa2011-01-24 14:53:00 -0600897 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800898 int N;
You Kim43a5070e2012-11-21 23:23:45 +0900899 if ((currentResolveList != null) && ((N = currentResolveList.size()) > 0)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800900 // Only display the first matches that are either of equal
901 // priority or have asked to be default options.
You Kim43a5070e2012-11-21 23:23:45 +0900902 ResolveInfo r0 = currentResolveList.get(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 for (int i=1; i<N; i++) {
You Kim43a5070e2012-11-21 23:23:45 +0900904 ResolveInfo ri = currentResolveList.get(i);
905 if (DEBUG) Log.v(
Adam Powell09a65602014-07-20 16:23:14 -0700906 TAG,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800907 r0.activityInfo.name + "=" +
908 r0.priority + "/" + r0.isDefault + " vs " +
909 ri.activityInfo.name + "=" +
910 ri.priority + "/" + ri.isDefault);
You Kim43a5070e2012-11-21 23:23:45 +0900911 if (r0.priority != ri.priority ||
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800912 r0.isDefault != ri.isDefault) {
913 while (i < N) {
Dianne Hackborn6d8dfbd2013-09-23 17:38:51 -0700914 if (mOrigResolveList == currentResolveList) {
915 mOrigResolveList = new ArrayList<ResolveInfo>(mOrigResolveList);
916 }
You Kim43a5070e2012-11-21 23:23:45 +0900917 currentResolveList.remove(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800918 N--;
919 }
920 }
921 }
922 if (N > 1) {
Adam Powell09a65602014-07-20 16:23:14 -0700923 Comparator<ResolveInfo> rComparator =
Dianne Hackbornec452d92014-11-11 17:16:56 -0800924 new ResolverComparator(ResolverActivity.this, mIntent);
You Kim43a5070e2012-11-21 23:23:45 +0900925 Collections.sort(currentResolveList, rComparator);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800926 }
Dianne Hackborneb034652009-09-07 00:49:58 -0700927 // First put the initial items at the top.
Dianne Hackborne8f2c7f2012-03-01 17:30:53 -0800928 if (mInitialIntents != null) {
929 for (int i=0; i<mInitialIntents.length; i++) {
930 Intent ii = mInitialIntents[i];
Dianne Hackborneb034652009-09-07 00:49:58 -0700931 if (ii == null) {
932 continue;
933 }
934 ActivityInfo ai = ii.resolveActivityInfo(
935 getPackageManager(), 0);
936 if (ai == null) {
Adam Powell09a65602014-07-20 16:23:14 -0700937 Log.w(TAG, "No activity found for " + ii);
Dianne Hackborneb034652009-09-07 00:49:58 -0700938 continue;
939 }
940 ResolveInfo ri = new ResolveInfo();
941 ri.activityInfo = ai;
Nicolas Prevot1a815922014-10-10 16:22:38 +0100942 UserManager userManager =
943 (UserManager) getSystemService(Context.USER_SERVICE);
944 if (userManager.isManagedProfile()) {
945 ri.noResourceId = true;
946 }
Dianne Hackborneb034652009-09-07 00:49:58 -0700947 if (ii instanceof LabeledIntent) {
948 LabeledIntent li = (LabeledIntent)ii;
949 ri.resolvePackageName = li.getSourcePackage();
950 ri.labelRes = li.getLabelResource();
951 ri.nonLocalizedLabel = li.getNonLocalizedLabel();
952 ri.icon = li.getIconResource();
953 }
Adam Powell88831a22014-11-20 18:17:00 -0800954 addResolveInfo(new DisplayResolveInfo(ri,
Dianne Hackborneb034652009-09-07 00:49:58 -0700955 ri.loadLabel(getPackageManager()), null, ii));
956 }
957 }
You Kim43a5070e2012-11-21 23:23:45 +0900958
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800959 // Check for applications with same name and use application name or
960 // package name if necessary
You Kim43a5070e2012-11-21 23:23:45 +0900961 r0 = currentResolveList.get(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800962 int start = 0;
963 CharSequence r0Label = r0.loadLabel(mPm);
Adam Powellc5878612012-05-04 18:42:38 -0700964 mShowExtended = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800965 for (int i = 1; i < N; i++) {
966 if (r0Label == null) {
967 r0Label = r0.activityInfo.packageName;
968 }
You Kim43a5070e2012-11-21 23:23:45 +0900969 ResolveInfo ri = currentResolveList.get(i);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800970 CharSequence riLabel = ri.loadLabel(mPm);
971 if (riLabel == null) {
972 riLabel = ri.activityInfo.packageName;
973 }
974 if (riLabel.equals(r0Label)) {
975 continue;
976 }
You Kim43a5070e2012-11-21 23:23:45 +0900977 processGroup(currentResolveList, start, (i-1), r0, r0Label);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800978 r0 = ri;
979 r0Label = riLabel;
980 start = i;
981 }
982 // Process last group
You Kim43a5070e2012-11-21 23:23:45 +0900983 processGroup(currentResolveList, start, (N-1), r0, r0Label);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800984 }
Kenny Guyb8a2df12014-12-12 16:56:08 +0000985
986 // Layout doesn't handle both profile button and last chosen
987 // so disable last chosen if profile button is present.
988 if (mOtherProfile != null && mLastChosenPosition >= 0) {
989 mLastChosenPosition = -1;
990 mFilterLastUsed = false;
991 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800992 }
993
994 private void processGroup(List<ResolveInfo> rList, int start, int end, ResolveInfo ro,
995 CharSequence roLabel) {
996 // Process labels from start to i
997 int num = end - start+1;
998 if (num == 1) {
999 // No duplicate labels. Use label for entry at start
Adam Powell88831a22014-11-20 18:17:00 -08001000 addResolveInfo(new DisplayResolveInfo(ro, roLabel, null, null));
Esteban Talavera6de72662014-12-11 17:54:07 +00001001 updateLastChosenPosition(ro);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001002 } else {
Adam Powellc5878612012-05-04 18:42:38 -07001003 mShowExtended = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001004 boolean usePkg = false;
1005 CharSequence startApp = ro.activityInfo.applicationInfo.loadLabel(mPm);
1006 if (startApp == null) {
1007 usePkg = true;
1008 }
1009 if (!usePkg) {
1010 // Use HashSet to track duplicates
1011 HashSet<CharSequence> duplicates =
1012 new HashSet<CharSequence>();
1013 duplicates.add(startApp);
1014 for (int j = start+1; j <= end ; j++) {
1015 ResolveInfo jRi = rList.get(j);
1016 CharSequence jApp = jRi.activityInfo.applicationInfo.loadLabel(mPm);
1017 if ( (jApp == null) || (duplicates.contains(jApp))) {
1018 usePkg = true;
1019 break;
1020 } else {
1021 duplicates.add(jApp);
1022 }
1023 }
1024 // Clear HashSet for later use
1025 duplicates.clear();
1026 }
1027 for (int k = start; k <= end; k++) {
1028 ResolveInfo add = rList.get(k);
1029 if (usePkg) {
1030 // Use application name for all entries from start to end-1
Adam Powell88831a22014-11-20 18:17:00 -08001031 addResolveInfo(new DisplayResolveInfo(add, roLabel,
Dianne Hackborneb034652009-09-07 00:49:58 -07001032 add.activityInfo.packageName, null));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001033 } else {
1034 // Use package name for all entries from start to end-1
Adam Powell88831a22014-11-20 18:17:00 -08001035 addResolveInfo(new DisplayResolveInfo(add, roLabel,
Dianne Hackborneb034652009-09-07 00:49:58 -07001036 add.activityInfo.applicationInfo.loadLabel(mPm), null));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001037 }
Esteban Talavera6de72662014-12-11 17:54:07 +00001038 updateLastChosenPosition(add);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 }
1040 }
1041 }
1042
Esteban Talavera6de72662014-12-11 17:54:07 +00001043 private void updateLastChosenPosition(ResolveInfo info) {
1044 if (mLastChosen != null
1045 && mLastChosen.activityInfo.packageName.equals(info.activityInfo.packageName)
1046 && mLastChosen.activityInfo.name.equals(info.activityInfo.name)) {
1047 mLastChosenPosition = mList.size() - 1;
1048 }
1049 }
1050
Adam Powell88831a22014-11-20 18:17:00 -08001051 private void addResolveInfo(DisplayResolveInfo dri) {
1052 if (dri.ri.targetUserId != UserHandle.USER_CURRENT && mOtherProfile == null) {
1053 // So far we only support a single other profile at a time.
1054 // The first one we see gets special treatment.
1055 mOtherProfile = dri;
1056 } else {
1057 mList.add(dri);
1058 }
1059 }
1060
Adam Powell278902c2014-07-12 18:33:22 -07001061 public ResolveInfo resolveInfoForPosition(int position, boolean filtered) {
1062 return (filtered ? getItem(position) : mList.get(position)).ri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001063 }
1064
Adam Powell278902c2014-07-12 18:33:22 -07001065 public Intent intentForPosition(int position, boolean filtered) {
1066 DisplayResolveInfo dri = filtered ? getItem(position) : mList.get(position);
Adam Powell88831a22014-11-20 18:17:00 -08001067 return intentForDisplayResolveInfo(dri);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001068 }
1069
1070 public int getCount() {
Adam Powell278902c2014-07-12 18:33:22 -07001071 int result = mList.size();
1072 if (mFilterLastUsed && mLastChosenPosition >= 0) {
1073 result--;
1074 }
1075 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001076 }
1077
Adam Powell278902c2014-07-12 18:33:22 -07001078 public DisplayResolveInfo getItem(int position) {
1079 if (mFilterLastUsed && mLastChosenPosition >= 0 && position >= mLastChosenPosition) {
1080 position++;
1081 }
You Kim43a5070e2012-11-21 23:23:45 +09001082 return mList.get(position);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001083 }
1084
1085 public long getItemId(int position) {
1086 return position;
1087 }
1088
1089 public View getView(int position, View convertView, ViewGroup parent) {
Adam Powellfd1e93d2014-09-07 16:52:22 -07001090 View view = convertView;
1091 if (view == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001092 view = mInflater.inflate(
1093 com.android.internal.R.layout.resolve_list_item, parent, false);
Adam Powellc5878612012-05-04 18:42:38 -07001094
Adam Powell0256c6f2013-05-29 16:42:33 -07001095 final ViewHolder holder = new ViewHolder(view);
1096 view.setTag(holder);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001097 }
Adam Powell278902c2014-07-12 18:33:22 -07001098 bindView(view, getItem(position));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001099 return view;
1100 }
1101
1102 private final void bindView(View view, DisplayResolveInfo info) {
Adam Powell0256c6f2013-05-29 16:42:33 -07001103 final ViewHolder holder = (ViewHolder) view.getTag();
1104 holder.text.setText(info.displayLabel);
Adam Powellc5878612012-05-04 18:42:38 -07001105 if (mShowExtended) {
Adam Powell0256c6f2013-05-29 16:42:33 -07001106 holder.text2.setVisibility(View.VISIBLE);
1107 holder.text2.setText(info.extendedInfo);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001108 } else {
Adam Powell0256c6f2013-05-29 16:42:33 -07001109 holder.text2.setVisibility(View.GONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001110 }
Dianne Hackborneb034652009-09-07 00:49:58 -07001111 if (info.displayIcon == null) {
Adam Powell0256c6f2013-05-29 16:42:33 -07001112 new LoadIconTask().execute(info);
Dianne Hackborneb034652009-09-07 00:49:58 -07001113 }
Adam Powell0256c6f2013-05-29 16:42:33 -07001114 holder.icon.setImageDrawable(info.displayIcon);
1115 }
1116 }
1117
1118 static class ViewHolder {
1119 public TextView text;
1120 public TextView text2;
1121 public ImageView icon;
1122
1123 public ViewHolder(View view) {
1124 text = (TextView) view.findViewById(com.android.internal.R.id.text1);
1125 text2 = (TextView) view.findViewById(com.android.internal.R.id.text2);
1126 icon = (ImageView) view.findViewById(R.id.icon);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001127 }
1128 }
1129
Adam Powell2d809622012-03-22 15:24:43 -07001130 class ItemLongClickListener implements AdapterView.OnItemLongClickListener {
1131
1132 @Override
1133 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Adam Powellfd1e93d2014-09-07 16:52:22 -07001134 position -= mListView.getHeaderViewsCount();
1135 if (position < 0) {
1136 // Header views don't count.
1137 return false;
1138 }
Adam Powell278902c2014-07-12 18:33:22 -07001139 ResolveInfo ri = mAdapter.resolveInfoForPosition(position, true);
Adam Powellc5878612012-05-04 18:42:38 -07001140 showAppDetails(ri);
Adam Powell2d809622012-03-22 15:24:43 -07001141 return true;
1142 }
1143
1144 }
Adam Powell0256c6f2013-05-29 16:42:33 -07001145
1146 class LoadIconTask extends AsyncTask<DisplayResolveInfo, Void, DisplayResolveInfo> {
1147 @Override
1148 protected DisplayResolveInfo doInBackground(DisplayResolveInfo... params) {
1149 final DisplayResolveInfo info = params[0];
1150 if (info.displayIcon == null) {
1151 info.displayIcon = loadIconForResolveInfo(info.ri);
1152 }
1153 return info;
1154 }
1155
1156 @Override
1157 protected void onPostExecute(DisplayResolveInfo info) {
Adam Powell88831a22014-11-20 18:17:00 -08001158 if (mProfileView != null && mAdapter.getOtherProfile() == info) {
1159 bindProfileView();
1160 }
Adam Powell0256c6f2013-05-29 16:42:33 -07001161 mAdapter.notifyDataSetChanged();
1162 }
1163 }
Adam Powell278902c2014-07-12 18:33:22 -07001164
1165 class LoadIconIntoViewTask extends AsyncTask<DisplayResolveInfo, Void, DisplayResolveInfo> {
1166 final ImageView mTargetView;
1167
1168 public LoadIconIntoViewTask(ImageView target) {
1169 mTargetView = target;
1170 }
1171
1172 @Override
1173 protected DisplayResolveInfo doInBackground(DisplayResolveInfo... params) {
1174 final DisplayResolveInfo info = params[0];
1175 if (info.displayIcon == null) {
1176 info.displayIcon = loadIconForResolveInfo(info.ri);
1177 }
1178 return info;
1179 }
1180
1181 @Override
1182 protected void onPostExecute(DisplayResolveInfo info) {
1183 mTargetView.setImageDrawable(info.displayIcon);
1184 }
1185 }
Adam Powell09a65602014-07-20 16:23:14 -07001186
Dianne Hackbornec452d92014-11-11 17:16:56 -08001187 static final boolean isSpecificUriMatch(int match) {
1188 match = match&IntentFilter.MATCH_CATEGORY_MASK;
1189 return match >= IntentFilter.MATCH_CATEGORY_HOST
1190 && match <= IntentFilter.MATCH_CATEGORY_PATH;
1191 }
1192
Adam Powell09a65602014-07-20 16:23:14 -07001193 class ResolverComparator implements Comparator<ResolveInfo> {
1194 private final Collator mCollator;
Dianne Hackbornec452d92014-11-11 17:16:56 -08001195 private final boolean mHttp;
Adam Powell09a65602014-07-20 16:23:14 -07001196
Dianne Hackbornec452d92014-11-11 17:16:56 -08001197 public ResolverComparator(Context context, Intent intent) {
Adam Powell09a65602014-07-20 16:23:14 -07001198 mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
Dianne Hackbornec452d92014-11-11 17:16:56 -08001199 String scheme = intent.getScheme();
1200 mHttp = "http".equals(scheme) || "https".equals(scheme);
Adam Powell09a65602014-07-20 16:23:14 -07001201 }
1202
1203 @Override
1204 public int compare(ResolveInfo lhs, ResolveInfo rhs) {
1205 // We want to put the one targeted to another user at the end of the dialog.
1206 if (lhs.targetUserId != UserHandle.USER_CURRENT) {
1207 return 1;
1208 }
Adam Powell09a65602014-07-20 16:23:14 -07001209
Dianne Hackbornec452d92014-11-11 17:16:56 -08001210 if (mHttp) {
1211 // Special case: we want filters that match URI paths/schemes to be
1212 // ordered before others. This is for the case when opening URIs,
1213 // to make native apps go above browsers.
1214 final boolean lhsSpecific = isSpecificUriMatch(lhs.match);
1215 final boolean rhsSpecific = isSpecificUriMatch(rhs.match);
1216 if (lhsSpecific != rhsSpecific) {
1217 return lhsSpecific ? -1 : 1;
1218 }
1219 }
1220
Adam Powell09a65602014-07-20 16:23:14 -07001221 if (mStats != null) {
1222 final long timeDiff =
1223 getPackageTimeSpent(rhs.activityInfo.packageName) -
1224 getPackageTimeSpent(lhs.activityInfo.packageName);
1225
1226 if (timeDiff != 0) {
1227 return timeDiff > 0 ? 1 : -1;
1228 }
1229 }
1230
1231 CharSequence sa = lhs.loadLabel(mPm);
1232 if (sa == null) sa = lhs.activityInfo.name;
1233 CharSequence sb = rhs.loadLabel(mPm);
1234 if (sb == null) sb = rhs.activityInfo.name;
1235
1236 return mCollator.compare(sa.toString(), sb.toString());
1237 }
1238
1239 private long getPackageTimeSpent(String packageName) {
1240 if (mStats != null) {
Adam Lesinski35168002014-07-21 15:25:30 -07001241 final UsageStats stats = mStats.get(packageName);
Adam Powell09a65602014-07-20 16:23:14 -07001242 if (stats != null) {
Adam Lesinski35168002014-07-21 15:25:30 -07001243 return stats.getTotalTimeInForeground();
Adam Powell09a65602014-07-20 16:23:14 -07001244 }
1245
1246 }
1247 return 0;
1248 }
1249 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001250}