Loading core/java/com/android/internal/app/ChooserActivity.java +256 −64 Original line number Diff line number Diff line Loading @@ -18,6 +18,11 @@ package com.android.internal.app; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.IntDef; import android.app.Activity; import android.app.ActivityManager; Loading Loading @@ -90,6 +95,8 @@ import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.widget.AbsListView; import android.widget.BaseAdapter; import android.widget.ImageView; Loading Loading @@ -152,11 +159,17 @@ public class ChooserActivity extends ResolverActivity { private static final boolean USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS = true; private static final boolean USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS = true; /** * The transition time between placeholders for direct share to a message * indicating that non are available. */ private static final int NO_DIRECT_SHARE_ANIM_IN_MILLIS = 200; // TODO(b/121287224): Re-evaluate this limit private static final int SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20; private static final int QUERY_TARGET_SERVICE_LIMIT = 5; private static final int WATCHDOG_TIMEOUT_MILLIS = 2000; private static final int WATCHDOG_TIMEOUT_MILLIS = 3000; private Bundle mReplacementExtras; private IntentSender mChosenComponentSender; Loading @@ -172,6 +185,8 @@ public class ChooserActivity extends ResolverActivity { private ChooserListAdapter mChooserListAdapter; private ChooserRowAdapter mChooserRowAdapter; private Drawable mChooserRowLayer; private int mChooserRowServiceSpacing; private SharedPreferences mPinnedSharedPrefs; private static final float PINNED_TARGET_SCORE_BOOST = 1000.f; Loading Loading @@ -220,7 +235,6 @@ public class ChooserActivity extends ResolverActivity { sri.connection.destroy(); mServiceConnections.remove(sri.connection); if (mServiceConnections.isEmpty()) { mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); sendVoiceChoicesIfNeeded(); mChooserListAdapter.setShowServiceTargets(true); } Loading @@ -230,8 +244,12 @@ public class ChooserActivity extends ResolverActivity { if (DEBUG) { Log.d(TAG, "CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT; unbinding services"); } if (isDestroyed()) { break; } unbindRemainingServices(); sendVoiceChoicesIfNeeded(); mChooserListAdapter.completeServiceTargetLoading(); mChooserListAdapter.setShowServiceTargets(true); break; Loading Loading @@ -399,11 +417,17 @@ public class ChooserActivity extends ResolverActivity { .setExtras(extras) .build()); mAppPredictorCallback = resultList -> { if (isFinishing() || isDestroyed()) { return; } final List<DisplayResolveInfo> driList = getDisplayResolveInfos(mChooserListAdapter); final List<ShortcutManager.ShareShortcutInfo> shareShortcutInfos = new ArrayList<>(); for (AppTarget appTarget : resultList) { if (appTarget.getShortcutInfo() == null) { continue; } shareShortcutInfos.add(new ShortcutManager.ShareShortcutInfo( appTarget.getShortcutInfo(), new ComponentName( Loading @@ -414,6 +438,10 @@ public class ChooserActivity extends ResolverActivity { mAppPredictor.registerPredictionUpdates(this.getMainExecutor(), mAppPredictorCallback); } mChooserRowLayer = getResources().getDrawable(R.drawable.chooser_row_layer_list, null); mChooserRowServiceSpacing = getResources() .getDimensionPixelSize(R.dimen.chooser_service_spacing); if (DEBUG) { Log.d(TAG, "System Time Cost is " + systemCost); } Loading Loading @@ -919,6 +947,10 @@ public class ChooserActivity extends ResolverActivity { @Override protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) { if (target instanceof NotSelectableTargetInfo) { return false; } if (mRefinementIntentSender != null) { final Intent fillIn = new Intent(); final List<Intent> sourceIntents = target.getAllSourceIntents(); Loading Loading @@ -1064,14 +1096,14 @@ public class ChooserActivity extends ResolverActivity { } } if (!mServiceConnections.isEmpty()) { if (DEBUG) { Log.d(TAG, "queryTargets setting watchdog timer for " + WATCHDOG_TIMEOUT_MILLIS + "ms"); } mChooserHandler.sendEmptyMessageDelayed(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT, WATCHDOG_TIMEOUT_MILLIS); } else { if (mServiceConnections.isEmpty()) { sendVoiceChoicesIfNeeded(); } } Loading Loading @@ -1213,7 +1245,6 @@ public class ChooserActivity extends ResolverActivity { conn.destroy(); } mServiceConnections.clear(); mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); } public void onSetupVoiceInteraction() { Loading Loading @@ -1420,7 +1451,93 @@ public class ChooserActivity extends ResolverActivity { return null; } final class ChooserTargetInfo implements TargetInfo { interface ChooserTargetInfo extends TargetInfo { float getModifiedScore(); ChooserTarget getChooserTarget(); } /** * Distinguish between targets that selectable by the user, vs those that are * placeholders for the system while information is loading in an async manner. */ abstract class NotSelectableTargetInfo implements ChooserTargetInfo { public Intent getResolvedIntent() { return null; } public ComponentName getResolvedComponentName() { return null; } public boolean start(Activity activity, Bundle options) { return false; } public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) { return false; } public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { return false; } public ResolveInfo getResolveInfo() { return null; } public CharSequence getDisplayLabel() { return null; } public CharSequence getExtendedInfo() { return null; } public Drawable getBadgeIcon() { return null; } public CharSequence getBadgeContentDescription() { return null; } public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) { return null; } public List<Intent> getAllSourceIntents() { return null; } public boolean isPinned() { return false; } public float getModifiedScore() { return 0.1f; } public ChooserTarget getChooserTarget() { return null; } } final class PlaceHolderTargetInfo extends NotSelectableTargetInfo { public Drawable getDisplayIcon() { return getDrawable(R.drawable.resolver_icon_placeholder); } } final class EmptyTargetInfo extends NotSelectableTargetInfo { public Drawable getDisplayIcon() { return null; } } final class SelectableTargetInfo implements ChooserTargetInfo { private final DisplayResolveInfo mSourceInfo; private final ResolveInfo mBackupResolveInfo; private final ChooserTarget mChooserTarget; Loading @@ -1431,7 +1548,7 @@ public class ChooserActivity extends ResolverActivity { private final int mFillInFlags; private final float mModifiedScore; public ChooserTargetInfo(DisplayResolveInfo sourceInfo, ChooserTarget chooserTarget, SelectableTargetInfo(DisplayResolveInfo sourceInfo, ChooserTarget chooserTarget, float modifiedScore) { mSourceInfo = sourceInfo; mChooserTarget = chooserTarget; Loading Loading @@ -1460,7 +1577,7 @@ public class ChooserActivity extends ResolverActivity { mFillInFlags = 0; } private ChooserTargetInfo(ChooserTargetInfo other, Intent fillInIntent, int flags) { private SelectableTargetInfo(SelectableTargetInfo other, Intent fillInIntent, int flags) { mSourceInfo = other.mSourceInfo; mBackupResolveInfo = other.mBackupResolveInfo; mChooserTarget = other.mChooserTarget; Loading Loading @@ -1616,7 +1733,7 @@ public class ChooserActivity extends ResolverActivity { @Override public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) { return new ChooserTargetInfo(this, fillInIntent, flags); return new SelectableTargetInfo(this, fillInIntent, flags); } @Override Loading Loading @@ -1644,7 +1761,9 @@ public class ChooserActivity extends ResolverActivity { private static final int MAX_SERVICE_TARGETS = 4; private static final int MAX_TARGETS_PER_SERVICE = 2; private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>(); // Reserve spots for incoming direct share targets by adding placeholders private ChooserTargetInfo mPlaceHolderTargetInfo = new PlaceHolderTargetInfo(); private List<ChooserTargetInfo> mServiceTargets; private final List<TargetInfo> mCallerTargets = new ArrayList<>(); private boolean mShowServiceTargets; Loading @@ -1663,6 +1782,8 @@ public class ChooserActivity extends ResolverActivity { super(context, payloadIntents, null, rList, launchedFromUid, filterLastUsed, resolverListController); mServiceTargets = createPlaceHolders(); if (initialIntents != null) { final PackageManager pm = getPackageManager(); for (int i = 0; i < initialIntents.length; i++) { Loading Loading @@ -1715,6 +1836,14 @@ public class ChooserActivity extends ResolverActivity { } } private List<ChooserTargetInfo> createPlaceHolders() { List<ChooserTargetInfo> list = new ArrayList<>(); for (int i = 0; i < MAX_SERVICE_TARGETS; i++) { list.add(mPlaceHolderTargetInfo); } return list; } @Override public boolean showsExtendedInfo(TargetInfo info) { // We have badges so we don't need this text shown. Loading Loading @@ -1770,22 +1899,33 @@ public class ChooserActivity extends ResolverActivity { @Override public int getCount() { return super.getCount() + getServiceTargetCount() + getCallerTargetCount(); return super.getCount() + getSelectableServiceTargetCount() + getCallerTargetCount(); } @Override public int getUnfilteredCount() { return super.getUnfilteredCount() + getServiceTargetCount() + getCallerTargetCount(); return super.getUnfilteredCount() + getSelectableServiceTargetCount() + getCallerTargetCount(); } public int getCallerTargetCount() { return mCallerTargets.size(); } public int getServiceTargetCount() { if (!mShowServiceTargets) { return 0; /** * Filter out placeholders and non-selectable service targets */ public int getSelectableServiceTargetCount() { int count = 0; for (ChooserTargetInfo info : mServiceTargets) { if (info instanceof SelectableTargetInfo) { count++; } } return count; } public int getServiceTargetCount() { return Math.min(mServiceTargets.size(), MAX_SERVICE_TARGETS); } Loading Loading @@ -1831,7 +1971,8 @@ public class ChooserActivity extends ResolverActivity { } offset += callerTargetCount; final int serviceTargetCount = getServiceTargetCount(); final int serviceTargetCount = filtered ? getServiceTargetCount() : getSelectableServiceTargetCount(); if (position - offset < serviceTargetCount) { return mServiceTargets.get(position - offset); } Loading @@ -1850,8 +1991,14 @@ public class ChooserActivity extends ResolverActivity { if (mTargetsNeedPruning && targets.size() > 0) { // First proper update since we got an onListRebuilt() with (transient) 0 items. // Clear out the target list and rebuild. mServiceTargets.clear(); mServiceTargets = createPlaceHolders(); mTargetsNeedPruning = false; // Add back any app-supplied direct share targets that may have been // wiped by this clear if (mCallerChooserTargets != null) { addServiceResults(null, Lists.newArrayList(mCallerChooserTargets)); } } final float parentScore = getScore(origTarget); Loading @@ -1867,7 +2014,7 @@ public class ChooserActivity extends ResolverActivity { // This incents ChooserTargetServices to define what's truly better. targetScore = lastScore * 0.95f; } insertServiceTarget(new ChooserTargetInfo(origTarget, target, targetScore)); insertServiceTarget(new SelectableTargetInfo(origTarget, target, targetScore)); if (DEBUG) { Log.d(TAG, " => " + target.toString() + " score=" + targetScore Loading Loading @@ -1905,11 +2052,33 @@ public class ChooserActivity extends ResolverActivity { } } /** * Calling this marks service target loading complete, and will attempt to no longer * update the direct share area. */ public void completeServiceTargetLoading() { mServiceTargets.removeIf(o -> o instanceof PlaceHolderTargetInfo); if (mServiceTargets.isEmpty()) { mServiceTargets.add(new EmptyTargetInfo()); } notifyDataSetChanged(); } private void insertServiceTarget(ChooserTargetInfo chooserTargetInfo) { // Avoid inserting any potentially late results if (mServiceTargets.size() == 1 && mServiceTargets.get(0) instanceof EmptyTargetInfo) { return; } final float newScore = chooserTargetInfo.getModifiedScore(); for (int i = 0, N = mServiceTargets.size(); i < N; i++) { final ChooserTargetInfo serviceTarget = mServiceTargets.get(i); if (newScore > serviceTarget.getModifiedScore()) { if (serviceTarget == null) { mServiceTargets.set(i, chooserTargetInfo); return; } else if (newScore > serviceTarget.getModifiedScore()) { mServiceTargets.add(i, chooserTargetInfo); return; } Loading Loading @@ -1968,7 +2137,7 @@ public class ChooserActivity extends ResolverActivity { // There can be at most one row of service targets. public int getServiceTargetRowCount() { return (int) mChooserListAdapter.getServiceTargetCount() == 0 ? 0 : 1; return 1; } @Override Loading Loading @@ -2054,55 +2223,78 @@ public class ChooserActivity extends ResolverActivity { final int start = getFirstRowPosition(rowPosition); final int startType = mChooserListAdapter.getPositionTargetType(start); int end = start + mColumnCount - 1; while (mChooserListAdapter.getPositionTargetType(end) != startType && end >= start) { end--; } if (startType == ChooserListAdapter.TARGET_SERVICE) { int nextStartType = mChooserListAdapter.getPositionTargetType( getFirstRowPosition(rowPosition + 1)); int serviceSpacing = holder.row.getContext().getResources() .getDimensionPixelSize(R.dimen.chooser_service_spacing); if (rowPosition == 0 && nextStartType != ChooserListAdapter.TARGET_SERVICE) { // if the row is the only row for target service setVertPadding(holder, 0, 0); } else { int top = rowPosition == 0 ? serviceSpacing : 0; if (nextStartType != ChooserListAdapter.TARGET_SERVICE) { setVertPadding(holder, top, serviceSpacing); } else { setVertPadding(holder, top, 0); } } } else { holder.row.setBackgroundColor(Color.TRANSPARENT); int lastStartType = mChooserListAdapter.getPositionTargetType( final int lastStartType = mChooserListAdapter.getPositionTargetType( getFirstRowPosition(rowPosition - 1)); if (lastStartType == ChooserListAdapter.TARGET_SERVICE || rowPosition == 0) { int serviceSpacing = holder.row.getContext().getResources() .getDimensionPixelSize(R.dimen.chooser_service_spacing); setVertPadding(holder, serviceSpacing, 0); if (startType != lastStartType || rowPosition == 0) { holder.row.setBackground(mChooserRowLayer); setVertPadding(holder, mChooserRowServiceSpacing, 0); } else { holder.row.setBackground(null); setVertPadding(holder, 0, 0); } int end = start + mColumnCount - 1; while (mChooserListAdapter.getPositionTargetType(end) != startType && end >= start) { end--; } final int oldHeight = holder.row.getLayoutParams().height; holder.row.getLayoutParams().height = Math.max(1, holder.measuredRowHeight); if (holder.row.getLayoutParams().height != oldHeight) { holder.row.requestLayout(); if (end == start && mChooserListAdapter.getItem(start) instanceof EmptyTargetInfo) { final TextView textView = holder.row.findViewById(R.id.chooser_row_text_option); if (textView.getVisibility() != View.VISIBLE) { textView.setAlpha(0.0f); textView.setVisibility(View.VISIBLE); textView.setText(R.string.chooser_no_direct_share_targets); ValueAnimator fadeAnim = ObjectAnimator.ofFloat(textView, "alpha", 0.0f, 1.0f); fadeAnim.setInterpolator(new DecelerateInterpolator(1.0f)); float translationInPx = getResources().getDimensionPixelSize( R.dimen.chooser_row_text_option_translate); textView.setTranslationY(translationInPx); ValueAnimator translateAnim = ObjectAnimator.ofFloat(textView, "translationY", 0.0f); translateAnim.setInterpolator(new DecelerateInterpolator(1.0f)); AnimatorSet animSet = new AnimatorSet(); animSet.setDuration(NO_DIRECT_SHARE_ANIM_IN_MILLIS); animSet.setStartDelay(NO_DIRECT_SHARE_ANIM_IN_MILLIS); animSet.playTogether(fadeAnim, translateAnim); animSet.start(); } } for (int i = 0; i < mColumnCount; i++) { final View v = holder.cells[i]; if (start + i <= end) { v.setVisibility(View.VISIBLE); setCellVisibility(holder, i, View.VISIBLE); holder.itemIndices[i] = start + i; mChooserListAdapter.bindView(holder.itemIndices[i], v); } else { setCellVisibility(holder, i, View.INVISIBLE); } } } private void setCellVisibility(RowViewHolder holder, int i, int visibility) { final View v = holder.cells[i]; if (visibility == View.VISIBLE) { holder.cellVisibility[i] = true; v.setVisibility(visibility); v.setAlpha(1.0f); } else if (visibility == View.INVISIBLE && holder.cellVisibility[i]) { holder.cellVisibility[i] = false; ValueAnimator fadeAnim = ObjectAnimator.ofFloat(v, "alpha", 1.0f, 0f); fadeAnim.setDuration(NO_DIRECT_SHARE_ANIM_IN_MILLIS); fadeAnim.setInterpolator(new AccelerateInterpolator(1.0f)); fadeAnim.addListener(new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animation) { v.setVisibility(View.INVISIBLE); } }); fadeAnim.start(); } } Loading Loading @@ -2132,14 +2324,16 @@ public class ChooserActivity extends ResolverActivity { } static class RowViewHolder { final View[] cells; final ViewGroup row; public final View[] cells; public final boolean [] cellVisibility; public final ViewGroup row; int measuredRowHeight; int[] itemIndices; public RowViewHolder(ViewGroup row, int cellCount) { this.row = row; this.cells = new View[cellCount]; this.cellVisibility = new boolean[cellCount]; this.itemIndices = new int[cellCount]; } Loading Loading @@ -2217,8 +2411,6 @@ public class ChooserActivity extends ResolverActivity { mChooserActivity.unbindService(this); mChooserActivity.mServiceConnections.remove(this); if (mChooserActivity.mServiceConnections.isEmpty()) { mChooserActivity.mChooserHandler.removeMessages( CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); mChooserActivity.sendVoiceChoicesIfNeeded(); } mConnectedComponent = null; Loading core/res/res/drawable/chooser_row_layer_list.xml 0 → 100644 +25 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- /* ** Copyright 2019, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ --> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:bottom="-2dp" android:left="-2dp" android:right="-2dp"> <shape android:shape="rectangle"> <stroke android:width="1dp" android:color="@color/chooser_row_divider"/> </shape> </item> </layer-list> core/res/res/layout/chooser_row.xml +7 −1 Original line number Diff line number Diff line Loading @@ -23,6 +23,12 @@ android:gravity="start|top" android:paddingStart="@dimen/chooser_grid_padding" android:paddingEnd="@dimen/chooser_grid_padding"> <TextView android:id="@+id/chooser_row_text_option" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:layout_gravity="center" android:visibility="gone" /> </LinearLayout> core/res/res/values/colors.xml +2 −0 Original line number Diff line number Diff line Loading @@ -215,4 +215,6 @@ <!-- Magnifier --> <color name="default_magnifier_color_overlay">#00FFFFFF</color> <color name="chooser_row_divider">#1f000000</color> </resources> core/res/res/values/dimens.xml +1 −0 Original line number Diff line number Diff line Loading @@ -717,6 +717,7 @@ <!-- chooser (sharesheet) spacing --> <dimen name="chooser_corner_radius">8dp</dimen> <dimen name="chooser_row_text_option_translate">25dp</dimen> <dimen name="chooser_view_spacing">18dp</dimen> <dimen name="chooser_edge_margin_thin">16dp</dimen> <dimen name="chooser_edge_margin_normal">24dp</dimen> Loading Loading
core/java/com/android/internal/app/ChooserActivity.java +256 −64 Original line number Diff line number Diff line Loading @@ -18,6 +18,11 @@ package com.android.internal.app; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.IntDef; import android.app.Activity; import android.app.ActivityManager; Loading Loading @@ -90,6 +95,8 @@ import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.widget.AbsListView; import android.widget.BaseAdapter; import android.widget.ImageView; Loading Loading @@ -152,11 +159,17 @@ public class ChooserActivity extends ResolverActivity { private static final boolean USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS = true; private static final boolean USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS = true; /** * The transition time between placeholders for direct share to a message * indicating that non are available. */ private static final int NO_DIRECT_SHARE_ANIM_IN_MILLIS = 200; // TODO(b/121287224): Re-evaluate this limit private static final int SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20; private static final int QUERY_TARGET_SERVICE_LIMIT = 5; private static final int WATCHDOG_TIMEOUT_MILLIS = 2000; private static final int WATCHDOG_TIMEOUT_MILLIS = 3000; private Bundle mReplacementExtras; private IntentSender mChosenComponentSender; Loading @@ -172,6 +185,8 @@ public class ChooserActivity extends ResolverActivity { private ChooserListAdapter mChooserListAdapter; private ChooserRowAdapter mChooserRowAdapter; private Drawable mChooserRowLayer; private int mChooserRowServiceSpacing; private SharedPreferences mPinnedSharedPrefs; private static final float PINNED_TARGET_SCORE_BOOST = 1000.f; Loading Loading @@ -220,7 +235,6 @@ public class ChooserActivity extends ResolverActivity { sri.connection.destroy(); mServiceConnections.remove(sri.connection); if (mServiceConnections.isEmpty()) { mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); sendVoiceChoicesIfNeeded(); mChooserListAdapter.setShowServiceTargets(true); } Loading @@ -230,8 +244,12 @@ public class ChooserActivity extends ResolverActivity { if (DEBUG) { Log.d(TAG, "CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT; unbinding services"); } if (isDestroyed()) { break; } unbindRemainingServices(); sendVoiceChoicesIfNeeded(); mChooserListAdapter.completeServiceTargetLoading(); mChooserListAdapter.setShowServiceTargets(true); break; Loading Loading @@ -399,11 +417,17 @@ public class ChooserActivity extends ResolverActivity { .setExtras(extras) .build()); mAppPredictorCallback = resultList -> { if (isFinishing() || isDestroyed()) { return; } final List<DisplayResolveInfo> driList = getDisplayResolveInfos(mChooserListAdapter); final List<ShortcutManager.ShareShortcutInfo> shareShortcutInfos = new ArrayList<>(); for (AppTarget appTarget : resultList) { if (appTarget.getShortcutInfo() == null) { continue; } shareShortcutInfos.add(new ShortcutManager.ShareShortcutInfo( appTarget.getShortcutInfo(), new ComponentName( Loading @@ -414,6 +438,10 @@ public class ChooserActivity extends ResolverActivity { mAppPredictor.registerPredictionUpdates(this.getMainExecutor(), mAppPredictorCallback); } mChooserRowLayer = getResources().getDrawable(R.drawable.chooser_row_layer_list, null); mChooserRowServiceSpacing = getResources() .getDimensionPixelSize(R.dimen.chooser_service_spacing); if (DEBUG) { Log.d(TAG, "System Time Cost is " + systemCost); } Loading Loading @@ -919,6 +947,10 @@ public class ChooserActivity extends ResolverActivity { @Override protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) { if (target instanceof NotSelectableTargetInfo) { return false; } if (mRefinementIntentSender != null) { final Intent fillIn = new Intent(); final List<Intent> sourceIntents = target.getAllSourceIntents(); Loading Loading @@ -1064,14 +1096,14 @@ public class ChooserActivity extends ResolverActivity { } } if (!mServiceConnections.isEmpty()) { if (DEBUG) { Log.d(TAG, "queryTargets setting watchdog timer for " + WATCHDOG_TIMEOUT_MILLIS + "ms"); } mChooserHandler.sendEmptyMessageDelayed(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT, WATCHDOG_TIMEOUT_MILLIS); } else { if (mServiceConnections.isEmpty()) { sendVoiceChoicesIfNeeded(); } } Loading Loading @@ -1213,7 +1245,6 @@ public class ChooserActivity extends ResolverActivity { conn.destroy(); } mServiceConnections.clear(); mChooserHandler.removeMessages(CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); } public void onSetupVoiceInteraction() { Loading Loading @@ -1420,7 +1451,93 @@ public class ChooserActivity extends ResolverActivity { return null; } final class ChooserTargetInfo implements TargetInfo { interface ChooserTargetInfo extends TargetInfo { float getModifiedScore(); ChooserTarget getChooserTarget(); } /** * Distinguish between targets that selectable by the user, vs those that are * placeholders for the system while information is loading in an async manner. */ abstract class NotSelectableTargetInfo implements ChooserTargetInfo { public Intent getResolvedIntent() { return null; } public ComponentName getResolvedComponentName() { return null; } public boolean start(Activity activity, Bundle options) { return false; } public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) { return false; } public boolean startAsUser(Activity activity, Bundle options, UserHandle user) { return false; } public ResolveInfo getResolveInfo() { return null; } public CharSequence getDisplayLabel() { return null; } public CharSequence getExtendedInfo() { return null; } public Drawable getBadgeIcon() { return null; } public CharSequence getBadgeContentDescription() { return null; } public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) { return null; } public List<Intent> getAllSourceIntents() { return null; } public boolean isPinned() { return false; } public float getModifiedScore() { return 0.1f; } public ChooserTarget getChooserTarget() { return null; } } final class PlaceHolderTargetInfo extends NotSelectableTargetInfo { public Drawable getDisplayIcon() { return getDrawable(R.drawable.resolver_icon_placeholder); } } final class EmptyTargetInfo extends NotSelectableTargetInfo { public Drawable getDisplayIcon() { return null; } } final class SelectableTargetInfo implements ChooserTargetInfo { private final DisplayResolveInfo mSourceInfo; private final ResolveInfo mBackupResolveInfo; private final ChooserTarget mChooserTarget; Loading @@ -1431,7 +1548,7 @@ public class ChooserActivity extends ResolverActivity { private final int mFillInFlags; private final float mModifiedScore; public ChooserTargetInfo(DisplayResolveInfo sourceInfo, ChooserTarget chooserTarget, SelectableTargetInfo(DisplayResolveInfo sourceInfo, ChooserTarget chooserTarget, float modifiedScore) { mSourceInfo = sourceInfo; mChooserTarget = chooserTarget; Loading Loading @@ -1460,7 +1577,7 @@ public class ChooserActivity extends ResolverActivity { mFillInFlags = 0; } private ChooserTargetInfo(ChooserTargetInfo other, Intent fillInIntent, int flags) { private SelectableTargetInfo(SelectableTargetInfo other, Intent fillInIntent, int flags) { mSourceInfo = other.mSourceInfo; mBackupResolveInfo = other.mBackupResolveInfo; mChooserTarget = other.mChooserTarget; Loading Loading @@ -1616,7 +1733,7 @@ public class ChooserActivity extends ResolverActivity { @Override public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) { return new ChooserTargetInfo(this, fillInIntent, flags); return new SelectableTargetInfo(this, fillInIntent, flags); } @Override Loading Loading @@ -1644,7 +1761,9 @@ public class ChooserActivity extends ResolverActivity { private static final int MAX_SERVICE_TARGETS = 4; private static final int MAX_TARGETS_PER_SERVICE = 2; private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>(); // Reserve spots for incoming direct share targets by adding placeholders private ChooserTargetInfo mPlaceHolderTargetInfo = new PlaceHolderTargetInfo(); private List<ChooserTargetInfo> mServiceTargets; private final List<TargetInfo> mCallerTargets = new ArrayList<>(); private boolean mShowServiceTargets; Loading @@ -1663,6 +1782,8 @@ public class ChooserActivity extends ResolverActivity { super(context, payloadIntents, null, rList, launchedFromUid, filterLastUsed, resolverListController); mServiceTargets = createPlaceHolders(); if (initialIntents != null) { final PackageManager pm = getPackageManager(); for (int i = 0; i < initialIntents.length; i++) { Loading Loading @@ -1715,6 +1836,14 @@ public class ChooserActivity extends ResolverActivity { } } private List<ChooserTargetInfo> createPlaceHolders() { List<ChooserTargetInfo> list = new ArrayList<>(); for (int i = 0; i < MAX_SERVICE_TARGETS; i++) { list.add(mPlaceHolderTargetInfo); } return list; } @Override public boolean showsExtendedInfo(TargetInfo info) { // We have badges so we don't need this text shown. Loading Loading @@ -1770,22 +1899,33 @@ public class ChooserActivity extends ResolverActivity { @Override public int getCount() { return super.getCount() + getServiceTargetCount() + getCallerTargetCount(); return super.getCount() + getSelectableServiceTargetCount() + getCallerTargetCount(); } @Override public int getUnfilteredCount() { return super.getUnfilteredCount() + getServiceTargetCount() + getCallerTargetCount(); return super.getUnfilteredCount() + getSelectableServiceTargetCount() + getCallerTargetCount(); } public int getCallerTargetCount() { return mCallerTargets.size(); } public int getServiceTargetCount() { if (!mShowServiceTargets) { return 0; /** * Filter out placeholders and non-selectable service targets */ public int getSelectableServiceTargetCount() { int count = 0; for (ChooserTargetInfo info : mServiceTargets) { if (info instanceof SelectableTargetInfo) { count++; } } return count; } public int getServiceTargetCount() { return Math.min(mServiceTargets.size(), MAX_SERVICE_TARGETS); } Loading Loading @@ -1831,7 +1971,8 @@ public class ChooserActivity extends ResolverActivity { } offset += callerTargetCount; final int serviceTargetCount = getServiceTargetCount(); final int serviceTargetCount = filtered ? getServiceTargetCount() : getSelectableServiceTargetCount(); if (position - offset < serviceTargetCount) { return mServiceTargets.get(position - offset); } Loading @@ -1850,8 +1991,14 @@ public class ChooserActivity extends ResolverActivity { if (mTargetsNeedPruning && targets.size() > 0) { // First proper update since we got an onListRebuilt() with (transient) 0 items. // Clear out the target list and rebuild. mServiceTargets.clear(); mServiceTargets = createPlaceHolders(); mTargetsNeedPruning = false; // Add back any app-supplied direct share targets that may have been // wiped by this clear if (mCallerChooserTargets != null) { addServiceResults(null, Lists.newArrayList(mCallerChooserTargets)); } } final float parentScore = getScore(origTarget); Loading @@ -1867,7 +2014,7 @@ public class ChooserActivity extends ResolverActivity { // This incents ChooserTargetServices to define what's truly better. targetScore = lastScore * 0.95f; } insertServiceTarget(new ChooserTargetInfo(origTarget, target, targetScore)); insertServiceTarget(new SelectableTargetInfo(origTarget, target, targetScore)); if (DEBUG) { Log.d(TAG, " => " + target.toString() + " score=" + targetScore Loading Loading @@ -1905,11 +2052,33 @@ public class ChooserActivity extends ResolverActivity { } } /** * Calling this marks service target loading complete, and will attempt to no longer * update the direct share area. */ public void completeServiceTargetLoading() { mServiceTargets.removeIf(o -> o instanceof PlaceHolderTargetInfo); if (mServiceTargets.isEmpty()) { mServiceTargets.add(new EmptyTargetInfo()); } notifyDataSetChanged(); } private void insertServiceTarget(ChooserTargetInfo chooserTargetInfo) { // Avoid inserting any potentially late results if (mServiceTargets.size() == 1 && mServiceTargets.get(0) instanceof EmptyTargetInfo) { return; } final float newScore = chooserTargetInfo.getModifiedScore(); for (int i = 0, N = mServiceTargets.size(); i < N; i++) { final ChooserTargetInfo serviceTarget = mServiceTargets.get(i); if (newScore > serviceTarget.getModifiedScore()) { if (serviceTarget == null) { mServiceTargets.set(i, chooserTargetInfo); return; } else if (newScore > serviceTarget.getModifiedScore()) { mServiceTargets.add(i, chooserTargetInfo); return; } Loading Loading @@ -1968,7 +2137,7 @@ public class ChooserActivity extends ResolverActivity { // There can be at most one row of service targets. public int getServiceTargetRowCount() { return (int) mChooserListAdapter.getServiceTargetCount() == 0 ? 0 : 1; return 1; } @Override Loading Loading @@ -2054,55 +2223,78 @@ public class ChooserActivity extends ResolverActivity { final int start = getFirstRowPosition(rowPosition); final int startType = mChooserListAdapter.getPositionTargetType(start); int end = start + mColumnCount - 1; while (mChooserListAdapter.getPositionTargetType(end) != startType && end >= start) { end--; } if (startType == ChooserListAdapter.TARGET_SERVICE) { int nextStartType = mChooserListAdapter.getPositionTargetType( getFirstRowPosition(rowPosition + 1)); int serviceSpacing = holder.row.getContext().getResources() .getDimensionPixelSize(R.dimen.chooser_service_spacing); if (rowPosition == 0 && nextStartType != ChooserListAdapter.TARGET_SERVICE) { // if the row is the only row for target service setVertPadding(holder, 0, 0); } else { int top = rowPosition == 0 ? serviceSpacing : 0; if (nextStartType != ChooserListAdapter.TARGET_SERVICE) { setVertPadding(holder, top, serviceSpacing); } else { setVertPadding(holder, top, 0); } } } else { holder.row.setBackgroundColor(Color.TRANSPARENT); int lastStartType = mChooserListAdapter.getPositionTargetType( final int lastStartType = mChooserListAdapter.getPositionTargetType( getFirstRowPosition(rowPosition - 1)); if (lastStartType == ChooserListAdapter.TARGET_SERVICE || rowPosition == 0) { int serviceSpacing = holder.row.getContext().getResources() .getDimensionPixelSize(R.dimen.chooser_service_spacing); setVertPadding(holder, serviceSpacing, 0); if (startType != lastStartType || rowPosition == 0) { holder.row.setBackground(mChooserRowLayer); setVertPadding(holder, mChooserRowServiceSpacing, 0); } else { holder.row.setBackground(null); setVertPadding(holder, 0, 0); } int end = start + mColumnCount - 1; while (mChooserListAdapter.getPositionTargetType(end) != startType && end >= start) { end--; } final int oldHeight = holder.row.getLayoutParams().height; holder.row.getLayoutParams().height = Math.max(1, holder.measuredRowHeight); if (holder.row.getLayoutParams().height != oldHeight) { holder.row.requestLayout(); if (end == start && mChooserListAdapter.getItem(start) instanceof EmptyTargetInfo) { final TextView textView = holder.row.findViewById(R.id.chooser_row_text_option); if (textView.getVisibility() != View.VISIBLE) { textView.setAlpha(0.0f); textView.setVisibility(View.VISIBLE); textView.setText(R.string.chooser_no_direct_share_targets); ValueAnimator fadeAnim = ObjectAnimator.ofFloat(textView, "alpha", 0.0f, 1.0f); fadeAnim.setInterpolator(new DecelerateInterpolator(1.0f)); float translationInPx = getResources().getDimensionPixelSize( R.dimen.chooser_row_text_option_translate); textView.setTranslationY(translationInPx); ValueAnimator translateAnim = ObjectAnimator.ofFloat(textView, "translationY", 0.0f); translateAnim.setInterpolator(new DecelerateInterpolator(1.0f)); AnimatorSet animSet = new AnimatorSet(); animSet.setDuration(NO_DIRECT_SHARE_ANIM_IN_MILLIS); animSet.setStartDelay(NO_DIRECT_SHARE_ANIM_IN_MILLIS); animSet.playTogether(fadeAnim, translateAnim); animSet.start(); } } for (int i = 0; i < mColumnCount; i++) { final View v = holder.cells[i]; if (start + i <= end) { v.setVisibility(View.VISIBLE); setCellVisibility(holder, i, View.VISIBLE); holder.itemIndices[i] = start + i; mChooserListAdapter.bindView(holder.itemIndices[i], v); } else { setCellVisibility(holder, i, View.INVISIBLE); } } } private void setCellVisibility(RowViewHolder holder, int i, int visibility) { final View v = holder.cells[i]; if (visibility == View.VISIBLE) { holder.cellVisibility[i] = true; v.setVisibility(visibility); v.setAlpha(1.0f); } else if (visibility == View.INVISIBLE && holder.cellVisibility[i]) { holder.cellVisibility[i] = false; ValueAnimator fadeAnim = ObjectAnimator.ofFloat(v, "alpha", 1.0f, 0f); fadeAnim.setDuration(NO_DIRECT_SHARE_ANIM_IN_MILLIS); fadeAnim.setInterpolator(new AccelerateInterpolator(1.0f)); fadeAnim.addListener(new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animation) { v.setVisibility(View.INVISIBLE); } }); fadeAnim.start(); } } Loading Loading @@ -2132,14 +2324,16 @@ public class ChooserActivity extends ResolverActivity { } static class RowViewHolder { final View[] cells; final ViewGroup row; public final View[] cells; public final boolean [] cellVisibility; public final ViewGroup row; int measuredRowHeight; int[] itemIndices; public RowViewHolder(ViewGroup row, int cellCount) { this.row = row; this.cells = new View[cellCount]; this.cellVisibility = new boolean[cellCount]; this.itemIndices = new int[cellCount]; } Loading Loading @@ -2217,8 +2411,6 @@ public class ChooserActivity extends ResolverActivity { mChooserActivity.unbindService(this); mChooserActivity.mServiceConnections.remove(this); if (mChooserActivity.mServiceConnections.isEmpty()) { mChooserActivity.mChooserHandler.removeMessages( CHOOSER_TARGET_SERVICE_WATCHDOG_TIMEOUT); mChooserActivity.sendVoiceChoicesIfNeeded(); } mConnectedComponent = null; Loading
core/res/res/drawable/chooser_row_layer_list.xml 0 → 100644 +25 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- /* ** Copyright 2019, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ --> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:bottom="-2dp" android:left="-2dp" android:right="-2dp"> <shape android:shape="rectangle"> <stroke android:width="1dp" android:color="@color/chooser_row_divider"/> </shape> </item> </layer-list>
core/res/res/layout/chooser_row.xml +7 −1 Original line number Diff line number Diff line Loading @@ -23,6 +23,12 @@ android:gravity="start|top" android:paddingStart="@dimen/chooser_grid_padding" android:paddingEnd="@dimen/chooser_grid_padding"> <TextView android:id="@+id/chooser_row_text_option" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:layout_gravity="center" android:visibility="gone" /> </LinearLayout>
core/res/res/values/colors.xml +2 −0 Original line number Diff line number Diff line Loading @@ -215,4 +215,6 @@ <!-- Magnifier --> <color name="default_magnifier_color_overlay">#00FFFFFF</color> <color name="chooser_row_divider">#1f000000</color> </resources>
core/res/res/values/dimens.xml +1 −0 Original line number Diff line number Diff line Loading @@ -717,6 +717,7 @@ <!-- chooser (sharesheet) spacing --> <dimen name="chooser_corner_radius">8dp</dimen> <dimen name="chooser_row_text_option_translate">25dp</dimen> <dimen name="chooser_view_spacing">18dp</dimen> <dimen name="chooser_edge_margin_thin">16dp</dimen> <dimen name="chooser_edge_margin_normal">24dp</dimen> Loading