• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.internal.app;
18 
19 import static java.lang.annotation.RetentionPolicy.SOURCE;
20 
21 import android.animation.Animator;
22 import android.animation.AnimatorListenerAdapter;
23 import android.animation.AnimatorSet;
24 import android.animation.ObjectAnimator;
25 import android.animation.ValueAnimator;
26 import android.annotation.IntDef;
27 import android.annotation.NonNull;
28 import android.annotation.Nullable;
29 import android.app.Activity;
30 import android.app.ActivityManager;
31 import android.app.SharedElementCallback;
32 import android.app.prediction.AppPredictionContext;
33 import android.app.prediction.AppPredictionManager;
34 import android.app.prediction.AppPredictor;
35 import android.app.prediction.AppTarget;
36 import android.app.prediction.AppTargetEvent;
37 import android.app.prediction.AppTargetId;
38 import android.compat.annotation.UnsupportedAppUsage;
39 import android.content.ClipData;
40 import android.content.ClipboardManager;
41 import android.content.ComponentName;
42 import android.content.ContentResolver;
43 import android.content.Context;
44 import android.content.Intent;
45 import android.content.IntentFilter;
46 import android.content.IntentSender;
47 import android.content.IntentSender.SendIntentException;
48 import android.content.ServiceConnection;
49 import android.content.SharedPreferences;
50 import android.content.pm.ActivityInfo;
51 import android.content.pm.ApplicationInfo;
52 import android.content.pm.PackageManager;
53 import android.content.pm.PackageManager.NameNotFoundException;
54 import android.content.pm.ResolveInfo;
55 import android.content.pm.ShortcutInfo;
56 import android.content.pm.ShortcutManager;
57 import android.content.res.Configuration;
58 import android.content.res.Resources;
59 import android.database.Cursor;
60 import android.database.DataSetObserver;
61 import android.graphics.Bitmap;
62 import android.graphics.Canvas;
63 import android.graphics.Color;
64 import android.graphics.Paint;
65 import android.graphics.Path;
66 import android.graphics.drawable.AnimatedVectorDrawable;
67 import android.graphics.drawable.Drawable;
68 import android.metrics.LogMaker;
69 import android.net.Uri;
70 import android.os.AsyncTask;
71 import android.os.Bundle;
72 import android.os.Environment;
73 import android.os.Handler;
74 import android.os.IBinder;
75 import android.os.Message;
76 import android.os.Parcelable;
77 import android.os.PatternMatcher;
78 import android.os.RemoteException;
79 import android.os.ResultReceiver;
80 import android.os.UserHandle;
81 import android.os.UserManager;
82 import android.os.storage.StorageManager;
83 import android.provider.DeviceConfig;
84 import android.provider.DocumentsContract;
85 import android.provider.Downloads;
86 import android.provider.OpenableColumns;
87 import android.provider.Settings;
88 import android.service.chooser.ChooserTarget;
89 import android.service.chooser.ChooserTargetService;
90 import android.service.chooser.IChooserTargetResult;
91 import android.service.chooser.IChooserTargetService;
92 import android.text.TextUtils;
93 import android.util.AttributeSet;
94 import android.util.HashedStringCache;
95 import android.util.Log;
96 import android.util.Pair;
97 import android.util.Size;
98 import android.util.Slog;
99 import android.view.LayoutInflater;
100 import android.view.View;
101 import android.view.View.MeasureSpec;
102 import android.view.View.OnClickListener;
103 import android.view.ViewGroup;
104 import android.view.ViewGroup.LayoutParams;
105 import android.view.ViewTreeObserver;
106 import android.view.WindowInsets;
107 import android.view.animation.AccelerateInterpolator;
108 import android.view.animation.DecelerateInterpolator;
109 import android.widget.Button;
110 import android.widget.ImageView;
111 import android.widget.Space;
112 import android.widget.TextView;
113 import android.widget.Toast;
114 
115 import com.android.internal.R;
116 import com.android.internal.annotations.VisibleForTesting;
117 import com.android.internal.app.ResolverListAdapter.ActivityInfoPresentationGetter;
118 import com.android.internal.app.ResolverListAdapter.ViewHolder;
119 import com.android.internal.app.chooser.ChooserTargetInfo;
120 import com.android.internal.app.chooser.DisplayResolveInfo;
121 import com.android.internal.app.chooser.MultiDisplayResolveInfo;
122 import com.android.internal.app.chooser.NotSelectableTargetInfo;
123 import com.android.internal.app.chooser.SelectableTargetInfo;
124 import com.android.internal.app.chooser.SelectableTargetInfo.SelectableTargetInfoCommunicator;
125 import com.android.internal.app.chooser.TargetInfo;
126 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
127 import com.android.internal.content.PackageMonitor;
128 import com.android.internal.logging.MetricsLogger;
129 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
130 import com.android.internal.util.FrameworkStatsLog;
131 import com.android.internal.widget.GridLayoutManager;
132 import com.android.internal.widget.RecyclerView;
133 import com.android.internal.widget.ResolverDrawerLayout;
134 import com.android.internal.widget.ViewPager;
135 
136 import com.google.android.collect.Lists;
137 
138 import java.io.File;
139 import java.io.IOException;
140 import java.lang.annotation.Retention;
141 import java.lang.annotation.RetentionPolicy;
142 import java.net.URISyntaxException;
143 import java.text.Collator;
144 import java.util.ArrayList;
145 import java.util.Collections;
146 import java.util.Comparator;
147 import java.util.HashMap;
148 import java.util.HashSet;
149 import java.util.List;
150 import java.util.Map;
151 import java.util.Set;
152 
153 /**
154  * The Chooser Activity handles intent resolution specifically for sharing intents -
155  * for example, those generated by @see android.content.Intent#createChooser(Intent, CharSequence).
156  *
157  */
158 public class ChooserActivity extends ResolverActivity implements
159         ChooserListAdapter.ChooserListCommunicator,
160         SelectableTargetInfoCommunicator {
161     private static final String TAG = "ChooserActivity";
162     private AppPredictor mPersonalAppPredictor;
163     private AppPredictor mWorkAppPredictor;
164     private boolean mShouldDisplayLandscape;
165 
166     private static final int MAX_TARGETS_PER_ROW_PORTRAIT = 4;
167     private static final int MAX_TARGETS_PER_ROW_LANDSCAPE = 8;
168 
169     @UnsupportedAppUsage
ChooserActivity()170     public ChooserActivity() {
171     }
172     /**
173      * Boolean extra to change the following behavior: Normally, ChooserActivity finishes itself
174      * in onStop when launched in a new task. If this extra is set to true, we do not finish
175      * ourselves when onStop gets called.
176      */
177     public static final String EXTRA_PRIVATE_RETAIN_IN_ON_STOP
178             = "com.android.internal.app.ChooserActivity.EXTRA_PRIVATE_RETAIN_IN_ON_STOP";
179 
180     /**
181      * Transition name for the first image preview.
182      * To be used for shared element transition into this activity.
183      * @hide
184      */
185     public static final String FIRST_IMAGE_PREVIEW_TRANSITION_NAME = "screenshot_preview_image";
186 
187     private static final String PREF_NUM_SHEET_EXPANSIONS = "pref_num_sheet_expansions";
188 
189     private static final String CHIP_LABEL_METADATA_KEY = "android.service.chooser.chip_label";
190     private static final String CHIP_ICON_METADATA_KEY = "android.service.chooser.chip_icon";
191 
192     private static final boolean DEBUG = true;
193 
194     private static final boolean USE_PREDICTION_MANAGER_FOR_SHARE_ACTIVITIES = true;
195     // TODO(b/123088566) Share these in a better way.
196     private static final String APP_PREDICTION_SHARE_UI_SURFACE = "share";
197     public static final String LAUNCH_LOCATION_DIRECT_SHARE = "direct_share";
198     public static final String CHOOSER_TARGET = "chooser_target";
199     private static final String SHORTCUT_TARGET = "shortcut_target";
200     private static final int APP_PREDICTION_SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20;
201     public static final String APP_PREDICTION_INTENT_FILTER_KEY = "intent_filter";
202 
203     @VisibleForTesting
204     public static final int LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS = 250;
205 
206     private boolean mIsAppPredictorComponentAvailable;
207     private Map<ChooserTarget, AppTarget> mDirectShareAppTargetCache;
208     private Map<ChooserTarget, ShortcutInfo> mDirectShareShortcutInfoCache;
209     private Map<ComponentName, ComponentName> mChooserTargetComponentNameCache;
210 
211     public static final int TARGET_TYPE_DEFAULT = 0;
212     public static final int TARGET_TYPE_CHOOSER_TARGET = 1;
213     public static final int TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER = 2;
214     public static final int TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE = 3;
215 
216     public static final int SELECTION_TYPE_SERVICE = 1;
217     public static final int SELECTION_TYPE_APP = 2;
218     public static final int SELECTION_TYPE_STANDARD = 3;
219     public static final int SELECTION_TYPE_COPY = 4;
220     public static final int SELECTION_TYPE_NEARBY = 5;
221     public static final int SELECTION_TYPE_EDIT = 6;
222 
223     private static final int SCROLL_STATUS_IDLE = 0;
224     private static final int SCROLL_STATUS_SCROLLING_VERTICAL = 1;
225     private static final int SCROLL_STATUS_SCROLLING_HORIZONTAL = 2;
226 
227     // statsd logger wrapper
228     protected ChooserActivityLogger mChooserActivityLogger;
229 
230     private static final boolean USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS = true;
231 
232     @IntDef(flag = false, prefix = { "TARGET_TYPE_" }, value = {
233             TARGET_TYPE_DEFAULT,
234             TARGET_TYPE_CHOOSER_TARGET,
235             TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER,
236             TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE
237     })
238     @Retention(RetentionPolicy.SOURCE)
239     public @interface ShareTargetType {}
240 
241     /**
242      * The transition time between placeholders for direct share to a message
243      * indicating that non are available.
244      */
245     private static final int NO_DIRECT_SHARE_ANIM_IN_MILLIS = 200;
246 
247     private static final float DIRECT_SHARE_EXPANSION_RATE = 0.78f;
248 
249     // TODO(b/121287224): Re-evaluate this limit
250     private static final int SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20;
251 
252     private static final int QUERY_TARGET_SERVICE_LIMIT = 5;
253 
254     private static final int DEFAULT_SALT_EXPIRATION_DAYS = 7;
255     private int mMaxHashSaltDays = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
256             SystemUiDeviceConfigFlags.HASH_SALT_MAX_DAYS,
257             DEFAULT_SALT_EXPIRATION_DAYS);
258 
259     private Bundle mReplacementExtras;
260     private IntentSender mChosenComponentSender;
261     private IntentSender mRefinementIntentSender;
262     private RefinementResultReceiver mRefinementResultReceiver;
263     private ChooserTarget[] mCallerChooserTargets;
264     private ComponentName[] mFilteredComponentNames;
265 
266     private Intent mReferrerFillInIntent;
267 
268     private long mChooserShownTime;
269     protected boolean mIsSuccessfullySelected;
270 
271     private long mQueriedTargetServicesTimeMs;
272     private long mQueriedSharingShortcutsTimeMs;
273 
274     private int mChooserRowServiceSpacing;
275 
276     private int mCurrAvailableWidth = 0;
277     private int mLastNumberOfChildren = -1;
278 
279     private static final String TARGET_DETAILS_FRAGMENT_TAG = "targetDetailsFragment";
280 
281     private final List<ChooserTargetServiceConnection> mServiceConnections = new ArrayList<>();
282     private final Set<Pair<ComponentName, UserHandle>> mServicesRequested = new HashSet<>();
283 
284     private static final int MAX_LOG_RANK_POSITION = 12;
285 
286     private static final int MAX_EXTRA_INITIAL_INTENTS = 2;
287     private static final int MAX_EXTRA_CHOOSER_TARGETS = 2;
288 
289     private SharedPreferences mPinnedSharedPrefs;
290     private static final String PINNED_SHARED_PREFS_NAME = "chooser_pin_settings";
291 
292     @Retention(SOURCE)
293     @IntDef({CONTENT_PREVIEW_FILE, CONTENT_PREVIEW_IMAGE, CONTENT_PREVIEW_TEXT})
294     private @interface ContentPreviewType {
295     }
296 
297     // Starting at 1 since 0 is considered "undefined" for some of the database transformations
298     // of tron logs.
299     protected static final int CONTENT_PREVIEW_IMAGE = 1;
300     protected static final int CONTENT_PREVIEW_FILE = 2;
301     protected static final int CONTENT_PREVIEW_TEXT = 3;
302     protected MetricsLogger mMetricsLogger;
303 
304     private ContentPreviewCoordinator mPreviewCoord;
305     private int mScrollStatus = SCROLL_STATUS_IDLE;
306 
307     @VisibleForTesting
308     protected ChooserMultiProfilePagerAdapter mChooserMultiProfilePagerAdapter;
309 
310     private boolean mRemoveSharedElements = false;
311 
312     private class ContentPreviewCoordinator {
313         private static final int IMAGE_FADE_IN_MILLIS = 150;
314         private static final int IMAGE_LOAD_TIMEOUT = 1;
315         private static final int IMAGE_LOAD_INTO_VIEW = 2;
316 
317         private final int mImageLoadTimeoutMillis =
318                 getResources().getInteger(R.integer.config_shortAnimTime);
319 
320         private final View mParentView;
321         private boolean mHideParentOnFail;
322         private boolean mAtLeastOneLoaded = false;
323 
324         class LoadUriTask {
325             public final Uri mUri;
326             public final int mImageResourceId;
327             public final int mExtraCount;
328             public final Bitmap mBmp;
329 
LoadUriTask(int imageResourceId, Uri uri, int extraCount, Bitmap bmp)330             LoadUriTask(int imageResourceId, Uri uri, int extraCount, Bitmap bmp) {
331                 this.mImageResourceId = imageResourceId;
332                 this.mUri = uri;
333                 this.mExtraCount = extraCount;
334                 this.mBmp = bmp;
335             }
336         }
337 
338         // If at least one image loads within the timeout period, allow other
339         // loads to continue. Otherwise terminate and optionally hide
340         // the parent area
341         private final Handler mHandler = new Handler() {
342             @Override
343             public void handleMessage(Message msg) {
344                 switch (msg.what) {
345                     case IMAGE_LOAD_TIMEOUT:
346                         maybeHideContentPreview();
347                         break;
348 
349                     case IMAGE_LOAD_INTO_VIEW:
350                         if (isFinishing()) break;
351 
352                         LoadUriTask task = (LoadUriTask) msg.obj;
353                         RoundedRectImageView imageView = mParentView.findViewById(
354                                 task.mImageResourceId);
355                         if (task.mBmp == null) {
356                             imageView.setVisibility(View.GONE);
357                             maybeHideContentPreview();
358                             return;
359                         }
360 
361                         mAtLeastOneLoaded = true;
362                         imageView.setVisibility(View.VISIBLE);
363                         imageView.setAlpha(0.0f);
364                         imageView.setImageBitmap(task.mBmp);
365 
366                         ValueAnimator fadeAnim = ObjectAnimator.ofFloat(imageView, "alpha", 0.0f,
367                                 1.0f);
368                         fadeAnim.setInterpolator(new DecelerateInterpolator(1.0f));
369                         fadeAnim.setDuration(IMAGE_FADE_IN_MILLIS);
370                         fadeAnim.start();
371 
372                         if (task.mExtraCount > 0) {
373                             imageView.setExtraImageCount(task.mExtraCount);
374                         }
375 
376                         setupPreDrawForSharedElementTransition(imageView);
377                 }
378             }
379         };
380 
setupPreDrawForSharedElementTransition(View v)381         private void setupPreDrawForSharedElementTransition(View v) {
382             v.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
383                 @Override
384                 public boolean onPreDraw() {
385                     v.getViewTreeObserver().removeOnPreDrawListener(this);
386 
387                     if (!mRemoveSharedElements && isActivityTransitionRunning()) {
388                         // Disable the window animations as it interferes with the
389                         // transition animation.
390                         getWindow().setWindowAnimations(0);
391                     }
392                     startPostponedEnterTransition();
393                     return true;
394                 }
395             });
396         }
397 
ContentPreviewCoordinator(View parentView, boolean hideParentOnFail)398         ContentPreviewCoordinator(View parentView, boolean hideParentOnFail) {
399             super();
400 
401             this.mParentView = parentView;
402             this.mHideParentOnFail = hideParentOnFail;
403         }
404 
loadUriIntoView(final int imageResourceId, final Uri uri, final int extraImages)405         private void loadUriIntoView(final int imageResourceId, final Uri uri,
406                 final int extraImages) {
407             mHandler.sendEmptyMessageDelayed(IMAGE_LOAD_TIMEOUT, mImageLoadTimeoutMillis);
408 
409             AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
410                 int size = getResources().getDimensionPixelSize(
411                         R.dimen.chooser_preview_image_max_dimen);
412                 final Bitmap bmp = loadThumbnail(uri, new Size(size, size));
413                 final Message msg = Message.obtain();
414                 msg.what = IMAGE_LOAD_INTO_VIEW;
415                 msg.obj = new LoadUriTask(imageResourceId, uri, extraImages, bmp);
416                 mHandler.sendMessage(msg);
417             });
418         }
419 
cancelLoads()420         private void cancelLoads() {
421             mHandler.removeMessages(IMAGE_LOAD_INTO_VIEW);
422             mHandler.removeMessages(IMAGE_LOAD_TIMEOUT);
423         }
424 
maybeHideContentPreview()425         private void maybeHideContentPreview() {
426             if (!mAtLeastOneLoaded) {
427                 if (mHideParentOnFail) {
428                     Log.i(TAG, "Hiding image preview area. Timed out waiting for preview to load"
429                             + " within " + mImageLoadTimeoutMillis + "ms.");
430                     collapseParentView();
431                     if (shouldShowTabs()) {
432                         hideStickyContentPreview();
433                     } else if (mChooserMultiProfilePagerAdapter.getCurrentRootAdapter() != null) {
434                         mChooserMultiProfilePagerAdapter.getCurrentRootAdapter()
435                                 .hideContentPreview();
436                     }
437                     mHideParentOnFail = false;
438                 }
439                 mRemoveSharedElements = true;
440                 startPostponedEnterTransition();
441             }
442         }
443 
collapseParentView()444         private void collapseParentView() {
445             // This will effectively hide the content preview row by forcing the height
446             // to zero. It is faster than forcing a relayout of the listview
447             final View v = mParentView;
448             int widthSpec = MeasureSpec.makeMeasureSpec(v.getWidth(), MeasureSpec.EXACTLY);
449             int heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY);
450             v.measure(widthSpec, heightSpec);
451             v.getLayoutParams().height = 0;
452             v.layout(v.getLeft(), v.getTop(), v.getRight(), v.getTop());
453             v.invalidate();
454         }
455     }
456 
457     private final ChooserHandler mChooserHandler = new ChooserHandler();
458 
459     private class ChooserHandler extends Handler {
460         private static final int CHOOSER_TARGET_SERVICE_RESULT = 1;
461         private static final int CHOOSER_TARGET_SERVICE_WATCHDOG_MIN_TIMEOUT = 2;
462         private static final int CHOOSER_TARGET_SERVICE_WATCHDOG_MAX_TIMEOUT = 3;
463         private static final int SHORTCUT_MANAGER_SHARE_TARGET_RESULT = 4;
464         private static final int SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED = 5;
465         private static final int LIST_VIEW_UPDATE_MESSAGE = 6;
466 
467         private static final int WATCHDOG_TIMEOUT_MAX_MILLIS = 1000;
468         private static final int WATCHDOG_TIMEOUT_MIN_MILLIS = 300;
469 
470         private boolean mMinTimeoutPassed = false;
471 
removeAllMessages()472         private void removeAllMessages() {
473             removeMessages(LIST_VIEW_UPDATE_MESSAGE);
474             removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_MIN_TIMEOUT);
475             removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_MAX_TIMEOUT);
476             removeMessages(CHOOSER_TARGET_SERVICE_RESULT);
477             removeMessages(SHORTCUT_MANAGER_SHARE_TARGET_RESULT);
478             removeMessages(SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED);
479         }
480 
restartServiceRequestTimer()481         private void restartServiceRequestTimer() {
482             mMinTimeoutPassed = false;
483             removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_MIN_TIMEOUT);
484             removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_MAX_TIMEOUT);
485 
486             if (DEBUG) {
487                 Log.d(TAG, "queryTargets setting watchdog timer for "
488                         + WATCHDOG_TIMEOUT_MAX_MILLIS + "ms");
489             }
490 
491             sendEmptyMessageDelayed(CHOOSER_TARGET_SERVICE_WATCHDOG_MIN_TIMEOUT,
492                     WATCHDOG_TIMEOUT_MIN_MILLIS);
493             sendEmptyMessageDelayed(CHOOSER_TARGET_SERVICE_WATCHDOG_MAX_TIMEOUT,
494                     WATCHDOG_TIMEOUT_MAX_MILLIS);
495         }
496 
maybeStopServiceRequestTimer()497         private void maybeStopServiceRequestTimer() {
498             // Set a minimum timeout threshold, to ensure both apis, sharing shortcuts
499             // and older-style direct share services, have had time to load, otherwise
500             // just checking mServiceConnections could force us to end prematurely
501             if (mMinTimeoutPassed && mServiceConnections.isEmpty()) {
502                 logDirectShareTargetReceived(
503                         MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_CHOOSER_SERVICE);
504                 sendVoiceChoicesIfNeeded();
505                 mChooserMultiProfilePagerAdapter.getActiveListAdapter()
506                         .completeServiceTargetLoading();
507             }
508         }
509 
510         @Override
handleMessage(Message msg)511         public void handleMessage(Message msg) {
512             if (mChooserMultiProfilePagerAdapter.getActiveListAdapter() == null || isDestroyed()) {
513                 return;
514             }
515 
516             switch (msg.what) {
517                 case CHOOSER_TARGET_SERVICE_RESULT:
518                     if (DEBUG) Log.d(TAG, "CHOOSER_TARGET_SERVICE_RESULT");
519                     final ServiceResultInfo sri = (ServiceResultInfo) msg.obj;
520                     if (!mServiceConnections.contains(sri.connection)) {
521                         Log.w(TAG, "ChooserTargetServiceConnection " + sri.connection
522                                 + sri.originalTarget.getResolveInfo().activityInfo.packageName
523                                 + " returned after being removed from active connections."
524                                 + " Have you considered returning results faster?");
525                         break;
526                     }
527                     if (sri.resultTargets != null) {
528                         ChooserListAdapter adapterForUserHandle =
529                                 mChooserMultiProfilePagerAdapter.getListAdapterForUserHandle(
530                                         sri.userHandle);
531                         if (adapterForUserHandle != null) {
532                             adapterForUserHandle.addServiceResults(sri.originalTarget,
533                                     sri.resultTargets, TARGET_TYPE_CHOOSER_TARGET,
534                                     /* directShareShortcutInfoCache */ null, mServiceConnections);
535                             if (!sri.resultTargets.isEmpty() && sri.originalTarget != null) {
536                                 mChooserTargetComponentNameCache.put(
537                                         sri.resultTargets.get(0).getComponentName(),
538                                         sri.originalTarget.getResolvedComponentName());
539                             }
540                         }
541                     }
542                     unbindService(sri.connection);
543                     sri.connection.destroy();
544                     mServiceConnections.remove(sri.connection);
545                     maybeStopServiceRequestTimer();
546                     break;
547 
548                 case CHOOSER_TARGET_SERVICE_WATCHDOG_MIN_TIMEOUT:
549                     mMinTimeoutPassed = true;
550                     maybeStopServiceRequestTimer();
551                     break;
552 
553                 case CHOOSER_TARGET_SERVICE_WATCHDOG_MAX_TIMEOUT:
554                     mMinTimeoutPassed = true;
555                     if (!mServiceConnections.isEmpty()) {
556                         getChooserActivityLogger().logSharesheetDirectLoadTimeout();
557                     }
558                     unbindRemainingServices();
559                     maybeStopServiceRequestTimer();
560                     break;
561 
562                 case LIST_VIEW_UPDATE_MESSAGE:
563                     if (DEBUG) {
564                         Log.d(TAG, "LIST_VIEW_UPDATE_MESSAGE; ");
565                     }
566 
567                     UserHandle userHandle = (UserHandle) msg.obj;
568                     mChooserMultiProfilePagerAdapter.getListAdapterForUserHandle(userHandle)
569                             .refreshListView();
570                     break;
571 
572                 case SHORTCUT_MANAGER_SHARE_TARGET_RESULT:
573                     if (DEBUG) Log.d(TAG, "SHORTCUT_MANAGER_SHARE_TARGET_RESULT");
574                     final ServiceResultInfo resultInfo = (ServiceResultInfo) msg.obj;
575                     if (resultInfo.resultTargets != null) {
576                         ChooserListAdapter adapterForUserHandle =
577                                 mChooserMultiProfilePagerAdapter.getListAdapterForUserHandle(
578                                         resultInfo.userHandle);
579                         if (adapterForUserHandle != null) {
580                             adapterForUserHandle.addServiceResults(
581                                     resultInfo.originalTarget, resultInfo.resultTargets, msg.arg1,
582                                     mDirectShareShortcutInfoCache, mServiceConnections);
583                         }
584                     }
585                     break;
586 
587                 case SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED:
588                     logDirectShareTargetReceived(
589                             MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_SHORTCUT_MANAGER);
590                     sendVoiceChoicesIfNeeded();
591                     getChooserActivityLogger().logSharesheetDirectLoadComplete();
592                     break;
593 
594                 default:
595                     super.handleMessage(msg);
596             }
597         }
598     };
599 
600     @Override
onCreate(Bundle savedInstanceState)601     protected void onCreate(Bundle savedInstanceState) {
602         final long intentReceivedTime = System.currentTimeMillis();
603         getChooserActivityLogger().logSharesheetTriggered();
604         // This is the only place this value is being set. Effectively final.
605         mIsAppPredictorComponentAvailable = isAppPredictionServiceAvailable();
606 
607         mIsSuccessfullySelected = false;
608         Intent intent = getIntent();
609         Parcelable targetParcelable = intent.getParcelableExtra(Intent.EXTRA_INTENT);
610         if (targetParcelable instanceof Uri) {
611             try {
612                 targetParcelable = Intent.parseUri(targetParcelable.toString(),
613                         Intent.URI_INTENT_SCHEME);
614             } catch (URISyntaxException ex) {
615                 // doesn't parse as an intent; let the next test fail and error out
616             }
617         }
618 
619         if (!(targetParcelable instanceof Intent)) {
620             Log.w("ChooserActivity", "Target is not an intent: " + targetParcelable);
621             finish();
622             super.onCreate(null);
623             return;
624         }
625         Intent target = (Intent) targetParcelable;
626         if (target != null) {
627             modifyTargetIntent(target);
628         }
629         Parcelable[] targetsParcelable
630                 = intent.getParcelableArrayExtra(Intent.EXTRA_ALTERNATE_INTENTS);
631         if (targetsParcelable != null) {
632             final boolean offset = target == null;
633             Intent[] additionalTargets =
634                     new Intent[offset ? targetsParcelable.length - 1 : targetsParcelable.length];
635             for (int i = 0; i < targetsParcelable.length; i++) {
636                 if (!(targetsParcelable[i] instanceof Intent)) {
637                     Log.w(TAG, "EXTRA_ALTERNATE_INTENTS array entry #" + i + " is not an Intent: "
638                             + targetsParcelable[i]);
639                     finish();
640                     super.onCreate(null);
641                     return;
642                 }
643                 final Intent additionalTarget = (Intent) targetsParcelable[i];
644                 if (i == 0 && target == null) {
645                     target = additionalTarget;
646                     modifyTargetIntent(target);
647                 } else {
648                     additionalTargets[offset ? i - 1 : i] = additionalTarget;
649                     modifyTargetIntent(additionalTarget);
650                 }
651             }
652             setAdditionalTargets(additionalTargets);
653         }
654 
655         mReplacementExtras = intent.getBundleExtra(Intent.EXTRA_REPLACEMENT_EXTRAS);
656 
657         // Do not allow the title to be changed when sharing content
658         CharSequence title = null;
659         if (target != null) {
660             if (!isSendAction(target)) {
661                 title = intent.getCharSequenceExtra(Intent.EXTRA_TITLE);
662             } else {
663                 Log.w(TAG, "Ignoring intent's EXTRA_TITLE, deprecated in P. You may wish to set a"
664                         + " preview title by using EXTRA_TITLE property of the wrapped"
665                         + " EXTRA_INTENT.");
666             }
667         }
668 
669         int defaultTitleRes = 0;
670         if (title == null) {
671             defaultTitleRes = com.android.internal.R.string.chooseActivity;
672         }
673 
674         Parcelable[] pa = intent.getParcelableArrayExtra(Intent.EXTRA_INITIAL_INTENTS);
675         Intent[] initialIntents = null;
676         if (pa != null) {
677             int count = Math.min(pa.length, MAX_EXTRA_INITIAL_INTENTS);
678             initialIntents = new Intent[count];
679             for (int i = 0; i < count; i++) {
680                 if (!(pa[i] instanceof Intent)) {
681                     Log.w(TAG, "Initial intent #" + i + " not an Intent: " + pa[i]);
682                     finish();
683                     super.onCreate(null);
684                     return;
685                 }
686                 final Intent in = (Intent) pa[i];
687                 modifyTargetIntent(in);
688                 initialIntents[i] = in;
689             }
690         }
691 
692         mReferrerFillInIntent = new Intent().putExtra(Intent.EXTRA_REFERRER, getReferrer());
693 
694         mChosenComponentSender = intent.getParcelableExtra(
695                 Intent.EXTRA_CHOSEN_COMPONENT_INTENT_SENDER);
696         mRefinementIntentSender = intent.getParcelableExtra(
697                 Intent.EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER);
698         setSafeForwardingMode(true);
699 
700         mPinnedSharedPrefs = getPinnedSharedPrefs(this);
701 
702         pa = intent.getParcelableArrayExtra(Intent.EXTRA_EXCLUDE_COMPONENTS);
703 
704 
705         // Exclude out Nearby from main list if chip is present, to avoid duplication
706         ComponentName nearbySharingComponent = getNearbySharingComponent();
707         boolean hasNearby = nearbySharingComponent != null;
708 
709         if (pa != null) {
710             ComponentName[] names = new ComponentName[pa.length + (hasNearby ? 1 : 0)];
711             for (int i = 0; i < pa.length; i++) {
712                 if (!(pa[i] instanceof ComponentName)) {
713                     Log.w(TAG, "Filtered component #" + i + " not a ComponentName: " + pa[i]);
714                     names = null;
715                     break;
716                 }
717                 names[i] = (ComponentName) pa[i];
718             }
719             if (hasNearby) {
720                 names[names.length - 1] = nearbySharingComponent;
721             }
722 
723             mFilteredComponentNames = names;
724         } else if (hasNearby) {
725             mFilteredComponentNames = new ComponentName[1];
726             mFilteredComponentNames[0] = nearbySharingComponent;
727         }
728 
729         pa = intent.getParcelableArrayExtra(Intent.EXTRA_CHOOSER_TARGETS);
730         if (pa != null) {
731             int count = Math.min(pa.length, MAX_EXTRA_CHOOSER_TARGETS);
732             ChooserTarget[] targets = new ChooserTarget[count];
733             for (int i = 0; i < count; i++) {
734                 if (!(pa[i] instanceof ChooserTarget)) {
735                     Log.w(TAG, "Chooser target #" + i + " not a ChooserTarget: " + pa[i]);
736                     targets = null;
737                     break;
738                 }
739                 targets[i] = (ChooserTarget) pa[i];
740             }
741             mCallerChooserTargets = targets;
742         }
743 
744         mShouldDisplayLandscape = shouldDisplayLandscape(
745                 getResources().getConfiguration().orientation);
746         setRetainInOnStop(intent.getBooleanExtra(EXTRA_PRIVATE_RETAIN_IN_ON_STOP, false));
747         super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents,
748                 null, false);
749 
750         mChooserShownTime = System.currentTimeMillis();
751         final long systemCost = mChooserShownTime - intentReceivedTime;
752 
753         getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN)
754                 .setSubtype(isWorkProfile() ? MetricsEvent.MANAGED_PROFILE :
755                         MetricsEvent.PARENT_PROFILE)
756                 .addTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE, target.getType())
757                 .addTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS, systemCost));
758 
759         mChooserRowServiceSpacing = getResources()
760                                         .getDimensionPixelSize(R.dimen.chooser_service_spacing);
761 
762         if (mResolverDrawerLayout != null) {
763             mResolverDrawerLayout.addOnLayoutChangeListener(this::handleLayoutChange);
764 
765             // expand/shrink direct share 4 -> 8 viewgroup
766             if (isSendAction(target)) {
767                 mResolverDrawerLayout.setOnScrollChangeListener(this::handleScroll);
768             }
769 
770             mResolverDrawerLayout.setOnCollapsedChangedListener(
771                     new ResolverDrawerLayout.OnCollapsedChangedListener() {
772 
773                         // Only consider one expansion per activity creation
774                         private boolean mWrittenOnce = false;
775 
776                         @Override
777                         public void onCollapsedChanged(boolean isCollapsed) {
778                             if (!isCollapsed && !mWrittenOnce) {
779                                 incrementNumSheetExpansions();
780                                 mWrittenOnce = true;
781                             }
782                             getChooserActivityLogger()
783                                     .logSharesheetExpansionChanged(isCollapsed);
784                         }
785                     });
786         }
787 
788         if (DEBUG) {
789             Log.d(TAG, "System Time Cost is " + systemCost);
790         }
791 
792         getChooserActivityLogger().logShareStarted(
793                 FrameworkStatsLog.SHARESHEET_STARTED,
794                 getReferrerPackageName(),
795                 target.getType(),
796                 mCallerChooserTargets == null ? 0 : mCallerChooserTargets.length,
797                 initialIntents == null ? 0 : initialIntents.length,
798                 isWorkProfile(),
799                 findPreferredContentPreview(getTargetIntent(), getContentResolver()),
800                 target.getAction()
801         );
802         mDirectShareShortcutInfoCache = new HashMap<>();
803         mChooserTargetComponentNameCache = new HashMap<>();
804 
805         setEnterSharedElementCallback(new SharedElementCallback() {
806             @Override
807             public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
808                 if (mRemoveSharedElements) {
809                     names.remove(FIRST_IMAGE_PREVIEW_TRANSITION_NAME);
810                     sharedElements.remove(FIRST_IMAGE_PREVIEW_TRANSITION_NAME);
811                 }
812                 super.onMapSharedElements(names, sharedElements);
813                 mRemoveSharedElements = false;
814             }
815         });
816         postponeEnterTransition();
817     }
818 
819     @Override
appliedThemeResId()820     protected int appliedThemeResId() {
821         return R.style.Theme_DeviceDefault_Chooser;
822     }
823 
setupAppPredictorForUser(UserHandle userHandle, AppPredictor.Callback appPredictorCallback)824     private AppPredictor setupAppPredictorForUser(UserHandle userHandle,
825             AppPredictor.Callback appPredictorCallback) {
826         AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled(userHandle);
827         if (appPredictor == null) {
828             return null;
829         }
830         mDirectShareAppTargetCache = new HashMap<>();
831         appPredictor.registerPredictionUpdates(this.getMainExecutor(), appPredictorCallback);
832         return appPredictor;
833     }
834 
createAppPredictorCallback( ChooserListAdapter chooserListAdapter)835     private AppPredictor.Callback createAppPredictorCallback(
836             ChooserListAdapter chooserListAdapter) {
837         return resultList -> {
838             if (isFinishing() || isDestroyed()) {
839                 return;
840             }
841             if (chooserListAdapter.getCount() == 0) {
842                 return;
843             }
844             if (resultList.isEmpty()
845                     && shouldQueryShortcutManager(chooserListAdapter.getUserHandle())) {
846                 // APS may be disabled, so try querying targets ourselves.
847                 queryDirectShareTargets(chooserListAdapter, true);
848                 return;
849             }
850             final List<ShortcutManager.ShareShortcutInfo> shareShortcutInfos =
851                     new ArrayList<>();
852 
853             List<AppTarget> shortcutResults = new ArrayList<>();
854             for (AppTarget appTarget : resultList) {
855                 if (appTarget.getShortcutInfo() == null) {
856                     continue;
857                 }
858                 shortcutResults.add(appTarget);
859             }
860             resultList = shortcutResults;
861             for (AppTarget appTarget : resultList) {
862                 shareShortcutInfos.add(new ShortcutManager.ShareShortcutInfo(
863                         appTarget.getShortcutInfo(),
864                         new ComponentName(
865                                 appTarget.getPackageName(), appTarget.getClassName())));
866             }
867             sendShareShortcutInfoList(shareShortcutInfos, chooserListAdapter, resultList,
868                     chooserListAdapter.getUserHandle());
869         };
870     }
871 
872     static SharedPreferences getPinnedSharedPrefs(Context context) {
873         // The code below is because in the android:ui process, no one can hear you scream.
874         // The package info in the context isn't initialized in the way it is for normal apps,
875         // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we
876         // build the path manually below using the same policy that appears in ContextImpl.
877         // This fails silently under the hood if there's a problem, so if we find ourselves in
878         // the case where we don't have access to credential encrypted storage we just won't
879         // have our pinned target info.
880         final File prefsFile = new File(new File(
881                 Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL,
882                         context.getUserId(), context.getPackageName()),
883                 "shared_prefs"),
884                 PINNED_SHARED_PREFS_NAME + ".xml");
885         return context.getSharedPreferences(prefsFile, MODE_PRIVATE);
886     }
887 
888     @Override
889     protected AbstractMultiProfilePagerAdapter createMultiProfilePagerAdapter(
890             Intent[] initialIntents,
891             List<ResolveInfo> rList,
892             boolean filterLastUsed) {
893         if (shouldShowTabs()) {
894             mChooserMultiProfilePagerAdapter = createChooserMultiProfilePagerAdapterForTwoProfiles(
895                     initialIntents, rList, filterLastUsed);
896         } else {
897             mChooserMultiProfilePagerAdapter = createChooserMultiProfilePagerAdapterForOneProfile(
898                     initialIntents, rList, filterLastUsed);
899         }
900         return mChooserMultiProfilePagerAdapter;
901     }
902 
903     private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForOneProfile(
904             Intent[] initialIntents,
905             List<ResolveInfo> rList,
906             boolean filterLastUsed) {
907         ChooserGridAdapter adapter = createChooserGridAdapter(
908                 /* context */ this,
909                 /* payloadIntents */ mIntents,
910                 initialIntents,
911                 rList,
912                 filterLastUsed,
913                 /* userHandle */ UserHandle.of(UserHandle.myUserId()));
914         return new ChooserMultiProfilePagerAdapter(
915                 /* context */ this,
916                 adapter,
917                 getPersonalProfileUserHandle(),
918                 /* workProfileUserHandle= */ null,
919                 isSendAction(getTargetIntent()), getMaxTargetsPerRow());
920     }
921 
922     private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForTwoProfiles(
923             Intent[] initialIntents,
924             List<ResolveInfo> rList,
925             boolean filterLastUsed) {
926         int selectedProfile = findSelectedProfile();
927         ChooserGridAdapter personalAdapter = createChooserGridAdapter(
928                 /* context */ this,
929                 /* payloadIntents */ mIntents,
930                 selectedProfile == PROFILE_PERSONAL ? initialIntents : null,
931                 rList,
932                 filterLastUsed,
933                 /* userHandle */ getPersonalProfileUserHandle());
934         ChooserGridAdapter workAdapter = createChooserGridAdapter(
935                 /* context */ this,
936                 /* payloadIntents */ mIntents,
937                 selectedProfile == PROFILE_WORK ? initialIntents : null,
938                 rList,
939                 filterLastUsed,
940                 /* userHandle */ getWorkProfileUserHandle());
941         return new ChooserMultiProfilePagerAdapter(
942                 /* context */ this,
943                 personalAdapter,
944                 workAdapter,
945                 selectedProfile,
946                 getPersonalProfileUserHandle(),
947                 getWorkProfileUserHandle(),
948                 isSendAction(getTargetIntent()), getMaxTargetsPerRow());
949     }
950 
951     private int findSelectedProfile() {
952         int selectedProfile = getSelectedProfileExtra();
953         if (selectedProfile == -1) {
954             selectedProfile = getProfileForUser(getUser());
955         }
956         return selectedProfile;
957     }
958 
959     @Override
960     protected boolean postRebuildList(boolean rebuildCompleted) {
961         updateStickyContentPreview();
962         if (shouldShowStickyContentPreview()
963                 || mChooserMultiProfilePagerAdapter
964                         .getCurrentRootAdapter().getSystemRowCount() != 0) {
965             logActionShareWithPreview();
966         }
967         return postRebuildListInternal(rebuildCompleted);
968     }
969 
970     /**
971      * Returns true if app prediction service is defined and the component exists on device.
972      */
973     @VisibleForTesting
974     public boolean isAppPredictionServiceAvailable() {
975         if (getPackageManager().getAppPredictionServicePackageName() == null) {
976             // Default AppPredictionService is not defined.
977             return false;
978         }
979 
980         final String appPredictionServiceName =
981                 getString(R.string.config_defaultAppPredictionService);
982         if (appPredictionServiceName == null) {
983             return false;
984         }
985         final ComponentName appPredictionComponentName =
986                 ComponentName.unflattenFromString(appPredictionServiceName);
987         if (appPredictionComponentName == null) {
988             return false;
989         }
990 
991         // Check if the app prediction component actually exists on the device.
992         Intent intent = new Intent();
993         intent.setComponent(appPredictionComponentName);
994         if (getPackageManager().resolveService(intent, PackageManager.MATCH_ALL) == null) {
995             Log.e(TAG, "App prediction service is defined, but does not exist: "
996                     + appPredictionServiceName);
997             return false;
998         }
999         return true;
1000     }
1001 
1002     /**
1003      * Check if the profile currently used is a work profile.
1004      * @return true if it is work profile, false if it is parent profile (or no work profile is
1005      * set up)
1006      */
1007     protected boolean isWorkProfile() {
1008         return getSystemService(UserManager.class)
1009                 .getUserInfo(UserHandle.myUserId()).isManagedProfile();
1010     }
1011 
1012     @Override
1013     protected PackageMonitor createPackageMonitor(ResolverListAdapter listAdapter) {
1014         return new PackageMonitor() {
1015             @Override
1016             public void onSomePackagesChanged() {
1017                 handlePackagesChanged(listAdapter);
1018             }
1019         };
1020     }
1021 
1022     /**
1023      * Update UI to reflect changes in data.
1024      */
1025     public void handlePackagesChanged() {
1026         handlePackagesChanged(/* listAdapter */ null);
1027     }
1028 
1029     /**
1030      * Update UI to reflect changes in data.
1031      * <p>If {@code listAdapter} is {@code null}, both profile list adapters are updated if
1032      * available.
1033      */
1034     private void handlePackagesChanged(@Nullable ResolverListAdapter listAdapter) {
1035         // Refresh pinned items
1036         mPinnedSharedPrefs = getPinnedSharedPrefs(this);
1037         if (listAdapter == null) {
1038             mChooserMultiProfilePagerAdapter.getActiveListAdapter().handlePackagesChanged();
1039             if (mChooserMultiProfilePagerAdapter.getCount() > 1) {
1040                 mChooserMultiProfilePagerAdapter.getInactiveListAdapter().handlePackagesChanged();
1041             }
1042         } else {
1043             listAdapter.handlePackagesChanged();
1044         }
1045         updateProfileViewButton();
1046     }
1047 
1048     private void onCopyButtonClicked(View v) {
1049         Intent targetIntent = getTargetIntent();
1050         if (targetIntent == null) {
1051             finish();
1052         } else {
1053             final String action = targetIntent.getAction();
1054 
1055             ClipData clipData = null;
1056             if (Intent.ACTION_SEND.equals(action)) {
1057                 String extraText = targetIntent.getStringExtra(Intent.EXTRA_TEXT);
1058                 Uri extraStream = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM);
1059 
1060                 if (extraText != null) {
1061                     clipData = ClipData.newPlainText(null, extraText);
1062                 } else if (extraStream != null) {
1063                     clipData = ClipData.newUri(getContentResolver(), null, extraStream);
1064                 } else {
1065                     Log.w(TAG, "No data available to copy to clipboard");
1066                     return;
1067                 }
1068             } else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
1069                 final ArrayList<Uri> streams = targetIntent.getParcelableArrayListExtra(
1070                         Intent.EXTRA_STREAM);
1071                 clipData = ClipData.newUri(getContentResolver(), null, streams.get(0));
1072                 for (int i = 1; i < streams.size(); i++) {
1073                     clipData.addItem(getContentResolver(), new ClipData.Item(streams.get(i)));
1074                 }
1075             } else {
1076                 // expected to only be visible with ACTION_SEND or ACTION_SEND_MULTIPLE
1077                 // so warn about unexpected action
1078                 Log.w(TAG, "Action (" + action + ") not supported for copying to clipboard");
1079                 return;
1080             }
1081 
1082             ClipboardManager clipboardManager = (ClipboardManager) getSystemService(
1083                     Context.CLIPBOARD_SERVICE);
1084             clipboardManager.setPrimaryClipAsPackage(clipData, getReferrerPackageName());
1085             Toast.makeText(getApplicationContext(), R.string.copied, Toast.LENGTH_SHORT).show();
1086 
1087             // Log share completion via copy
1088             LogMaker targetLogMaker = new LogMaker(
1089                     MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SYSTEM_TARGET).setSubtype(1);
1090             getMetricsLogger().write(targetLogMaker);
1091             getChooserActivityLogger().logShareTargetSelected(
1092                     SELECTION_TYPE_COPY,
1093                     "",
1094                     -1);
1095 
1096             setResult(RESULT_OK);
1097             finish();
1098         }
1099     }
1100 
1101     @Override
1102     public void onConfigurationChanged(Configuration newConfig) {
1103         super.onConfigurationChanged(newConfig);
1104         ViewPager viewPager = findViewById(R.id.profile_pager);
1105         if (viewPager.isLayoutRtl()) {
1106             mMultiProfilePagerAdapter.setupViewPager(viewPager);
1107         }
1108 
1109         mShouldDisplayLandscape = shouldDisplayLandscape(newConfig.orientation);
1110         adjustPreviewWidth(newConfig.orientation, null);
1111         updateStickyContentPreview();
1112     }
1113 
1114     private boolean shouldDisplayLandscape(int orientation) {
1115         // Sharesheet fixes the # of items per row and therefore can not correctly lay out
1116         // when in the restricted size of multi-window mode. In the future, would be nice
1117         // to use minimum dp size requirements instead
1118         return orientation == Configuration.ORIENTATION_LANDSCAPE && !isInMultiWindowMode();
1119     }
1120 
1121     private void adjustPreviewWidth(int orientation, View parent) {
1122         int width = -1;
1123         if (mShouldDisplayLandscape) {
1124             width = getResources().getDimensionPixelSize(R.dimen.chooser_preview_width);
1125         }
1126 
1127         parent = parent == null ? getWindow().getDecorView() : parent;
1128 
1129         updateLayoutWidth(R.id.content_preview_text_layout, width, parent);
1130         updateLayoutWidth(R.id.content_preview_title_layout, width, parent);
1131         updateLayoutWidth(R.id.content_preview_file_layout, width, parent);
1132     }
1133 
1134     private void updateLayoutWidth(int layoutResourceId, int width, View parent) {
1135         View view = parent.findViewById(layoutResourceId);
1136         if (view != null && view.getLayoutParams() != null) {
1137             LayoutParams params = view.getLayoutParams();
1138             params.width = width;
1139             view.setLayoutParams(params);
1140         }
1141     }
1142 
1143     private ViewGroup createContentPreviewView(ViewGroup parent) {
1144         Intent targetIntent = getTargetIntent();
1145         int previewType = findPreferredContentPreview(targetIntent, getContentResolver());
1146         return displayContentPreview(previewType, targetIntent, getLayoutInflater(), parent);
1147     }
1148 
1149     @VisibleForTesting
1150     protected ComponentName getNearbySharingComponent() {
1151         String nearbyComponent = Settings.Secure.getString(
1152                 getContentResolver(),
1153                 Settings.Secure.NEARBY_SHARING_COMPONENT);
1154         if (TextUtils.isEmpty(nearbyComponent)) {
1155             nearbyComponent = getString(R.string.config_defaultNearbySharingComponent);
1156         }
1157         if (TextUtils.isEmpty(nearbyComponent)) {
1158             return null;
1159         }
1160         return ComponentName.unflattenFromString(nearbyComponent);
1161     }
1162 
1163     @VisibleForTesting
1164     protected @Nullable ComponentName getEditSharingComponent() {
1165         String editorPackage = getApplicationContext().getString(R.string.config_systemImageEditor);
1166         if (editorPackage == null || TextUtils.isEmpty(editorPackage)) {
1167             return null;
1168         }
1169         return ComponentName.unflattenFromString(editorPackage);
1170     }
1171 
1172     @VisibleForTesting
1173     protected TargetInfo getEditSharingTarget(Intent originalIntent) {
1174         final ComponentName cn = getEditSharingComponent();
1175 
1176         final Intent resolveIntent = new Intent(originalIntent);
1177         resolveIntent.setComponent(cn);
1178         resolveIntent.setAction(Intent.ACTION_EDIT);
1179         final ResolveInfo ri = getPackageManager().resolveActivity(
1180                 resolveIntent, PackageManager.GET_META_DATA);
1181         if (ri == null || ri.activityInfo == null) {
1182             Log.e(TAG, "Device-specified image edit component (" + cn
1183                     + ") not available");
1184             return null;
1185         }
1186 
1187         final DisplayResolveInfo dri = new DisplayResolveInfo(
1188                 originalIntent, ri, getString(R.string.screenshot_edit), "", resolveIntent, null);
1189         dri.setDisplayIcon(getDrawable(R.drawable.ic_screenshot_edit));
1190         return dri;
1191     }
1192 
1193 
1194     @VisibleForTesting
1195     protected TargetInfo getNearbySharingTarget(Intent originalIntent) {
1196         final ComponentName cn = getNearbySharingComponent();
1197         if (cn == null) return null;
1198 
1199         final Intent resolveIntent = new Intent(originalIntent);
1200         resolveIntent.setComponent(cn);
1201         final ResolveInfo ri = getPackageManager().resolveActivity(
1202                 resolveIntent, PackageManager.GET_META_DATA);
1203         if (ri == null || ri.activityInfo == null) {
1204             Log.e(TAG, "Device-specified nearby sharing component (" + cn
1205                     + ") not available");
1206             return null;
1207         }
1208 
1209         // Allow the nearby sharing component to provide a more appropriate icon and label
1210         // for the chip.
1211         CharSequence name = null;
1212         Drawable icon = null;
1213         final Bundle metaData = ri.activityInfo.metaData;
1214         if (metaData != null) {
1215             try {
1216                 final Resources pkgRes = getPackageManager().getResourcesForActivity(cn);
1217                 final int nameResId = metaData.getInt(CHIP_LABEL_METADATA_KEY);
1218                 name = pkgRes.getString(nameResId);
1219                 final int resId = metaData.getInt(CHIP_ICON_METADATA_KEY);
1220                 icon = pkgRes.getDrawable(resId);
1221             } catch (Resources.NotFoundException ex) {
1222             } catch (NameNotFoundException ex) {
1223             }
1224         }
1225         if (TextUtils.isEmpty(name)) {
1226             name = ri.loadLabel(getPackageManager());
1227         }
1228         if (icon == null) {
1229             icon = ri.loadIcon(getPackageManager());
1230         }
1231 
1232         final DisplayResolveInfo dri = new DisplayResolveInfo(
1233                 originalIntent, ri, name, "", resolveIntent, null);
1234         dri.setDisplayIcon(icon);
1235         return dri;
1236     }
1237 
1238     private Button createActionButton(Drawable icon, CharSequence title, View.OnClickListener r) {
1239         Button b = (Button) LayoutInflater.from(this).inflate(R.layout.chooser_action_button, null);
1240         if (icon != null) {
1241             final int size = getResources()
1242                     .getDimensionPixelSize(R.dimen.chooser_action_button_icon_size);
1243             icon.setBounds(0, 0, size, size);
1244             b.setCompoundDrawablesRelative(icon, null, null, null);
1245         }
1246         b.setText(title);
1247         b.setOnClickListener(r);
1248         return b;
1249     }
1250 
1251     private Button createCopyButton() {
1252         final Button b = createActionButton(
1253                 getDrawable(R.drawable.ic_menu_copy_material),
1254                 getString(R.string.copy), this::onCopyButtonClicked);
1255         b.setId(R.id.chooser_copy_button);
1256         return b;
1257     }
1258 
1259     private @Nullable Button createNearbyButton(Intent originalIntent) {
1260         final TargetInfo ti = getNearbySharingTarget(originalIntent);
1261         if (ti == null) return null;
1262 
1263         final Button b = createActionButton(
1264                 ti.getDisplayIcon(this),
1265                 ti.getDisplayLabel(),
1266                 (View unused) -> {
1267                     // Log share completion via nearby
1268                     getChooserActivityLogger().logShareTargetSelected(
1269                             SELECTION_TYPE_NEARBY,
1270                             "",
1271                             -1);
1272                     // Action bar is user-independent, always start as primary
1273                     safelyStartActivityAsUser(ti, getPersonalProfileUserHandle());
1274                     finish();
1275                 }
1276         );
1277         b.setId(R.id.chooser_nearby_button);
1278         return b;
1279     }
1280 
1281     private @Nullable Button createEditButton(Intent originalIntent) {
1282         final TargetInfo ti = getEditSharingTarget(originalIntent);
1283         if (ti == null) return null;
1284 
1285         final Button b = createActionButton(
1286                 ti.getDisplayIcon(this),
1287                 ti.getDisplayLabel(),
1288                 (View unused) -> {
1289                     // Log share completion via edit
1290                     getChooserActivityLogger().logShareTargetSelected(
1291                             SELECTION_TYPE_EDIT,
1292                             "",
1293                             -1);
1294                     // Action bar is user-independent, always start as primary
1295                     safelyStartActivityAsUser(ti, getPersonalProfileUserHandle());
1296                     finish();
1297                 }
1298         );
1299         b.setId(R.id.chooser_edit_button);
1300         return b;
1301     }
1302 
1303     private void addActionButton(ViewGroup parent, Button b) {
1304         if (b == null) return;
1305         final ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(
1306                         LayoutParams.WRAP_CONTENT,
1307                         LayoutParams.WRAP_CONTENT
1308                 );
1309         final int gap = getResources().getDimensionPixelSize(R.dimen.resolver_icon_margin) / 2;
1310         lp.setMarginsRelative(gap, 0, gap, 0);
1311         parent.addView(b, lp);
1312     }
1313 
1314     private ViewGroup displayContentPreview(@ContentPreviewType int previewType,
1315             Intent targetIntent, LayoutInflater layoutInflater, ViewGroup parent) {
1316         ViewGroup layout = null;
1317 
1318         switch (previewType) {
1319             case CONTENT_PREVIEW_TEXT:
1320                 layout = displayTextContentPreview(targetIntent, layoutInflater, parent);
1321                 break;
1322             case CONTENT_PREVIEW_IMAGE:
1323                 layout = displayImageContentPreview(targetIntent, layoutInflater, parent);
1324                 break;
1325             case CONTENT_PREVIEW_FILE:
1326                 layout = displayFileContentPreview(targetIntent, layoutInflater, parent);
1327                 break;
1328             default:
1329                 Log.e(TAG, "Unexpected content preview type: " + previewType);
1330         }
1331 
1332         if (layout != null) {
1333             adjustPreviewWidth(getResources().getConfiguration().orientation, layout);
1334         }
1335 
1336         return layout;
1337     }
1338 
1339     private ViewGroup displayTextContentPreview(Intent targetIntent, LayoutInflater layoutInflater,
1340             ViewGroup parent) {
1341         ViewGroup contentPreviewLayout = (ViewGroup) layoutInflater.inflate(
1342                 R.layout.chooser_grid_preview_text, parent, false);
1343 
1344         final ViewGroup actionRow =
1345                 (ViewGroup) contentPreviewLayout.findViewById(R.id.chooser_action_row);
1346         addActionButton(actionRow, createCopyButton());
1347         addActionButton(actionRow, createNearbyButton(targetIntent));
1348 
1349         CharSequence sharingText = targetIntent.getCharSequenceExtra(Intent.EXTRA_TEXT);
1350         if (sharingText == null) {
1351             contentPreviewLayout.findViewById(R.id.content_preview_text_layout).setVisibility(
1352                     View.GONE);
1353         } else {
1354             TextView textView = contentPreviewLayout.findViewById(R.id.content_preview_text);
1355             textView.setText(sharingText);
1356         }
1357 
1358         String previewTitle = targetIntent.getStringExtra(Intent.EXTRA_TITLE);
1359         if (TextUtils.isEmpty(previewTitle)) {
1360             contentPreviewLayout.findViewById(R.id.content_preview_title_layout).setVisibility(
1361                     View.GONE);
1362         } else {
1363             TextView previewTitleView = contentPreviewLayout.findViewById(
1364                     R.id.content_preview_title);
1365             previewTitleView.setText(previewTitle);
1366 
1367             ClipData previewData = targetIntent.getClipData();
1368             Uri previewThumbnail = null;
1369             if (previewData != null) {
1370                 if (previewData.getItemCount() > 0) {
1371                     ClipData.Item previewDataItem = previewData.getItemAt(0);
1372                     previewThumbnail = previewDataItem.getUri();
1373                 }
1374             }
1375 
1376             ImageView previewThumbnailView = contentPreviewLayout.findViewById(
1377                     R.id.content_preview_thumbnail);
1378             if (previewThumbnail == null) {
1379                 previewThumbnailView.setVisibility(View.GONE);
1380             } else {
1381                 mPreviewCoord = new ContentPreviewCoordinator(contentPreviewLayout, false);
1382                 mPreviewCoord.loadUriIntoView(R.id.content_preview_thumbnail, previewThumbnail, 0);
1383             }
1384         }
1385 
1386         return contentPreviewLayout;
1387     }
1388 
1389     private ViewGroup displayImageContentPreview(Intent targetIntent, LayoutInflater layoutInflater,
1390             ViewGroup parent) {
1391         ViewGroup contentPreviewLayout = (ViewGroup) layoutInflater.inflate(
1392                 R.layout.chooser_grid_preview_image, parent, false);
1393         ViewGroup imagePreview = contentPreviewLayout.findViewById(R.id.content_preview_image_area);
1394 
1395         final ViewGroup actionRow =
1396                 (ViewGroup) contentPreviewLayout.findViewById(R.id.chooser_action_row);
1397         //TODO: addActionButton(actionRow, createCopyButton());
1398         addActionButton(actionRow, createNearbyButton(targetIntent));
1399         addActionButton(actionRow, createEditButton(targetIntent));
1400 
1401         mPreviewCoord = new ContentPreviewCoordinator(contentPreviewLayout, false);
1402 
1403         String action = targetIntent.getAction();
1404         if (Intent.ACTION_SEND.equals(action)) {
1405             Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM);
1406             imagePreview.findViewById(R.id.content_preview_image_1_large)
1407                     .setTransitionName(ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME);
1408             mPreviewCoord.loadUriIntoView(R.id.content_preview_image_1_large, uri, 0);
1409         } else {
1410             ContentResolver resolver = getContentResolver();
1411 
1412             List<Uri> uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
1413             List<Uri> imageUris = new ArrayList<>();
1414             for (Uri uri : uris) {
1415                 if (isImageType(resolver.getType(uri))) {
1416                     imageUris.add(uri);
1417                 }
1418             }
1419 
1420             if (imageUris.size() == 0) {
1421                 Log.i(TAG, "Attempted to display image preview area with zero"
1422                         + " available images detected in EXTRA_STREAM list");
1423                 imagePreview.setVisibility(View.GONE);
1424                 return contentPreviewLayout;
1425             }
1426 
1427             imagePreview.findViewById(R.id.content_preview_image_1_large)
1428                     .setTransitionName(ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME);
1429             mPreviewCoord.loadUriIntoView(R.id.content_preview_image_1_large, imageUris.get(0), 0);
1430 
1431             if (imageUris.size() == 2) {
1432                 mPreviewCoord.loadUriIntoView(R.id.content_preview_image_2_large,
1433                         imageUris.get(1), 0);
1434             } else if (imageUris.size() > 2) {
1435                 mPreviewCoord.loadUriIntoView(R.id.content_preview_image_2_small,
1436                         imageUris.get(1), 0);
1437                 mPreviewCoord.loadUriIntoView(R.id.content_preview_image_3_small,
1438                         imageUris.get(2), imageUris.size() - 3);
1439             }
1440         }
1441 
1442         return contentPreviewLayout;
1443     }
1444 
1445     private static class FileInfo {
1446         public final String name;
1447         public final boolean hasThumbnail;
1448 
1449         FileInfo(String name, boolean hasThumbnail) {
1450             this.name = name;
1451             this.hasThumbnail = hasThumbnail;
1452         }
1453     }
1454 
1455     /**
1456      * Wrapping the ContentResolver call to expose for easier mocking,
1457      * and to avoid mocking Android core classes.
1458      */
1459     @VisibleForTesting
1460     public Cursor queryResolver(ContentResolver resolver, Uri uri) {
1461         return resolver.query(uri, null, null, null, null);
1462     }
1463 
1464     private FileInfo extractFileInfo(Uri uri, ContentResolver resolver) {
1465         String fileName = null;
1466         boolean hasThumbnail = false;
1467 
1468         try (Cursor cursor = queryResolver(resolver, uri)) {
1469             if (cursor != null && cursor.getCount() > 0) {
1470                 int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
1471                 int titleIndex = cursor.getColumnIndex(Downloads.Impl.COLUMN_TITLE);
1472                 int flagsIndex = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_FLAGS);
1473 
1474                 cursor.moveToFirst();
1475                 if (nameIndex != -1) {
1476                     fileName = cursor.getString(nameIndex);
1477                 } else if (titleIndex != -1) {
1478                     fileName = cursor.getString(titleIndex);
1479                 }
1480 
1481                 if (flagsIndex != -1) {
1482                     hasThumbnail = (cursor.getInt(flagsIndex)
1483                             & DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL) != 0;
1484                 }
1485             }
1486         } catch (SecurityException | NullPointerException e) {
1487             logContentPreviewWarning(uri);
1488         }
1489 
1490         if (TextUtils.isEmpty(fileName)) {
1491             fileName = uri.getPath();
1492             int index = fileName.lastIndexOf('/');
1493             if (index != -1) {
1494                 fileName = fileName.substring(index + 1);
1495             }
1496         }
1497 
1498         return new FileInfo(fileName, hasThumbnail);
1499     }
1500 
1501     private void logContentPreviewWarning(Uri uri) {
1502         // The ContentResolver already logs the exception. Log something more informative.
1503         Log.w(TAG, "Could not load (" + uri.toString() + ") thumbnail/name for preview. If "
1504                 + "desired, consider using Intent#createChooser to launch the ChooserActivity, "
1505                 + "and set your Intent's clipData and flags in accordance with that method's "
1506                 + "documentation");
1507     }
1508 
1509     private ViewGroup displayFileContentPreview(Intent targetIntent, LayoutInflater layoutInflater,
1510             ViewGroup parent) {
1511 
1512         ViewGroup contentPreviewLayout = (ViewGroup) layoutInflater.inflate(
1513                 R.layout.chooser_grid_preview_file, parent, false);
1514 
1515         final ViewGroup actionRow =
1516                 (ViewGroup) contentPreviewLayout.findViewById(R.id.chooser_action_row);
1517         //TODO(b/120417119): addActionButton(actionRow, createCopyButton());
1518         addActionButton(actionRow, createNearbyButton(targetIntent));
1519 
1520 
1521         String action = targetIntent.getAction();
1522         if (Intent.ACTION_SEND.equals(action)) {
1523             Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM);
1524             loadFileUriIntoView(uri, contentPreviewLayout);
1525         } else {
1526             List<Uri> uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
1527             int uriCount = uris.size();
1528 
1529             if (uriCount == 0) {
1530                 contentPreviewLayout.setVisibility(View.GONE);
1531                 Log.i(TAG,
1532                         "Appears to be no uris available in EXTRA_STREAM, removing "
1533                                 + "preview area");
1534                 return contentPreviewLayout;
1535             } else if (uriCount == 1) {
1536                 loadFileUriIntoView(uris.get(0), contentPreviewLayout);
1537             } else {
1538                 FileInfo fileInfo = extractFileInfo(uris.get(0), getContentResolver());
1539                 int remUriCount = uriCount - 1;
1540                 String fileName = getResources().getQuantityString(R.plurals.file_count,
1541                         remUriCount, fileInfo.name, remUriCount);
1542 
1543                 TextView fileNameView = contentPreviewLayout.findViewById(
1544                         R.id.content_preview_filename);
1545                 fileNameView.setText(fileName);
1546 
1547                 View thumbnailView = contentPreviewLayout.findViewById(
1548                         R.id.content_preview_file_thumbnail);
1549                 thumbnailView.setVisibility(View.GONE);
1550 
1551                 ImageView fileIconView = contentPreviewLayout.findViewById(
1552                         R.id.content_preview_file_icon);
1553                 fileIconView.setVisibility(View.VISIBLE);
1554                 fileIconView.setImageResource(R.drawable.ic_file_copy);
1555             }
1556         }
1557 
1558         return contentPreviewLayout;
1559     }
1560 
1561     private void loadFileUriIntoView(final Uri uri, final View parent) {
1562         FileInfo fileInfo = extractFileInfo(uri, getContentResolver());
1563 
1564         TextView fileNameView = parent.findViewById(R.id.content_preview_filename);
1565         fileNameView.setText(fileInfo.name);
1566 
1567         if (fileInfo.hasThumbnail) {
1568             mPreviewCoord = new ContentPreviewCoordinator(parent, false);
1569             mPreviewCoord.loadUriIntoView(R.id.content_preview_file_thumbnail, uri, 0);
1570         } else {
1571             View thumbnailView = parent.findViewById(R.id.content_preview_file_thumbnail);
1572             thumbnailView.setVisibility(View.GONE);
1573 
1574             ImageView fileIconView = parent.findViewById(R.id.content_preview_file_icon);
1575             fileIconView.setVisibility(View.VISIBLE);
1576             fileIconView.setImageResource(R.drawable.chooser_file_generic);
1577         }
1578     }
1579 
1580     @VisibleForTesting
1581     protected boolean isImageType(String mimeType) {
1582         return mimeType != null && mimeType.startsWith("image/");
1583     }
1584 
1585     @ContentPreviewType
1586     private int findPreferredContentPreview(Uri uri, ContentResolver resolver) {
1587         if (uri == null) {
1588             return CONTENT_PREVIEW_TEXT;
1589         }
1590 
1591         String mimeType = resolver.getType(uri);
1592         return isImageType(mimeType) ? CONTENT_PREVIEW_IMAGE : CONTENT_PREVIEW_FILE;
1593     }
1594 
1595     /**
1596      * In {@link android.content.Intent#getType}, the app may specify a very general
1597      * mime-type that broadly covers all data being shared, such as {@literal *}/*
1598      * when sending an image and text. We therefore should inspect each item for the
1599      * the preferred type, in order of IMAGE, FILE, TEXT.
1600      */
1601     @ContentPreviewType
1602     private int findPreferredContentPreview(Intent targetIntent, ContentResolver resolver) {
1603         String action = targetIntent.getAction();
1604         if (Intent.ACTION_SEND.equals(action)) {
1605             Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM);
1606             return findPreferredContentPreview(uri, resolver);
1607         } else if (Intent.ACTION_SEND_MULTIPLE.equals(action)) {
1608             List<Uri> uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
1609             if (uris == null || uris.isEmpty()) {
1610                 return CONTENT_PREVIEW_TEXT;
1611             }
1612 
1613             for (Uri uri : uris) {
1614                 // Defaulting to file preview when there are mixed image/file types is
1615                 // preferable, as it shows the user the correct number of items being shared
1616                 if (findPreferredContentPreview(uri, resolver) == CONTENT_PREVIEW_FILE) {
1617                     return CONTENT_PREVIEW_FILE;
1618                 }
1619             }
1620 
1621             return CONTENT_PREVIEW_IMAGE;
1622         }
1623 
1624         return CONTENT_PREVIEW_TEXT;
1625     }
1626 
1627     private int getNumSheetExpansions() {
1628         return getPreferences(Context.MODE_PRIVATE).getInt(PREF_NUM_SHEET_EXPANSIONS, 0);
1629     }
1630 
1631     private void incrementNumSheetExpansions() {
1632         getPreferences(Context.MODE_PRIVATE).edit().putInt(PREF_NUM_SHEET_EXPANSIONS,
1633                 getNumSheetExpansions() + 1).apply();
1634     }
1635 
1636     @Override
1637     protected void onDestroy() {
1638         super.onDestroy();
1639         if (mRefinementResultReceiver != null) {
1640             mRefinementResultReceiver.destroy();
1641             mRefinementResultReceiver = null;
1642         }
1643         unbindRemainingServices();
1644         mChooserHandler.removeAllMessages();
1645 
1646         if (mPreviewCoord != null) mPreviewCoord.cancelLoads();
1647 
1648         mChooserMultiProfilePagerAdapter.getActiveListAdapter().destroyAppPredictor();
1649         if (mChooserMultiProfilePagerAdapter.getInactiveListAdapter() != null) {
1650             mChooserMultiProfilePagerAdapter.getInactiveListAdapter().destroyAppPredictor();
1651         }
1652     }
1653 
1654     @Override // ResolverListCommunicator
1655     public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) {
1656         Intent result = defIntent;
1657         if (mReplacementExtras != null) {
1658             final Bundle replExtras = mReplacementExtras.getBundle(aInfo.packageName);
1659             if (replExtras != null) {
1660                 result = new Intent(defIntent);
1661                 result.putExtras(replExtras);
1662             }
1663         }
1664         if (aInfo.name.equals(IntentForwarderActivity.FORWARD_INTENT_TO_PARENT)
1665                 || aInfo.name.equals(IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE)) {
1666             result = Intent.createChooser(result,
1667                     getIntent().getCharSequenceExtra(Intent.EXTRA_TITLE));
1668 
1669             // Don't auto-launch single intents if the intent is being forwarded. This is done
1670             // because automatically launching a resolving application as a response to the user
1671             // action of switching accounts is pretty unexpected.
1672             result.putExtra(Intent.EXTRA_AUTO_LAUNCH_SINGLE_CHOICE, false);
1673         }
1674         return result;
1675     }
1676 
1677     @Override
1678     public void onActivityStarted(TargetInfo cti) {
1679         if (mChosenComponentSender != null) {
1680             final ComponentName target = cti.getResolvedComponentName();
1681             if (target != null) {
1682                 final Intent fillIn = new Intent().putExtra(Intent.EXTRA_CHOSEN_COMPONENT, target);
1683                 try {
1684                     mChosenComponentSender.sendIntent(this, Activity.RESULT_OK, fillIn, null, null);
1685                 } catch (IntentSender.SendIntentException e) {
1686                     Slog.e(TAG, "Unable to launch supplied IntentSender to report "
1687                             + "the chosen component: " + e);
1688                 }
1689             }
1690         }
1691     }
1692 
1693     @Override
1694     public void addUseDifferentAppLabelIfNecessary(ResolverListAdapter adapter) {
1695         if (mCallerChooserTargets != null && mCallerChooserTargets.length > 0) {
1696             mChooserMultiProfilePagerAdapter.getActiveListAdapter().addServiceResults(
1697                     /* origTarget */ null,
1698                     Lists.newArrayList(mCallerChooserTargets),
1699                     TARGET_TYPE_DEFAULT,
1700                     /* directShareShortcutInfoCache */ null, mServiceConnections);
1701         }
1702     }
1703 
1704     @Override
1705     public int getLayoutResource() {
1706         return R.layout.chooser_grid;
1707     }
1708 
1709     @Override // ResolverListCommunicator
1710     public boolean shouldGetActivityMetadata() {
1711         return true;
1712     }
1713 
1714     @Override
1715     public boolean shouldAutoLaunchSingleChoice(TargetInfo target) {
1716         // Note that this is only safe because the Intent handled by the ChooserActivity is
1717         // guaranteed to contain no extras unknown to the local ClassLoader. That is why this
1718         // method can not be replaced in the ResolverActivity whole hog.
1719         if (!super.shouldAutoLaunchSingleChoice(target)) {
1720             return false;
1721         }
1722 
1723         return getIntent().getBooleanExtra(Intent.EXTRA_AUTO_LAUNCH_SINGLE_CHOICE, true);
1724     }
1725 
1726     private void showTargetDetails(DisplayResolveInfo ti) {
1727         if (ti == null) return;
1728 
1729         ArrayList<DisplayResolveInfo> targetList;
1730 
1731         // For multiple targets, include info on all targets
1732         if (ti instanceof MultiDisplayResolveInfo) {
1733             MultiDisplayResolveInfo mti = (MultiDisplayResolveInfo) ti;
1734             targetList = mti.getTargets();
1735         } else {
1736             targetList = new ArrayList<DisplayResolveInfo>();
1737             targetList.add(ti);
1738         }
1739 
1740         ChooserTargetActionsDialogFragment f = new ChooserTargetActionsDialogFragment();
1741         Bundle b = new Bundle();
1742         b.putParcelable(ChooserTargetActionsDialogFragment.USER_HANDLE_KEY,
1743                 mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
1744         b.putParcelableArrayList(ChooserTargetActionsDialogFragment.TARGET_INFOS_KEY,
1745                 targetList);
1746         f.setArguments(b);
1747 
1748         f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
1749     }
1750 
1751     private void modifyTargetIntent(Intent in) {
1752         if (isSendAction(in)) {
1753             in.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
1754                     Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
1755         }
1756     }
1757 
1758     @Override
1759     protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) {
1760         if (mRefinementIntentSender != null) {
1761             final Intent fillIn = new Intent();
1762             final List<Intent> sourceIntents = target.getAllSourceIntents();
1763             if (!sourceIntents.isEmpty()) {
1764                 fillIn.putExtra(Intent.EXTRA_INTENT, sourceIntents.get(0));
1765                 if (sourceIntents.size() > 1) {
1766                     final Intent[] alts = new Intent[sourceIntents.size() - 1];
1767                     for (int i = 1, N = sourceIntents.size(); i < N; i++) {
1768                         alts[i - 1] = sourceIntents.get(i);
1769                     }
1770                     fillIn.putExtra(Intent.EXTRA_ALTERNATE_INTENTS, alts);
1771                 }
1772                 if (mRefinementResultReceiver != null) {
1773                     mRefinementResultReceiver.destroy();
1774                 }
1775                 mRefinementResultReceiver = new RefinementResultReceiver(this, target, null);
1776                 fillIn.putExtra(Intent.EXTRA_RESULT_RECEIVER,
1777                         mRefinementResultReceiver);
1778                 try {
1779                     mRefinementIntentSender.sendIntent(this, 0, fillIn, null, null);
1780                     return false;
1781                 } catch (SendIntentException e) {
1782                     Log.e(TAG, "Refinement IntentSender failed to send", e);
1783                 }
1784             }
1785         }
1786         updateModelAndChooserCounts(target);
1787         return super.onTargetSelected(target, alwaysCheck);
1788     }
1789 
1790     @Override
1791     public void startSelected(int which, boolean always, boolean filtered) {
1792         ChooserListAdapter currentListAdapter =
1793                 mChooserMultiProfilePagerAdapter.getActiveListAdapter();
1794         TargetInfo targetInfo = currentListAdapter
1795                 .targetInfoForPosition(which, filtered);
1796         if (targetInfo != null && targetInfo instanceof NotSelectableTargetInfo) {
1797             return;
1798         }
1799 
1800         final long selectionCost = System.currentTimeMillis() - mChooserShownTime;
1801 
1802         if (targetInfo instanceof MultiDisplayResolveInfo) {
1803             MultiDisplayResolveInfo mti = (MultiDisplayResolveInfo) targetInfo;
1804             if (!mti.hasSelected()) {
1805                 ChooserStackedAppDialogFragment f = new ChooserStackedAppDialogFragment();
1806                 Bundle b = new Bundle();
1807                 b.putParcelable(ChooserTargetActionsDialogFragment.USER_HANDLE_KEY,
1808                         mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
1809                 b.putObject(ChooserStackedAppDialogFragment.MULTI_DRI_KEY,
1810                         mti);
1811                 b.putInt(ChooserStackedAppDialogFragment.WHICH_KEY, which);
1812                 f.setArguments(b);
1813 
1814                 f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
1815                 return;
1816             }
1817         }
1818 
1819         super.startSelected(which, always, filtered);
1820 
1821 
1822         if (currentListAdapter.getCount() > 0) {
1823             // Log the index of which type of target the user picked.
1824             // Lower values mean the ranking was better.
1825             int cat = 0;
1826             int value = which;
1827             int directTargetAlsoRanked = -1;
1828             int numCallerProvided = 0;
1829             HashedStringCache.HashResult directTargetHashed = null;
1830             switch (currentListAdapter.getPositionTargetType(which)) {
1831                 case ChooserListAdapter.TARGET_SERVICE:
1832                     cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SERVICE_TARGET;
1833                     // Log the package name + target name to answer the question if most users
1834                     // share to mostly the same person or to a bunch of different people.
1835                     ChooserTarget target = currentListAdapter.getChooserTargetForValue(value);
1836                     directTargetHashed = HashedStringCache.getInstance().hashString(
1837                             this,
1838                             TAG,
1839                             target.getComponentName().getPackageName()
1840                                     + target.getTitle().toString(),
1841                             mMaxHashSaltDays);
1842                     directTargetAlsoRanked = getRankedPosition((SelectableTargetInfo) targetInfo);
1843 
1844                     if (mCallerChooserTargets != null) {
1845                         numCallerProvided = mCallerChooserTargets.length;
1846                     }
1847                     getChooserActivityLogger().logShareTargetSelected(
1848                             SELECTION_TYPE_SERVICE,
1849                             targetInfo.getResolveInfo().activityInfo.processName,
1850                             value
1851                     );
1852                     break;
1853                 case ChooserListAdapter.TARGET_CALLER:
1854                 case ChooserListAdapter.TARGET_STANDARD:
1855                     cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_APP_TARGET;
1856                     value -= currentListAdapter.getSurfacedTargetInfo().size();
1857                     numCallerProvided = currentListAdapter.getCallerTargetCount();
1858                     getChooserActivityLogger().logShareTargetSelected(
1859                             SELECTION_TYPE_APP,
1860                             targetInfo.getResolveInfo().activityInfo.processName,
1861                             value
1862                     );
1863                     break;
1864                 case ChooserListAdapter.TARGET_STANDARD_AZ:
1865                     // A-Z targets are unranked standard targets; we use -1 to mark that they
1866                     // are from the alphabetical pool.
1867                     value = -1;
1868                     cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_STANDARD_TARGET;
1869                     getChooserActivityLogger().logShareTargetSelected(
1870                             SELECTION_TYPE_STANDARD,
1871                             targetInfo.getResolveInfo().activityInfo.processName,
1872                             value
1873                     );
1874                     break;
1875             }
1876 
1877             if (cat != 0) {
1878                 LogMaker targetLogMaker = new LogMaker(cat).setSubtype(value);
1879                 if (directTargetHashed != null) {
1880                     targetLogMaker.addTaggedData(
1881                             MetricsEvent.FIELD_HASHED_TARGET_NAME, directTargetHashed.hashedString);
1882                     targetLogMaker.addTaggedData(
1883                                     MetricsEvent.FIELD_HASHED_TARGET_SALT_GEN,
1884                                     directTargetHashed.saltGeneration);
1885                     targetLogMaker.addTaggedData(MetricsEvent.FIELD_RANKED_POSITION,
1886                                     directTargetAlsoRanked);
1887                 }
1888                 targetLogMaker.addTaggedData(MetricsEvent.FIELD_IS_CATEGORY_USED,
1889                         numCallerProvided);
1890                 getMetricsLogger().write(targetLogMaker);
1891             }
1892 
1893             if (mIsSuccessfullySelected) {
1894                 if (DEBUG) {
1895                     Log.d(TAG, "User Selection Time Cost is " + selectionCost);
1896                     Log.d(TAG, "position of selected app/service/caller is " +
1897                             Integer.toString(value));
1898                 }
1899                 MetricsLogger.histogram(null, "user_selection_cost_for_smart_sharing",
1900                         (int) selectionCost);
1901                 MetricsLogger.histogram(null, "app_position_for_smart_sharing", value);
1902             }
1903         }
1904     }
1905 
1906     private int getRankedPosition(SelectableTargetInfo targetInfo) {
1907         String targetPackageName =
1908                 targetInfo.getChooserTarget().getComponentName().getPackageName();
1909         ChooserListAdapter currentListAdapter =
1910                 mChooserMultiProfilePagerAdapter.getActiveListAdapter();
1911         int maxRankedResults = Math.min(currentListAdapter.mDisplayList.size(),
1912                 MAX_LOG_RANK_POSITION);
1913 
1914         for (int i = 0; i < maxRankedResults; i++) {
1915             if (currentListAdapter.mDisplayList.get(i)
1916                     .getResolveInfo().activityInfo.packageName.equals(targetPackageName)) {
1917                 return i;
1918             }
1919         }
1920         return -1;
1921     }
1922 
1923     @Override
1924     protected boolean shouldAddFooterView() {
1925         // To accommodate for window insets
1926         return true;
1927     }
1928 
1929     @Override
1930     protected void applyFooterView(int height) {
1931         int count = mChooserMultiProfilePagerAdapter.getItemCount();
1932 
1933         for (int i = 0; i < count; i++) {
1934             mChooserMultiProfilePagerAdapter.getAdapterForIndex(i).setFooterHeight(height);
1935         }
1936     }
1937 
1938     @VisibleForTesting
1939     protected void queryTargetServices(ChooserListAdapter adapter) {
1940 
1941         mQueriedTargetServicesTimeMs = System.currentTimeMillis();
1942 
1943         Context selectedProfileContext = createContextAsUser(
1944                 adapter.getUserHandle(), 0 /* flags */);
1945         final PackageManager pm = selectedProfileContext.getPackageManager();
1946         ShortcutManager sm = selectedProfileContext.getSystemService(ShortcutManager.class);
1947         int targetsToQuery = 0;
1948 
1949         for (int i = 0, N = adapter.getDisplayResolveInfoCount(); i < N; i++) {
1950             final DisplayResolveInfo dri = adapter.getDisplayResolveInfo(i);
1951             if (adapter.getScore(dri) == 0) {
1952                 // A score of 0 means the app hasn't been used in some time;
1953                 // don't query it as it's not likely to be relevant.
1954                 continue;
1955             }
1956             final ActivityInfo ai = dri.getResolveInfo().activityInfo;
1957             if (sm.hasShareTargets(ai.packageName)) {
1958                 // Share targets will be queried from ShortcutManager
1959                 continue;
1960             }
1961             final Bundle md = ai.metaData;
1962             final String serviceName = md != null ? convertServiceName(ai.packageName,
1963                     md.getString(ChooserTargetService.META_DATA_NAME)) : null;
1964             if (serviceName != null && ChooserFlags.USE_SERVICE_TARGETS_FOR_DIRECT_TARGETS) {
1965                 final ComponentName serviceComponent = new ComponentName(
1966                         ai.packageName, serviceName);
1967 
1968                 UserHandle userHandle = adapter.getUserHandle();
1969                 Pair<ComponentName, UserHandle> requestedItem =
1970                         new Pair<>(serviceComponent, userHandle);
1971                 if (mServicesRequested.contains(requestedItem)) {
1972                     continue;
1973                 }
1974                 mServicesRequested.add(requestedItem);
1975 
1976                 final Intent serviceIntent = new Intent(ChooserTargetService.SERVICE_INTERFACE)
1977                         .setComponent(serviceComponent);
1978 
1979                 if (DEBUG) {
1980                     Log.d(TAG, "queryTargets found target with service " + serviceComponent);
1981                 }
1982 
1983                 try {
1984                     final String perm = pm.getServiceInfo(serviceComponent, 0).permission;
1985                     if (!ChooserTargetService.BIND_PERMISSION.equals(perm)) {
1986                         Log.w(TAG, "ChooserTargetService " + serviceComponent + " does not require"
1987                                 + " permission " + ChooserTargetService.BIND_PERMISSION
1988                                 + " - this service will not be queried for ChooserTargets."
1989                                 + " add android:permission=\""
1990                                 + ChooserTargetService.BIND_PERMISSION + "\""
1991                                 + " to the <service> tag for " + serviceComponent
1992                                 + " in the manifest.");
1993                         continue;
1994                     }
1995                 } catch (NameNotFoundException e) {
1996                     Log.e(TAG, "Could not look up service " + serviceComponent
1997                             + "; component name not found");
1998                     continue;
1999                 }
2000 
2001                 final ChooserTargetServiceConnection conn =
2002                         new ChooserTargetServiceConnection(this, dri,
2003                                 adapter.getUserHandle());
2004 
2005                 // Explicitly specify the user handle instead of calling bindService
2006                 // to avoid the warning from calling from the system process without an explicit
2007                 // user handle
2008                 if (bindServiceAsUser(serviceIntent, conn, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND,
2009                         adapter.getUserHandle())) {
2010                     if (DEBUG) {
2011                         Log.d(TAG, "Binding service connection for target " + dri
2012                                 + " intent " + serviceIntent);
2013                     }
2014                     mServiceConnections.add(conn);
2015                     targetsToQuery++;
2016                 }
2017             }
2018             if (targetsToQuery >= QUERY_TARGET_SERVICE_LIMIT) {
2019                 if (DEBUG) {
2020                     Log.d(TAG, "queryTargets hit query target limit "
2021                             + QUERY_TARGET_SERVICE_LIMIT);
2022                 }
2023                 break;
2024             }
2025         }
2026 
2027         mChooserHandler.restartServiceRequestTimer();
2028     }
2029 
2030     private IntentFilter getTargetIntentFilter() {
2031         try {
2032             final Intent intent = getTargetIntent();
2033             String dataString = intent.getDataString();
2034             if (!TextUtils.isEmpty(dataString)) {
2035                 return new IntentFilter(intent.getAction(), dataString);
2036             }
2037             if (intent.getType() == null) {
2038                 Log.e(TAG, "Failed to get target intent filter: intent data and type are null");
2039                 return null;
2040             }
2041             IntentFilter intentFilter = new IntentFilter(intent.getAction(), intent.getType());
2042             List<Uri> contentUris = new ArrayList<>();
2043             if (Intent.ACTION_SEND.equals(intent.getAction())) {
2044                 Uri uri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM);
2045                 if (uri != null) {
2046                     contentUris.add(uri);
2047                 }
2048             } else {
2049                 List<Uri> uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
2050                 if (uris != null) {
2051                     contentUris.addAll(uris);
2052                 }
2053             }
2054             for (Uri uri : contentUris) {
2055                 intentFilter.addDataScheme(uri.getScheme());
2056                 intentFilter.addDataAuthority(uri.getAuthority(), null);
2057                 intentFilter.addDataPath(uri.getPath(), PatternMatcher.PATTERN_LITERAL);
2058             }
2059             return intentFilter;
2060         } catch (Exception e) {
2061             Log.e(TAG, "Failed to get target intent filter", e);
2062             return null;
2063         }
2064     }
2065 
2066     @VisibleForTesting
2067     protected void queryDirectShareTargets(
2068                 ChooserListAdapter adapter, boolean skipAppPredictionService) {
2069         mQueriedSharingShortcutsTimeMs = System.currentTimeMillis();
2070         UserHandle userHandle = adapter.getUserHandle();
2071         if (!skipAppPredictionService) {
2072             AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled(userHandle);
2073             if (appPredictor != null) {
2074                 appPredictor.requestPredictionUpdate();
2075                 return;
2076             }
2077         }
2078         // Default to just querying ShortcutManager if AppPredictor not present.
2079         final IntentFilter filter = getTargetIntentFilter();
2080         if (filter == null) {
2081             return;
2082         }
2083 
2084         AsyncTask.execute(() -> {
2085             Context selectedProfileContext = createContextAsUser(userHandle, 0 /* flags */);
2086             ShortcutManager sm = (ShortcutManager) selectedProfileContext
2087                     .getSystemService(Context.SHORTCUT_SERVICE);
2088             List<ShortcutManager.ShareShortcutInfo> resultList = sm.getShareTargets(filter);
2089             sendShareShortcutInfoList(resultList, adapter, null, userHandle);
2090         });
2091     }
2092 
2093     /**
2094      * Returns {@code false} if {@code userHandle} is the work profile and it's either
2095      * in quiet mode or not running.
2096      */
2097     private boolean shouldQueryShortcutManager(UserHandle userHandle) {
2098         if (!shouldShowTabs()) {
2099             return true;
2100         }
2101         if (!getWorkProfileUserHandle().equals(userHandle)) {
2102             return true;
2103         }
2104         if (!isUserRunning(userHandle)) {
2105             return false;
2106         }
2107         if (!isUserUnlocked(userHandle)) {
2108             return false;
2109         }
2110         if (isQuietModeEnabled(userHandle)) {
2111             return false;
2112         }
2113         return true;
2114     }
2115 
2116     private void sendShareShortcutInfoList(
2117                 List<ShortcutManager.ShareShortcutInfo> resultList,
2118                 ChooserListAdapter chooserListAdapter,
2119                 @Nullable List<AppTarget> appTargets, UserHandle userHandle) {
2120         if (appTargets != null && appTargets.size() != resultList.size()) {
2121             throw new RuntimeException("resultList and appTargets must have the same size."
2122                     + " resultList.size()=" + resultList.size()
2123                     + " appTargets.size()=" + appTargets.size());
2124         }
2125         Context selectedProfileContext = createContextAsUser(userHandle, 0 /* flags */);
2126         for (int i = resultList.size() - 1; i >= 0; i--) {
2127             final String packageName = resultList.get(i).getTargetComponent().getPackageName();
2128             if (!isPackageEnabled(selectedProfileContext, packageName)) {
2129                 resultList.remove(i);
2130                 if (appTargets != null) {
2131                     appTargets.remove(i);
2132                 }
2133             }
2134         }
2135 
2136         // If |appTargets| is not null, results are from AppPredictionService and already sorted.
2137         final int shortcutType = (appTargets == null ? TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER :
2138                 TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE);
2139 
2140         // Match ShareShortcutInfos with DisplayResolveInfos to be able to use the old code path
2141         // for direct share targets. After ShareSheet is refactored we should use the
2142         // ShareShortcutInfos directly.
2143         boolean resultMessageSent = false;
2144         for (int i = 0; i < chooserListAdapter.getDisplayResolveInfoCount(); i++) {
2145             List<ShortcutManager.ShareShortcutInfo> matchingShortcuts = new ArrayList<>();
2146             for (int j = 0; j < resultList.size(); j++) {
2147                 if (chooserListAdapter.getDisplayResolveInfo(i).getResolvedComponentName().equals(
2148                             resultList.get(j).getTargetComponent())) {
2149                     matchingShortcuts.add(resultList.get(j));
2150                 }
2151             }
2152             if (matchingShortcuts.isEmpty()) {
2153                 continue;
2154             }
2155             List<ChooserTarget> chooserTargets = convertToChooserTarget(
2156                     matchingShortcuts, resultList, appTargets, shortcutType);
2157 
2158 
2159 
2160             final Message msg = Message.obtain();
2161             msg.what = ChooserHandler.SHORTCUT_MANAGER_SHARE_TARGET_RESULT;
2162             msg.obj = new ServiceResultInfo(chooserListAdapter.getDisplayResolveInfo(i),
2163                     chooserTargets, null, userHandle);
2164             msg.arg1 = shortcutType;
2165             mChooserHandler.sendMessage(msg);
2166             resultMessageSent = true;
2167         }
2168 
2169         if (resultMessageSent) {
2170             sendShortcutManagerShareTargetResultCompleted();
2171         }
2172     }
2173 
2174     private void sendShortcutManagerShareTargetResultCompleted() {
2175         final Message msg = Message.obtain();
2176         msg.what = ChooserHandler.SHORTCUT_MANAGER_SHARE_TARGET_RESULT_COMPLETED;
2177         mChooserHandler.sendMessage(msg);
2178     }
2179 
2180     private boolean isPackageEnabled(Context context, String packageName) {
2181         if (TextUtils.isEmpty(packageName)) {
2182             return false;
2183         }
2184         ApplicationInfo appInfo;
2185         try {
2186             appInfo = context.getPackageManager().getApplicationInfo(packageName, 0);
2187         } catch (NameNotFoundException e) {
2188             return false;
2189         }
2190 
2191         if (appInfo != null && appInfo.enabled
2192                 && (appInfo.flags & ApplicationInfo.FLAG_SUSPENDED) == 0) {
2193             return true;
2194         }
2195         return false;
2196     }
2197 
2198     /**
2199      * Converts a list of ShareShortcutInfos to ChooserTargets.
2200      * @param matchingShortcuts List of shortcuts, all from the same package, that match the current
2201      *                         share intent filter.
2202      * @param allShortcuts List of all the shortcuts from all the packages on the device that are
2203      *                    returned for the current sharing action.
2204      * @param allAppTargets List of AppTargets. Null if the results are not from prediction service.
2205      * @param shortcutType One of the values TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER or
2206      *                    TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE
2207      * @return A list of ChooserTargets sorted by score in descending order.
2208      */
2209     @VisibleForTesting
2210     @NonNull
2211     public List<ChooserTarget> convertToChooserTarget(
2212             @NonNull List<ShortcutManager.ShareShortcutInfo> matchingShortcuts,
2213             @NonNull List<ShortcutManager.ShareShortcutInfo> allShortcuts,
2214             @Nullable List<AppTarget> allAppTargets, @ShareTargetType int shortcutType) {
2215         // A set of distinct scores for the matched shortcuts. We use index of a rank in the sorted
2216         // list instead of the actual rank value when converting a rank to a score.
2217         List<Integer> scoreList = new ArrayList<>();
2218         if (shortcutType == TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER) {
2219             for (int i = 0; i < matchingShortcuts.size(); i++) {
2220                 int shortcutRank = matchingShortcuts.get(i).getShortcutInfo().getRank();
2221                 if (!scoreList.contains(shortcutRank)) {
2222                     scoreList.add(shortcutRank);
2223                 }
2224             }
2225             Collections.sort(scoreList);
2226         }
2227 
2228         List<ChooserTarget> chooserTargetList = new ArrayList<>(matchingShortcuts.size());
2229         for (int i = 0; i < matchingShortcuts.size(); i++) {
2230             ShortcutInfo shortcutInfo = matchingShortcuts.get(i).getShortcutInfo();
2231             int indexInAllShortcuts = allShortcuts.indexOf(matchingShortcuts.get(i));
2232 
2233             float score;
2234             if (shortcutType == TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE) {
2235                 // Incoming results are ordered. Create a score based on index in the original list.
2236                 score = Math.max(1.0f - (0.01f * indexInAllShortcuts), 0.0f);
2237             } else {
2238                 // Create a score based on the rank of the shortcut.
2239                 int rankIndex = scoreList.indexOf(shortcutInfo.getRank());
2240                 score = Math.max(1.0f - (0.01f * rankIndex), 0.0f);
2241             }
2242 
2243             Bundle extras = new Bundle();
2244             extras.putString(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId());
2245 
2246             ChooserTarget chooserTarget = new ChooserTarget(
2247                     shortcutInfo.getLabel(),
2248                     null, // Icon will be loaded later if this target is selected to be shown.
2249                     score, matchingShortcuts.get(i).getTargetComponent().clone(), extras);
2250 
2251             chooserTargetList.add(chooserTarget);
2252             if (mDirectShareAppTargetCache != null && allAppTargets != null) {
2253                 mDirectShareAppTargetCache.put(chooserTarget,
2254                         allAppTargets.get(indexInAllShortcuts));
2255             }
2256             if (mDirectShareShortcutInfoCache != null) {
2257                 mDirectShareShortcutInfoCache.put(chooserTarget, shortcutInfo);
2258             }
2259         }
2260         // Sort ChooserTargets by score in descending order
2261         Comparator<ChooserTarget> byScore =
2262                 (ChooserTarget a, ChooserTarget b) -> -Float.compare(a.getScore(), b.getScore());
2263         Collections.sort(chooserTargetList, byScore);
2264         return chooserTargetList;
2265     }
2266 
2267     private String convertServiceName(String packageName, String serviceName) {
2268         if (TextUtils.isEmpty(serviceName)) {
2269             return null;
2270         }
2271 
2272         final String fullName;
2273         if (serviceName.startsWith(".")) {
2274             // Relative to the app package. Prepend the app package name.
2275             fullName = packageName + serviceName;
2276         } else if (serviceName.indexOf('.') >= 0) {
2277             // Fully qualified package name.
2278             fullName = serviceName;
2279         } else {
2280             fullName = null;
2281         }
2282         return fullName;
2283     }
2284 
2285     void unbindRemainingServices() {
2286         if (DEBUG) {
2287             Log.d(TAG, "unbindRemainingServices, " + mServiceConnections.size() + " left");
2288         }
2289         for (int i = 0, N = mServiceConnections.size(); i < N; i++) {
2290             final ChooserTargetServiceConnection conn = mServiceConnections.get(i);
2291             if (DEBUG) Log.d(TAG, "unbinding " + conn);
2292             unbindService(conn);
2293             conn.destroy();
2294         }
2295         mServicesRequested.clear();
2296         mServiceConnections.clear();
2297     }
2298 
2299     private void logDirectShareTargetReceived(int logCategory) {
2300         final long queryTime =
2301                 logCategory == MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_SHORTCUT_MANAGER
2302                         ? mQueriedSharingShortcutsTimeMs : mQueriedTargetServicesTimeMs;
2303         final int apiLatency = (int) (System.currentTimeMillis() - queryTime);
2304         getMetricsLogger().write(new LogMaker(logCategory).setSubtype(apiLatency));
2305     }
2306 
2307     void updateModelAndChooserCounts(TargetInfo info) {
2308         if (info != null && info instanceof MultiDisplayResolveInfo) {
2309             info = ((MultiDisplayResolveInfo) info).getSelectedTarget();
2310         }
2311         if (info != null) {
2312             sendClickToAppPredictor(info);
2313             final ResolveInfo ri = info.getResolveInfo();
2314             Intent targetIntent = getTargetIntent();
2315             if (ri != null && ri.activityInfo != null && targetIntent != null) {
2316                 ChooserListAdapter currentListAdapter =
2317                         mChooserMultiProfilePagerAdapter.getActiveListAdapter();
2318                 if (currentListAdapter != null) {
2319                     sendImpressionToAppPredictor(info, currentListAdapter);
2320                     currentListAdapter.updateModel(info.getResolvedComponentName());
2321                     currentListAdapter.updateChooserCounts(ri.activityInfo.packageName,
2322                             targetIntent.getAction());
2323                 }
2324                 if (DEBUG) {
2325                     Log.d(TAG, "ResolveInfo Package is " + ri.activityInfo.packageName);
2326                     Log.d(TAG, "Action to be updated is " + targetIntent.getAction());
2327                 }
2328             } else if (DEBUG) {
2329                 Log.d(TAG, "Can not log Chooser Counts of null ResovleInfo");
2330             }
2331         }
2332         mIsSuccessfullySelected = true;
2333     }
2334 
2335     private void sendImpressionToAppPredictor(TargetInfo targetInfo, ChooserListAdapter adapter) {
2336         AppPredictor directShareAppPredictor = getAppPredictorForDirectShareIfEnabled(
2337                 mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
2338         if (directShareAppPredictor == null) {
2339             return;
2340         }
2341         // Send DS target impression info to AppPredictor, only when user chooses app share.
2342         if (targetInfo instanceof ChooserTargetInfo) {
2343             return;
2344         }
2345         List<ChooserTargetInfo> surfacedTargetInfo = adapter.getSurfacedTargetInfo();
2346         List<AppTargetId> targetIds = new ArrayList<>();
2347         for (ChooserTargetInfo chooserTargetInfo : surfacedTargetInfo) {
2348             ChooserTarget chooserTarget = chooserTargetInfo.getChooserTarget();
2349             ComponentName componentName = mChooserTargetComponentNameCache.getOrDefault(
2350                     chooserTarget.getComponentName(), chooserTarget.getComponentName());
2351             if (mDirectShareShortcutInfoCache.containsKey(chooserTarget)) {
2352                 String shortcutId = mDirectShareShortcutInfoCache.get(chooserTarget).getId();
2353                 targetIds.add(new AppTargetId(
2354                         String.format("%s/%s/%s", shortcutId, componentName.flattenToString(),
2355                                 SHORTCUT_TARGET)));
2356             }
2357         }
2358         directShareAppPredictor.notifyLaunchLocationShown(LAUNCH_LOCATION_DIRECT_SHARE, targetIds);
2359     }
2360 
2361     private void sendClickToAppPredictor(TargetInfo targetInfo) {
2362         AppPredictor directShareAppPredictor = getAppPredictorForDirectShareIfEnabled(
2363                 mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
2364         if (directShareAppPredictor == null) {
2365             return;
2366         }
2367         if (!(targetInfo instanceof ChooserTargetInfo)) {
2368             return;
2369         }
2370         ChooserTarget chooserTarget = ((ChooserTargetInfo) targetInfo).getChooserTarget();
2371         AppTarget appTarget = null;
2372         if (mDirectShareAppTargetCache != null) {
2373             appTarget = mDirectShareAppTargetCache.get(chooserTarget);
2374         }
2375         // This is a direct share click that was provided by the APS
2376         if (appTarget != null) {
2377             directShareAppPredictor.notifyAppTargetEvent(
2378                     new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
2379                         .setLaunchLocation(LAUNCH_LOCATION_DIRECT_SHARE)
2380                         .build());
2381         }
2382     }
2383 
2384     @Nullable
2385     private AppPredictor createAppPredictor(UserHandle userHandle) {
2386         if (!mIsAppPredictorComponentAvailable) {
2387             return null;
2388         }
2389 
2390         if (getPersonalProfileUserHandle().equals(userHandle)) {
2391             if (mPersonalAppPredictor != null) {
2392                 return mPersonalAppPredictor;
2393             }
2394         } else {
2395             if (mWorkAppPredictor != null) {
2396                 return mWorkAppPredictor;
2397             }
2398         }
2399 
2400         // TODO(b/148230574): Currently AppPredictor fetches only the same-profile app targets.
2401         // Make AppPredictor work cross-profile.
2402         Context contextAsUser = createContextAsUser(userHandle, 0 /* flags */);
2403         final IntentFilter filter = getTargetIntentFilter();
2404         Bundle extras = new Bundle();
2405         extras.putParcelable(APP_PREDICTION_INTENT_FILTER_KEY, filter);
2406         AppPredictionContext appPredictionContext = new AppPredictionContext.Builder(contextAsUser)
2407             .setUiSurface(APP_PREDICTION_SHARE_UI_SURFACE)
2408             .setPredictedTargetCount(APP_PREDICTION_SHARE_TARGET_QUERY_PACKAGE_LIMIT)
2409             .setExtras(extras)
2410             .build();
2411         AppPredictionManager appPredictionManager =
2412                 contextAsUser
2413                         .getSystemService(AppPredictionManager.class);
2414         AppPredictor appPredictionSession = appPredictionManager.createAppPredictionSession(
2415                 appPredictionContext);
2416         if (getPersonalProfileUserHandle().equals(userHandle)) {
2417             mPersonalAppPredictor = appPredictionSession;
2418         } else {
2419             mWorkAppPredictor = appPredictionSession;
2420         }
2421         return appPredictionSession;
2422     }
2423 
2424     /**
2425      * This will return an app predictor if it is enabled for direct share sorting
2426      * and if one exists. Otherwise, it returns null.
2427      * @param userHandle
2428      */
2429     @Nullable
2430     private AppPredictor getAppPredictorForDirectShareIfEnabled(UserHandle userHandle) {
2431         return ChooserFlags.USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS
2432                 && !ActivityManager.isLowRamDeviceStatic() ? createAppPredictor(userHandle) : null;
2433     }
2434 
2435     /**
2436      * This will return an app predictor if it is enabled for share activity sorting
2437      * and if one exists. Otherwise, it returns null.
2438      */
2439     @Nullable
2440     private AppPredictor getAppPredictorForShareActivitiesIfEnabled(UserHandle userHandle) {
2441         return USE_PREDICTION_MANAGER_FOR_SHARE_ACTIVITIES ? createAppPredictor(userHandle) : null;
2442     }
2443 
2444     void onRefinementResult(TargetInfo selectedTarget, Intent matchingIntent) {
2445         if (mRefinementResultReceiver != null) {
2446             mRefinementResultReceiver.destroy();
2447             mRefinementResultReceiver = null;
2448         }
2449         if (selectedTarget == null) {
2450             Log.e(TAG, "Refinement result intent did not match any known targets; canceling");
2451         } else if (!checkTargetSourceIntent(selectedTarget, matchingIntent)) {
2452             Log.e(TAG, "onRefinementResult: Selected target " + selectedTarget
2453                     + " cannot match refined source intent " + matchingIntent);
2454         } else {
2455             TargetInfo clonedTarget = selectedTarget.cloneFilledIn(matchingIntent, 0);
2456             if (super.onTargetSelected(clonedTarget, false)) {
2457                 updateModelAndChooserCounts(clonedTarget);
2458                 finish();
2459                 return;
2460             }
2461         }
2462         onRefinementCanceled();
2463     }
2464 
2465     void onRefinementCanceled() {
2466         if (mRefinementResultReceiver != null) {
2467             mRefinementResultReceiver.destroy();
2468             mRefinementResultReceiver = null;
2469         }
2470         finish();
2471     }
2472 
2473     boolean checkTargetSourceIntent(TargetInfo target, Intent matchingIntent) {
2474         final List<Intent> targetIntents = target.getAllSourceIntents();
2475         for (int i = 0, N = targetIntents.size(); i < N; i++) {
2476             final Intent targetIntent = targetIntents.get(i);
2477             if (targetIntent.filterEquals(matchingIntent)) {
2478                 return true;
2479             }
2480         }
2481         return false;
2482     }
2483 
2484     void filterServiceTargets(Context contextAsUser, String packageName,
2485             List<ChooserTarget> targets) {
2486         if (targets == null) {
2487             return;
2488         }
2489 
2490         final PackageManager pm = contextAsUser.getPackageManager();
2491         for (int i = targets.size() - 1; i >= 0; i--) {
2492             final ChooserTarget target = targets.get(i);
2493             final ComponentName targetName = target.getComponentName();
2494             if (packageName != null && packageName.equals(targetName.getPackageName())) {
2495                 // Anything from the original target's package is fine.
2496                 continue;
2497             }
2498 
2499             boolean remove;
2500             try {
2501                 final ActivityInfo ai = pm.getActivityInfo(targetName, 0);
2502                 remove = !ai.exported || ai.permission != null;
2503             } catch (NameNotFoundException e) {
2504                 Log.e(TAG, "Target " + target + " returned by " + packageName
2505                         + " component not found");
2506                 remove = true;
2507             }
2508 
2509             if (remove) {
2510                 targets.remove(i);
2511             }
2512         }
2513     }
2514 
2515     /**
2516      * Sort intents alphabetically based on display label.
2517      */
2518     static class AzInfoComparator implements Comparator<DisplayResolveInfo> {
2519         Collator mCollator;
2520         AzInfoComparator(Context context) {
2521             mCollator = Collator.getInstance(context.getResources().getConfiguration().locale);
2522         }
2523 
2524         @Override
2525         public int compare(
2526                 DisplayResolveInfo lhsp, DisplayResolveInfo rhsp) {
2527             return mCollator.compare(lhsp.getDisplayLabel(), rhsp.getDisplayLabel());
2528         }
2529     }
2530 
2531     protected MetricsLogger getMetricsLogger() {
2532         if (mMetricsLogger == null) {
2533             mMetricsLogger = new MetricsLogger();
2534         }
2535         return mMetricsLogger;
2536     }
2537 
2538     protected ChooserActivityLogger getChooserActivityLogger() {
2539         if (mChooserActivityLogger == null) {
2540             mChooserActivityLogger = new ChooserActivityLoggerImpl();
2541         }
2542         return mChooserActivityLogger;
2543     }
2544 
2545     public class ChooserListController extends ResolverListController {
2546         public ChooserListController(Context context,
2547                 PackageManager pm,
2548                 Intent targetIntent,
2549                 String referrerPackageName,
2550                 int launchedFromUid,
2551                 UserHandle userId,
2552                 AbstractResolverComparator resolverComparator) {
2553             super(context, pm, targetIntent, referrerPackageName, launchedFromUid, userId,
2554                     resolverComparator);
2555         }
2556 
2557         @Override
2558         boolean isComponentFiltered(ComponentName name) {
2559             if (mFilteredComponentNames == null) {
2560                 return false;
2561             }
2562             for (ComponentName filteredComponentName : mFilteredComponentNames) {
2563                 if (name.equals(filteredComponentName)) {
2564                     return true;
2565                 }
2566             }
2567             return false;
2568         }
2569 
2570         @Override
2571         public boolean isComponentPinned(ComponentName name) {
2572             return mPinnedSharedPrefs.getBoolean(name.flattenToString(), false);
2573         }
2574     }
2575 
2576     @VisibleForTesting
2577     public ChooserGridAdapter createChooserGridAdapter(Context context,
2578             List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList,
2579             boolean filterLastUsed, UserHandle userHandle) {
2580         ChooserListAdapter chooserListAdapter = createChooserListAdapter(context, payloadIntents,
2581                 initialIntents, rList, filterLastUsed,
2582                 createListController(userHandle));
2583         AppPredictor.Callback appPredictorCallback = createAppPredictorCallback(chooserListAdapter);
2584         AppPredictor appPredictor = setupAppPredictorForUser(userHandle, appPredictorCallback);
2585         chooserListAdapter.setAppPredictor(appPredictor);
2586         chooserListAdapter.setAppPredictorCallback(appPredictorCallback);
2587         return new ChooserGridAdapter(chooserListAdapter);
2588     }
2589 
2590     @VisibleForTesting
2591     public ChooserListAdapter createChooserListAdapter(Context context,
2592             List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList,
2593             boolean filterLastUsed, ResolverListController resolverListController) {
2594         return new ChooserListAdapter(context, payloadIntents, initialIntents, rList,
2595                 filterLastUsed, resolverListController, this,
2596                 this, context.getPackageManager(),
2597                 getChooserActivityLogger());
2598     }
2599 
2600     @VisibleForTesting
2601     protected ResolverListController createListController(UserHandle userHandle) {
2602         AppPredictor appPredictor = getAppPredictorForShareActivitiesIfEnabled(userHandle);
2603         AbstractResolverComparator resolverComparator;
2604         if (appPredictor != null) {
2605             resolverComparator = new AppPredictionServiceResolverComparator(this, getTargetIntent(),
2606                     getReferrerPackageName(), appPredictor, userHandle, getChooserActivityLogger());
2607         } else {
2608             resolverComparator =
2609                     new ResolverRankerServiceResolverComparator(this, getTargetIntent(),
2610                         getReferrerPackageName(), null, getChooserActivityLogger());
2611         }
2612 
2613         return new ChooserListController(
2614                 this,
2615                 mPm,
2616                 getTargetIntent(),
2617                 getReferrerPackageName(),
2618                 mLaunchedFromUid,
2619                 userHandle,
2620                 resolverComparator);
2621     }
2622 
2623     @VisibleForTesting
2624     protected Bitmap loadThumbnail(Uri uri, Size size) {
2625         if (uri == null || size == null) {
2626             return null;
2627         }
2628 
2629         try {
2630             return getContentResolver().loadThumbnail(uri, size, null);
2631         } catch (IOException | NullPointerException | SecurityException ex) {
2632             logContentPreviewWarning(uri);
2633         }
2634         return null;
2635     }
2636 
2637     static final class PlaceHolderTargetInfo extends NotSelectableTargetInfo {
2638         public Drawable getDisplayIcon(Context context) {
2639             AnimatedVectorDrawable avd = (AnimatedVectorDrawable)
2640                     context.getDrawable(R.drawable.chooser_direct_share_icon_placeholder);
2641             avd.start(); // Start animation after generation
2642             return avd;
2643         }
2644     }
2645 
2646     protected static final class EmptyTargetInfo extends NotSelectableTargetInfo {
2647         public EmptyTargetInfo() {}
2648 
2649         public Drawable getDisplayIcon(Context context) {
2650             return null;
2651         }
2652     }
2653 
2654     private void handleScroll(View view, int x, int y, int oldx, int oldy) {
2655         if (mChooserMultiProfilePagerAdapter.getCurrentRootAdapter() != null) {
2656             mChooserMultiProfilePagerAdapter.getCurrentRootAdapter().handleScroll(view, y, oldy);
2657         }
2658     }
2659 
2660     /*
2661      * Need to dynamically adjust how many icons can fit per row before we add them,
2662      * which also means setting the correct offset to initially show the content
2663      * preview area + 2 rows of targets
2664      */
2665     private void handleLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
2666             int oldTop, int oldRight, int oldBottom) {
2667         if (mChooserMultiProfilePagerAdapter == null) {
2668             return;
2669         }
2670         RecyclerView recyclerView = mChooserMultiProfilePagerAdapter.getActiveAdapterView();
2671         ChooserGridAdapter gridAdapter = mChooserMultiProfilePagerAdapter.getCurrentRootAdapter();
2672         // Skip height calculation if recycler view was scrolled to prevent it inaccurately
2673         // calculating the height, as the logic below does not account for the scrolled offset.
2674         if (gridAdapter == null || recyclerView == null
2675                 || recyclerView.computeVerticalScrollOffset() != 0) {
2676             return;
2677         }
2678 
2679         final int availableWidth = right - left - v.getPaddingLeft() - v.getPaddingRight();
2680         boolean isLayoutUpdated = gridAdapter.consumeLayoutRequest()
2681                 || gridAdapter.calculateChooserTargetWidth(availableWidth)
2682                 || recyclerView.getAdapter() == null
2683                 || availableWidth != mCurrAvailableWidth;
2684         if (isLayoutUpdated
2685                 || mLastNumberOfChildren != recyclerView.getChildCount()) {
2686             mCurrAvailableWidth = availableWidth;
2687             if (isLayoutUpdated) {
2688                 // It is very important we call setAdapter from here. Otherwise in some cases
2689                 // the resolver list doesn't get populated, such as b/150922090, b/150918223
2690                 // and b/150936654
2691                 recyclerView.setAdapter(gridAdapter);
2692                 ((GridLayoutManager) recyclerView.getLayoutManager()).setSpanCount(
2693                         getMaxTargetsPerRow());
2694             }
2695 
2696             UserHandle currentUserHandle = mChooserMultiProfilePagerAdapter.getCurrentUserHandle();
2697             int currentProfile = getProfileForUser(currentUserHandle);
2698             int initialProfile = findSelectedProfile();
2699             if (currentProfile != initialProfile) {
2700                 return;
2701             }
2702 
2703             if (mLastNumberOfChildren == recyclerView.getChildCount()) {
2704                 return;
2705             }
2706 
2707             getMainThreadHandler().post(() -> {
2708                 if (mResolverDrawerLayout == null || gridAdapter == null) {
2709                     return;
2710                 }
2711 
2712                 final int bottomInset = mSystemWindowInsets != null
2713                                             ? mSystemWindowInsets.bottom : 0;
2714                 int offset = bottomInset;
2715                 int rowsToShow = gridAdapter.getSystemRowCount()
2716                         + gridAdapter.getProfileRowCount()
2717                         + gridAdapter.getServiceTargetRowCount()
2718                         + gridAdapter.getCallerAndRankedTargetRowCount();
2719 
2720                 // then this is most likely not a SEND_* action, so check
2721                 // the app target count
2722                 if (rowsToShow == 0) {
2723                     rowsToShow = gridAdapter.getRowCount();
2724                 }
2725 
2726                 // still zero? then use a default height and leave, which
2727                 // can happen when there are no targets to show
2728                 if (rowsToShow == 0 && !shouldShowStickyContentPreview()) {
2729                     offset += getResources().getDimensionPixelSize(
2730                             R.dimen.chooser_max_collapsed_height);
2731                     mResolverDrawerLayout.setCollapsibleHeightReserved(offset);
2732                     return;
2733                 }
2734 
2735                 View stickyContentPreview = findViewById(R.id.content_preview_container);
2736                 if (shouldShowStickyContentPreview() && isStickyContentPreviewShowing()) {
2737                     offset += stickyContentPreview.getHeight();
2738                 }
2739 
2740                 if (shouldShowTabs()) {
2741                     offset += findViewById(R.id.tabs).getHeight();
2742                 }
2743 
2744                 View tabDivider = findViewById(R.id.resolver_tab_divider);
2745                 if (tabDivider.getVisibility() == View.VISIBLE) {
2746                     offset += tabDivider.getHeight();
2747                 }
2748 
2749                 if (recyclerView.getVisibility() == View.VISIBLE) {
2750                     int directShareHeight = 0;
2751                     rowsToShow = Math.min(4, rowsToShow);
2752                     boolean shouldShowExtraRow = shouldShowExtraRow(rowsToShow);
2753                     mLastNumberOfChildren = recyclerView.getChildCount();
2754                     for (int i = 0, childCount = recyclerView.getChildCount();
2755                             i < childCount && rowsToShow > 0; i++) {
2756                         View child = recyclerView.getChildAt(i);
2757                         if (((GridLayoutManager.LayoutParams)
2758                                 child.getLayoutParams()).getSpanIndex() != 0) {
2759                             continue;
2760                         }
2761                         int height = child.getHeight();
2762                         offset += height;
2763                         if (shouldShowExtraRow) {
2764                             offset += height;
2765                         }
2766 
2767                         if (gridAdapter.getTargetType(
2768                                 recyclerView.getChildAdapterPosition(child))
2769                                 == ChooserListAdapter.TARGET_SERVICE) {
2770                             directShareHeight = height;
2771                         }
2772                         rowsToShow--;
2773                     }
2774 
2775                     boolean isExpandable = getResources().getConfiguration().orientation
2776                             == Configuration.ORIENTATION_PORTRAIT && !isInMultiWindowMode();
2777                     if (directShareHeight != 0 && isSendAction(getTargetIntent())
2778                             && isExpandable) {
2779                         // make sure to leave room for direct share 4->8 expansion
2780                         int requiredExpansionHeight =
2781                                 (int) (directShareHeight / DIRECT_SHARE_EXPANSION_RATE);
2782                         int topInset = mSystemWindowInsets != null ? mSystemWindowInsets.top : 0;
2783                         int minHeight = bottom - top - mResolverDrawerLayout.getAlwaysShowHeight()
2784                                 - requiredExpansionHeight - topInset - bottomInset;
2785 
2786                         offset = Math.min(offset, minHeight);
2787                     }
2788                 } else {
2789                     ViewGroup currentEmptyStateView = getActiveEmptyStateView();
2790                     if (currentEmptyStateView.getVisibility() == View.VISIBLE) {
2791                         offset += currentEmptyStateView.getHeight();
2792                     }
2793                 }
2794 
2795                 mResolverDrawerLayout.setCollapsibleHeightReserved(Math.min(offset, bottom - top));
2796             });
2797         }
2798     }
2799 
2800     /**
2801      * If we have a tabbed view and are showing 1 row in the current profile and an empty
2802      * state screen in the other profile, to prevent cropping of the empty state screen we show
2803      * a second row in the current profile.
2804      */
2805     private boolean shouldShowExtraRow(int rowsToShow) {
2806         return shouldShowTabs()
2807                 && rowsToShow == 1
2808                 && mChooserMultiProfilePagerAdapter.shouldShowEmptyStateScreen(
2809                         mChooserMultiProfilePagerAdapter.getInactiveListAdapter());
2810     }
2811 
2812     /**
2813      * Returns {@link #PROFILE_PERSONAL}, {@link #PROFILE_WORK}, or -1 if the given user handle
2814      * does not match either the personal or work user handle.
2815      **/
2816     private int getProfileForUser(UserHandle currentUserHandle) {
2817         if (currentUserHandle.equals(getPersonalProfileUserHandle())) {
2818             return PROFILE_PERSONAL;
2819         } else if (currentUserHandle.equals(getWorkProfileUserHandle())) {
2820             return PROFILE_WORK;
2821         }
2822         Log.e(TAG, "User " + currentUserHandle + " does not belong to a personal or work profile.");
2823         return -1;
2824     }
2825 
2826     private ViewGroup getActiveEmptyStateView() {
2827         int currentPage = mChooserMultiProfilePagerAdapter.getCurrentPage();
2828         return mChooserMultiProfilePagerAdapter.getItem(currentPage).getEmptyStateView();
2829     }
2830 
2831     static class BaseChooserTargetComparator implements Comparator<ChooserTarget> {
2832         @Override
2833         public int compare(ChooserTarget lhs, ChooserTarget rhs) {
2834             // Descending order
2835             return (int) Math.signum(rhs.getScore() - lhs.getScore());
2836         }
2837     }
2838 
2839     @Override // ResolverListCommunicator
2840     public void onHandlePackagesChanged(ResolverListAdapter listAdapter) {
2841         mServicesRequested.clear();
2842         mChooserMultiProfilePagerAdapter.getActiveListAdapter().notifyDataSetChanged();
2843         super.onHandlePackagesChanged(listAdapter);
2844     }
2845 
2846     @Override // SelectableTargetInfoCommunicator
2847     public ActivityInfoPresentationGetter makePresentationGetter(ActivityInfo info) {
2848         return mChooserMultiProfilePagerAdapter.getActiveListAdapter().makePresentationGetter(info);
2849     }
2850 
2851     @Override // SelectableTargetInfoCommunicator
2852     public Intent getReferrerFillInIntent() {
2853         return mReferrerFillInIntent;
2854     }
2855 
2856     @Override // ChooserListCommunicator
2857     public int getMaxRankedTargets() {
2858         return getMaxTargetsPerRow();
2859     }
2860 
2861     @Override // ChooserListCommunicator
2862     public void sendListViewUpdateMessage(UserHandle userHandle) {
2863         Message msg = Message.obtain();
2864         msg.what = ChooserHandler.LIST_VIEW_UPDATE_MESSAGE;
2865         msg.obj = userHandle;
2866         mChooserHandler.sendMessageDelayed(msg, LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
2867     }
2868 
2869     @Override
2870     public void onListRebuilt(ResolverListAdapter listAdapter) {
2871         setupScrollListener();
2872         maybeSetupGlobalLayoutListener();
2873 
2874         ChooserListAdapter chooserListAdapter = (ChooserListAdapter) listAdapter;
2875         if (chooserListAdapter.getUserHandle()
2876                 .equals(mChooserMultiProfilePagerAdapter.getCurrentUserHandle())) {
2877             mChooserMultiProfilePagerAdapter.getActiveAdapterView()
2878                     .setAdapter(mChooserMultiProfilePagerAdapter.getCurrentRootAdapter());
2879             mChooserMultiProfilePagerAdapter
2880                     .setupListAdapter(mChooserMultiProfilePagerAdapter.getCurrentPage());
2881         }
2882 
2883         if (chooserListAdapter.mDisplayList == null
2884                 || chooserListAdapter.mDisplayList.isEmpty()) {
2885             chooserListAdapter.notifyDataSetChanged();
2886         } else {
2887             chooserListAdapter.updateAlphabeticalList();
2888         }
2889 
2890         // don't support direct share on low ram devices
2891         if (ActivityManager.isLowRamDeviceStatic()) {
2892             getChooserActivityLogger().logSharesheetAppLoadComplete();
2893             return;
2894         }
2895 
2896         // no need to query direct share for work profile when its locked or disabled
2897         if (!shouldQueryShortcutManager(chooserListAdapter.getUserHandle())) {
2898             getChooserActivityLogger().logSharesheetAppLoadComplete();
2899             return;
2900         }
2901 
2902         if (ChooserFlags.USE_PREDICTION_MANAGER_FOR_DIRECT_TARGETS) {
2903             if (DEBUG) {
2904                 Log.d(TAG, "querying direct share targets from ShortcutManager");
2905             }
2906 
2907             queryDirectShareTargets(chooserListAdapter, false);
2908         }
2909         if (USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS) {
2910             if (DEBUG) {
2911                 Log.d(TAG, "List built querying services");
2912             }
2913 
2914             queryTargetServices(chooserListAdapter);
2915         }
2916 
2917         getChooserActivityLogger().logSharesheetAppLoadComplete();
2918     }
2919 
2920     @VisibleForTesting
2921     protected boolean isUserRunning(UserHandle userHandle) {
2922         UserManager userManager = getSystemService(UserManager.class);
2923         return userManager.isUserRunning(userHandle);
2924     }
2925 
2926     @VisibleForTesting
2927     protected boolean isUserUnlocked(UserHandle userHandle) {
2928         UserManager userManager = getSystemService(UserManager.class);
2929         return userManager.isUserUnlocked(userHandle);
2930     }
2931 
2932     @VisibleForTesting
2933     protected boolean isQuietModeEnabled(UserHandle userHandle) {
2934         UserManager userManager = getSystemService(UserManager.class);
2935         return userManager.isQuietModeEnabled(userHandle);
2936     }
2937 
2938     private void setupScrollListener() {
2939         if (mResolverDrawerLayout == null) {
2940             return;
2941         }
2942         int elevatedViewResId = shouldShowTabs() ? R.id.resolver_tab_divider : R.id.chooser_header;
2943         final View elevatedView = mResolverDrawerLayout.findViewById(elevatedViewResId);
2944         final float defaultElevation = elevatedView.getElevation();
2945         final float chooserHeaderScrollElevation =
2946                 getResources().getDimensionPixelSize(R.dimen.chooser_header_scroll_elevation);
2947         mChooserMultiProfilePagerAdapter.getActiveAdapterView().addOnScrollListener(
2948                 new RecyclerView.OnScrollListener() {
2949                     public void onScrollStateChanged(RecyclerView view, int scrollState) {
2950                         if (scrollState == RecyclerView.SCROLL_STATE_IDLE) {
2951                             if (mScrollStatus == SCROLL_STATUS_SCROLLING_VERTICAL) {
2952                                 mScrollStatus = SCROLL_STATUS_IDLE;
2953                                 setHorizontalScrollingEnabled(true);
2954                             }
2955                         } else if (scrollState == RecyclerView.SCROLL_STATE_DRAGGING) {
2956                             if (mScrollStatus == SCROLL_STATUS_IDLE) {
2957                                 mScrollStatus = SCROLL_STATUS_SCROLLING_VERTICAL;
2958                                 setHorizontalScrollingEnabled(false);
2959                             }
2960                         }
2961                     }
2962 
2963                     public void onScrolled(RecyclerView view, int dx, int dy) {
2964                         if (view.getChildCount() > 0) {
2965                             View child = view.getLayoutManager().findViewByPosition(0);
2966                             if (child == null || child.getTop() < 0) {
2967                                 elevatedView.setElevation(chooserHeaderScrollElevation);
2968                                 return;
2969                             }
2970                         }
2971 
2972                         elevatedView.setElevation(defaultElevation);
2973                     }
2974                 });
2975     }
2976 
2977     private void maybeSetupGlobalLayoutListener() {
2978         if (shouldShowTabs()) {
2979             return;
2980         }
2981         final View recyclerView = mChooserMultiProfilePagerAdapter.getActiveAdapterView();
2982         recyclerView.getViewTreeObserver()
2983                 .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
2984                     @Override
2985                     public void onGlobalLayout() {
2986                         // Fixes an issue were the accessibility border disappears on list creation.
2987                         recyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
2988                         final TextView titleView = findViewById(R.id.title);
2989                         if (titleView != null) {
2990                             titleView.setFocusable(true);
2991                             titleView.setFocusableInTouchMode(true);
2992                             titleView.requestFocus();
2993                             titleView.requestAccessibilityFocus();
2994                         }
2995                     }
2996                 });
2997     }
2998 
2999     @Override // ChooserListCommunicator
3000     public boolean isSendAction(Intent targetIntent) {
3001         if (targetIntent == null) {
3002             return false;
3003         }
3004 
3005         String action = targetIntent.getAction();
3006         if (action == null) {
3007             return false;
3008         }
3009 
3010         if (Intent.ACTION_SEND.equals(action) || Intent.ACTION_SEND_MULTIPLE.equals(action)) {
3011             return true;
3012         }
3013 
3014         return false;
3015     }
3016 
3017     /**
3018      * The sticky content preview is shown only when we have a tabbed view. It's shown above
3019      * the tabs so it is not part of the scrollable list. If we are not in tabbed view,
3020      * we instead show the content preview as a regular list item.
3021      */
3022     private boolean shouldShowStickyContentPreview() {
3023         return shouldShowStickyContentPreviewNoOrientationCheck()
3024                 && !getResources().getBoolean(R.bool.resolver_landscape_phone);
3025     }
3026 
3027     private boolean shouldShowStickyContentPreviewNoOrientationCheck() {
3028         return shouldShowTabs()
3029                 && mMultiProfilePagerAdapter.getListAdapterForUserHandle(
3030                 UserHandle.of(UserHandle.myUserId())).getCount() > 0
3031                 && isSendAction(getTargetIntent());
3032     }
3033 
3034     private void updateStickyContentPreview() {
3035         if (shouldShowStickyContentPreviewNoOrientationCheck()) {
3036             // The sticky content preview is only shown when we show the work and personal tabs.
3037             // We don't show it in landscape as otherwise there is no room for scrolling.
3038             // If the sticky content preview will be shown at some point with orientation change,
3039             // then always preload it to avoid subsequent resizing of the share sheet.
3040             ViewGroup contentPreviewContainer = findViewById(R.id.content_preview_container);
3041             if (contentPreviewContainer.getChildCount() == 0) {
3042                 ViewGroup contentPreviewView = createContentPreviewView(contentPreviewContainer);
3043                 contentPreviewContainer.addView(contentPreviewView);
3044             }
3045         }
3046         if (shouldShowStickyContentPreview()) {
3047             showStickyContentPreview();
3048         } else {
3049             hideStickyContentPreview();
3050         }
3051     }
3052 
3053     private void showStickyContentPreview() {
3054         if (isStickyContentPreviewShowing()) {
3055             return;
3056         }
3057         ViewGroup contentPreviewContainer = findViewById(R.id.content_preview_container);
3058         contentPreviewContainer.setVisibility(View.VISIBLE);
3059     }
3060 
3061     private boolean isStickyContentPreviewShowing() {
3062         ViewGroup contentPreviewContainer = findViewById(R.id.content_preview_container);
3063         return contentPreviewContainer.getVisibility() == View.VISIBLE;
3064     }
3065 
3066     private void hideStickyContentPreview() {
3067         if (!isStickyContentPreviewShowing()) {
3068             return;
3069         }
3070         ViewGroup contentPreviewContainer = findViewById(R.id.content_preview_container);
3071         contentPreviewContainer.setVisibility(View.GONE);
3072     }
3073 
3074     private void logActionShareWithPreview() {
3075         Intent targetIntent = getTargetIntent();
3076         int previewType = findPreferredContentPreview(targetIntent, getContentResolver());
3077         getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_SHARE_WITH_PREVIEW)
3078                 .setSubtype(previewType));
3079     }
3080 
3081     class ViewHolderBase extends RecyclerView.ViewHolder {
3082         private int mViewType;
3083 
3084         ViewHolderBase(View itemView, int viewType) {
3085             super(itemView);
3086             this.mViewType = viewType;
3087         }
3088 
3089         int getViewType() {
3090             return mViewType;
3091         }
3092     }
3093 
3094     /**
3095      * Used to bind types of individual item including
3096      * {@link ChooserGridAdapter#VIEW_TYPE_NORMAL},
3097      * {@link ChooserGridAdapter#VIEW_TYPE_CONTENT_PREVIEW},
3098      * {@link ChooserGridAdapter#VIEW_TYPE_PROFILE},
3099      * and {@link ChooserGridAdapter#VIEW_TYPE_AZ_LABEL}.
3100      */
3101     final class ItemViewHolder extends ViewHolderBase {
3102         ResolverListAdapter.ViewHolder mWrappedViewHolder;
3103         int mListPosition = ChooserListAdapter.NO_POSITION;
3104 
3105         ItemViewHolder(View itemView, boolean isClickable, int viewType) {
3106             super(itemView, viewType);
3107             mWrappedViewHolder = new ResolverListAdapter.ViewHolder(itemView);
3108             if (isClickable) {
3109                 itemView.setOnClickListener(v -> startSelected(mListPosition,
3110                         false/* always */, true/* filterd */));
3111 
3112                 itemView.setOnLongClickListener(v -> {
3113                     final TargetInfo ti = mChooserMultiProfilePagerAdapter.getActiveListAdapter()
3114                             .targetInfoForPosition(mListPosition, /* filtered */ true);
3115 
3116                     // This should always be the case for ItemViewHolder, check for validity
3117                     if (ti instanceof DisplayResolveInfo) {
3118                         showTargetDetails((DisplayResolveInfo) ti);
3119                     }
3120                     return true;
3121                 });
3122             }
3123         }
3124     }
3125 
3126     /**
3127      * Add a footer to the list, to support scrolling behavior below the navbar.
3128      */
3129     final class FooterViewHolder extends ViewHolderBase {
3130         FooterViewHolder(View itemView, int viewType) {
3131             super(itemView, viewType);
3132         }
3133     }
3134 
3135     /**
3136      * Intentionally override the {@link ResolverActivity} implementation as we only need that
3137      * implementation for the intent resolver case.
3138      */
3139     @Override
3140     public void onButtonClick(View v) {}
3141 
3142     /**
3143      * Intentionally override the {@link ResolverActivity} implementation as we only need that
3144      * implementation for the intent resolver case.
3145      */
3146     @Override
3147     protected void resetButtonBar() {}
3148 
3149     @Override
3150     protected String getMetricsCategory() {
3151         return METRICS_CATEGORY_CHOOSER;
3152     }
3153 
3154     @Override
3155     protected void onProfileTabSelected() {
3156         ChooserGridAdapter currentRootAdapter =
3157                 mChooserMultiProfilePagerAdapter.getCurrentRootAdapter();
3158         currentRootAdapter.updateDirectShareExpansion();
3159         // This fixes an edge case where after performing a variety of gestures, vertical scrolling
3160         // ends up disabled. That's because at some point the old tab's vertical scrolling is
3161         // disabled and the new tab's is enabled. For context, see b/159997845
3162         setVerticalScrollEnabled(true);
3163         if (mResolverDrawerLayout != null) {
3164             mResolverDrawerLayout.scrollNestedScrollableChildBackToTop();
3165         }
3166     }
3167 
3168     @Override
3169     protected WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
3170         if (shouldShowTabs()) {
3171             mChooserMultiProfilePagerAdapter
3172                     .setEmptyStateBottomOffset(insets.getSystemWindowInsetBottom());
3173             mChooserMultiProfilePagerAdapter.setupContainerPadding(
3174                     getActiveEmptyStateView().findViewById(R.id.resolver_empty_state_container));
3175         }
3176         return super.onApplyWindowInsets(v, insets);
3177     }
3178 
3179     private void setHorizontalScrollingEnabled(boolean enabled) {
3180         ResolverViewPager viewPager = findViewById(R.id.profile_pager);
3181         viewPager.setSwipingEnabled(enabled);
3182     }
3183 
3184     private void setVerticalScrollEnabled(boolean enabled) {
3185         ChooserGridLayoutManager layoutManager =
3186                 (ChooserGridLayoutManager) mChooserMultiProfilePagerAdapter.getActiveAdapterView()
3187                         .getLayoutManager();
3188         layoutManager.setVerticalScrollEnabled(enabled);
3189     }
3190 
3191     @Override
3192     void onHorizontalSwipeStateChanged(int state) {
3193         if (state == ViewPager.SCROLL_STATE_DRAGGING) {
3194             if (mScrollStatus == SCROLL_STATUS_IDLE) {
3195                 mScrollStatus = SCROLL_STATUS_SCROLLING_HORIZONTAL;
3196                 setVerticalScrollEnabled(false);
3197             }
3198         } else if (state == ViewPager.SCROLL_STATE_IDLE) {
3199             if (mScrollStatus == SCROLL_STATUS_SCROLLING_HORIZONTAL) {
3200                 mScrollStatus = SCROLL_STATUS_IDLE;
3201                 setVerticalScrollEnabled(true);
3202             }
3203         }
3204     }
3205 
3206     int getMaxTargetsPerRow() {
3207         int maxTargets = MAX_TARGETS_PER_ROW_PORTRAIT;
3208         if (mShouldDisplayLandscape) {
3209             maxTargets = MAX_TARGETS_PER_ROW_LANDSCAPE;
3210         }
3211         return maxTargets;
3212     }
3213     /**
3214      * Adapter for all types of items and targets in ShareSheet.
3215      * Note that ranked sections like Direct Share - while appearing grid-like - are handled on the
3216      * row level by this adapter but not on the item level. Individual targets within the row are
3217      * handled by {@link ChooserListAdapter}
3218      */
3219     @VisibleForTesting
3220     public final class ChooserGridAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
3221         private ChooserListAdapter mChooserListAdapter;
3222         private final LayoutInflater mLayoutInflater;
3223 
3224         private DirectShareViewHolder mDirectShareViewHolder;
3225         private int mChooserTargetWidth = 0;
3226         private boolean mShowAzLabelIfPoss;
3227 
3228         private boolean mHideContentPreview = false;
3229         private boolean mLayoutRequested = false;
3230 
3231         private int mFooterHeight = 0;
3232 
3233         private static final int VIEW_TYPE_DIRECT_SHARE = 0;
3234         private static final int VIEW_TYPE_NORMAL = 1;
3235         private static final int VIEW_TYPE_CONTENT_PREVIEW = 2;
3236         private static final int VIEW_TYPE_PROFILE = 3;
3237         private static final int VIEW_TYPE_AZ_LABEL = 4;
3238         private static final int VIEW_TYPE_CALLER_AND_RANK = 5;
3239         private static final int VIEW_TYPE_FOOTER = 6;
3240 
3241         private static final int NUM_EXPANSIONS_TO_HIDE_AZ_LABEL = 20;
3242 
3243         ChooserGridAdapter(ChooserListAdapter wrappedAdapter) {
3244             super();
3245             mChooserListAdapter = wrappedAdapter;
3246             mLayoutInflater = LayoutInflater.from(ChooserActivity.this);
3247 
3248             mShowAzLabelIfPoss = getNumSheetExpansions() < NUM_EXPANSIONS_TO_HIDE_AZ_LABEL;
3249 
3250             wrappedAdapter.registerDataSetObserver(new DataSetObserver() {
3251                 @Override
3252                 public void onChanged() {
3253                     super.onChanged();
3254                     notifyDataSetChanged();
3255                 }
3256 
3257                 @Override
3258                 public void onInvalidated() {
3259                     super.onInvalidated();
3260                     notifyDataSetChanged();
3261                 }
3262             });
3263         }
3264 
3265         public void setFooterHeight(int height) {
3266             mFooterHeight = height;
3267         }
3268 
3269         /**
3270          * Calculate the chooser target width to maximize space per item
3271          *
3272          * @param width The new row width to use for recalculation
3273          * @return true if the view width has changed
3274          */
3275         public boolean calculateChooserTargetWidth(int width) {
3276             if (width == 0) {
3277                 return false;
3278             }
3279 
3280             int newWidth = width / getMaxTargetsPerRow();
3281             if (newWidth != mChooserTargetWidth) {
3282                 mChooserTargetWidth = newWidth;
3283                 return true;
3284             }
3285 
3286             return false;
3287         }
3288 
3289         /**
3290          * Hides the list item content preview.
3291          * <p>Not to be confused with the sticky content preview which is above the
3292          * personal and work tabs.
3293          */
3294         public void hideContentPreview() {
3295             mHideContentPreview = true;
3296             mLayoutRequested = true;
3297             notifyDataSetChanged();
3298         }
3299 
3300         public boolean consumeLayoutRequest() {
3301             boolean oldValue = mLayoutRequested;
3302             mLayoutRequested = false;
3303             return oldValue;
3304         }
3305 
3306         public int getRowCount() {
3307             return (int) (
3308                     getSystemRowCount()
3309                             + getProfileRowCount()
3310                             + getServiceTargetRowCount()
3311                             + getCallerAndRankedTargetRowCount()
3312                             + getAzLabelRowCount()
3313                             + Math.ceil(
3314                             (float) mChooserListAdapter.getAlphaTargetCount()
3315                                     / getMaxTargetsPerRow())
3316             );
3317         }
3318 
3319         /**
3320          * Whether the "system" row of targets is displayed.
3321          * This area includes the content preview (if present) and action row.
3322          */
3323         public int getSystemRowCount() {
3324             // For the tabbed case we show the sticky content preview above the tabs,
3325             // please refer to shouldShowStickyContentPreview
3326             if (shouldShowTabs()) {
3327                 return 0;
3328             }
3329 
3330             if (!isSendAction(getTargetIntent())) {
3331                 return 0;
3332             }
3333 
3334             if (mChooserListAdapter == null || mChooserListAdapter.getCount() == 0) {
3335                 return 0;
3336             }
3337 
3338             return 1;
3339         }
3340 
3341         public int getProfileRowCount() {
3342             if (shouldShowTabs()) {
3343                 return 0;
3344             }
3345             return mChooserListAdapter.getOtherProfile() == null ? 0 : 1;
3346         }
3347 
3348         public int getFooterRowCount() {
3349             return 1;
3350         }
3351 
3352         public int getCallerAndRankedTargetRowCount() {
3353             return (int) Math.ceil(
3354                     ((float) mChooserListAdapter.getCallerTargetCount()
3355                             + mChooserListAdapter.getRankedTargetCount()) / getMaxTargetsPerRow());
3356         }
3357 
3358         // There can be at most one row in the listview, that is internally
3359         // a ViewGroup with 2 rows
3360         public int getServiceTargetRowCount() {
3361             if (isSendAction(getTargetIntent())
3362                     && !ActivityManager.isLowRamDeviceStatic()) {
3363                 return 1;
3364             }
3365             return 0;
3366         }
3367 
3368         public int getAzLabelRowCount() {
3369             // Only show a label if the a-z list is showing
3370             return (mShowAzLabelIfPoss && mChooserListAdapter.getAlphaTargetCount() > 0) ? 1 : 0;
3371         }
3372 
3373         @Override
3374         public int getItemCount() {
3375             return (int) (
3376                     getSystemRowCount()
3377                             + getProfileRowCount()
3378                             + getServiceTargetRowCount()
3379                             + getCallerAndRankedTargetRowCount()
3380                             + getAzLabelRowCount()
3381                             + mChooserListAdapter.getAlphaTargetCount()
3382                             + getFooterRowCount()
3383             );
3384         }
3385 
3386         @Override
3387         public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
3388             switch (viewType) {
3389                 case VIEW_TYPE_CONTENT_PREVIEW:
3390                     return new ItemViewHolder(createContentPreviewView(parent), false, viewType);
3391                 case VIEW_TYPE_PROFILE:
3392                     return new ItemViewHolder(createProfileView(parent), false, viewType);
3393                 case VIEW_TYPE_AZ_LABEL:
3394                     return new ItemViewHolder(createAzLabelView(parent), false, viewType);
3395                 case VIEW_TYPE_NORMAL:
3396                     return new ItemViewHolder(
3397                             mChooserListAdapter.createView(parent), true, viewType);
3398                 case VIEW_TYPE_DIRECT_SHARE:
3399                 case VIEW_TYPE_CALLER_AND_RANK:
3400                     return createItemGroupViewHolder(viewType, parent);
3401                 case VIEW_TYPE_FOOTER:
3402                     Space sp = new Space(parent.getContext());
3403                     sp.setLayoutParams(new RecyclerView.LayoutParams(
3404                             LayoutParams.MATCH_PARENT, mFooterHeight));
3405                     return new FooterViewHolder(sp, viewType);
3406                 default:
3407                     // Since we catch all possible viewTypes above, no chance this is being called.
3408                     return null;
3409             }
3410         }
3411 
3412         @Override
3413         public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
3414             int viewType = ((ViewHolderBase) holder).getViewType();
3415             switch (viewType) {
3416                 case VIEW_TYPE_DIRECT_SHARE:
3417                 case VIEW_TYPE_CALLER_AND_RANK:
3418                     bindItemGroupViewHolder(position, (ItemGroupViewHolder) holder);
3419                     break;
3420                 case VIEW_TYPE_NORMAL:
3421                     bindItemViewHolder(position, (ItemViewHolder) holder);
3422                     break;
3423                 default:
3424             }
3425         }
3426 
3427         @Override
3428         public int getItemViewType(int position) {
3429             int count;
3430 
3431             int countSum = (count = getSystemRowCount());
3432             if (count > 0 && position < countSum) return VIEW_TYPE_CONTENT_PREVIEW;
3433 
3434             countSum += (count = getProfileRowCount());
3435             if (count > 0 && position < countSum) return VIEW_TYPE_PROFILE;
3436 
3437             countSum += (count = getServiceTargetRowCount());
3438             if (count > 0 && position < countSum) return VIEW_TYPE_DIRECT_SHARE;
3439 
3440             countSum += (count = getCallerAndRankedTargetRowCount());
3441             if (count > 0 && position < countSum) return VIEW_TYPE_CALLER_AND_RANK;
3442 
3443             countSum += (count = getAzLabelRowCount());
3444             if (count > 0 && position < countSum) return VIEW_TYPE_AZ_LABEL;
3445 
3446             if (position == getItemCount() - 1) return VIEW_TYPE_FOOTER;
3447 
3448             return VIEW_TYPE_NORMAL;
3449         }
3450 
3451         public int getTargetType(int position) {
3452             return mChooserListAdapter.getPositionTargetType(getListPosition(position));
3453         }
3454 
3455         private View createProfileView(ViewGroup parent) {
3456             View profileRow = mLayoutInflater.inflate(R.layout.chooser_profile_row, parent, false);
3457             mProfileView = profileRow.findViewById(R.id.profile_button);
3458             mProfileView.setOnClickListener(ChooserActivity.this::onProfileClick);
3459             updateProfileViewButton();
3460             return profileRow;
3461         }
3462 
3463         private View createAzLabelView(ViewGroup parent) {
3464             return mLayoutInflater.inflate(R.layout.chooser_az_label_row, parent, false);
3465         }
3466 
3467         private ItemGroupViewHolder loadViewsIntoGroup(ItemGroupViewHolder holder) {
3468             final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
3469             final int exactSpec = MeasureSpec.makeMeasureSpec(mChooserTargetWidth,
3470                     MeasureSpec.EXACTLY);
3471             int columnCount = holder.getColumnCount();
3472 
3473             final boolean isDirectShare = holder instanceof DirectShareViewHolder;
3474 
3475             for (int i = 0; i < columnCount; i++) {
3476                 final View v = mChooserListAdapter.createView(holder.getRowByIndex(i));
3477                 final int column = i;
3478                 v.setOnClickListener(new OnClickListener() {
3479                     @Override
3480                     public void onClick(View v) {
3481                         startSelected(holder.getItemIndex(column), false, true);
3482                     }
3483                 });
3484 
3485                 // Direct Share targets should not show any menu
3486                 if (!isDirectShare) {
3487                     v.setOnLongClickListener(v1 -> {
3488                         final TargetInfo ti = mChooserListAdapter.targetInfoForPosition(
3489                                 holder.getItemIndex(column), true);
3490                         // This should always be the case for non-DS targets, check for validity
3491                         if (ti instanceof DisplayResolveInfo) {
3492                             showTargetDetails((DisplayResolveInfo) ti);
3493                         }
3494                         return true;
3495                     });
3496                 }
3497 
3498                 holder.addView(i, v);
3499 
3500                 // Force Direct Share to be 2 lines and auto-wrap to second line via hoz scroll =
3501                 // false. TextView#setHorizontallyScrolling must be reset after #setLines. Must be
3502                 // done before measuring.
3503                 if (isDirectShare) {
3504                     final ViewHolder vh = (ViewHolder) v.getTag();
3505                     vh.text.setLines(2);
3506                     vh.text.setHorizontallyScrolling(false);
3507                     vh.text2.setVisibility(View.GONE);
3508                 }
3509 
3510                 // Force height to be a given so we don't have visual disruption during scaling.
3511                 v.measure(exactSpec, spec);
3512                 setViewBounds(v, v.getMeasuredWidth(), v.getMeasuredHeight());
3513             }
3514 
3515             final ViewGroup viewGroup = holder.getViewGroup();
3516 
3517             // Pre-measure and fix height so we can scale later.
3518             holder.measure();
3519             setViewBounds(viewGroup, LayoutParams.MATCH_PARENT, holder.getMeasuredRowHeight());
3520 
3521             if (isDirectShare) {
3522                 DirectShareViewHolder dsvh = (DirectShareViewHolder) holder;
3523                 setViewBounds(dsvh.getRow(0), LayoutParams.MATCH_PARENT, dsvh.getMinRowHeight());
3524                 setViewBounds(dsvh.getRow(1), LayoutParams.MATCH_PARENT, dsvh.getMinRowHeight());
3525             }
3526 
3527             viewGroup.setTag(holder);
3528             return holder;
3529         }
3530 
3531         private void setViewBounds(View view, int widthPx, int heightPx) {
3532             LayoutParams lp = view.getLayoutParams();
3533             if (lp == null) {
3534                 lp = new LayoutParams(widthPx, heightPx);
3535                 view.setLayoutParams(lp);
3536             } else {
3537                 lp.height = heightPx;
3538                 lp.width = widthPx;
3539             }
3540         }
3541 
3542         ItemGroupViewHolder createItemGroupViewHolder(int viewType, ViewGroup parent) {
3543             if (viewType == VIEW_TYPE_DIRECT_SHARE) {
3544                 ViewGroup parentGroup = (ViewGroup) mLayoutInflater.inflate(
3545                         R.layout.chooser_row_direct_share, parent, false);
3546                 ViewGroup row1 = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row,
3547                         parentGroup, false);
3548                 ViewGroup row2 = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row,
3549                         parentGroup, false);
3550                 parentGroup.addView(row1);
3551                 parentGroup.addView(row2);
3552 
3553                 mDirectShareViewHolder = new DirectShareViewHolder(parentGroup,
3554                         Lists.newArrayList(row1, row2), getMaxTargetsPerRow(), viewType);
3555                 loadViewsIntoGroup(mDirectShareViewHolder);
3556 
3557                 return mDirectShareViewHolder;
3558             } else {
3559                 ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row, parent,
3560                         false);
3561                 ItemGroupViewHolder holder =
3562                         new SingleRowViewHolder(row, getMaxTargetsPerRow(), viewType);
3563                 loadViewsIntoGroup(holder);
3564 
3565                 return holder;
3566             }
3567         }
3568 
3569         /**
3570          * Need to merge CALLER + ranked STANDARD into a single row and prevent a separator from
3571          * showing on top of the AZ list if the AZ label is visible. All other types are placed into
3572          * their own row as determined by their target type, and dividers are added in the list to
3573          * separate each type.
3574          */
3575         int getRowType(int rowPosition) {
3576             // Merge caller and ranked standard into a single row
3577             int positionType = mChooserListAdapter.getPositionTargetType(rowPosition);
3578             if (positionType == ChooserListAdapter.TARGET_CALLER) {
3579                 return ChooserListAdapter.TARGET_STANDARD;
3580             }
3581 
3582             // If an the A-Z label is shown, prevent a separator from appearing by making the A-Z
3583             // row type the same as the suggestion row type
3584             if (getAzLabelRowCount() > 0 && positionType == ChooserListAdapter.TARGET_STANDARD_AZ) {
3585                 return ChooserListAdapter.TARGET_STANDARD;
3586             }
3587 
3588             return positionType;
3589         }
3590 
3591         void bindItemViewHolder(int position, ItemViewHolder holder) {
3592             View v = holder.itemView;
3593             int listPosition = getListPosition(position);
3594             holder.mListPosition = listPosition;
3595             mChooserListAdapter.bindView(listPosition, v);
3596         }
3597 
3598         void bindItemGroupViewHolder(int position, ItemGroupViewHolder holder) {
3599             final ViewGroup viewGroup = (ViewGroup) holder.itemView;
3600             int start = getListPosition(position);
3601             int startType = getRowType(start);
3602 
3603             int columnCount = holder.getColumnCount();
3604             int end = start + columnCount - 1;
3605             while (getRowType(end) != startType && end >= start) {
3606                 end--;
3607             }
3608 
3609             if (end == start && mChooserListAdapter.getItem(start) instanceof EmptyTargetInfo) {
3610                 final TextView textView = viewGroup.findViewById(R.id.chooser_row_text_option);
3611 
3612                 if (textView.getVisibility() != View.VISIBLE) {
3613                     textView.setAlpha(0.0f);
3614                     textView.setVisibility(View.VISIBLE);
3615                     textView.setText(R.string.chooser_no_direct_share_targets);
3616 
3617                     ValueAnimator fadeAnim = ObjectAnimator.ofFloat(textView, "alpha", 0.0f, 1.0f);
3618                     fadeAnim.setInterpolator(new DecelerateInterpolator(1.0f));
3619 
3620                     float translationInPx = getResources().getDimensionPixelSize(
3621                             R.dimen.chooser_row_text_option_translate);
3622                     textView.setTranslationY(translationInPx);
3623                     ValueAnimator translateAnim = ObjectAnimator.ofFloat(textView, "translationY",
3624                             0.0f);
3625                     translateAnim.setInterpolator(new DecelerateInterpolator(1.0f));
3626 
3627                     AnimatorSet animSet = new AnimatorSet();
3628                     animSet.setDuration(NO_DIRECT_SHARE_ANIM_IN_MILLIS);
3629                     animSet.setStartDelay(NO_DIRECT_SHARE_ANIM_IN_MILLIS);
3630                     animSet.playTogether(fadeAnim, translateAnim);
3631                     animSet.start();
3632                 }
3633             }
3634 
3635             for (int i = 0; i < columnCount; i++) {
3636                 final View v = holder.getView(i);
3637 
3638                 if (start + i <= end) {
3639                     holder.setViewVisibility(i, View.VISIBLE);
3640                     holder.setItemIndex(i, start + i);
3641                     mChooserListAdapter.bindView(holder.getItemIndex(i), v);
3642                 } else {
3643                     holder.setViewVisibility(i, View.INVISIBLE);
3644                 }
3645             }
3646         }
3647 
3648         int getListPosition(int position) {
3649             position -= getSystemRowCount() + getProfileRowCount();
3650 
3651             final int serviceCount = mChooserListAdapter.getServiceTargetCount();
3652             final int serviceRows = (int) Math.ceil((float) serviceCount / getMaxRankedTargets());
3653             if (position < serviceRows) {
3654                 return position * getMaxTargetsPerRow();
3655             }
3656 
3657             position -= serviceRows;
3658 
3659             final int callerAndRankedCount = mChooserListAdapter.getCallerTargetCount()
3660                                                  + mChooserListAdapter.getRankedTargetCount();
3661             final int callerAndRankedRows = getCallerAndRankedTargetRowCount();
3662             if (position < callerAndRankedRows) {
3663                 return serviceCount + position * getMaxTargetsPerRow();
3664             }
3665 
3666             position -= getAzLabelRowCount() + callerAndRankedRows;
3667 
3668             return callerAndRankedCount + serviceCount + position;
3669         }
3670 
3671         public void handleScroll(View v, int y, int oldy) {
3672             boolean canExpandDirectShare = canExpandDirectShare();
3673             if (mDirectShareViewHolder != null && canExpandDirectShare) {
3674                 mDirectShareViewHolder.handleScroll(
3675                         mChooserMultiProfilePagerAdapter.getActiveAdapterView(), y, oldy,
3676                         getMaxTargetsPerRow());
3677             }
3678         }
3679 
3680         /**
3681          * Only expand direct share area if there is a minimum number of targets.
3682          */
3683         private boolean canExpandDirectShare() {
3684             // Do not enable until we have confirmed more apps are using sharing shortcuts
3685             // Check git history for enablement logic
3686             return false;
3687         }
3688 
3689         public ChooserListAdapter getListAdapter() {
3690             return mChooserListAdapter;
3691         }
3692 
3693         boolean shouldCellSpan(int position) {
3694             return getItemViewType(position) == VIEW_TYPE_NORMAL;
3695         }
3696 
3697         void updateDirectShareExpansion() {
3698             if (mDirectShareViewHolder == null || !canExpandDirectShare()) {
3699                 return;
3700             }
3701             RecyclerView activeAdapterView =
3702                     mChooserMultiProfilePagerAdapter.getActiveAdapterView();
3703             if (mResolverDrawerLayout.isCollapsed()) {
3704                 mDirectShareViewHolder.collapse(activeAdapterView);
3705             } else {
3706                 mDirectShareViewHolder.expand(activeAdapterView);
3707             }
3708         }
3709     }
3710 
3711     /**
3712      * Used to bind types for group of items including:
3713      * {@link ChooserGridAdapter#VIEW_TYPE_DIRECT_SHARE},
3714      * and {@link ChooserGridAdapter#VIEW_TYPE_CALLER_AND_RANK}.
3715      */
3716     abstract class ItemGroupViewHolder extends ViewHolderBase {
3717         protected int mMeasuredRowHeight;
3718         private int[] mItemIndices;
3719         protected final View[] mCells;
3720         private final int mColumnCount;
3721 
3722         ItemGroupViewHolder(int cellCount, View itemView, int viewType) {
3723             super(itemView, viewType);
3724             this.mCells = new View[cellCount];
3725             this.mItemIndices = new int[cellCount];
3726             this.mColumnCount = cellCount;
3727         }
3728 
3729         abstract ViewGroup addView(int index, View v);
3730 
3731         abstract ViewGroup getViewGroup();
3732 
3733         abstract ViewGroup getRowByIndex(int index);
3734 
3735         abstract ViewGroup getRow(int rowNumber);
3736 
3737         abstract void setViewVisibility(int i, int visibility);
3738 
3739         public int getColumnCount() {
3740             return mColumnCount;
3741         }
3742 
3743         public void measure() {
3744             final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
3745             getViewGroup().measure(spec, spec);
3746             mMeasuredRowHeight = getViewGroup().getMeasuredHeight();
3747         }
3748 
3749         public int getMeasuredRowHeight() {
3750             return mMeasuredRowHeight;
3751         }
3752 
3753         public void setItemIndex(int itemIndex, int listIndex) {
3754             mItemIndices[itemIndex] = listIndex;
3755         }
3756 
3757         public int getItemIndex(int itemIndex) {
3758             return mItemIndices[itemIndex];
3759         }
3760 
3761         public View getView(int index) {
3762             return mCells[index];
3763         }
3764     }
3765 
3766     class SingleRowViewHolder extends ItemGroupViewHolder {
3767         private final ViewGroup mRow;
3768 
3769         SingleRowViewHolder(ViewGroup row, int cellCount, int viewType) {
3770             super(cellCount, row, viewType);
3771 
3772             this.mRow = row;
3773         }
3774 
3775         public ViewGroup getViewGroup() {
3776             return mRow;
3777         }
3778 
3779         public ViewGroup getRowByIndex(int index) {
3780             return mRow;
3781         }
3782 
3783         public ViewGroup getRow(int rowNumber) {
3784             if (rowNumber == 0) return mRow;
3785             return null;
3786         }
3787 
3788         public ViewGroup addView(int index, View v) {
3789             mRow.addView(v);
3790             mCells[index] = v;
3791 
3792             return mRow;
3793         }
3794 
3795         public void setViewVisibility(int i, int visibility) {
3796             getView(i).setVisibility(visibility);
3797         }
3798     }
3799 
3800     class DirectShareViewHolder extends ItemGroupViewHolder {
3801         private final ViewGroup mParent;
3802         private final List<ViewGroup> mRows;
3803         private int mCellCountPerRow;
3804 
3805         private boolean mHideDirectShareExpansion = false;
3806         private int mDirectShareMinHeight = 0;
3807         private int mDirectShareCurrHeight = 0;
3808         private int mDirectShareMaxHeight = 0;
3809 
3810         private final boolean[] mCellVisibility;
3811 
3812         DirectShareViewHolder(ViewGroup parent, List<ViewGroup> rows, int cellCountPerRow,
3813                 int viewType) {
3814             super(rows.size() * cellCountPerRow, parent, viewType);
3815 
3816             this.mParent = parent;
3817             this.mRows = rows;
3818             this.mCellCountPerRow = cellCountPerRow;
3819             this.mCellVisibility = new boolean[rows.size() * cellCountPerRow];
3820         }
3821 
3822         public ViewGroup addView(int index, View v) {
3823             ViewGroup row = getRowByIndex(index);
3824             row.addView(v);
3825             mCells[index] = v;
3826 
3827             return row;
3828         }
3829 
3830         public ViewGroup getViewGroup() {
3831             return mParent;
3832         }
3833 
3834         public ViewGroup getRowByIndex(int index) {
3835             return mRows.get(index / mCellCountPerRow);
3836         }
3837 
3838         public ViewGroup getRow(int rowNumber) {
3839             return mRows.get(rowNumber);
3840         }
3841 
3842         public void measure() {
3843             final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
3844             getRow(0).measure(spec, spec);
3845             getRow(1).measure(spec, spec);
3846 
3847             mDirectShareMinHeight = getRow(0).getMeasuredHeight();
3848             mDirectShareCurrHeight = mDirectShareCurrHeight > 0
3849                     ? mDirectShareCurrHeight : mDirectShareMinHeight;
3850             mDirectShareMaxHeight = 2 * mDirectShareMinHeight;
3851         }
3852 
3853         public int getMeasuredRowHeight() {
3854             return mDirectShareCurrHeight;
3855         }
3856 
3857         public int getMinRowHeight() {
3858             return mDirectShareMinHeight;
3859         }
3860 
3861         public void setViewVisibility(int i, int visibility) {
3862             final View v = getView(i);
3863             if (visibility == View.VISIBLE) {
3864                 mCellVisibility[i] = true;
3865                 v.setVisibility(visibility);
3866                 v.setAlpha(1.0f);
3867             } else if (visibility == View.INVISIBLE && mCellVisibility[i]) {
3868                 mCellVisibility[i] = false;
3869 
3870                 ValueAnimator fadeAnim = ObjectAnimator.ofFloat(v, "alpha", 1.0f, 0f);
3871                 fadeAnim.setDuration(NO_DIRECT_SHARE_ANIM_IN_MILLIS);
3872                 fadeAnim.setInterpolator(new AccelerateInterpolator(1.0f));
3873                 fadeAnim.addListener(new AnimatorListenerAdapter() {
3874                     public void onAnimationEnd(Animator animation) {
3875                         v.setVisibility(View.INVISIBLE);
3876                     }
3877                 });
3878                 fadeAnim.start();
3879             }
3880         }
3881 
3882         public void handleScroll(RecyclerView view, int y, int oldy, int maxTargetsPerRow) {
3883             // only exit early if fully collapsed, otherwise onListRebuilt() with shifting
3884             // targets can lock us into an expanded mode
3885             boolean notExpanded = mDirectShareCurrHeight == mDirectShareMinHeight;
3886             if (notExpanded) {
3887                 if (mHideDirectShareExpansion) {
3888                     return;
3889                 }
3890 
3891                 // only expand if we have more than maxTargetsPerRow, and delay that decision
3892                 // until they start to scroll
3893                 ChooserListAdapter adapter =
3894                         mChooserMultiProfilePagerAdapter.getActiveListAdapter();
3895                 int validTargets = adapter.getSelectableServiceTargetCount();
3896                 if (validTargets <= maxTargetsPerRow) {
3897                     mHideDirectShareExpansion = true;
3898                     return;
3899                 }
3900             }
3901 
3902             int yDiff = (int) ((oldy - y) * DIRECT_SHARE_EXPANSION_RATE);
3903 
3904             int prevHeight = mDirectShareCurrHeight;
3905             int newHeight = Math.min(prevHeight + yDiff, mDirectShareMaxHeight);
3906             newHeight = Math.max(newHeight, mDirectShareMinHeight);
3907             yDiff = newHeight - prevHeight;
3908 
3909             updateDirectShareRowHeight(view, yDiff, newHeight);
3910         }
3911 
3912         void expand(RecyclerView view) {
3913             updateDirectShareRowHeight(view, mDirectShareMaxHeight - mDirectShareCurrHeight,
3914                     mDirectShareMaxHeight);
3915         }
3916 
3917         void collapse(RecyclerView view) {
3918             updateDirectShareRowHeight(view, mDirectShareMinHeight - mDirectShareCurrHeight,
3919                     mDirectShareMinHeight);
3920         }
3921 
3922         private void updateDirectShareRowHeight(RecyclerView view, int yDiff, int newHeight) {
3923             if (view == null || view.getChildCount() == 0 || yDiff == 0) {
3924                 return;
3925             }
3926 
3927             // locate the item to expand, and offset the rows below that one
3928             boolean foundExpansion = false;
3929             for (int i = 0; i < view.getChildCount(); i++) {
3930                 View child = view.getChildAt(i);
3931 
3932                 if (foundExpansion) {
3933                     child.offsetTopAndBottom(yDiff);
3934                 } else {
3935                     if (child.getTag() != null && child.getTag() instanceof DirectShareViewHolder) {
3936                         int widthSpec = MeasureSpec.makeMeasureSpec(child.getWidth(),
3937                                 MeasureSpec.EXACTLY);
3938                         int heightSpec = MeasureSpec.makeMeasureSpec(newHeight,
3939                                 MeasureSpec.EXACTLY);
3940                         child.measure(widthSpec, heightSpec);
3941                         child.getLayoutParams().height = child.getMeasuredHeight();
3942                         child.layout(child.getLeft(), child.getTop(), child.getRight(),
3943                                 child.getTop() + child.getMeasuredHeight());
3944 
3945                         foundExpansion = true;
3946                     }
3947                 }
3948             }
3949 
3950             if (foundExpansion) {
3951                 mDirectShareCurrHeight = newHeight;
3952             }
3953         }
3954     }
3955 
3956     static class ChooserTargetServiceConnection implements ServiceConnection {
3957         private DisplayResolveInfo mOriginalTarget;
3958         private ComponentName mConnectedComponent;
3959         private ChooserActivity mChooserActivity;
3960         private final UserHandle mUserHandle;
3961         private final Object mLock = new Object();
3962 
3963         private final IChooserTargetResult mChooserTargetResult = new IChooserTargetResult.Stub() {
3964             @Override
3965             public void sendResult(List<ChooserTarget> targets) throws RemoteException {
3966                 synchronized (mLock) {
3967                     if (mChooserActivity == null) {
3968                         Log.e(TAG, "destroyed ChooserTargetServiceConnection received result from "
3969                                 + mConnectedComponent + "; ignoring...");
3970                         return;
3971                     }
3972                     Context contextAsUser =
3973                             mChooserActivity.createContextAsUser(mUserHandle, 0 /* flags */);
3974                     mChooserActivity.filterServiceTargets(contextAsUser,
3975                             mOriginalTarget.getResolveInfo().activityInfo.packageName, targets);
3976                     final Message msg = Message.obtain();
3977                     msg.what = ChooserHandler.CHOOSER_TARGET_SERVICE_RESULT;
3978                     msg.obj = new ServiceResultInfo(mOriginalTarget, targets,
3979                             ChooserTargetServiceConnection.this, mUserHandle);
3980                     mChooserActivity.mChooserHandler.sendMessage(msg);
3981                 }
3982             }
3983         };
3984 
3985         public ChooserTargetServiceConnection(ChooserActivity chooserActivity,
3986                 DisplayResolveInfo dri, UserHandle userHandle) {
3987             mChooserActivity = chooserActivity;
3988             mOriginalTarget = dri;
3989             mUserHandle = userHandle;
3990         }
3991 
3992         @Override
3993         public void onServiceConnected(ComponentName name, IBinder service) {
3994             if (DEBUG) Log.d(TAG, "onServiceConnected: " + name);
3995             synchronized (mLock) {
3996                 if (mChooserActivity == null) {
3997                     Log.e(TAG, "destroyed ChooserTargetServiceConnection got onServiceConnected");
3998                     return;
3999                 }
4000 
4001                 final IChooserTargetService icts = IChooserTargetService.Stub.asInterface(service);
4002                 try {
4003                     icts.getChooserTargets(mOriginalTarget.getResolvedComponentName(),
4004                             mOriginalTarget.getResolveInfo().filter, mChooserTargetResult);
4005                 } catch (RemoteException e) {
4006                     Log.e(TAG, "Querying ChooserTargetService " + name + " failed.", e);
4007                     mChooserActivity.unbindService(this);
4008                     mChooserActivity.mServiceConnections.remove(this);
4009                     destroy();
4010                 }
4011             }
4012         }
4013 
4014         @Override
4015         public void onServiceDisconnected(ComponentName name) {
4016             if (DEBUG) Log.d(TAG, "onServiceDisconnected: " + name);
4017             synchronized (mLock) {
4018                 if (mChooserActivity == null) {
4019                     Log.e(TAG,
4020                             "destroyed ChooserTargetServiceConnection got onServiceDisconnected");
4021                     return;
4022                 }
4023 
4024                 mChooserActivity.unbindService(this);
4025                 mChooserActivity.mServiceConnections.remove(this);
4026                 if (mChooserActivity.mServiceConnections.isEmpty()) {
4027                     mChooserActivity.sendVoiceChoicesIfNeeded();
4028                 }
4029                 mConnectedComponent = null;
4030                 destroy();
4031             }
4032         }
4033 
4034         public void destroy() {
4035             synchronized (mLock) {
4036                 mChooserActivity = null;
4037                 mOriginalTarget = null;
4038             }
4039         }
4040 
4041         @Override
4042         public String toString() {
4043             return "ChooserTargetServiceConnection{service="
4044                     + mConnectedComponent + ", activity="
4045                     + (mOriginalTarget != null
4046                     ? mOriginalTarget.getResolveInfo().activityInfo.toString()
4047                     : "<connection destroyed>") + "}";
4048         }
4049 
4050         public ComponentName getComponentName() {
4051             return mOriginalTarget.getResolveInfo().activityInfo.getComponentName();
4052         }
4053     }
4054 
4055     static class ServiceResultInfo {
4056         public final DisplayResolveInfo originalTarget;
4057         public final List<ChooserTarget> resultTargets;
4058         public final ChooserTargetServiceConnection connection;
4059         public final UserHandle userHandle;
4060 
4061         public ServiceResultInfo(DisplayResolveInfo ot, List<ChooserTarget> rt,
4062                 ChooserTargetServiceConnection c, UserHandle userHandle) {
4063             originalTarget = ot;
4064             resultTargets = rt;
4065             connection = c;
4066             this.userHandle = userHandle;
4067         }
4068     }
4069 
4070     static class ChooserTargetRankingInfo {
4071         public final List<AppTarget> scores;
4072         public final UserHandle userHandle;
4073 
4074         ChooserTargetRankingInfo(List<AppTarget> chooserTargetScores,
4075                 UserHandle userHandle) {
4076             this.scores = chooserTargetScores;
4077             this.userHandle = userHandle;
4078         }
4079     }
4080 
4081     static class RefinementResultReceiver extends ResultReceiver {
4082         private ChooserActivity mChooserActivity;
4083         private TargetInfo mSelectedTarget;
4084 
4085         public RefinementResultReceiver(ChooserActivity host, TargetInfo target,
4086                 Handler handler) {
4087             super(handler);
4088             mChooserActivity = host;
4089             mSelectedTarget = target;
4090         }
4091 
4092         @Override
4093         protected void onReceiveResult(int resultCode, Bundle resultData) {
4094             if (mChooserActivity == null) {
4095                 Log.e(TAG, "Destroyed RefinementResultReceiver received a result");
4096                 return;
4097             }
4098             if (resultData == null) {
4099                 Log.e(TAG, "RefinementResultReceiver received null resultData");
4100                 return;
4101             }
4102 
4103             switch (resultCode) {
4104                 case RESULT_CANCELED:
4105                     mChooserActivity.onRefinementCanceled();
4106                     break;
4107                 case RESULT_OK:
4108                     Parcelable intentParcelable = resultData.getParcelable(Intent.EXTRA_INTENT);
4109                     if (intentParcelable instanceof Intent) {
4110                         mChooserActivity.onRefinementResult(mSelectedTarget,
4111                                 (Intent) intentParcelable);
4112                     } else {
4113                         Log.e(TAG, "RefinementResultReceiver received RESULT_OK but no Intent"
4114                                 + " in resultData with key Intent.EXTRA_INTENT");
4115                     }
4116                     break;
4117                 default:
4118                     Log.w(TAG, "Unknown result code " + resultCode
4119                             + " sent to RefinementResultReceiver");
4120                     break;
4121             }
4122         }
4123 
4124         public void destroy() {
4125             mChooserActivity = null;
4126             mSelectedTarget = null;
4127         }
4128     }
4129 
4130     /**
4131      * Used internally to round image corners while obeying view padding.
4132      */
4133     public static class RoundedRectImageView extends ImageView {
4134         private int mRadius = 0;
4135         private Path mPath = new Path();
4136         private Paint mOverlayPaint = new Paint(0);
4137         private Paint mRoundRectPaint = new Paint(0);
4138         private Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
4139         private String mExtraImageCount = null;
4140 
4141         public RoundedRectImageView(Context context) {
4142             super(context);
4143         }
4144 
4145         public RoundedRectImageView(Context context, AttributeSet attrs) {
4146             this(context, attrs, 0);
4147         }
4148 
4149         public RoundedRectImageView(Context context, AttributeSet attrs, int defStyleAttr) {
4150             this(context, attrs, defStyleAttr, 0);
4151         }
4152 
4153         public RoundedRectImageView(Context context, AttributeSet attrs, int defStyleAttr,
4154                 int defStyleRes) {
4155             super(context, attrs, defStyleAttr, defStyleRes);
4156             mRadius = context.getResources().getDimensionPixelSize(R.dimen.chooser_corner_radius);
4157 
4158             mOverlayPaint.setColor(0x99000000);
4159             mOverlayPaint.setStyle(Paint.Style.FILL);
4160 
4161             mRoundRectPaint.setColor(context.getResources().getColor(R.color.chooser_row_divider));
4162             mRoundRectPaint.setStyle(Paint.Style.STROKE);
4163             mRoundRectPaint.setStrokeWidth(context.getResources()
4164                     .getDimensionPixelSize(R.dimen.chooser_preview_image_border));
4165 
4166             mTextPaint.setColor(Color.WHITE);
4167             mTextPaint.setTextSize(context.getResources()
4168                     .getDimensionPixelSize(R.dimen.chooser_preview_image_font_size));
4169             mTextPaint.setTextAlign(Paint.Align.CENTER);
4170         }
4171 
4172         private void updatePath(int width, int height) {
4173             mPath.reset();
4174 
4175             int imageWidth = width - getPaddingRight() - getPaddingLeft();
4176             int imageHeight = height - getPaddingBottom() - getPaddingTop();
4177             mPath.addRoundRect(getPaddingLeft(), getPaddingTop(), imageWidth, imageHeight, mRadius,
4178                     mRadius, Path.Direction.CW);
4179         }
4180 
4181         /**
4182           * Sets the corner radius on all corners
4183           *
4184           * param radius 0 for no radius, &gt; 0 for a visible corner radius
4185           */
4186         public void setRadius(int radius) {
4187             mRadius = radius;
4188             updatePath(getWidth(), getHeight());
4189         }
4190 
4191         /**
4192           * Display an overlay with extra image count on 3rd image
4193           */
4194         public void setExtraImageCount(int count) {
4195             if (count > 0) {
4196                 this.mExtraImageCount = "+" + count;
4197             } else {
4198                 this.mExtraImageCount = null;
4199             }
4200         }
4201 
4202         @Override
4203         protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
4204             super.onSizeChanged(width, height, oldWidth, oldHeight);
4205             updatePath(width, height);
4206         }
4207 
4208         @Override
4209         protected void onDraw(Canvas canvas) {
4210             if (mRadius != 0) {
4211                 canvas.clipPath(mPath);
4212             }
4213 
4214             super.onDraw(canvas);
4215 
4216             int x = getPaddingLeft();
4217             int y = getPaddingRight();
4218             int width = getWidth() - getPaddingRight() - getPaddingLeft();
4219             int height = getHeight() - getPaddingBottom() - getPaddingTop();
4220             if (mExtraImageCount != null) {
4221                 canvas.drawRect(x, y, width, height, mOverlayPaint);
4222 
4223                 int xPos = canvas.getWidth() / 2;
4224                 int yPos = (int) ((canvas.getHeight() / 2.0f)
4225                         - ((mTextPaint.descent() + mTextPaint.ascent()) / 2.0f));
4226 
4227                 canvas.drawText(mExtraImageCount, xPos, yPos, mTextPaint);
4228             }
4229 
4230             canvas.drawRoundRect(x, y, width, height, mRadius, mRadius, mRoundRectPaint);
4231         }
4232     }
4233 
4234     @Override
4235     protected void maybeLogProfileChange() {
4236         getChooserActivityLogger().logShareheetProfileChanged();
4237     }
4238 }
4239