blob: 4523d3b2739f627ce02f4396eeb75789dd2f5a78 [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
Adam Powelle7c74cc2016-01-28 16:42:27 +000017package com.android.internal.app;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018
Adam Powell0b3c1122014-10-09 12:50:14 -070019import android.app.Activity;
Ng Zhi And3ec5fc2018-05-10 09:13:00 -070020import android.app.ActivityManager;
Adam Powell0b3c1122014-10-09 12:50:14 -070021import android.content.ComponentName;
Adam Powell24428412015-04-01 17:19:56 -070022import android.content.Context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.content.Intent;
Adam Powell0b3c1122014-10-09 12:50:14 -070024import android.content.IntentSender;
Adam Powell2ed547e2015-04-29 18:45:04 -070025import android.content.IntentSender.SendIntentException;
Adam Powell24428412015-04-01 17:19:56 -070026import android.content.ServiceConnection;
Adam Powell23882512016-01-29 10:21:00 -080027import android.content.SharedPreferences;
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +000028import android.content.pm.ActivityInfo;
Adam Powell7d758002015-05-06 17:49:36 -070029import android.content.pm.LabeledIntent;
Adam Powell24428412015-04-01 17:19:56 -070030import android.content.pm.PackageManager;
31import android.content.pm.PackageManager.NameNotFoundException;
32import android.content.pm.ResolveInfo;
Adam Powell7d758002015-05-06 17:49:36 -070033import android.database.DataSetObserver;
Adam Powell63b31692015-09-28 10:45:00 -070034import android.graphics.Color;
Adam Powell24428412015-04-01 17:19:56 -070035import android.graphics.drawable.Drawable;
Adam Powell13036be2015-05-12 14:43:56 -070036import android.graphics.drawable.Icon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.os.Bundle;
Adam Powell23882512016-01-29 10:21:00 -080038import android.os.Environment;
Adam Powell24428412015-04-01 17:19:56 -070039import android.os.Handler;
40import android.os.IBinder;
41import android.os.Message;
Dianne Hackborneb034652009-09-07 00:49:58 -070042import android.os.Parcelable;
Adam Powell52c39212016-04-07 15:14:18 -070043import android.os.Process;
Adam Powell24428412015-04-01 17:19:56 -070044import android.os.RemoteException;
Adam Powell2ed547e2015-04-29 18:45:04 -070045import android.os.ResultReceiver;
Adam Powell24428412015-04-01 17:19:56 -070046import android.os.UserHandle;
Adam Powell7d758002015-05-06 17:49:36 -070047import android.os.UserManager;
Jeff Sharkey8212ae02016-02-10 14:46:43 -070048import android.os.storage.StorageManager;
Adam Powell24428412015-04-01 17:19:56 -070049import android.service.chooser.ChooserTarget;
50import android.service.chooser.ChooserTargetService;
51import android.service.chooser.IChooserTargetResult;
52import android.service.chooser.IChooserTargetService;
53import android.text.TextUtils;
Dianne Hackborneb034652009-09-07 00:49:58 -070054import android.util.Log;
Adam Powell0b3c1122014-10-09 12:50:14 -070055import android.util.Slog;
Adam Powell7d758002015-05-06 17:49:36 -070056import android.view.LayoutInflater;
Adam Powell24428412015-04-01 17:19:56 -070057import android.view.View;
Adam Powell63b31692015-09-28 10:45:00 -070058import android.view.View.MeasureSpec;
Adam Powell7d758002015-05-06 17:49:36 -070059import android.view.View.OnClickListener;
Adam Powell98b7f892015-06-19 12:38:45 -070060import android.view.View.OnLongClickListener;
Adam Powell24428412015-04-01 17:19:56 -070061import android.view.ViewGroup;
Adam Powell63b31692015-09-28 10:45:00 -070062import android.view.ViewGroup.LayoutParams;
Adam Powell7d758002015-05-06 17:49:36 -070063import android.widget.AbsListView;
64import android.widget.BaseAdapter;
Jason Monk027dcfa2017-06-27 18:37:35 -040065import android.widget.LinearLayout;
Adam Powell7d758002015-05-06 17:49:36 -070066import android.widget.ListView;
Jason Monk027dcfa2017-06-27 18:37:35 -040067import android.widget.Space;
68
Adam Powell7d758002015-05-06 17:49:36 -070069import com.android.internal.R;
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -080070import com.android.internal.annotations.VisibleForTesting;
Adam Powell98b7f892015-06-19 12:38:45 -070071import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010072import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Alison Cichowlas3e340502018-08-07 17:15:01 -040073
Adam Powell52c39212016-04-07 15:14:18 -070074import com.google.android.collect.Lists;
Adam Powell24428412015-04-01 17:19:56 -070075
Adam Powell23882512016-01-29 10:21:00 -080076import java.io.File;
Adam Powell24428412015-04-01 17:19:56 -070077import java.util.ArrayList;
Adam Powella182e452015-07-06 16:57:56 -070078import java.util.Collections;
79import java.util.Comparator;
Adam Powell24428412015-04-01 17:19:56 -070080import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081
82public class ChooserActivity extends ResolverActivity {
Adam Powell0b3c1122014-10-09 12:50:14 -070083 private static final String TAG = "ChooserActivity";
84
Jorim Jaggif631ef72017-02-24 13:49:47 +010085 /**
86 * Boolean extra to change the following behavior: Normally, ChooserActivity finishes itself
87 * in onStop when launched in a new task. If this extra is set to true, we do not finish
88 * ourselves when onStop gets called.
89 */
90 public static final String EXTRA_PRIVATE_RETAIN_IN_ON_STOP
91 = "com.android.internal.app.ChooserActivity.EXTRA_PRIVATE_RETAIN_IN_ON_STOP";
92
Adam Powell24428412015-04-01 17:19:56 -070093 private static final boolean DEBUG = false;
94
Adam Powell2ed547e2015-04-29 18:45:04 -070095 private static final int QUERY_TARGET_SERVICE_LIMIT = 5;
Adam Powell3f23dfc2017-10-02 10:41:03 -070096 private static final int WATCHDOG_TIMEOUT_MILLIS = 2000;
Adam Powell24428412015-04-01 17:19:56 -070097
Adam Powelle49d9392014-07-17 18:45:19 -070098 private Bundle mReplacementExtras;
Adam Powell0b3c1122014-10-09 12:50:14 -070099 private IntentSender mChosenComponentSender;
Adam Powell2ed547e2015-04-29 18:45:04 -0700100 private IntentSender mRefinementIntentSender;
101 private RefinementResultReceiver mRefinementResultReceiver;
Adam Powell52c39212016-04-07 15:14:18 -0700102 private ChooserTarget[] mCallerChooserTargets;
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800103 private ComponentName[] mFilteredComponentNames;
Adam Powelle49d9392014-07-17 18:45:19 -0700104
Adam Powell13036be2015-05-12 14:43:56 -0700105 private Intent mReferrerFillInIntent;
106
Kang Li9082f5b2016-12-02 10:56:21 -0800107 private long mChooserShownTime;
Kang Li64b018e2017-01-05 17:30:06 -0800108 protected boolean mIsSuccessfullySelected;
Kang Li9082f5b2016-12-02 10:56:21 -0800109
Adam Powell7d758002015-05-06 17:49:36 -0700110 private ChooserListAdapter mChooserListAdapter;
Adam Powell63b31692015-09-28 10:45:00 -0700111 private ChooserRowAdapter mChooserRowAdapter;
Adam Powell24428412015-04-01 17:19:56 -0700112
Adam Powell23882512016-01-29 10:21:00 -0800113 private SharedPreferences mPinnedSharedPrefs;
114 private static final float PINNED_TARGET_SCORE_BOOST = 1000.f;
Adam Powell52c39212016-04-07 15:14:18 -0700115 private static final float CALLER_TARGET_SCORE_BOOST = 900.f;
Adam Powell23882512016-01-29 10:21:00 -0800116 private static final String PINNED_SHARED_PREFS_NAME = "chooser_pin_settings";
117 private static final String TARGET_DETAILS_FRAGMENT_TAG = "targetDetailsFragment";
118
Adam Powell24428412015-04-01 17:19:56 -0700119 private final List<ChooserTargetServiceConnection> mServiceConnections = new ArrayList<>();
120
121 private static final int CHOOSER_TARGET_SERVICE_RESULT = 1;
122 private static final int CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT = 2;
123
Adam Powell13036be2015-05-12 14:43:56 -0700124 private final Handler mChooserHandler = new Handler() {
Adam Powell24428412015-04-01 17:19:56 -0700125 @Override
126 public void handleMessage(Message msg) {
127 switch (msg.what) {
128 case CHOOSER_TARGET_SERVICE_RESULT:
129 if (DEBUG) Log.d(TAG, "CHOOSER_TARGET_SERVICE_RESULT");
130 if (isDestroyed()) break;
131 final ServiceResultInfo sri = (ServiceResultInfo) msg.obj;
132 if (!mServiceConnections.contains(sri.connection)) {
133 Log.w(TAG, "ChooserTargetServiceConnection " + sri.connection
134 + " returned after being removed from active connections."
135 + " Have you considered returning results faster?");
136 break;
137 }
Adam Powella182e452015-07-06 16:57:56 -0700138 if (sri.resultTargets != null) {
139 mChooserListAdapter.addServiceResults(sri.originalTarget,
140 sri.resultTargets);
141 }
Adam Powell24428412015-04-01 17:19:56 -0700142 unbindService(sri.connection);
Adam Powell9761ab22015-09-08 17:01:49 -0700143 sri.connection.destroy();
Adam Powell24428412015-04-01 17:19:56 -0700144 mServiceConnections.remove(sri.connection);
Adam Powell4c470d62015-06-19 17:46:17 -0700145 if (mServiceConnections.isEmpty()) {
146 mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
147 sendVoiceChoicesIfNeeded();
Adam Powell565943f2016-02-11 10:29:05 -0800148 mChooserListAdapter.setShowServiceTargets(true);
Adam Powell4c470d62015-06-19 17:46:17 -0700149 }
Adam Powell24428412015-04-01 17:19:56 -0700150 break;
151
152 case CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT:
153 if (DEBUG) {
154 Log.d(TAG, "CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT; unbinding services");
155 }
156 unbindRemainingServices();
Adam Powell4c470d62015-06-19 17:46:17 -0700157 sendVoiceChoicesIfNeeded();
Adam Powell565943f2016-02-11 10:29:05 -0800158 mChooserListAdapter.setShowServiceTargets(true);
Adam Powell24428412015-04-01 17:19:56 -0700159 break;
160
161 default:
162 super.handleMessage(msg);
163 }
164 }
165 };
166
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 @Override
168 protected void onCreate(Bundle savedInstanceState) {
Kang Li9082f5b2016-12-02 10:56:21 -0800169 final long intentReceivedTime = System.currentTimeMillis();
170 mIsSuccessfullySelected = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 Intent intent = getIntent();
Dianne Hackborneb034652009-09-07 00:49:58 -0700172 Parcelable targetParcelable = intent.getParcelableExtra(Intent.EXTRA_INTENT);
173 if (!(targetParcelable instanceof Intent)) {
Christopher Tate9d6376a2014-02-12 13:14:10 -0800174 Log.w("ChooserActivity", "Target is not an intent: " + targetParcelable);
Dianne Hackborneb034652009-09-07 00:49:58 -0700175 finish();
Christopher Tate9d6376a2014-02-12 13:14:10 -0800176 super.onCreate(null);
Dianne Hackborneb034652009-09-07 00:49:58 -0700177 return;
178 }
Adam Powell24428412015-04-01 17:19:56 -0700179 Intent target = (Intent) targetParcelable;
Craig Mautner411d2aed2014-05-08 09:07:43 -0700180 if (target != null) {
Adam Powelle49d9392014-07-17 18:45:19 -0700181 modifyTargetIntent(target);
Craig Mautner411d2aed2014-05-08 09:07:43 -0700182 }
Adam Powell2ed547e2015-04-29 18:45:04 -0700183 Parcelable[] targetsParcelable
184 = intent.getParcelableArrayExtra(Intent.EXTRA_ALTERNATE_INTENTS);
185 if (targetsParcelable != null) {
186 final boolean offset = target == null;
187 Intent[] additionalTargets =
188 new Intent[offset ? targetsParcelable.length - 1 : targetsParcelable.length];
189 for (int i = 0; i < targetsParcelable.length; i++) {
190 if (!(targetsParcelable[i] instanceof Intent)) {
191 Log.w(TAG, "EXTRA_ALTERNATE_INTENTS array entry #" + i + " is not an Intent: "
192 + targetsParcelable[i]);
193 finish();
194 super.onCreate(null);
195 return;
196 }
197 final Intent additionalTarget = (Intent) targetsParcelable[i];
198 if (i == 0 && target == null) {
199 target = additionalTarget;
200 modifyTargetIntent(target);
201 } else {
202 additionalTargets[offset ? i - 1 : i] = additionalTarget;
203 modifyTargetIntent(additionalTarget);
204 }
205 }
206 setAdditionalTargets(additionalTargets);
207 }
208
Adam Powelle49d9392014-07-17 18:45:19 -0700209 mReplacementExtras = intent.getBundleExtra(Intent.EXTRA_REPLACEMENT_EXTRAS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 CharSequence title = intent.getCharSequenceExtra(Intent.EXTRA_TITLE);
Adam Powell278902c2014-07-12 18:33:22 -0700211 int defaultTitleRes = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 if (title == null) {
Adam Powell278902c2014-07-12 18:33:22 -0700213 defaultTitleRes = com.android.internal.R.string.chooseActivity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 }
Dianne Hackborneb034652009-09-07 00:49:58 -0700215 Parcelable[] pa = intent.getParcelableArrayExtra(Intent.EXTRA_INITIAL_INTENTS);
216 Intent[] initialIntents = null;
217 if (pa != null) {
218 initialIntents = new Intent[pa.length];
219 for (int i=0; i<pa.length; i++) {
220 if (!(pa[i] instanceof Intent)) {
Adam Powell2ed547e2015-04-29 18:45:04 -0700221 Log.w(TAG, "Initial intent #" + i + " not an Intent: " + pa[i]);
Dianne Hackborneb034652009-09-07 00:49:58 -0700222 finish();
Christopher Tate9d6376a2014-02-12 13:14:10 -0800223 super.onCreate(null);
Dianne Hackborneb034652009-09-07 00:49:58 -0700224 return;
225 }
Craig Mautner411d2aed2014-05-08 09:07:43 -0700226 final Intent in = (Intent) pa[i];
Adam Powelle49d9392014-07-17 18:45:19 -0700227 modifyTargetIntent(in);
Craig Mautner411d2aed2014-05-08 09:07:43 -0700228 initialIntents[i] = in;
Dianne Hackborneb034652009-09-07 00:49:58 -0700229 }
230 }
Adam Powell24428412015-04-01 17:19:56 -0700231
Adam Powell13036be2015-05-12 14:43:56 -0700232 mReferrerFillInIntent = new Intent().putExtra(Intent.EXTRA_REFERRER, getReferrer());
233
Adam Powell0b3c1122014-10-09 12:50:14 -0700234 mChosenComponentSender = intent.getParcelableExtra(
235 Intent.EXTRA_CHOSEN_COMPONENT_INTENT_SENDER);
Adam Powell2ed547e2015-04-29 18:45:04 -0700236 mRefinementIntentSender = intent.getParcelableExtra(
237 Intent.EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER);
Dianne Hackborn028ceeb2014-08-17 17:45:48 -0700238 setSafeForwardingMode(true);
Adam Powell23882512016-01-29 10:21:00 -0800239
Adam Powell52c39212016-04-07 15:14:18 -0700240 pa = intent.getParcelableArrayExtra(Intent.EXTRA_EXCLUDE_COMPONENTS);
241 if (pa != null) {
242 ComponentName[] names = new ComponentName[pa.length];
243 for (int i = 0; i < pa.length; i++) {
244 if (!(pa[i] instanceof ComponentName)) {
245 Log.w(TAG, "Filtered component #" + i + " not a ComponentName: " + pa[i]);
246 names = null;
247 break;
248 }
249 names[i] = (ComponentName) pa[i];
250 }
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800251 mFilteredComponentNames = names;
Adam Powell52c39212016-04-07 15:14:18 -0700252 }
253
254 pa = intent.getParcelableArrayExtra(Intent.EXTRA_CHOOSER_TARGETS);
255 if (pa != null) {
256 ChooserTarget[] targets = new ChooserTarget[pa.length];
257 for (int i = 0; i < pa.length; i++) {
258 if (!(pa[i] instanceof ChooserTarget)) {
259 Log.w(TAG, "Chooser target #" + i + " not a ChooserTarget: " + pa[i]);
260 targets = null;
261 break;
262 }
263 targets[i] = (ChooserTarget) pa[i];
264 }
265 mCallerChooserTargets = targets;
266 }
267
Adam Powell23882512016-01-29 10:21:00 -0800268 mPinnedSharedPrefs = getPinnedSharedPrefs(this);
Jorim Jaggif631ef72017-02-24 13:49:47 +0100269 setRetainInOnStop(intent.getBooleanExtra(EXTRA_PRIVATE_RETAIN_IN_ON_STOP, false));
Adam Powell278902c2014-07-12 18:33:22 -0700270 super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents,
271 null, false);
Adam Powell98b7f892015-06-19 12:38:45 -0700272
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500273 MetricsLogger.action(this, MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN);
Kang Li9082f5b2016-12-02 10:56:21 -0800274
275 mChooserShownTime = System.currentTimeMillis();
276 final long systemCost = mChooserShownTime - intentReceivedTime;
277 MetricsLogger.histogram(null, "system_cost_for_smart_sharing", (int) systemCost);
278 if (DEBUG) {
279 Log.d(TAG, "System Time Cost is " + systemCost);
280 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800281 }
Adam Powelle49d9392014-07-17 18:45:19 -0700282
Adam Powell23882512016-01-29 10:21:00 -0800283 static SharedPreferences getPinnedSharedPrefs(Context context) {
284 // The code below is because in the android:ui process, no one can hear you scream.
285 // The package info in the context isn't initialized in the way it is for normal apps,
286 // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we
287 // build the path manually below using the same policy that appears in ContextImpl.
288 // This fails silently under the hood if there's a problem, so if we find ourselves in
289 // the case where we don't have access to credential encrypted storage we just won't
290 // have our pinned target info.
291 final File prefsFile = new File(new File(
Jeff Sharkey8212ae02016-02-10 14:46:43 -0700292 Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL,
Adam Powell23882512016-01-29 10:21:00 -0800293 context.getUserId(), context.getPackageName()),
294 "shared_prefs"),
295 PINNED_SHARED_PREFS_NAME + ".xml");
296 return context.getSharedPreferences(prefsFile, MODE_PRIVATE);
297 }
298
Adam Powell0b3c1122014-10-09 12:50:14 -0700299 @Override
Adam Powell2ed547e2015-04-29 18:45:04 -0700300 protected void onDestroy() {
301 super.onDestroy();
302 if (mRefinementResultReceiver != null) {
303 mRefinementResultReceiver.destroy();
304 mRefinementResultReceiver = null;
305 }
Adam Powell9761ab22015-09-08 17:01:49 -0700306 unbindRemainingServices();
307 mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_RESULT);
Adam Powell2ed547e2015-04-29 18:45:04 -0700308 }
309
310 @Override
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000311 public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) {
312 Intent result = defIntent;
Adam Powelle49d9392014-07-17 18:45:19 -0700313 if (mReplacementExtras != null) {
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000314 final Bundle replExtras = mReplacementExtras.getBundle(aInfo.packageName);
Adam Powelle49d9392014-07-17 18:45:19 -0700315 if (replExtras != null) {
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000316 result = new Intent(defIntent);
Adam Powelle49d9392014-07-17 18:45:19 -0700317 result.putExtras(replExtras);
Adam Powelle49d9392014-07-17 18:45:19 -0700318 }
319 }
Nicolas Prevot741abfc2015-08-11 12:03:51 +0100320 if (aInfo.name.equals(IntentForwarderActivity.FORWARD_INTENT_TO_PARENT)
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000321 || aInfo.name.equals(IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE)) {
322 result = Intent.createChooser(result,
323 getIntent().getCharSequenceExtra(Intent.EXTRA_TITLE));
Hakan Seyalioglu7317e8a2016-12-12 16:15:38 -0800324
325 // Don't auto-launch single intents if the intent is being forwarded. This is done
326 // because automatically launching a resolving application as a response to the user
327 // action of switching accounts is pretty unexpected.
328 result.putExtra(Intent.EXTRA_AUTO_LAUNCH_SINGLE_CHOICE, false);
Nicolas Prevot0e2b73f2014-10-27 10:06:11 +0000329 }
330 return result;
Adam Powelle49d9392014-07-17 18:45:19 -0700331 }
332
Adam Powell0b3c1122014-10-09 12:50:14 -0700333 @Override
Adam Powell23882512016-01-29 10:21:00 -0800334 public void onActivityStarted(TargetInfo cti) {
Adam Powell0b3c1122014-10-09 12:50:14 -0700335 if (mChosenComponentSender != null) {
Adam Powell24428412015-04-01 17:19:56 -0700336 final ComponentName target = cti.getResolvedComponentName();
Adam Powell0b3c1122014-10-09 12:50:14 -0700337 if (target != null) {
338 final Intent fillIn = new Intent().putExtra(Intent.EXTRA_CHOSEN_COMPONENT, target);
339 try {
340 mChosenComponentSender.sendIntent(this, Activity.RESULT_OK, fillIn, null, null);
341 } catch (IntentSender.SendIntentException e) {
342 Slog.e(TAG, "Unable to launch supplied IntentSender to report "
343 + "the chosen component: " + e);
344 }
345 }
346 }
347 }
348
Adam Powell24428412015-04-01 17:19:56 -0700349 @Override
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800350 public void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter) {
Adam Powell7d758002015-05-06 17:49:36 -0700351 final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null;
352 mChooserListAdapter = (ChooserListAdapter) adapter;
Adam Powell52c39212016-04-07 15:14:18 -0700353 if (mCallerChooserTargets != null && mCallerChooserTargets.length > 0) {
354 mChooserListAdapter.addServiceResults(null, Lists.newArrayList(mCallerChooserTargets));
355 }
Adam Powell63b31692015-09-28 10:45:00 -0700356 mChooserRowAdapter = new ChooserRowAdapter(mChooserListAdapter);
357 mChooserRowAdapter.registerDataSetObserver(new OffsetDataSetObserver(adapterView));
358 adapterView.setAdapter(mChooserRowAdapter);
Adam Powell7d758002015-05-06 17:49:36 -0700359 if (listView != null) {
360 listView.setItemsCanFocus(true);
361 }
362 }
363
364 @Override
Adam Powell23882512016-01-29 10:21:00 -0800365 public int getLayoutResource() {
Adam Powell7d758002015-05-06 17:49:36 -0700366 return R.layout.chooser_grid;
Adam Powell24428412015-04-01 17:19:56 -0700367 }
368
369 @Override
Adam Powell23882512016-01-29 10:21:00 -0800370 public boolean shouldGetActivityMetadata() {
Adam Powell24428412015-04-01 17:19:56 -0700371 return true;
372 }
373
Adam Powell9761ab22015-09-08 17:01:49 -0700374 @Override
Ben Lin145b0ca2016-10-14 14:23:40 -0700375 public boolean shouldAutoLaunchSingleChoice(TargetInfo target) {
Hakan Seyalioglu13405c52017-01-31 19:01:31 -0800376 // Note that this is only safe because the Intent handled by the ChooserActivity is
377 // guaranteed to contain no extras unknown to the local ClassLoader. That is why this
378 // method can not be replaced in the ResolverActivity whole hog.
Ben Lin145b0ca2016-10-14 14:23:40 -0700379 return getIntent().getBooleanExtra(Intent.EXTRA_AUTO_LAUNCH_SINGLE_CHOICE,
380 super.shouldAutoLaunchSingleChoice(target));
381 }
382
383 @Override
Adam Powell23882512016-01-29 10:21:00 -0800384 public void showTargetDetails(ResolveInfo ri) {
sanryhuang296ca9e2018-03-31 11:17:13 +0800385 if (ri == null) {
386 return;
387 }
388
Adam Powell23882512016-01-29 10:21:00 -0800389 ComponentName name = ri.activityInfo.getComponentName();
390 boolean pinned = mPinnedSharedPrefs.getBoolean(name.flattenToString(), false);
391 ResolverTargetActionsDialogFragment f =
392 new ResolverTargetActionsDialogFragment(ri.loadLabel(getPackageManager()),
393 name, pinned);
394 f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
395 }
396
Adam Powelle49d9392014-07-17 18:45:19 -0700397 private void modifyTargetIntent(Intent in) {
398 final String action = in.getAction();
399 if (Intent.ACTION_SEND.equals(action) ||
400 Intent.ACTION_SEND_MULTIPLE.equals(action)) {
401 in.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
Dianne Hackborn13420f22014-07-18 15:43:56 -0700402 Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
Adam Powelle49d9392014-07-17 18:45:19 -0700403 }
404 }
Adam Powell24428412015-04-01 17:19:56 -0700405
Adam Powell2ed547e2015-04-29 18:45:04 -0700406 @Override
407 protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) {
408 if (mRefinementIntentSender != null) {
409 final Intent fillIn = new Intent();
410 final List<Intent> sourceIntents = target.getAllSourceIntents();
411 if (!sourceIntents.isEmpty()) {
412 fillIn.putExtra(Intent.EXTRA_INTENT, sourceIntents.get(0));
413 if (sourceIntents.size() > 1) {
414 final Intent[] alts = new Intent[sourceIntents.size() - 1];
415 for (int i = 1, N = sourceIntents.size(); i < N; i++) {
416 alts[i - 1] = sourceIntents.get(i);
417 }
418 fillIn.putExtra(Intent.EXTRA_ALTERNATE_INTENTS, alts);
419 }
420 if (mRefinementResultReceiver != null) {
421 mRefinementResultReceiver.destroy();
422 }
423 mRefinementResultReceiver = new RefinementResultReceiver(this, target, null);
424 fillIn.putExtra(Intent.EXTRA_RESULT_RECEIVER,
425 mRefinementResultReceiver);
426 try {
427 mRefinementIntentSender.sendIntent(this, 0, fillIn, null, null);
428 return false;
429 } catch (SendIntentException e) {
430 Log.e(TAG, "Refinement IntentSender failed to send", e);
431 }
432 }
433 }
Kang Li9fa2a2c2017-01-06 13:33:24 -0800434 updateModelAndChooserCounts(target);
Adam Powell2ed547e2015-04-29 18:45:04 -0700435 return super.onTargetSelected(target, alwaysCheck);
436 }
437
Adam Powell98b7f892015-06-19 12:38:45 -0700438 @Override
Adam Powell23882512016-01-29 10:21:00 -0800439 public void startSelected(int which, boolean always, boolean filtered) {
Kang Li9082f5b2016-12-02 10:56:21 -0800440 final long selectionCost = System.currentTimeMillis() - mChooserShownTime;
Adam Powell98b7f892015-06-19 12:38:45 -0700441 super.startSelected(which, always, filtered);
442
443 if (mChooserListAdapter != null) {
444 // Log the index of which type of target the user picked.
445 // Lower values mean the ranking was better.
446 int cat = 0;
447 int value = which;
448 switch (mChooserListAdapter.getPositionTargetType(which)) {
449 case ChooserListAdapter.TARGET_CALLER:
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500450 cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_APP_TARGET;
Adam Powell98b7f892015-06-19 12:38:45 -0700451 break;
452 case ChooserListAdapter.TARGET_SERVICE:
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500453 cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SERVICE_TARGET;
Adam Powell98b7f892015-06-19 12:38:45 -0700454 value -= mChooserListAdapter.getCallerTargetCount();
455 break;
456 case ChooserListAdapter.TARGET_STANDARD:
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500457 cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_STANDARD_TARGET;
Adam Powell98b7f892015-06-19 12:38:45 -0700458 value -= mChooserListAdapter.getCallerTargetCount()
459 + mChooserListAdapter.getServiceTargetCount();
460 break;
461 }
462
463 if (cat != 0) {
464 MetricsLogger.action(this, cat, value);
465 }
Kang Li9082f5b2016-12-02 10:56:21 -0800466
467 if (mIsSuccessfullySelected) {
468 if (DEBUG) {
469 Log.d(TAG, "User Selection Time Cost is " + selectionCost);
470 Log.d(TAG, "position of selected app/service/caller is " +
471 Integer.toString(value));
472 }
473 MetricsLogger.histogram(null, "user_selection_cost_for_smart_sharing",
474 (int) selectionCost);
475 MetricsLogger.histogram(null, "app_position_for_smart_sharing", value);
476 }
Adam Powell98b7f892015-06-19 12:38:45 -0700477 }
478 }
479
Adam Powell24428412015-04-01 17:19:56 -0700480 void queryTargetServices(ChooserListAdapter adapter) {
481 final PackageManager pm = getPackageManager();
482 int targetsToQuery = 0;
483 for (int i = 0, N = adapter.getDisplayResolveInfoCount(); i < N; i++) {
484 final DisplayResolveInfo dri = adapter.getDisplayResolveInfo(i);
Adam Powell3a09c522015-10-21 13:21:28 -0700485 if (adapter.getScore(dri) == 0) {
486 // A score of 0 means the app hasn't been used in some time;
487 // don't query it as it's not likely to be relevant.
488 continue;
489 }
Adam Powell24428412015-04-01 17:19:56 -0700490 final ActivityInfo ai = dri.getResolveInfo().activityInfo;
491 final Bundle md = ai.metaData;
492 final String serviceName = md != null ? convertServiceName(ai.packageName,
493 md.getString(ChooserTargetService.META_DATA_NAME)) : null;
494 if (serviceName != null) {
495 final ComponentName serviceComponent = new ComponentName(
496 ai.packageName, serviceName);
497 final Intent serviceIntent = new Intent(ChooserTargetService.SERVICE_INTERFACE)
498 .setComponent(serviceComponent);
499
500 if (DEBUG) {
501 Log.d(TAG, "queryTargets found target with service " + serviceComponent);
502 }
503
504 try {
505 final String perm = pm.getServiceInfo(serviceComponent, 0).permission;
506 if (!ChooserTargetService.BIND_PERMISSION.equals(perm)) {
507 Log.w(TAG, "ChooserTargetService " + serviceComponent + " does not require"
508 + " permission " + ChooserTargetService.BIND_PERMISSION
509 + " - this service will not be queried for ChooserTargets."
510 + " add android:permission=\""
511 + ChooserTargetService.BIND_PERMISSION + "\""
512 + " to the <service> tag for " + serviceComponent
513 + " in the manifest.");
514 continue;
515 }
516 } catch (NameNotFoundException e) {
Adam Powell52c39212016-04-07 15:14:18 -0700517 Log.e(TAG, "Could not look up service " + serviceComponent
518 + "; component name not found");
Adam Powell24428412015-04-01 17:19:56 -0700519 continue;
520 }
521
Adam Powell9761ab22015-09-08 17:01:49 -0700522 final ChooserTargetServiceConnection conn =
523 new ChooserTargetServiceConnection(this, dri);
Adam Powell52c39212016-04-07 15:14:18 -0700524
525 // Explicitly specify Process.myUserHandle instead of calling bindService
526 // to avoid the warning from calling from the system process without an explicit
527 // user handle
528 if (bindServiceAsUser(serviceIntent, conn, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND,
529 Process.myUserHandle())) {
Adam Powell24428412015-04-01 17:19:56 -0700530 if (DEBUG) {
531 Log.d(TAG, "Binding service connection for target " + dri
532 + " intent " + serviceIntent);
533 }
534 mServiceConnections.add(conn);
535 targetsToQuery++;
536 }
537 }
Adam Powell2ed547e2015-04-29 18:45:04 -0700538 if (targetsToQuery >= QUERY_TARGET_SERVICE_LIMIT) {
539 if (DEBUG) Log.d(TAG, "queryTargets hit query target limit "
540 + QUERY_TARGET_SERVICE_LIMIT);
Adam Powell24428412015-04-01 17:19:56 -0700541 break;
542 }
543 }
544
545 if (!mServiceConnections.isEmpty()) {
546 if (DEBUG) Log.d(TAG, "queryTargets setting watchdog timer for "
547 + WATCHDOG_TIMEOUT_MILLIS + "ms");
Adam Powell13036be2015-05-12 14:43:56 -0700548 mChooserHandler.sendEmptyMessageDelayed(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT,
Adam Powell24428412015-04-01 17:19:56 -0700549 WATCHDOG_TIMEOUT_MILLIS);
Adam Powell4c470d62015-06-19 17:46:17 -0700550 } else {
551 sendVoiceChoicesIfNeeded();
Adam Powell24428412015-04-01 17:19:56 -0700552 }
553 }
554
555 private String convertServiceName(String packageName, String serviceName) {
556 if (TextUtils.isEmpty(serviceName)) {
557 return null;
558 }
559
560 final String fullName;
561 if (serviceName.startsWith(".")) {
562 // Relative to the app package. Prepend the app package name.
563 fullName = packageName + serviceName;
564 } else if (serviceName.indexOf('.') >= 0) {
565 // Fully qualified package name.
566 fullName = serviceName;
567 } else {
568 fullName = null;
569 }
570 return fullName;
571 }
572
573 void unbindRemainingServices() {
574 if (DEBUG) {
575 Log.d(TAG, "unbindRemainingServices, " + mServiceConnections.size() + " left");
576 }
577 for (int i = 0, N = mServiceConnections.size(); i < N; i++) {
578 final ChooserTargetServiceConnection conn = mServiceConnections.get(i);
579 if (DEBUG) Log.d(TAG, "unbinding " + conn);
580 unbindService(conn);
Adam Powell9761ab22015-09-08 17:01:49 -0700581 conn.destroy();
Adam Powell24428412015-04-01 17:19:56 -0700582 }
583 mServiceConnections.clear();
Adam Powell13036be2015-05-12 14:43:56 -0700584 mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
Adam Powell24428412015-04-01 17:19:56 -0700585 }
586
Adam Powell23882512016-01-29 10:21:00 -0800587 public void onSetupVoiceInteraction() {
Adam Powell4c470d62015-06-19 17:46:17 -0700588 // Do nothing. We'll send the voice stuff ourselves.
589 }
590
Kang Li9fa2a2c2017-01-06 13:33:24 -0800591 void updateModelAndChooserCounts(TargetInfo info) {
Kang Li53b43142016-11-14 14:38:25 -0800592 if (info != null) {
Kang Li53b43142016-11-14 14:38:25 -0800593 final ResolveInfo ri = info.getResolveInfo();
Kang Li64b018e2017-01-05 17:30:06 -0800594 Intent targetIntent = getTargetIntent();
595 if (ri != null && ri.activityInfo != null && targetIntent != null) {
Kang Li0cef910d2017-01-05 09:14:36 -0800596 if (mAdapter != null) {
597 mAdapter.updateModel(info.getResolvedComponentName());
Kang Li9fa2a2c2017-01-06 13:33:24 -0800598 mAdapter.updateChooserCounts(ri.activityInfo.packageName, getUserId(),
599 targetIntent.getAction());
Kang Li0cef910d2017-01-05 09:14:36 -0800600 }
Kang Li53b43142016-11-14 14:38:25 -0800601 if (DEBUG) {
Kang Li64b018e2017-01-05 17:30:06 -0800602 Log.d(TAG, "ResolveInfo Package is " + ri.activityInfo.packageName);
Kang Li64b018e2017-01-05 17:30:06 -0800603 Log.d(TAG, "Action to be updated is " + targetIntent.getAction());
Kang Li53b43142016-11-14 14:38:25 -0800604 }
605 } else if(DEBUG) {
606 Log.d(TAG, "Can not log Chooser Counts of null ResovleInfo");
607 }
608 }
Kang Li9082f5b2016-12-02 10:56:21 -0800609 mIsSuccessfullySelected = true;
Kang Li53b43142016-11-14 14:38:25 -0800610 }
611
Adam Powell2ed547e2015-04-29 18:45:04 -0700612 void onRefinementResult(TargetInfo selectedTarget, Intent matchingIntent) {
613 if (mRefinementResultReceiver != null) {
614 mRefinementResultReceiver.destroy();
615 mRefinementResultReceiver = null;
616 }
Adam Powell2ed547e2015-04-29 18:45:04 -0700617 if (selectedTarget == null) {
618 Log.e(TAG, "Refinement result intent did not match any known targets; canceling");
619 } else if (!checkTargetSourceIntent(selectedTarget, matchingIntent)) {
620 Log.e(TAG, "onRefinementResult: Selected target " + selectedTarget
621 + " cannot match refined source intent " + matchingIntent);
Kang Li53b43142016-11-14 14:38:25 -0800622 } else {
623 TargetInfo clonedTarget = selectedTarget.cloneFilledIn(matchingIntent, 0);
624 if (super.onTargetSelected(clonedTarget, false)) {
Kang Li9fa2a2c2017-01-06 13:33:24 -0800625 updateModelAndChooserCounts(clonedTarget);
Kang Li53b43142016-11-14 14:38:25 -0800626 finish();
627 return;
628 }
Adam Powell2ed547e2015-04-29 18:45:04 -0700629 }
630 onRefinementCanceled();
631 }
632
633 void onRefinementCanceled() {
634 if (mRefinementResultReceiver != null) {
635 mRefinementResultReceiver.destroy();
636 mRefinementResultReceiver = null;
637 }
638 finish();
639 }
640
641 boolean checkTargetSourceIntent(TargetInfo target, Intent matchingIntent) {
642 final List<Intent> targetIntents = target.getAllSourceIntents();
643 for (int i = 0, N = targetIntents.size(); i < N; i++) {
644 final Intent targetIntent = targetIntents.get(i);
645 if (targetIntent.filterEquals(matchingIntent)) {
646 return true;
647 }
648 }
649 return false;
650 }
651
Adam Powell666d82a2015-07-15 20:14:57 -0700652 void filterServiceTargets(String packageName, List<ChooserTarget> targets) {
653 if (targets == null) {
654 return;
655 }
656
657 final PackageManager pm = getPackageManager();
658 for (int i = targets.size() - 1; i >= 0; i--) {
659 final ChooserTarget target = targets.get(i);
660 final ComponentName targetName = target.getComponentName();
661 if (packageName != null && packageName.equals(targetName.getPackageName())) {
662 // Anything from the original target's package is fine.
663 continue;
664 }
665
666 boolean remove;
667 try {
668 final ActivityInfo ai = pm.getActivityInfo(targetName, 0);
669 remove = !ai.exported || ai.permission != null;
670 } catch (NameNotFoundException e) {
671 Log.e(TAG, "Target " + target + " returned by " + packageName
672 + " component not found");
673 remove = true;
674 }
675
676 if (remove) {
677 targets.remove(i);
678 }
679 }
680 }
681
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800682 public class ChooserListController extends ResolverListController {
683 public ChooserListController(Context context,
684 PackageManager pm,
685 Intent targetIntent,
686 String referrerPackageName,
687 int launchedFromUid) {
688 super(context, pm, targetIntent, referrerPackageName, launchedFromUid);
689 }
690
691 @Override
692 boolean isComponentPinned(ComponentName name) {
693 return mPinnedSharedPrefs.getBoolean(name.flattenToString(), false);
694 }
695
696 @Override
697 boolean isComponentFiltered(ComponentName name) {
698 if (mFilteredComponentNames == null) {
699 return false;
700 }
701 for (ComponentName filteredComponentName : mFilteredComponentNames) {
702 if (name.equals(filteredComponentName)) {
703 return true;
704 }
705 }
706 return false;
707 }
708
709 @Override
710 public float getScore(DisplayResolveInfo target) {
711 if (target == null) {
712 return CALLER_TARGET_SCORE_BOOST;
713 }
714 float score = super.getScore(target);
715 if (target.isPinned()) {
716 score += PINNED_TARGET_SCORE_BOOST;
717 }
718 return score;
719 }
720 }
721
Adam Powell24428412015-04-01 17:19:56 -0700722 @Override
Adam Powell23882512016-01-29 10:21:00 -0800723 public ResolveListAdapter createAdapter(Context context, List<Intent> payloadIntents,
Adam Powell7d758002015-05-06 17:49:36 -0700724 Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid,
725 boolean filterLastUsed) {
726 final ChooserListAdapter adapter = new ChooserListAdapter(context, payloadIntents,
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800727 initialIntents, rList, launchedFromUid, filterLastUsed, createListController());
Adam Powell24428412015-04-01 17:19:56 -0700728 return adapter;
729 }
730
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800731 @VisibleForTesting
732 protected ResolverListController createListController() {
733 return new ChooserListController(
734 this,
735 mPm,
736 getTargetIntent(),
737 getReferrerPackageName(),
738 mLaunchedFromUid);
739 }
740
Adam Powell2ed547e2015-04-29 18:45:04 -0700741 final class ChooserTargetInfo implements TargetInfo {
742 private final DisplayResolveInfo mSourceInfo;
Adam Powell0ccc0e92015-04-23 17:19:37 -0700743 private final ResolveInfo mBackupResolveInfo;
Adam Powell24428412015-04-01 17:19:56 -0700744 private final ChooserTarget mChooserTarget;
Adam Powell7d758002015-05-06 17:49:36 -0700745 private Drawable mBadgeIcon = null;
Alan Viverettece5d92c2015-07-31 16:46:56 -0400746 private CharSequence mBadgeContentDescription;
Adam Powell13036be2015-05-12 14:43:56 -0700747 private Drawable mDisplayIcon;
Adam Powell2ed547e2015-04-29 18:45:04 -0700748 private final Intent mFillInIntent;
749 private final int mFillInFlags;
Adam Powella182e452015-07-06 16:57:56 -0700750 private final float mModifiedScore;
Adam Powell24428412015-04-01 17:19:56 -0700751
Adam Powella182e452015-07-06 16:57:56 -0700752 public ChooserTargetInfo(DisplayResolveInfo sourceInfo, ChooserTarget chooserTarget,
753 float modifiedScore) {
Adam Powell24428412015-04-01 17:19:56 -0700754 mSourceInfo = sourceInfo;
755 mChooserTarget = chooserTarget;
Adam Powella182e452015-07-06 16:57:56 -0700756 mModifiedScore = modifiedScore;
Adam Powell7d758002015-05-06 17:49:36 -0700757 if (sourceInfo != null) {
758 final ResolveInfo ri = sourceInfo.getResolveInfo();
759 if (ri != null) {
760 final ActivityInfo ai = ri.activityInfo;
761 if (ai != null && ai.applicationInfo != null) {
Alan Viverettece5d92c2015-07-31 16:46:56 -0400762 final PackageManager pm = getPackageManager();
763 mBadgeIcon = pm.getApplicationIcon(ai.applicationInfo);
764 mBadgeContentDescription = pm.getApplicationLabel(ai.applicationInfo);
Adam Powell7d758002015-05-06 17:49:36 -0700765 }
766 }
767 }
Adam Powell13036be2015-05-12 14:43:56 -0700768 final Icon icon = chooserTarget.getIcon();
769 // TODO do this in the background
770 mDisplayIcon = icon != null ? icon.loadDrawable(ChooserActivity.this) : null;
Adam Powell0ccc0e92015-04-23 17:19:37 -0700771
772 if (sourceInfo != null) {
773 mBackupResolveInfo = null;
774 } else {
775 mBackupResolveInfo = getPackageManager().resolveActivity(getResolvedIntent(), 0);
776 }
Adam Powell2ed547e2015-04-29 18:45:04 -0700777
778 mFillInIntent = null;
779 mFillInFlags = 0;
780 }
781
782 private ChooserTargetInfo(ChooserTargetInfo other, Intent fillInIntent, int flags) {
783 mSourceInfo = other.mSourceInfo;
784 mBackupResolveInfo = other.mBackupResolveInfo;
785 mChooserTarget = other.mChooserTarget;
Adam Powell7d758002015-05-06 17:49:36 -0700786 mBadgeIcon = other.mBadgeIcon;
Alan Viverettece5d92c2015-07-31 16:46:56 -0400787 mBadgeContentDescription = other.mBadgeContentDescription;
Adam Powell2ed547e2015-04-29 18:45:04 -0700788 mDisplayIcon = other.mDisplayIcon;
789 mFillInIntent = fillInIntent;
790 mFillInFlags = flags;
Adam Powella182e452015-07-06 16:57:56 -0700791 mModifiedScore = other.mModifiedScore;
792 }
793
794 public float getModifiedScore() {
795 return mModifiedScore;
Adam Powell24428412015-04-01 17:19:56 -0700796 }
797
798 @Override
799 public Intent getResolvedIntent() {
Adam Powell7d758002015-05-06 17:49:36 -0700800 if (mSourceInfo != null) {
Adam Powell0ccc0e92015-04-23 17:19:37 -0700801 return mSourceInfo.getResolvedIntent();
802 }
Adam Powell52c39212016-04-07 15:14:18 -0700803
804 final Intent targetIntent = new Intent(getTargetIntent());
805 targetIntent.setComponent(mChooserTarget.getComponentName());
806 targetIntent.putExtras(mChooserTarget.getIntentExtras());
807 return targetIntent;
Adam Powell24428412015-04-01 17:19:56 -0700808 }
809
810 @Override
811 public ComponentName getResolvedComponentName() {
Adam Powell0ccc0e92015-04-23 17:19:37 -0700812 if (mSourceInfo != null) {
813 return mSourceInfo.getResolvedComponentName();
814 } else if (mBackupResolveInfo != null) {
815 return new ComponentName(mBackupResolveInfo.activityInfo.packageName,
816 mBackupResolveInfo.activityInfo.name);
817 }
818 return null;
819 }
820
Adam Powell666d82a2015-07-15 20:14:57 -0700821 private Intent getBaseIntentToSend() {
Adam Powell52c39212016-04-07 15:14:18 -0700822 Intent result = getResolvedIntent();
Adam Powell2ed547e2015-04-29 18:45:04 -0700823 if (result == null) {
Adam Powell666d82a2015-07-15 20:14:57 -0700824 Log.e(TAG, "ChooserTargetInfo: no base intent available to send");
Adam Powell13036be2015-05-12 14:43:56 -0700825 } else {
Adam Powell2ed547e2015-04-29 18:45:04 -0700826 result = new Intent(result);
Adam Powell13036be2015-05-12 14:43:56 -0700827 if (mFillInIntent != null) {
828 result.fillIn(mFillInIntent, mFillInFlags);
829 }
830 result.fillIn(mReferrerFillInIntent, 0);
Adam Powell2ed547e2015-04-29 18:45:04 -0700831 }
832 return result;
Adam Powell24428412015-04-01 17:19:56 -0700833 }
834
835 @Override
836 public boolean start(Activity activity, Bundle options) {
Adam Powell666d82a2015-07-15 20:14:57 -0700837 throw new RuntimeException("ChooserTargets should be started as caller.");
Adam Powell24428412015-04-01 17:19:56 -0700838 }
839
840 @Override
Alison Cichowlas3e340502018-08-07 17:15:01 -0400841 public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) {
Adam Powell666d82a2015-07-15 20:14:57 -0700842 final Intent intent = getBaseIntentToSend();
Adam Powell2ed547e2015-04-29 18:45:04 -0700843 if (intent == null) {
844 return false;
845 }
Adam Powell666d82a2015-07-15 20:14:57 -0700846 intent.setComponent(mChooserTarget.getComponentName());
Makoto Onuki99302b52017-03-29 12:42:26 -0700847 intent.putExtras(mChooserTarget.getIntentExtras());
Adam Powell52c39212016-04-07 15:14:18 -0700848
849 // Important: we will ignore the target security checks in ActivityManager
850 // if and only if the ChooserTarget's target package is the same package
851 // where we got the ChooserTargetService that provided it. This lets a
852 // ChooserTargetService provide a non-exported or permission-guarded target
853 // to the chooser for the user to pick.
854 //
855 // If mSourceInfo is null, we got this ChooserTarget from the caller or elsewhere
856 // so we'll obey the caller's normal security checks.
857 final boolean ignoreTargetSecurity = mSourceInfo != null
858 && mSourceInfo.getResolvedComponentName().getPackageName()
859 .equals(mChooserTarget.getComponentName().getPackageName());
Alison Cichowlas3e340502018-08-07 17:15:01 -0400860 return activity.startAsCallerImpl(intent, options, ignoreTargetSecurity, userId);
Adam Powell24428412015-04-01 17:19:56 -0700861 }
862
863 @Override
864 public boolean startAsUser(Activity activity, Bundle options, UserHandle user) {
Adam Powell666d82a2015-07-15 20:14:57 -0700865 throw new RuntimeException("ChooserTargets should be started as caller.");
Adam Powell24428412015-04-01 17:19:56 -0700866 }
867
868 @Override
869 public ResolveInfo getResolveInfo() {
Adam Powell0ccc0e92015-04-23 17:19:37 -0700870 return mSourceInfo != null ? mSourceInfo.getResolveInfo() : mBackupResolveInfo;
Adam Powell24428412015-04-01 17:19:56 -0700871 }
872
873 @Override
874 public CharSequence getDisplayLabel() {
875 return mChooserTarget.getTitle();
876 }
877
878 @Override
879 public CharSequence getExtendedInfo() {
Adam Powell00f4aad2015-09-17 13:38:16 -0700880 // ChooserTargets have badge icons, so we won't show the extended info to disambiguate.
881 return null;
Adam Powell24428412015-04-01 17:19:56 -0700882 }
883
884 @Override
885 public Drawable getDisplayIcon() {
886 return mDisplayIcon;
887 }
Adam Powell2ed547e2015-04-29 18:45:04 -0700888
889 @Override
Adam Powell7d758002015-05-06 17:49:36 -0700890 public Drawable getBadgeIcon() {
891 return mBadgeIcon;
892 }
893
894 @Override
Alan Viverettece5d92c2015-07-31 16:46:56 -0400895 public CharSequence getBadgeContentDescription() {
896 return mBadgeContentDescription;
897 }
898
899 @Override
Adam Powell2ed547e2015-04-29 18:45:04 -0700900 public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) {
901 return new ChooserTargetInfo(this, fillInIntent, flags);
902 }
903
904 @Override
905 public List<Intent> getAllSourceIntents() {
906 final List<Intent> results = new ArrayList<>();
907 if (mSourceInfo != null) {
908 // We only queried the service for the first one in our sourceinfo.
909 results.add(mSourceInfo.getAllSourceIntents().get(0));
910 }
911 return results;
912 }
Adam Powell23882512016-01-29 10:21:00 -0800913
914 @Override
915 public boolean isPinned() {
916 return mSourceInfo != null ? mSourceInfo.isPinned() : false;
917 }
Adam Powell24428412015-04-01 17:19:56 -0700918 }
919
920 public class ChooserListAdapter extends ResolveListAdapter {
Adam Powell7d758002015-05-06 17:49:36 -0700921 public static final int TARGET_BAD = -1;
922 public static final int TARGET_CALLER = 0;
923 public static final int TARGET_SERVICE = 1;
924 public static final int TARGET_STANDARD = 2;
925
Dan Sandler62aad002018-05-23 02:13:51 -0400926 private static final int MAX_SERVICE_TARGETS = 4;
Dan Sandlerf5e17692018-06-04 22:13:40 -0400927 private static final int MAX_TARGETS_PER_SERVICE = 2;
Adam Powella182e452015-07-06 16:57:56 -0700928
Adam Powell24428412015-04-01 17:19:56 -0700929 private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>();
Adam Powell7d758002015-05-06 17:49:36 -0700930 private final List<TargetInfo> mCallerTargets = new ArrayList<>();
Adam Powell565943f2016-02-11 10:29:05 -0800931 private boolean mShowServiceTargets;
Adam Powell24428412015-04-01 17:19:56 -0700932
Adam Powella182e452015-07-06 16:57:56 -0700933 private float mLateFee = 1.f;
934
Dan Sandlerf5e17692018-06-04 22:13:40 -0400935 private boolean mTargetsNeedPruning = false;
936
Adam Powella182e452015-07-06 16:57:56 -0700937 private final BaseChooserTargetComparator mBaseTargetComparator
938 = new BaseChooserTargetComparator();
939
Adam Powell7d758002015-05-06 17:49:36 -0700940 public ChooserListAdapter(Context context, List<Intent> payloadIntents,
941 Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid,
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800942 boolean filterLastUsed, ResolverListController resolverListController) {
Adam Powell7d758002015-05-06 17:49:36 -0700943 // Don't send the initial intents through the shared ResolverActivity path,
944 // we want to separate them into a different section.
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -0800945 super(context, payloadIntents, null, rList, launchedFromUid, filterLastUsed,
946 resolverListController);
Adam Powell0ccc0e92015-04-23 17:19:37 -0700947
Adam Powell7d758002015-05-06 17:49:36 -0700948 if (initialIntents != null) {
949 final PackageManager pm = getPackageManager();
950 for (int i = 0; i < initialIntents.length; i++) {
951 final Intent ii = initialIntents[i];
952 if (ii == null) {
953 continue;
954 }
Adam Powell86100d12016-05-12 16:13:17 -0700955
956 // We reimplement Intent#resolveActivityInfo here because if we have an
957 // implicit intent, we want the ResolveInfo returned by PackageManager
958 // instead of one we reconstruct ourselves. The ResolveInfo returned might
959 // have extra metadata and resolvePackageName set and we want to respect that.
960 ResolveInfo ri = null;
961 ActivityInfo ai = null;
962 final ComponentName cn = ii.getComponent();
963 if (cn != null) {
964 try {
965 ai = pm.getActivityInfo(ii.getComponent(), 0);
966 ri = new ResolveInfo();
967 ri.activityInfo = ai;
968 } catch (PackageManager.NameNotFoundException ignored) {
969 // ai will == null below
970 }
971 }
972 if (ai == null) {
973 ri = pm.resolveActivity(ii, PackageManager.MATCH_DEFAULT_ONLY);
974 ai = ri != null ? ri.activityInfo : null;
975 }
Adam Powell7d758002015-05-06 17:49:36 -0700976 if (ai == null) {
977 Log.w(TAG, "No activity found for " + ii);
978 continue;
979 }
Adam Powell7d758002015-05-06 17:49:36 -0700980 UserManager userManager =
981 (UserManager) getSystemService(Context.USER_SERVICE);
Adam Powell7d758002015-05-06 17:49:36 -0700982 if (ii instanceof LabeledIntent) {
983 LabeledIntent li = (LabeledIntent)ii;
984 ri.resolvePackageName = li.getSourcePackage();
985 ri.labelRes = li.getLabelResource();
986 ri.nonLocalizedLabel = li.getNonLocalizedLabel();
987 ri.icon = li.getIconResource();
Sudheer Shanka9ded7602015-05-19 21:17:25 +0100988 ri.iconResourceId = ri.icon;
989 }
990 if (userManager.isManagedProfile()) {
991 ri.noResourceId = true;
992 ri.icon = 0;
Adam Powell7d758002015-05-06 17:49:36 -0700993 }
994 mCallerTargets.add(new DisplayResolveInfo(ii, ri,
995 ri.loadLabel(pm), null, ii));
Adam Powelld974c7b2015-04-28 15:41:46 -0700996 }
Adam Powell0ccc0e92015-04-23 17:19:37 -0700997 }
Adam Powell24428412015-04-01 17:19:56 -0700998 }
999
1000 @Override
1001 public boolean showsExtendedInfo(TargetInfo info) {
Adam Powell00f4aad2015-09-17 13:38:16 -07001002 // We have badges so we don't need this text shown.
1003 return false;
Adam Powell24428412015-04-01 17:19:56 -07001004 }
1005
1006 @Override
Adam Powell23882512016-01-29 10:21:00 -08001007 public boolean isComponentPinned(ComponentName name) {
1008 return mPinnedSharedPrefs.getBoolean(name.flattenToString(), false);
1009 }
1010
1011 @Override
Adam Powell7d758002015-05-06 17:49:36 -07001012 public View onCreateView(ViewGroup parent) {
Adam Powell24428412015-04-01 17:19:56 -07001013 return mInflater.inflate(
1014 com.android.internal.R.layout.resolve_grid_item, parent, false);
1015 }
1016
1017 @Override
1018 public void onListRebuilt() {
Ng Zhi And3ec5fc2018-05-10 09:13:00 -07001019 // don't support direct share on low ram devices
1020 if (ActivityManager.isLowRamDeviceStatic()) {
1021 return;
1022 }
1023
Adam Powell24428412015-04-01 17:19:56 -07001024 if (mServiceTargets != null) {
Dan Sandlerf5e17692018-06-04 22:13:40 -04001025 if (getDisplayInfoCount() == 0) {
1026 // b/109676071: When packages change, onListRebuilt() is called before
1027 // ResolverActivity.mDisplayList is re-populated; pruning now would cause the
1028 // list to disappear briefly, so instead we detect this case (the
1029 // set of targets suddenly dropping to zero) and remember to prune later.
1030 mTargetsNeedPruning = true;
1031 }
Adam Powell24428412015-04-01 17:19:56 -07001032 }
Hakan Seyalioglue1276bf2016-12-07 16:38:57 -08001033 if (DEBUG) Log.d(TAG, "List built querying services");
1034 queryTargetServices(this);
Adam Powell24428412015-04-01 17:19:56 -07001035 }
1036
1037 @Override
Adam Powellc6d5e3a2015-04-23 12:22:20 -07001038 public boolean shouldGetResolvedFilter() {
1039 return true;
1040 }
1041
1042 @Override
Adam Powell24428412015-04-01 17:19:56 -07001043 public int getCount() {
Adam Powella182e452015-07-06 16:57:56 -07001044 return super.getCount() + getServiceTargetCount() + getCallerTargetCount();
Adam Powell24428412015-04-01 17:19:56 -07001045 }
1046
Adam Powell50077352015-05-26 18:01:55 -07001047 @Override
1048 public int getUnfilteredCount() {
Adam Powella182e452015-07-06 16:57:56 -07001049 return super.getUnfilteredCount() + getServiceTargetCount() + getCallerTargetCount();
Adam Powell50077352015-05-26 18:01:55 -07001050 }
1051
1052 public int getCallerTargetCount() {
Adam Powell7d758002015-05-06 17:49:36 -07001053 return mCallerTargets.size();
1054 }
1055
Adam Powell50077352015-05-26 18:01:55 -07001056 public int getServiceTargetCount() {
Adam Powell565943f2016-02-11 10:29:05 -08001057 if (!mShowServiceTargets) {
1058 return 0;
1059 }
Adam Powella182e452015-07-06 16:57:56 -07001060 return Math.min(mServiceTargets.size(), MAX_SERVICE_TARGETS);
Adam Powell7d758002015-05-06 17:49:36 -07001061 }
1062
1063 public int getStandardTargetCount() {
1064 return super.getCount();
1065 }
1066
1067 public int getPositionTargetType(int position) {
1068 int offset = 0;
1069
Adam Powella182e452015-07-06 16:57:56 -07001070 final int callerTargetCount = getCallerTargetCount();
Adam Powell7d758002015-05-06 17:49:36 -07001071 if (position < callerTargetCount) {
1072 return TARGET_CALLER;
1073 }
1074 offset += callerTargetCount;
1075
Adam Powella182e452015-07-06 16:57:56 -07001076 final int serviceTargetCount = getServiceTargetCount();
Adam Powell7d758002015-05-06 17:49:36 -07001077 if (position - offset < serviceTargetCount) {
1078 return TARGET_SERVICE;
1079 }
1080 offset += serviceTargetCount;
1081
1082 final int standardTargetCount = super.getCount();
1083 if (position - offset < standardTargetCount) {
1084 return TARGET_STANDARD;
1085 }
1086
1087 return TARGET_BAD;
1088 }
1089
Adam Powell24428412015-04-01 17:19:56 -07001090 @Override
1091 public TargetInfo getItem(int position) {
Adam Powell50077352015-05-26 18:01:55 -07001092 return targetInfoForPosition(position, true);
1093 }
1094
1095 @Override
1096 public TargetInfo targetInfoForPosition(int position, boolean filtered) {
Adam Powell24428412015-04-01 17:19:56 -07001097 int offset = 0;
Adam Powell0ccc0e92015-04-23 17:19:37 -07001098
Adam Powella182e452015-07-06 16:57:56 -07001099 final int callerTargetCount = getCallerTargetCount();
Adam Powell0ccc0e92015-04-23 17:19:37 -07001100 if (position < callerTargetCount) {
1101 return mCallerTargets.get(position);
Adam Powell24428412015-04-01 17:19:56 -07001102 }
Adam Powell0ccc0e92015-04-23 17:19:37 -07001103 offset += callerTargetCount;
1104
Adam Powella182e452015-07-06 16:57:56 -07001105 final int serviceTargetCount = getServiceTargetCount();
Adam Powell0ccc0e92015-04-23 17:19:37 -07001106 if (position - offset < serviceTargetCount) {
1107 return mServiceTargets.get(position - offset);
1108 }
1109 offset += serviceTargetCount;
1110
Adam Powell50077352015-05-26 18:01:55 -07001111 return filtered ? super.getItem(position - offset)
1112 : getDisplayInfoAt(position - offset);
Adam Powell24428412015-04-01 17:19:56 -07001113 }
1114
1115 public void addServiceResults(DisplayResolveInfo origTarget, List<ChooserTarget> targets) {
1116 if (DEBUG) Log.d(TAG, "addServiceResults " + origTarget + ", " + targets.size()
1117 + " targets");
Dan Sandlerf5e17692018-06-04 22:13:40 -04001118
1119 if (mTargetsNeedPruning && targets.size() > 0) {
1120 // First proper update since we got an onListRebuilt() with (transient) 0 items.
1121 // Clear out the target list and rebuild.
1122 mServiceTargets.clear();
1123 mTargetsNeedPruning = false;
1124 }
1125
Adam Powella182e452015-07-06 16:57:56 -07001126 final float parentScore = getScore(origTarget);
1127 Collections.sort(targets, mBaseTargetComparator);
1128 float lastScore = 0;
Adam Powellbba00302016-02-03 16:01:09 -08001129 for (int i = 0, N = Math.min(targets.size(), MAX_TARGETS_PER_SERVICE); i < N; i++) {
Adam Powella182e452015-07-06 16:57:56 -07001130 final ChooserTarget target = targets.get(i);
1131 float targetScore = target.getScore();
1132 targetScore *= parentScore;
1133 targetScore *= mLateFee;
1134 if (i > 0 && targetScore >= lastScore) {
1135 // Apply a decay so that the top app can't crowd out everything else.
1136 // This incents ChooserTargetServices to define what's truly better.
1137 targetScore = lastScore * 0.95f;
1138 }
1139 insertServiceTarget(new ChooserTargetInfo(origTarget, target, targetScore));
1140
1141 if (DEBUG) {
1142 Log.d(TAG, " => " + target.toString() + " score=" + targetScore
1143 + " base=" + target.getScore()
1144 + " lastScore=" + lastScore
1145 + " parentScore=" + parentScore
1146 + " lateFee=" + mLateFee);
1147 }
1148
1149 lastScore = targetScore;
Adam Powell24428412015-04-01 17:19:56 -07001150 }
1151
Adam Powella182e452015-07-06 16:57:56 -07001152 mLateFee *= 0.95f;
Adam Powell24428412015-04-01 17:19:56 -07001153
1154 notifyDataSetChanged();
1155 }
1156
Adam Powell565943f2016-02-11 10:29:05 -08001157 /**
1158 * Set to true to reveal all service targets at once.
1159 */
1160 public void setShowServiceTargets(boolean show) {
Adam Powell08adbfe2017-05-10 07:48:30 -07001161 if (show != mShowServiceTargets) {
1162 mShowServiceTargets = show;
1163 notifyDataSetChanged();
1164 }
Adam Powell565943f2016-02-11 10:29:05 -08001165 }
1166
Adam Powella182e452015-07-06 16:57:56 -07001167 private void insertServiceTarget(ChooserTargetInfo chooserTargetInfo) {
1168 final float newScore = chooserTargetInfo.getModifiedScore();
1169 for (int i = 0, N = mServiceTargets.size(); i < N; i++) {
1170 final ChooserTargetInfo serviceTarget = mServiceTargets.get(i);
1171 if (newScore > serviceTarget.getModifiedScore()) {
1172 mServiceTargets.add(i, chooserTargetInfo);
1173 return;
1174 }
1175 }
1176 mServiceTargets.add(chooserTargetInfo);
1177 }
Adam Powell24428412015-04-01 17:19:56 -07001178 }
1179
Adam Powella182e452015-07-06 16:57:56 -07001180 static class BaseChooserTargetComparator implements Comparator<ChooserTarget> {
1181 @Override
1182 public int compare(ChooserTarget lhs, ChooserTarget rhs) {
1183 // Descending order
Adam Powell77a533f2015-10-16 10:47:32 -07001184 return (int) Math.signum(rhs.getScore() - lhs.getScore());
Adam Powella182e452015-07-06 16:57:56 -07001185 }
1186 }
1187
Adam Powell7d758002015-05-06 17:49:36 -07001188 class ChooserRowAdapter extends BaseAdapter {
1189 private ChooserListAdapter mChooserListAdapter;
1190 private final LayoutInflater mLayoutInflater;
1191 private final int mColumnCount = 4;
Adam Powell08adbfe2017-05-10 07:48:30 -07001192 private int mAnimationCount = 0;
Adam Powell7d758002015-05-06 17:49:36 -07001193
1194 public ChooserRowAdapter(ChooserListAdapter wrappedAdapter) {
1195 mChooserListAdapter = wrappedAdapter;
1196 mLayoutInflater = LayoutInflater.from(ChooserActivity.this);
1197
1198 wrappedAdapter.registerDataSetObserver(new DataSetObserver() {
1199 @Override
1200 public void onChanged() {
1201 super.onChanged();
1202 notifyDataSetChanged();
1203 }
1204
1205 @Override
1206 public void onInvalidated() {
1207 super.onInvalidated();
1208 notifyDataSetInvalidated();
1209 }
1210 });
1211 }
1212
1213 @Override
1214 public int getCount() {
1215 return (int) (
Adam Powell63b31692015-09-28 10:45:00 -07001216 getCallerTargetRowCount()
1217 + getServiceTargetRowCount()
Adam Powell7d758002015-05-06 17:49:36 -07001218 + Math.ceil((float) mChooserListAdapter.getStandardTargetCount() / mColumnCount)
1219 );
1220 }
1221
Adam Powell63b31692015-09-28 10:45:00 -07001222 public int getCallerTargetRowCount() {
1223 return (int) Math.ceil(
1224 (float) mChooserListAdapter.getCallerTargetCount() / mColumnCount);
1225 }
1226
Dan Sandler62aad002018-05-23 02:13:51 -04001227 // There can be at most one row of service targets.
Adam Powell63b31692015-09-28 10:45:00 -07001228 public int getServiceTargetRowCount() {
Dan Sandler62aad002018-05-23 02:13:51 -04001229 return (int) mChooserListAdapter.getServiceTargetCount() == 0 ? 0 : 1;
Adam Powell63b31692015-09-28 10:45:00 -07001230 }
1231
Adam Powell7d758002015-05-06 17:49:36 -07001232 @Override
1233 public Object getItem(int position) {
1234 // We have nothing useful to return here.
1235 return position;
1236 }
1237
1238 @Override
1239 public long getItemId(int position) {
1240 return position;
1241 }
1242
1243 @Override
1244 public View getView(int position, View convertView, ViewGroup parent) {
Adam Powell63b31692015-09-28 10:45:00 -07001245 final RowViewHolder holder;
Adam Powell7d758002015-05-06 17:49:36 -07001246 if (convertView == null) {
1247 holder = createViewHolder(parent);
1248 } else {
Adam Powell63b31692015-09-28 10:45:00 -07001249 holder = (RowViewHolder) convertView.getTag();
Adam Powell7d758002015-05-06 17:49:36 -07001250 }
1251 bindViewHolder(position, holder);
1252
Adam Powell63b31692015-09-28 10:45:00 -07001253 return holder.row;
Adam Powell7d758002015-05-06 17:49:36 -07001254 }
1255
Adam Powell63b31692015-09-28 10:45:00 -07001256 RowViewHolder createViewHolder(ViewGroup parent) {
Adam Powell7d758002015-05-06 17:49:36 -07001257 final ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row,
1258 parent, false);
Adam Powell63b31692015-09-28 10:45:00 -07001259 final RowViewHolder holder = new RowViewHolder(row, mColumnCount);
1260 final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
1261
Adam Powell7d758002015-05-06 17:49:36 -07001262 for (int i = 0; i < mColumnCount; i++) {
Adam Powell63b31692015-09-28 10:45:00 -07001263 final View v = mChooserListAdapter.createView(row);
Adam Powell4eb98712015-10-14 13:10:18 -07001264 final int column = i;
Adam Powell63b31692015-09-28 10:45:00 -07001265 v.setOnClickListener(new OnClickListener() {
1266 @Override
1267 public void onClick(View v) {
Adam Powell4eb98712015-10-14 13:10:18 -07001268 startSelected(holder.itemIndices[column], false, true);
Adam Powell63b31692015-09-28 10:45:00 -07001269 }
1270 });
1271 v.setOnLongClickListener(new OnLongClickListener() {
1272 @Override
1273 public boolean onLongClick(View v) {
Adam Powell23882512016-01-29 10:21:00 -08001274 showTargetDetails(
Adam Powell4eb98712015-10-14 13:10:18 -07001275 mChooserListAdapter.resolveInfoForPosition(
1276 holder.itemIndices[column], true));
Adam Powell63b31692015-09-28 10:45:00 -07001277 return true;
1278 }
1279 });
1280 row.addView(v);
1281 holder.cells[i] = v;
1282
1283 // Force height to be a given so we don't have visual disruption during scaling.
1284 LayoutParams lp = v.getLayoutParams();
1285 v.measure(spec, spec);
1286 if (lp == null) {
1287 lp = new LayoutParams(LayoutParams.MATCH_PARENT, v.getMeasuredHeight());
1288 row.setLayoutParams(lp);
1289 } else {
1290 lp.height = v.getMeasuredHeight();
1291 }
Jason Monk027dcfa2017-06-27 18:37:35 -04001292 if (i != (mColumnCount - 1)) {
1293 row.addView(new Space(ChooserActivity.this),
1294 new LinearLayout.LayoutParams(0, 0, 1));
1295 }
Adam Powell63b31692015-09-28 10:45:00 -07001296 }
1297
1298 // Pre-measure so we can scale later.
1299 holder.measure();
1300 LayoutParams lp = row.getLayoutParams();
1301 if (lp == null) {
1302 lp = new LayoutParams(LayoutParams.MATCH_PARENT, holder.measuredRowHeight);
1303 row.setLayoutParams(lp);
1304 } else {
1305 lp.height = holder.measuredRowHeight;
Adam Powell7d758002015-05-06 17:49:36 -07001306 }
1307 row.setTag(holder);
Adam Powell7d758002015-05-06 17:49:36 -07001308 return holder;
1309 }
1310
Adam Powell63b31692015-09-28 10:45:00 -07001311 void bindViewHolder(int rowPosition, RowViewHolder holder) {
Adam Powell7d758002015-05-06 17:49:36 -07001312 final int start = getFirstRowPosition(rowPosition);
1313 final int startType = mChooserListAdapter.getPositionTargetType(start);
1314
1315 int end = start + mColumnCount - 1;
1316 while (mChooserListAdapter.getPositionTargetType(end) != startType && end >= start) {
1317 end--;
1318 }
1319
Adam Powell7d758002015-05-06 17:49:36 -07001320 if (startType == ChooserListAdapter.TARGET_SERVICE) {
Adam Powell63b31692015-09-28 10:45:00 -07001321 holder.row.setBackgroundColor(
1322 getColor(R.color.chooser_service_row_background_color));
Jason Monk027dcfa2017-06-27 18:37:35 -04001323 int nextStartType = mChooserListAdapter.getPositionTargetType(
1324 getFirstRowPosition(rowPosition + 1));
1325 int serviceSpacing = holder.row.getContext().getResources()
1326 .getDimensionPixelSize(R.dimen.chooser_service_spacing);
Beverly3aee1782017-09-21 10:01:41 -04001327 if (rowPosition == 0 && nextStartType != ChooserListAdapter.TARGET_SERVICE) {
1328 // if the row is the only row for target service
1329 setVertPadding(holder, 0, 0);
Jason Monk027dcfa2017-06-27 18:37:35 -04001330 } else {
Beverly3aee1782017-09-21 10:01:41 -04001331 int top = rowPosition == 0 ? serviceSpacing : 0;
1332 if (nextStartType != ChooserListAdapter.TARGET_SERVICE) {
1333 setVertPadding(holder, top, serviceSpacing);
1334 } else {
1335 setVertPadding(holder, top, 0);
1336 }
Jason Monk027dcfa2017-06-27 18:37:35 -04001337 }
Adam Powell7d758002015-05-06 17:49:36 -07001338 } else {
Adam Powell63b31692015-09-28 10:45:00 -07001339 holder.row.setBackgroundColor(Color.TRANSPARENT);
Jason Monk027dcfa2017-06-27 18:37:35 -04001340 int lastStartType = mChooserListAdapter.getPositionTargetType(
1341 getFirstRowPosition(rowPosition - 1));
1342 if (lastStartType == ChooserListAdapter.TARGET_SERVICE || rowPosition == 0) {
1343 int serviceSpacing = holder.row.getContext().getResources()
1344 .getDimensionPixelSize(R.dimen.chooser_service_spacing);
1345 setVertPadding(holder, serviceSpacing, 0);
1346 } else {
1347 setVertPadding(holder, 0, 0);
1348 }
Adam Powell63b31692015-09-28 10:45:00 -07001349 }
1350
1351 final int oldHeight = holder.row.getLayoutParams().height;
Dan Sandler62aad002018-05-23 02:13:51 -04001352 holder.row.getLayoutParams().height = Math.max(1, holder.measuredRowHeight);
Adam Powell63b31692015-09-28 10:45:00 -07001353 if (holder.row.getLayoutParams().height != oldHeight) {
1354 holder.row.requestLayout();
Adam Powell7d758002015-05-06 17:49:36 -07001355 }
1356
1357 for (int i = 0; i < mColumnCount; i++) {
Adam Powell63b31692015-09-28 10:45:00 -07001358 final View v = holder.cells[i];
Adam Powell7d758002015-05-06 17:49:36 -07001359 if (start + i <= end) {
1360 v.setVisibility(View.VISIBLE);
Adam Powell4eb98712015-10-14 13:10:18 -07001361 holder.itemIndices[i] = start + i;
1362 mChooserListAdapter.bindView(holder.itemIndices[i], v);
Adam Powell7d758002015-05-06 17:49:36 -07001363 } else {
Jason Monk027dcfa2017-06-27 18:37:35 -04001364 v.setVisibility(View.INVISIBLE);
Adam Powell7d758002015-05-06 17:49:36 -07001365 }
1366 }
1367 }
1368
Jason Monk027dcfa2017-06-27 18:37:35 -04001369 private void setVertPadding(RowViewHolder holder, int top, int bottom) {
1370 holder.row.setPadding(holder.row.getPaddingLeft(), top,
1371 holder.row.getPaddingRight(), bottom);
1372 }
1373
Adam Powell7d758002015-05-06 17:49:36 -07001374 int getFirstRowPosition(int row) {
Adam Powell50077352015-05-26 18:01:55 -07001375 final int callerCount = mChooserListAdapter.getCallerTargetCount();
Adam Powell7d758002015-05-06 17:49:36 -07001376 final int callerRows = (int) Math.ceil((float) callerCount / mColumnCount);
1377
1378 if (row < callerRows) {
1379 return row * mColumnCount;
1380 }
1381
Adam Powell50077352015-05-26 18:01:55 -07001382 final int serviceCount = mChooserListAdapter.getServiceTargetCount();
Adam Powell7d758002015-05-06 17:49:36 -07001383 final int serviceRows = (int) Math.ceil((float) serviceCount / mColumnCount);
1384
1385 if (row < callerRows + serviceRows) {
1386 return callerCount + (row - callerRows) * mColumnCount;
1387 }
1388
1389 return callerCount + serviceCount
1390 + (row - callerRows - serviceRows) * mColumnCount;
1391 }
1392 }
1393
Adam Powell63b31692015-09-28 10:45:00 -07001394 static class RowViewHolder {
1395 final View[] cells;
1396 final ViewGroup row;
1397 int measuredRowHeight;
Adam Powell4eb98712015-10-14 13:10:18 -07001398 int[] itemIndices;
Adam Powell63b31692015-09-28 10:45:00 -07001399
1400 public RowViewHolder(ViewGroup row, int cellCount) {
1401 this.row = row;
1402 this.cells = new View[cellCount];
Adam Powell4eb98712015-10-14 13:10:18 -07001403 this.itemIndices = new int[cellCount];
Adam Powell63b31692015-09-28 10:45:00 -07001404 }
1405
1406 public void measure() {
1407 final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
1408 row.measure(spec, spec);
1409 measuredRowHeight = row.getMeasuredHeight();
1410 }
1411 }
1412
Adam Powell9761ab22015-09-08 17:01:49 -07001413 static class ChooserTargetServiceConnection implements ServiceConnection {
Adam Powell52c39212016-04-07 15:14:18 -07001414 private DisplayResolveInfo mOriginalTarget;
Adam Powell9761ab22015-09-08 17:01:49 -07001415 private ComponentName mConnectedComponent;
1416 private ChooserActivity mChooserActivity;
1417 private final Object mLock = new Object();
Adam Powell24428412015-04-01 17:19:56 -07001418
1419 private final IChooserTargetResult mChooserTargetResult = new IChooserTargetResult.Stub() {
1420 @Override
1421 public void sendResult(List<ChooserTarget> targets) throws RemoteException {
Adam Powell9761ab22015-09-08 17:01:49 -07001422 synchronized (mLock) {
1423 if (mChooserActivity == null) {
1424 Log.e(TAG, "destroyed ChooserTargetServiceConnection received result from "
1425 + mConnectedComponent + "; ignoring...");
1426 return;
1427 }
1428 mChooserActivity.filterServiceTargets(
1429 mOriginalTarget.getResolveInfo().activityInfo.packageName, targets);
1430 final Message msg = Message.obtain();
1431 msg.what = CHOOSER_TARGET_SERVICE_RESULT;
1432 msg.obj = new ServiceResultInfo(mOriginalTarget, targets,
1433 ChooserTargetServiceConnection.this);
1434 mChooserActivity.mChooserHandler.sendMessage(msg);
1435 }
Adam Powell24428412015-04-01 17:19:56 -07001436 }
1437 };
1438
Adam Powell9761ab22015-09-08 17:01:49 -07001439 public ChooserTargetServiceConnection(ChooserActivity chooserActivity,
1440 DisplayResolveInfo dri) {
1441 mChooserActivity = chooserActivity;
Adam Powell24428412015-04-01 17:19:56 -07001442 mOriginalTarget = dri;
1443 }
1444
1445 @Override
1446 public void onServiceConnected(ComponentName name, IBinder service) {
1447 if (DEBUG) Log.d(TAG, "onServiceConnected: " + name);
Adam Powell9761ab22015-09-08 17:01:49 -07001448 synchronized (mLock) {
1449 if (mChooserActivity == null) {
1450 Log.e(TAG, "destroyed ChooserTargetServiceConnection got onServiceConnected");
1451 return;
1452 }
1453
1454 final IChooserTargetService icts = IChooserTargetService.Stub.asInterface(service);
1455 try {
1456 icts.getChooserTargets(mOriginalTarget.getResolvedComponentName(),
1457 mOriginalTarget.getResolveInfo().filter, mChooserTargetResult);
1458 } catch (RemoteException e) {
1459 Log.e(TAG, "Querying ChooserTargetService " + name + " failed.", e);
1460 mChooserActivity.unbindService(this);
Adam Powell9761ab22015-09-08 17:01:49 -07001461 mChooserActivity.mServiceConnections.remove(this);
Dan Sandlerfcd7fae2017-09-25 17:40:04 -04001462 destroy();
Adam Powell9761ab22015-09-08 17:01:49 -07001463 }
Adam Powell24428412015-04-01 17:19:56 -07001464 }
1465 }
1466
1467 @Override
1468 public void onServiceDisconnected(ComponentName name) {
1469 if (DEBUG) Log.d(TAG, "onServiceDisconnected: " + name);
Adam Powell9761ab22015-09-08 17:01:49 -07001470 synchronized (mLock) {
1471 if (mChooserActivity == null) {
1472 Log.e(TAG,
1473 "destroyed ChooserTargetServiceConnection got onServiceDisconnected");
1474 return;
1475 }
1476
1477 mChooserActivity.unbindService(this);
Adam Powell9761ab22015-09-08 17:01:49 -07001478 mChooserActivity.mServiceConnections.remove(this);
1479 if (mChooserActivity.mServiceConnections.isEmpty()) {
1480 mChooserActivity.mChooserHandler.removeMessages(
1481 CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT);
1482 mChooserActivity.sendVoiceChoicesIfNeeded();
1483 }
1484 mConnectedComponent = null;
Dan Sandlerfcd7fae2017-09-25 17:40:04 -04001485 destroy();
Adam Powell9761ab22015-09-08 17:01:49 -07001486 }
1487 }
1488
1489 public void destroy() {
1490 synchronized (mLock) {
1491 mChooserActivity = null;
Adam Powell52c39212016-04-07 15:14:18 -07001492 mOriginalTarget = null;
Adam Powell4c470d62015-06-19 17:46:17 -07001493 }
Adam Powell24428412015-04-01 17:19:56 -07001494 }
1495
1496 @Override
1497 public String toString() {
Adam Powell9761ab22015-09-08 17:01:49 -07001498 return "ChooserTargetServiceConnection{service="
1499 + mConnectedComponent + ", activity="
Adam Powell52c39212016-04-07 15:14:18 -07001500 + (mOriginalTarget != null
1501 ? mOriginalTarget.getResolveInfo().activityInfo.toString()
1502 : "<connection destroyed>") + "}";
Adam Powell24428412015-04-01 17:19:56 -07001503 }
1504 }
1505
1506 static class ServiceResultInfo {
1507 public final DisplayResolveInfo originalTarget;
1508 public final List<ChooserTarget> resultTargets;
1509 public final ChooserTargetServiceConnection connection;
1510
1511 public ServiceResultInfo(DisplayResolveInfo ot, List<ChooserTarget> rt,
1512 ChooserTargetServiceConnection c) {
1513 originalTarget = ot;
1514 resultTargets = rt;
1515 connection = c;
1516 }
1517 }
Adam Powell2ed547e2015-04-29 18:45:04 -07001518
1519 static class RefinementResultReceiver extends ResultReceiver {
1520 private ChooserActivity mChooserActivity;
1521 private TargetInfo mSelectedTarget;
1522
1523 public RefinementResultReceiver(ChooserActivity host, TargetInfo target,
1524 Handler handler) {
1525 super(handler);
1526 mChooserActivity = host;
1527 mSelectedTarget = target;
1528 }
1529
1530 @Override
1531 protected void onReceiveResult(int resultCode, Bundle resultData) {
1532 if (mChooserActivity == null) {
1533 Log.e(TAG, "Destroyed RefinementResultReceiver received a result");
1534 return;
1535 }
1536 if (resultData == null) {
1537 Log.e(TAG, "RefinementResultReceiver received null resultData");
1538 return;
1539 }
1540
1541 switch (resultCode) {
1542 case RESULT_CANCELED:
1543 mChooserActivity.onRefinementCanceled();
1544 break;
1545 case RESULT_OK:
1546 Parcelable intentParcelable = resultData.getParcelable(Intent.EXTRA_INTENT);
1547 if (intentParcelable instanceof Intent) {
1548 mChooserActivity.onRefinementResult(mSelectedTarget,
1549 (Intent) intentParcelable);
1550 } else {
1551 Log.e(TAG, "RefinementResultReceiver received RESULT_OK but no Intent"
1552 + " in resultData with key Intent.EXTRA_INTENT");
1553 }
1554 break;
1555 default:
1556 Log.w(TAG, "Unknown result code " + resultCode
1557 + " sent to RefinementResultReceiver");
1558 break;
1559 }
1560 }
1561
1562 public void destroy() {
1563 mChooserActivity = null;
1564 mSelectedTarget = null;
1565 }
1566 }
Adam Powell63b31692015-09-28 10:45:00 -07001567
1568 class OffsetDataSetObserver extends DataSetObserver {
1569 private final AbsListView mListView;
1570 private int mCachedViewType = -1;
1571 private View mCachedView;
1572
1573 public OffsetDataSetObserver(AbsListView listView) {
1574 mListView = listView;
1575 }
1576
1577 @Override
1578 public void onChanged() {
1579 if (mResolverDrawerLayout == null) {
1580 return;
1581 }
1582
1583 final int chooserTargetRows = mChooserRowAdapter.getServiceTargetRowCount();
1584 int offset = 0;
1585 for (int i = 0; i < chooserTargetRows; i++) {
1586 final int pos = mChooserRowAdapter.getCallerTargetRowCount() + i;
1587 final int vt = mChooserRowAdapter.getItemViewType(pos);
1588 if (vt != mCachedViewType) {
1589 mCachedView = null;
1590 }
1591 final View v = mChooserRowAdapter.getView(pos, mCachedView, mListView);
1592 int height = ((RowViewHolder) (v.getTag())).measuredRowHeight;
1593
Dan Sandler62aad002018-05-23 02:13:51 -04001594 offset += (int) (height);
Adam Powell63b31692015-09-28 10:45:00 -07001595
1596 if (vt >= 0) {
1597 mCachedViewType = vt;
1598 mCachedView = v;
1599 } else {
1600 mCachedViewType = -1;
1601 }
1602 }
1603
1604 mResolverDrawerLayout.setCollapsibleHeightReserved(offset);
1605 }
1606 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001607}