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

Commit 625a64c2 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "First version of Slice data APIs (hidden for now)"

parents e0fa2c04 d9edfa94
Loading
Loading
Loading
Loading
+35 −1
Original line number Diff line number Diff line
@@ -47,6 +47,8 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.slice.Slice;
import android.slice.SliceProvider;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
@@ -178,6 +180,8 @@ public abstract class ContentResolver {
    public static final Intent ACTION_SYNC_CONN_STATUS_CHANGED =
            new Intent("com.android.sync.SYNC_CONN_STATUS_CHANGED");

    /** @hide */
    public static final String SCHEME_SLICE = "slice";
    public static final String SCHEME_CONTENT = "content";
    public static final String SCHEME_ANDROID_RESOURCE = "android.resource";
    public static final String SCHEME_FILE = "file";
@@ -1717,6 +1721,36 @@ public abstract class ContentResolver {
        }
    }

    /**
     * Turns a slice Uri into slice content.
     *
     * @param uri The URI to a slice provider
     * @return The Slice provided by the app or null if none is given.
     * @see Slice
     * @hide
     */
    public final @Nullable Slice bindSlice(@NonNull Uri uri) {
        Preconditions.checkNotNull(uri, "uri");
        IContentProvider provider = acquireProvider(uri);
        if (provider == null) {
            throw new IllegalArgumentException("Unknown URI " + uri);
        }
        try {
            Bundle extras = new Bundle();
            extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
            final Bundle res = provider.call(mPackageName, SliceProvider.METHOD_SLICE, null,
                    extras);
            Bundle.setDefusable(res, true);
            return res.getParcelable(SliceProvider.EXTRA_SLICE);
        } catch (RemoteException e) {
            // Arbitrary and not worth documenting, as Activity
            // Manager will kill this process shortly anyway.
            return null;
        } finally {
            releaseProvider(provider);
        }
    }

    /**
     * Returns the content provider for the given content URI.
     *
@@ -1725,7 +1759,7 @@ public abstract class ContentResolver {
     * @hide
     */
    public final IContentProvider acquireProvider(Uri uri) {
        if (!SCHEME_CONTENT.equals(uri.getScheme())) {
        if (!SCHEME_CONTENT.equals(uri.getScheme()) && !SCHEME_SLICE.equals(uri.getScheme())) {
            return null;
        }
        final String auth = uri.getAuthority();
+311 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.
 */

package android.slice;

import static android.slice.SliceItem.TYPE_ACTION;
import static android.slice.SliceItem.TYPE_COLOR;
import static android.slice.SliceItem.TYPE_IMAGE;
import static android.slice.SliceItem.TYPE_REMOTE_INPUT;
import static android.slice.SliceItem.TYPE_REMOTE_VIEW;
import static android.slice.SliceItem.TYPE_SLICE;
import static android.slice.SliceItem.TYPE_TEXT;
import static android.slice.SliceItem.TYPE_TIMESTAMP;

import android.annotation.NonNull;
import android.annotation.StringDef;
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.widget.RemoteViews;

import java.util.ArrayList;
import java.util.Arrays;

/**
 * A slice is a piece of app content and actions that can be surfaced outside of the app.
 *
 * <p>They are constructed using {@link Builder} in a tree structure
 * that provides the OS some information about how the content should be displayed.
 * @hide
 */
public final class Slice implements Parcelable {

    /**
     * @hide
     */
    @StringDef({HINT_TITLE, HINT_LIST, HINT_LIST_ITEM, HINT_LARGE, HINT_ACTIONS, HINT_SELECTED,
            HINT_SOURCE, HINT_MESSAGE, HINT_HORIZONTAL, HINT_NO_TINT})
    public @interface SliceHint{ }

    /**
     * Hint that this content is a title of other content in the slice.
     */
    public static final String HINT_TITLE       = "title";
    /**
     * Hint that all sub-items/sub-slices within this content should be considered
     * to have {@link #HINT_LIST_ITEM}.
     */
    public static final String HINT_LIST        = "list";
    /**
     * Hint that this item is part of a list and should be formatted as if is part
     * of a list.
     */
    public static final String HINT_LIST_ITEM   = "list_item";
    /**
     * Hint that this content is important and should be larger when displayed if
     * possible.
     */
    public static final String HINT_LARGE       = "large";
    /**
     * Hint that this slice contains a number of actions that can be grouped together
     * in a sort of controls area of the UI.
     */
    public static final String HINT_ACTIONS     = "actions";
    /**
     * Hint indicating that this item (and its sub-items) are the current selection.
     */
    public static final String HINT_SELECTED    = "selected";
    /**
     * Hint to indicate that this is a message as part of a communication
     * sequence in this slice.
     */
    public static final String HINT_MESSAGE     = "message";
    /**
     * Hint to tag the source (i.e. sender) of a {@link #HINT_MESSAGE}.
     */
    public static final String HINT_SOURCE      = "source";
    /**
     * Hint that list items within this slice or subslice would appear better
     * if organized horizontally.
     */
    public static final String HINT_HORIZONTAL  = "horizontal";
    /**
     * Hint to indicate that this content should not be tinted.
     */
    public static final String HINT_NO_TINT     = "no_tint";

    // These two are coming over from prototyping, but we probably don't want in
    // public API, at least not right now.
    /**
     * @hide
     */
    public static final String HINT_ALT         = "alt";
    /**
     * @hide
     */
    public static final String HINT_PARTIAL     = "partial";

    private final SliceItem[] mItems;
    private final @SliceHint String[] mHints;
    private Uri mUri;

    /**
     * @hide
     */
    public Slice(ArrayList<SliceItem> items, @SliceHint String[] hints, Uri uri) {
        mHints = hints;
        mItems = items.toArray(new SliceItem[items.size()]);
        mUri = uri;
    }

    protected Slice(Parcel in) {
        mHints = in.readStringArray();
        int n = in.readInt();
        mItems = new SliceItem[n];
        for (int i = 0; i < n; i++) {
            mItems[i] = SliceItem.CREATOR.createFromParcel(in);
        }
        mUri = Uri.CREATOR.createFromParcel(in);
    }

    /**
     * @return The Uri that this slice represents.
     */
    public Uri getUri() {
        return mUri;
    }

    /**
     * @return All child {@link SliceItem}s that this Slice contains.
     */
    public SliceItem[] getItems() {
        return mItems;
    }

    /**
     * @return All hints associated with this Slice.
     */
    public @SliceHint String[] getHints() {
        return mHints;
    }

    /**
     * @hide
     */
    public SliceItem getPrimaryIcon() {
        for (SliceItem item : getItems()) {
            if (item.getType() == TYPE_IMAGE) {
                return item;
            }
            if (!(item.getType() == TYPE_SLICE && item.hasHint(Slice.HINT_LIST))
                    && !item.hasHint(Slice.HINT_ACTIONS)
                    && !item.hasHint(Slice.HINT_LIST_ITEM)
                    && (item.getType() != TYPE_ACTION)) {
                SliceItem icon = SliceQuery.find(item, TYPE_IMAGE);
                if (icon != null) return icon;
            }
        }
        return null;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeStringArray(mHints);
        dest.writeInt(mItems.length);
        for (int i = 0; i < mItems.length; i++) {
            mItems[i].writeToParcel(dest, flags);
        }
        mUri.writeToParcel(dest, 0);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    /**
     * A Builder used to construct {@link Slice}s
     */
    public static class Builder {

        private final Uri mUri;
        private ArrayList<SliceItem> mItems = new ArrayList<>();
        private @SliceHint ArrayList<String> mHints = new ArrayList<>();

        /**
         * Create a builder which will construct a {@link Slice} for the Given Uri.
         * @param uri Uri to tag for this slice.
         */
        public Builder(@NonNull Uri uri) {
            mUri = uri;
        }

        /**
         * Create a builder for a {@link Slice} that is a sub-slice of the slice
         * being constructed by the provided builder.
         * @param parent The builder constructing the parent slice
         */
        public Builder(@NonNull Slice.Builder parent) {
            mUri = parent.mUri.buildUpon().appendPath("_gen")
                    .appendPath(String.valueOf(mItems.size())).build();
        }

        /**
         * Add hints to the Slice being constructed
         */
        public Builder addHints(@SliceHint String... hints) {
            mHints.addAll(Arrays.asList(hints));
            return this;
        }

        /**
         * Add a sub-slice to the slice being constructed
         */
        public Builder addSubSlice(@NonNull Slice slice) {
            mItems.add(new SliceItem(slice, TYPE_SLICE, slice.getHints()));
            return this;
        }

        /**
         * Add an action to the slice being constructed
         */
        public Slice.Builder addAction(@NonNull PendingIntent action, @NonNull Slice s) {
            mItems.add(new SliceItem(action, s, TYPE_ACTION, new String[0]));
            return this;
        }

        /**
         * Add text to the slice being constructed
         */
        public Builder addText(CharSequence text, @SliceHint String... hints) {
            mItems.add(new SliceItem(text, TYPE_TEXT, hints));
            return this;
        }

        /**
         * Add an image to the slice being constructed
         */
        public Builder addIcon(Icon icon, @SliceHint String... hints) {
            mItems.add(new SliceItem(icon, TYPE_IMAGE, hints));
            return this;
        }

        /**
         * @hide This isn't final
         */
        public Builder addRemoteView(RemoteViews remoteView, @SliceHint String... hints) {
            mItems.add(new SliceItem(remoteView, TYPE_REMOTE_VIEW, hints));
            return this;
        }

        /**
         * Add remote input to the slice being constructed
         */
        public Slice.Builder addRemoteInput(RemoteInput remoteInput, @SliceHint String... hints) {
            mItems.add(new SliceItem(remoteInput, TYPE_REMOTE_INPUT, hints));
            return this;
        }

        /**
         * Add a color to the slice being constructed
         */
        public Builder addColor(int color, @SliceHint String... hints) {
            mItems.add(new SliceItem(color, TYPE_COLOR, hints));
            return this;
        }

        /**
         * Add a timestamp to the slice being constructed
         */
        public Slice.Builder addTimestamp(long time, @SliceHint String... hints) {
            mItems.add(new SliceItem(time, TYPE_TIMESTAMP, hints));
            return this;
        }

        /**
         * Construct the slice.
         */
        public Slice build() {
            return new Slice(mItems, mHints.toArray(new String[mHints.size()]), mUri);
        }
    }

    public static final Creator<Slice> CREATOR = new Creator<Slice>() {
        @Override
        public Slice createFromParcel(Parcel in) {
            return new Slice(in);
        }

        @Override
        public Slice[] newArray(int size) {
            return new Slice[size];
        }
    };
}
+312 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.
 */

package android.slice;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.graphics.drawable.Icon;
import android.os.Parcel;
import android.os.Parcelable;
import android.slice.Slice.SliceHint;
import android.text.TextUtils;
import android.util.Pair;
import android.widget.RemoteViews;

import com.android.internal.util.ArrayUtils;


/**
 * A SliceItem is a single unit in the tree structure of a {@link Slice}.
 *
 * A SliceItem a piece of content and some hints about what that content
 * means or how it should be displayed. The types of content can be:
 * <li>{@link #TYPE_SLICE}</li>
 * <li>{@link #TYPE_TEXT}</li>
 * <li>{@link #TYPE_IMAGE}</li>
 * <li>{@link #TYPE_ACTION}</li>
 * <li>{@link #TYPE_COLOR}</li>
 * <li>{@link #TYPE_TIMESTAMP}</li>
 * <li>{@link #TYPE_REMOTE_INPUT}</li>
 *
 * The hints that a {@link SliceItem} are a set of strings which annotate
 * the content. The hints that are guaranteed to be understood by the system
 * are defined on {@link Slice}.
 * @hide
 */
public final class SliceItem implements Parcelable {

    /**
     * @hide
     */
    @IntDef({TYPE_SLICE, TYPE_TEXT, TYPE_IMAGE, TYPE_ACTION, TYPE_COLOR,
            TYPE_TIMESTAMP, TYPE_REMOTE_INPUT})
    public @interface SliceType {}

    /**
     * A {@link SliceItem} that contains a {@link Slice}
     */
    public static final int TYPE_SLICE        = 1;
    /**
     * A {@link SliceItem} that contains a {@link CharSequence}
     */
    public static final int TYPE_TEXT         = 2;
    /**
     * A {@link SliceItem} that contains an {@link Icon}
     */
    public static final int TYPE_IMAGE        = 3;
    /**
     * A {@link SliceItem} that contains a {@link PendingIntent}
     *
     * Note: Actions contain 2 pieces of data, In addition to the pending intent, the
     * item contains a {@link Slice} that the action applies to.
     */
    public static final int TYPE_ACTION       = 4;
    /**
     * @hide This isn't final
     */
    public static final int TYPE_REMOTE_VIEW  = 5;
    /**
     * A {@link SliceItem} that contains a Color int.
     */
    public static final int TYPE_COLOR        = 6;
    /**
     * A {@link SliceItem} that contains a timestamp.
     */
    public static final int TYPE_TIMESTAMP    = 8;
    /**
     * A {@link SliceItem} that contains a {@link RemoteInput}.
     */
    public static final int TYPE_REMOTE_INPUT = 9;

    /**
     * @hide
     */
    protected @SliceHint String[] mHints;
    private final int mType;
    private final Object mObj;

    /**
     * @hide
     */
    public SliceItem(Object obj, @SliceType int type, @SliceHint String[] hints) {
        mHints = hints;
        mType = type;
        mObj = obj;
    }

    /**
     * @hide
     */
    public SliceItem(PendingIntent intent, Slice slice, int type, @SliceHint String[] hints) {
        this(new Pair<>(intent, slice), type, hints);
    }

    /**
     * Gets all hints associated with this SliceItem.
     * @return Array of hints.
     */
    public @NonNull @SliceHint String[] getHints() {
        return mHints;
    }

    /**
     * @hide
     */
    public void addHint(@SliceHint String hint) {
        mHints = ArrayUtils.appendElement(String.class, mHints, hint);
    }

    public @SliceType int getType() {
        return mType;
    }

    /**
     * @return The text held by this {@link #TYPE_TEXT} SliceItem
     */
    public CharSequence getText() {
        return (CharSequence) mObj;
    }

    /**
     * @return The icon held by this {@link #TYPE_IMAGE} SliceItem
     */
    public Icon getIcon() {
        return (Icon) mObj;
    }

    /**
     * @return The pending intent held by this {@link #TYPE_ACTION} SliceItem
     */
    public PendingIntent getAction() {
        return ((Pair<PendingIntent, Slice>) mObj).first;
    }

    /**
     * @hide This isn't final
     */
    public RemoteViews getRemoteView() {
        return (RemoteViews) mObj;
    }

    /**
     * @return The remote input held by this {@link #TYPE_REMOTE_INPUT} SliceItem
     */
    public RemoteInput getRemoteInput() {
        return (RemoteInput) mObj;
    }

    /**
     * @return The color held by this {@link #TYPE_COLOR} SliceItem
     */
    public int getColor() {
        return (Integer) mObj;
    }

    /**
     * @return The slice held by this {@link #TYPE_ACTION} or {@link #TYPE_SLICE} SliceItem
     */
    public Slice getSlice() {
        if (getType() == TYPE_ACTION) {
            return ((Pair<PendingIntent, Slice>) mObj).second;
        }
        return (Slice) mObj;
    }

    /**
     * @return The timestamp held by this {@link #TYPE_TIMESTAMP} SliceItem
     */
    public long getTimestamp() {
        return (Long) mObj;
    }

    /**
     * @param hint The hint to check for
     * @return true if this item contains the given hint
     */
    public boolean hasHint(@SliceHint String hint) {
        return ArrayUtils.contains(mHints, hint);
    }

    /**
     * @hide
     */
    public SliceItem(Parcel in) {
        mHints = in.readStringArray();
        mType = in.readInt();
        mObj = readObj(mType, in);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeStringArray(mHints);
        dest.writeInt(mType);
        writeObj(dest, flags, mObj, mType);
    }

    /**
     * @hide
     */
    public boolean hasHints(@SliceHint String[] hints) {
        if (hints == null) return true;
        for (String hint : hints) {
            if (!ArrayUtils.contains(mHints, hint)) {
                return false;
            }
        }
        return true;
    }

    /**
     * @hide
     */
    public boolean hasAnyHints(@SliceHint String[] hints) {
        if (hints == null) return true;
        for (String hint : hints) {
            if (ArrayUtils.contains(mHints, hint)) {
                return true;
            }
        }
        return false;
    }

    private void writeObj(Parcel dest, int flags, Object obj, int type) {
        switch (type) {
            case TYPE_SLICE:
            case TYPE_REMOTE_VIEW:
            case TYPE_IMAGE:
            case TYPE_REMOTE_INPUT:
                ((Parcelable) obj).writeToParcel(dest, flags);
                break;
            case TYPE_ACTION:
                ((Pair<PendingIntent, Slice>) obj).first.writeToParcel(dest, flags);
                ((Pair<PendingIntent, Slice>) obj).second.writeToParcel(dest, flags);
                break;
            case TYPE_TEXT:
                TextUtils.writeToParcel((CharSequence) mObj, dest, flags);
                break;
            case TYPE_COLOR:
                dest.writeInt((Integer) mObj);
                break;
            case TYPE_TIMESTAMP:
                dest.writeLong((Long) mObj);
                break;
        }
    }

    private static Object readObj(int type, Parcel in) {
        switch (type) {
            case TYPE_SLICE:
                return Slice.CREATOR.createFromParcel(in);
            case TYPE_TEXT:
                return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
            case TYPE_IMAGE:
                return Icon.CREATOR.createFromParcel(in);
            case TYPE_ACTION:
                return new Pair<PendingIntent, Slice>(
                        PendingIntent.CREATOR.createFromParcel(in),
                        Slice.CREATOR.createFromParcel(in));
            case TYPE_REMOTE_VIEW:
                return RemoteViews.CREATOR.createFromParcel(in);
            case TYPE_COLOR:
                return in.readInt();
            case TYPE_TIMESTAMP:
                return in.readLong();
            case TYPE_REMOTE_INPUT:
                return RemoteInput.CREATOR.createFromParcel(in);
        }
        throw new RuntimeException("Unsupported type " + type);
    }

    public static final Creator<SliceItem> CREATOR = new Creator<SliceItem>() {
        @Override
        public SliceItem createFromParcel(Parcel in) {
            return new SliceItem(in);
        }

        @Override
        public SliceItem[] newArray(int size) {
            return new SliceItem[size];
        }
    };
}
+156 −0

File added.

Preview size limit exceeded, changes collapsed.

+137 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading