Merge "Open dialog panel after long pressing a direct share target to allow pin/unpin the target. Communicate with ShortcutManager/Launcherapps to store pin info in ShortcutService."
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 595c963..3cb39e7 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -1620,29 +1620,48 @@
return getIntent().getBooleanExtra(Intent.EXTRA_AUTO_LAUNCH_SINGLE_CHOICE, true);
}
- private void showTargetDetails(DisplayResolveInfo ti) {
- if (ti == null) return;
+ private void showTargetDetails(TargetInfo targetInfo) {
+ if (targetInfo == null) return;
ArrayList<DisplayResolveInfo> targetList;
+ ChooserTargetActionsDialogFragment fragment = new ChooserTargetActionsDialogFragment();
+ Bundle bundle = new Bundle();
- // For multiple targets, include info on all targets
- if (ti instanceof MultiDisplayResolveInfo) {
- MultiDisplayResolveInfo mti = (MultiDisplayResolveInfo) ti;
+ if (targetInfo instanceof SelectableTargetInfo) {
+ SelectableTargetInfo selectableTargetInfo = (SelectableTargetInfo) targetInfo;
+ if (selectableTargetInfo.getDisplayResolveInfo() == null
+ || selectableTargetInfo.getChooserTarget() == null) {
+ Log.e(TAG, "displayResolveInfo or chooserTarget in selectableTargetInfo are null");
+ return;
+ }
+ targetList = new ArrayList<>();
+ targetList.add(selectableTargetInfo.getDisplayResolveInfo());
+ bundle.putString(ChooserTargetActionsDialogFragment.SHORTCUT_ID_KEY,
+ selectableTargetInfo.getChooserTarget().getIntentExtras().getString(
+ Intent.EXTRA_SHORTCUT_ID));
+ bundle.putBoolean(ChooserTargetActionsDialogFragment.IS_SHORTCUT_PINNED_KEY,
+ selectableTargetInfo.isPinned());
+ bundle.putParcelable(ChooserTargetActionsDialogFragment.INTENT_FILTER_KEY,
+ getTargetIntentFilter());
+ if (selectableTargetInfo.getDisplayLabel() != null) {
+ bundle.putString(ChooserTargetActionsDialogFragment.SHORTCUT_TITLE_KEY,
+ selectableTargetInfo.getDisplayLabel().toString());
+ }
+ } else if (targetInfo instanceof MultiDisplayResolveInfo) {
+ // For multiple targets, include info on all targets
+ MultiDisplayResolveInfo mti = (MultiDisplayResolveInfo) targetInfo;
targetList = mti.getTargets();
} else {
targetList = new ArrayList<DisplayResolveInfo>();
- targetList.add(ti);
+ targetList.add((DisplayResolveInfo) targetInfo);
}
-
- ChooserTargetActionsDialogFragment f = new ChooserTargetActionsDialogFragment();
- Bundle b = new Bundle();
- b.putParcelable(ChooserTargetActionsDialogFragment.USER_HANDLE_KEY,
+ bundle.putParcelable(ChooserTargetActionsDialogFragment.USER_HANDLE_KEY,
mChooserMultiProfilePagerAdapter.getCurrentUserHandle());
- b.putParcelableArrayList(ChooserTargetActionsDialogFragment.TARGET_INFOS_KEY,
+ bundle.putParcelableArrayList(ChooserTargetActionsDialogFragment.TARGET_INFOS_KEY,
targetList);
- f.setArguments(b);
+ fragment.setArguments(bundle);
- f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
+ fragment.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG);
}
private void modifyTargetIntent(Intent in) {
@@ -2868,8 +2887,10 @@
private boolean shouldShowTargetDetails(TargetInfo ti) {
ComponentName nearbyShare = getNearbySharingComponent();
// Suppress target details for nearby share to hide pin/unpin action
- return !(nearbyShare != null && nearbyShare.equals(ti.getResolvedComponentName())
- && shouldNearbyShareBeFirstInRankedRow());
+ boolean isNearbyShare = nearbyShare != null && nearbyShare.equals(
+ ti.getResolvedComponentName()) && shouldNearbyShareBeFirstInRankedRow();
+ return ti instanceof SelectableTargetInfo
+ || (ti instanceof DisplayResolveInfo && !isNearbyShare);
}
/**
@@ -2966,8 +2987,6 @@
private DirectShareViewHolder mDirectShareViewHolder;
private int mChooserTargetWidth = 0;
private boolean mShowAzLabelIfPoss;
-
- private boolean mHideContentPreview = false;
private boolean mLayoutRequested = false;
private int mFooterHeight = 0;
@@ -3038,7 +3057,6 @@
* personal and work tabs.
*/
public void hideContentPreview() {
- mHideContentPreview = true;
mLayoutRequested = true;
notifyDataSetChanged();
}
@@ -3228,18 +3246,15 @@
}
});
- // Direct Share targets should not show any menu
- if (!isDirectShare) {
- v.setOnLongClickListener(v1 -> {
- final TargetInfo ti = mChooserListAdapter.targetInfoForPosition(
- holder.getItemIndex(column), true);
- // This should always be the case for non-DS targets, check for validity
- if (ti instanceof DisplayResolveInfo && shouldShowTargetDetails(ti)) {
- showTargetDetails((DisplayResolveInfo) ti);
- }
- return true;
- });
- }
+ // Show menu for both direct share and app share targets after long click.
+ v.setOnLongClickListener(v1 -> {
+ TargetInfo ti = mChooserListAdapter.targetInfoForPosition(
+ holder.getItemIndex(column), true);
+ if (shouldShowTargetDetails(ti)) {
+ showTargetDetails(ti);
+ }
+ return true;
+ });
holder.addView(i, v);
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index 140fabc..f6445849 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -74,6 +74,7 @@
public static final float CALLER_TARGET_SCORE_BOOST = 900.f;
/** {@link #getBaseScore} */
public static final float SHORTCUT_TARGET_SCORE_BOOST = 90.f;
+ private static final float PINNED_SHORTCUT_TARGET_SCORE_BOOST = 1000.f;
private final int mMaxShortcutTargetsPerApp;
private final ChooserListCommunicator mChooserListCommunicator;
@@ -275,8 +276,10 @@
Drawable bkg = mContext.getDrawable(R.drawable.chooser_group_background);
holder.text.setPaddingRelative(0, 0, bkg.getIntrinsicWidth() /* end */, 0);
holder.text.setBackground(bkg);
- } else if (info.isPinned() && getPositionTargetType(position) == TARGET_STANDARD) {
- // If the target is pinned and in the suggested row show a pinned indicator
+ } else if (info.isPinned() && (getPositionTargetType(position) == TARGET_STANDARD
+ || getPositionTargetType(position) == TARGET_SERVICE)) {
+ // If the appShare or directShare target is pinned and in the suggested row show a
+ // pinned indicator
Drawable bkg = mContext.getDrawable(R.drawable.chooser_pinned_background);
holder.text.setPaddingRelative(bkg.getIntrinsicWidth() /* start */, 0, 0, 0);
holder.text.setBackground(bkg);
@@ -523,11 +526,16 @@
targetScore = lastScore * 0.95f;
}
}
+ ShortcutInfo shortcutInfo = isShortcutResult ? directShareToShortcutInfos.get(target)
+ : null;
+ if ((shortcutInfo != null) && shortcutInfo.isPinned()) {
+ targetScore += PINNED_SHORTCUT_TARGET_SCORE_BOOST;
+ }
UserHandle userHandle = getUserHandle();
Context contextAsUser = mContext.createContextAsUser(userHandle, 0 /* flags */);
boolean isInserted = insertServiceTarget(new SelectableTargetInfo(contextAsUser,
origTarget, target, targetScore, mSelectableTargetInfoCommunicator,
- (isShortcutResult ? directShareToShortcutInfos.get(target) : null)));
+ shortcutInfo));
if (isInserted && isShortcutResult) {
mNumShortcutResults++;
diff --git a/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java b/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java
index 9afc0e9..4f1f380 100644
--- a/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java
+++ b/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java
@@ -29,9 +29,14 @@
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.ComponentName;
+import android.content.Context;
import android.content.DialogInterface;
+import android.content.IntentFilter;
import android.content.SharedPreferences;
+import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
@@ -51,6 +56,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
+import java.util.stream.Collectors;
/**
* Shows a dialog with actions to take on a chooser target.
@@ -60,9 +66,17 @@
protected ArrayList<DisplayResolveInfo> mTargetInfos = new ArrayList<>();
protected UserHandle mUserHandle;
+ protected String mShortcutId;
+ protected String mShortcutTitle;
+ protected boolean mIsShortcutPinned;
+ protected IntentFilter mIntentFilter;
public static final String USER_HANDLE_KEY = "user_handle";
public static final String TARGET_INFOS_KEY = "target_infos";
+ public static final String SHORTCUT_ID_KEY = "shortcut_id";
+ public static final String SHORTCUT_TITLE_KEY = "shortcut_title";
+ public static final String IS_SHORTCUT_PINNED_KEY = "is_shortcut_pinned";
+ public static final String INTENT_FILTER_KEY = "intent_filter";
public ChooserTargetActionsDialogFragment() {}
@@ -79,6 +93,10 @@
void setStateFromBundle(Bundle b) {
mTargetInfos = (ArrayList<DisplayResolveInfo>) b.get(TARGET_INFOS_KEY);
mUserHandle = (UserHandle) b.get(USER_HANDLE_KEY);
+ mShortcutId = b.getString(SHORTCUT_ID_KEY);
+ mShortcutTitle = b.getString(SHORTCUT_TITLE_KEY);
+ mIsShortcutPinned = b.getBoolean(IS_SHORTCUT_PINNED_KEY);
+ mIntentFilter = (IntentFilter) b.get(INTENT_FILTER_KEY);
}
@Override
@@ -89,6 +107,11 @@
mUserHandle);
outState.putParcelableArrayList(ChooserTargetActionsDialogFragment.TARGET_INFOS_KEY,
mTargetInfos);
+ outState.putString(ChooserTargetActionsDialogFragment.SHORTCUT_ID_KEY, mShortcutId);
+ outState.putBoolean(ChooserTargetActionsDialogFragment.IS_SHORTCUT_PINNED_KEY,
+ mIsShortcutPinned);
+ outState.putString(ChooserTargetActionsDialogFragment.SHORTCUT_TITLE_KEY, mShortcutTitle);
+ outState.putParcelable(ChooserTargetActionsDialogFragment.INTENT_FILTER_KEY, mIntentFilter);
}
/**
@@ -121,7 +144,7 @@
RecyclerView rv = v.findViewById(R.id.listContainer);
final ResolveInfoPresentationGetter pg = getProvidingAppPresentationGetter();
- title.setText(pg.getLabel());
+ title.setText(isShortcutTarget() ? mShortcutTitle : pg.getLabel());
icon.setImageDrawable(pg.getIcon(mUserHandle));
rv.setAdapter(new VHAdapter(items));
@@ -180,11 +203,45 @@
@Override
public void onClick(DialogInterface dialog, int which) {
- pinComponent(mTargetInfos.get(which).getResolvedComponentName());
+ if (isShortcutTarget()) {
+ toggleShortcutPinned(mTargetInfos.get(which).getResolvedComponentName());
+ } else {
+ pinComponent(mTargetInfos.get(which).getResolvedComponentName());
+ }
((ChooserActivity) getActivity()).handlePackagesChanged();
dismiss();
}
+ private void toggleShortcutPinned(ComponentName name) {
+ if (mIntentFilter == null) {
+ return;
+ }
+ // Fetch existing pinned shortcuts of the given package.
+ List<String> pinnedShortcuts = getPinnedShortcutsFromPackageAsUser(getContext(),
+ mUserHandle, mIntentFilter, name.getPackageName());
+ // If the shortcut has already been pinned, unpin it; otherwise, pin it.
+ if (mIsShortcutPinned) {
+ pinnedShortcuts.remove(mShortcutId);
+ } else {
+ pinnedShortcuts.add(mShortcutId);
+ }
+ // Update pinned shortcut list in ShortcutService via LauncherApps
+ getContext().getSystemService(LauncherApps.class).pinShortcuts(
+ name.getPackageName(), pinnedShortcuts, mUserHandle);
+ }
+
+ private static List<String> getPinnedShortcutsFromPackageAsUser(Context context,
+ UserHandle user, IntentFilter filter, String packageName) {
+ Context contextAsUser = context.createContextAsUser(user, 0 /* flags */);
+ List<ShortcutManager.ShareShortcutInfo> targets = contextAsUser.getSystemService(
+ ShortcutManager.class).getShareTargets(filter);
+ return targets.stream()
+ .map(ShortcutManager.ShareShortcutInfo::getShortcutInfo)
+ .filter(s -> s.isPinned() && s.getPackage().equals(packageName))
+ .map(ShortcutInfo::getId)
+ .collect(Collectors.toList());
+ }
+
private void pinComponent(ComponentName name) {
SharedPreferences sp = ChooserActivity.getPinnedSharedPrefs(getContext());
final String key = name.flattenToString();
@@ -211,12 +268,13 @@
@NonNull
protected CharSequence getItemLabel(DisplayResolveInfo dri) {
final PackageManager pm = getContext().getPackageManager();
- return getPinLabel(dri.isPinned(), dri.getResolveInfo().loadLabel(pm));
+ return getPinLabel(isPinned(dri),
+ isShortcutTarget() ? "" : dri.getResolveInfo().loadLabel(pm));
}
@Nullable
protected Drawable getItemIcon(DisplayResolveInfo dri) {
- return getPinIcon(dri.isPinned());
+ return getPinIcon(isPinned(dri));
}
private ResolveInfoPresentationGetter getProvidingAppPresentationGetter() {
@@ -229,4 +287,11 @@
mTargetInfos.get(0).getResolveInfo());
}
+ private boolean isPinned(DisplayResolveInfo dri) {
+ return isShortcutTarget() ? mIsShortcutPinned : dri.isPinned();
+ }
+
+ private boolean isShortcutTarget() {
+ return mShortcutId != null;
+ }
}
diff --git a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
index 0153363..264e4f7 100644
--- a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
+++ b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
@@ -64,6 +64,7 @@
private Drawable mDisplayIcon;
private final Intent mFillInIntent;
private final int mFillInFlags;
+ private final boolean mIsPinned;
private final float mModifiedScore;
private boolean mIsSuspended = false;
@@ -77,6 +78,7 @@
mModifiedScore = modifiedScore;
mPm = mContext.getPackageManager();
mSelectableTargetInfoCommunicator = selectableTargetInfoComunicator;
+ mIsPinned = shortcutInfo != null && shortcutInfo.isPinned();
if (sourceInfo != null) {
final ResolveInfo ri = sourceInfo.getResolveInfo();
if (ri != null) {
@@ -120,6 +122,7 @@
mFillInIntent = fillInIntent;
mFillInFlags = flags;
mModifiedScore = other.mModifiedScore;
+ mIsPinned = other.mIsPinned;
mDisplayLabel = sanitizeDisplayLabel(mChooserTarget.getTitle());
}
@@ -292,7 +295,7 @@
@Override
public boolean isPinned() {
- return mSourceInfo != null && mSourceInfo.isPinned();
+ return mIsPinned;
}
/**