Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b367fb75 authored by Mady Mellor's avatar Mady Mellor
Browse files

Update shortcuts to match spec

Necessary information for a shortcut is label, icon, intent. This alters
how that information is retrieved from a Slice and falls back to using
the app icon / title / intent if any of the information is missing from
the slice ensuring that there is always an actionable shortcut.

Also adds notion of HINT_HIDDEN, allowing apps to exclude parts of a slice
from being visible in a template, note when creating a shortcut HINT_HIDDEN
is ignored.

Test: Manual with SlicesApp, test different varients of having HINT_TITLE
      and not having HINT_TITLE on an action / icon / label as well as
      not having an icon / label / or action.
Bug: 68378574

Change-Id: I1de9e502204dfb7fd83a7ec7622e8fd464146cfd
parent 97a0ae3a
Loading
Loading
Loading
Loading
+14 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.annotation.StringDef;
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.app.slice.widget.SliceView;
import android.content.ContentResolver;
import android.content.IContentProvider;
import android.graphics.drawable.Icon;
@@ -54,7 +55,12 @@ public final class Slice implements Parcelable {
    public @interface SliceHint{ }

    /**
     * Hint that this content is a title of other content in the slice.
     * Hint that this content is a title of other content in the slice. This can also indicate that
     * the content should be used in the shortcut representation of the slice (icon, label, action),
     * normally this should be indicated by adding the hint on the action containing that content.
     *
     * @see SliceView#MODE_SHORTCUT
     * @see SliceItem#TYPE_ACTION
     */
    public static final String HINT_TITLE       = "title";
    /**
@@ -99,6 +105,13 @@ public final class Slice implements Parcelable {
     * Hint to indicate that this content should not be tinted.
     */
    public static final String HINT_NO_TINT     = "no_tint";
    /**
     * Hint to indicate that this content should not be shown in the {@link SliceView#MODE_SMALL}
     * and {@link SliceView#MODE_LARGE} modes of SliceView. This content may be used to populate
     * the {@link SliceView#MODE_SHORTCUT} format of the slice.
     * @hide
     */
    public static final String HINT_HIDDEN = "hidden";
    /**
     * Hint to indicate that this slice is incomplete and an update will be sent once
     * loading is complete. Slices which contain HINT_PARTIAL will not be cached by the
+6 −0
Original line number Diff line number Diff line
@@ -126,6 +126,9 @@ public class GridView extends LinearLayout implements SliceListView {
     * Returns true if this item is just an image.
     */
    private boolean addItem(SliceItem item) {
        if (item.hasHint(Slice.HINT_HIDDEN)) {
            return false;
        }
        if (item.getType() == SliceItem.TYPE_IMAGE) {
            ImageView v = new ImageView(getContext());
            v.setImageIcon(item.getIcon());
@@ -145,6 +148,9 @@ public class GridView extends LinearLayout implements SliceListView {
                items.addAll(item.getSlice().getItems());
            }
            items.forEach(i -> {
                if (i.hasHint(Slice.HINT_HIDDEN)) {
                    return;
                }
                Context context = getContext();
                switch (i.getType()) {
                    case SliceItem.TYPE_TEXT:
+12 −3
Original line number Diff line number Diff line
@@ -85,9 +85,14 @@ public class LargeTemplateView extends SliceModeView {
            addList(slice, items);
        } else {
            slice.getItems().forEach(item -> {
                if (item.hasHint(Slice.HINT_ACTIONS)) {
                if (item.hasHint(Slice.HINT_HIDDEN)) {
                    // If it's hidden we don't show it
                    return;
                } else if (item.hasHint(Slice.HINT_ACTIONS)) {
                    // Action groups don't show in lists
                    return;
                } else if (item.getType() == SliceItem.TYPE_COLOR) {
                    // A color is not a list item
                    return;
                } else if (item.getType() == SliceItem.TYPE_SLICE
                        && item.hasHint(Slice.HINT_LIST)) {
@@ -108,8 +113,12 @@ public class LargeTemplateView extends SliceModeView {

    private void addList(Slice slice, List<SliceItem> items) {
        List<SliceItem> sliceItems = slice.getItems();
        sliceItems.forEach(i -> i.addHint(Slice.HINT_LIST_ITEM));
        items.addAll(sliceItems);
        sliceItems.forEach(i -> {
            if (!i.hasHint(Slice.HINT_HIDDEN) && i.getType() != SliceItem.TYPE_COLOR) {
                i.addHint(Slice.HINT_LIST_ITEM);
                items.add(i);
            }
        });
    }

    /**
+84 −15
Original line number Diff line number Diff line
@@ -24,13 +24,20 @@ import android.app.slice.SliceQuery;
import android.app.slice.widget.SliceView.SliceModeView;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.net.Uri;

import com.android.internal.R;

import java.util.List;

/**
 * @hide
 */
@@ -38,27 +45,26 @@ public class ShortcutView extends SliceModeView {

    private static final String TAG = "ShortcutView";

    private PendingIntent mAction;
    private Uri mUri;
    private PendingIntent mAction;
    private SliceItem mLabel;
    private SliceItem mIcon;

    private int mLargeIconSize;
    private int mSmallIconSize;

    public ShortcutView(Context context) {
        super(context);
        mSmallIconSize = getContext().getResources().getDimensionPixelSize(R.dimen.slice_icon_size);
        final Resources res = getResources();
        mSmallIconSize = res.getDimensionPixelSize(R.dimen.slice_icon_size);
        mLargeIconSize = res.getDimensionPixelSize(R.dimen.slice_shortcut_size);
    }

    @Override
    public void setSlice(Slice slice) {
        removeAllViews();
        SliceItem sliceItem = SliceQuery.find(slice, SliceItem.TYPE_ACTION);
        SliceItem iconItem = SliceQuery.getPrimaryIcon(slice);
        SliceItem textItem = sliceItem != null
                ? SliceQuery.find(sliceItem, SliceItem.TYPE_TEXT)
                : SliceQuery.find(slice, SliceItem.TYPE_TEXT);
        SliceItem colorItem = sliceItem != null
                ? SliceQuery.find(sliceItem, SliceItem.TYPE_COLOR)
                : SliceQuery.find(slice, SliceItem.TYPE_COLOR);
        determineShortcutItems(getContext(), slice);
        SliceItem colorItem = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
        if (colorItem == null) {
            colorItem = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
        }
@@ -67,13 +73,11 @@ public class ShortcutView extends SliceModeView {
        ShapeDrawable circle = new ShapeDrawable(new OvalShape());
        circle.setTint(color);
        setBackground(circle);
        if (iconItem != null) {
            final boolean isLarge = iconItem.hasHint(Slice.HINT_LARGE);
        if (mIcon != null) {
            final boolean isLarge = mIcon.hasHint(Slice.HINT_LARGE);
            final int iconSize = isLarge ? mLargeIconSize : mSmallIconSize;
            SliceViewUtil.createCircledIcon(getContext(), color, iconSize, iconItem.getIcon(),
            SliceViewUtil.createCircledIcon(getContext(), color, iconSize, mIcon.getIcon(),
                    isLarge, this /* parent */);
            mAction = sliceItem != null ? sliceItem.getAction()
                    : null;
            mUri = slice.getUri();
            setClickable(true);
        } else {
@@ -103,4 +107,69 @@ public class ShortcutView extends SliceModeView {
        }
        return true;
    }

    /**
     * Looks at the slice and determines which items are best to use to compose the shortcut.
     */
    private void determineShortcutItems(Context context, Slice slice) {
        List<String> h = slice.getHints();
        SliceItem sliceItem = new SliceItem(slice, SliceItem.TYPE_SLICE,
                h.toArray(new String[h.size()]));
        SliceItem titleItem = SliceQuery.find(slice, SliceItem.TYPE_ACTION,
                Slice.HINT_TITLE, null);

        if (titleItem != null) {
            // Preferred case: hinted action containing hinted image and text
            mAction = titleItem.getAction();
            mIcon = SliceQuery.find(titleItem.getSlice(), SliceItem.TYPE_IMAGE, Slice.HINT_TITLE,
                    null);
            mLabel = SliceQuery.find(titleItem.getSlice(), SliceItem.TYPE_TEXT, Slice.HINT_TITLE,
                    null);
        } else {
            // No hinted action; just use the first one
            SliceItem actionItem = SliceQuery.find(sliceItem, SliceItem.TYPE_ACTION, (String) null,
                    null);
            mAction = (actionItem != null) ? actionItem.getAction() : null;
        }
        // First fallback: any hinted image and text
        if (mIcon == null) {
            mIcon = SliceQuery.find(sliceItem, SliceItem.TYPE_IMAGE, Slice.HINT_TITLE,
                    null);
        }
        if (mLabel == null) {
            mLabel = SliceQuery.find(sliceItem, SliceItem.TYPE_TEXT, Slice.HINT_TITLE,
                    null);
        }
        // Second fallback: first image and text
        if (mIcon == null) {
            mIcon = SliceQuery.find(sliceItem, SliceItem.TYPE_IMAGE, (String) null,
                    null);
        }
        if (mLabel == null) {
            mLabel = SliceQuery.find(sliceItem, SliceItem.TYPE_TEXT, (String) null,
                    null);
        }
        // Final fallback: use app info
        if (mIcon == null || mLabel == null || mAction == null) {
            PackageManager pm = context.getPackageManager();
            ProviderInfo providerInfo = pm.resolveContentProvider(
                    slice.getUri().getAuthority(), 0);
            ApplicationInfo appInfo = providerInfo.applicationInfo;
            if (appInfo != null) {
                if (mIcon == null) {
                    Drawable icon = appInfo.loadDefaultIcon(pm);
                    mIcon = new SliceItem(SliceViewUtil.createIconFromDrawable(icon),
                            SliceItem.TYPE_IMAGE, new String[] {Slice.HINT_LARGE});
                }
                if (mLabel == null) {
                    mLabel = new SliceItem(pm.getApplicationLabel(appInfo),
                            SliceItem.TYPE_TEXT, null);
                }
                if (mAction == null) {
                    mAction = PendingIntent.getActivity(context, 0,
                            pm.getLaunchIntentForPackage(appInfo.packageName), 0);
                }
            }
        }
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -115,7 +115,9 @@ public class SliceView extends ViewGroup {
     */
    public static final String MODE_LARGE       = "SLICE_LARGE";
    /**
     * Mode indicating this slice should be presented as an icon.
     * Mode indicating this slice should be presented as an icon. A shortcut requires an intent,
     * icon, and label. This can be indicated by using {@link Slice#HINT_TITLE} on an action in a
     * slice.
     */
    public static final String MODE_SHORTCUT    = "SLICE_ICON";

Loading