blob: ed32a1b7be2c49cc20b118295a5712c625cccc2f [file] [log] [blame]
/*
* 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.app.slice;
import android.annotation.NonNull;
import android.annotation.StringDef;
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Pair;
import android.widget.RemoteViews;
import com.android.internal.util.ArrayUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.List;
/**
* 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 #FORMAT_SLICE}</li>
* <li>{@link #FORMAT_TEXT}</li>
* <li>{@link #FORMAT_IMAGE}</li>
* <li>{@link #FORMAT_ACTION}</li>
* <li>{@link #FORMAT_INT}</li>
* <li>{@link #FORMAT_LONG}</li>
* <li>{@link #FORMAT_REMOTE_INPUT}</li>
* <li>{@link #FORMAT_BUNDLE}</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}.
*/
public final class SliceItem implements Parcelable {
private static final String TAG = "SliceItem";
/**
* @hide
*/
@StringDef(prefix = { "FORMAT_" }, value = {
FORMAT_SLICE,
FORMAT_TEXT,
FORMAT_IMAGE,
FORMAT_ACTION,
FORMAT_INT,
FORMAT_LONG,
FORMAT_REMOTE_INPUT,
FORMAT_BUNDLE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface SliceType {}
/**
* A {@link SliceItem} that contains a {@link Slice}
*/
public static final String FORMAT_SLICE = "slice";
/**
* A {@link SliceItem} that contains a {@link CharSequence}
*/
public static final String FORMAT_TEXT = "text";
/**
* A {@link SliceItem} that contains an {@link Icon}
*/
public static final String FORMAT_IMAGE = "image";
/**
* 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 String FORMAT_ACTION = "action";
/**
* A {@link SliceItem} that contains an int.
*/
public static final String FORMAT_INT = "int";
/**
* A {@link SliceItem} that contains a long.
*/
public static final String FORMAT_LONG = "long";
/**
* @deprecated TO BE REMOVED
* @removed
*/
@Deprecated
public static final String FORMAT_TIMESTAMP = FORMAT_LONG;
/**
* A {@link SliceItem} that contains a {@link RemoteInput}.
*/
public static final String FORMAT_REMOTE_INPUT = "input";
/**
* A {@link SliceItem} that contains a {@link Bundle}.
*/
public static final String FORMAT_BUNDLE = "bundle";
/**
* @hide
*/
protected @Slice.SliceHint
String[] mHints;
private final String mFormat;
private final String mSubType;
private final Object mObj;
/**
* @hide
*/
public SliceItem(Object obj, @SliceType String format, String subType,
List<String> hints) {
this(obj, format, subType, hints.toArray(new String[hints.size()]));
}
/**
* @hide
*/
public SliceItem(Object obj, @SliceType String format, String subType,
@Slice.SliceHint String[] hints) {
mHints = hints;
mFormat = format;
mSubType = subType;
mObj = obj;
}
/**
* @hide
*/
public SliceItem(PendingIntent intent, Slice slice, String format, String subType,
@Slice.SliceHint String[] hints) {
this(new Pair<>(intent, slice), format, subType, hints);
}
/**
* Gets all hints associated with this SliceItem.
* @return Array of hints.
*/
public @NonNull @Slice.SliceHint List<String> getHints() {
return Arrays.asList(mHints);
}
/**
* Get the format of this SliceItem.
* <p>
* The format will be one of the following types supported by the platform:
* <li>{@link #FORMAT_SLICE}</li>
* <li>{@link #FORMAT_TEXT}</li>
* <li>{@link #FORMAT_IMAGE}</li>
* <li>{@link #FORMAT_ACTION}</li>
* <li>{@link #FORMAT_INT}</li>
* <li>{@link #FORMAT_LONG}</li>
* <li>{@link #FORMAT_REMOTE_INPUT}</li>
* <li>{@link #FORMAT_BUNDLE}</li>
* @see #getSubType() ()
*/
public String getFormat() {
return mFormat;
}
/**
* Get the sub-type of this SliceItem.
* <p>
* Subtypes provide additional information about the type of this information beyond basic
* interpretations inferred by {@link #getFormat()}. For example a slice may contain
* many {@link #FORMAT_TEXT} items, but only some of them may be {@link Slice#SUBTYPE_MESSAGE}.
* @see #getFormat()
*/
public String getSubType() {
return mSubType;
}
/**
* @return The text held by this {@link #FORMAT_TEXT} SliceItem
*/
public CharSequence getText() {
return (CharSequence) mObj;
}
/**
* @return The parcelable held by this {@link #FORMAT_BUNDLE} SliceItem
*/
public Bundle getBundle() {
return (Bundle) mObj;
}
/**
* @return The icon held by this {@link #FORMAT_IMAGE} SliceItem
*/
public Icon getIcon() {
return (Icon) mObj;
}
/**
* @return The pending intent held by this {@link #FORMAT_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 #FORMAT_REMOTE_INPUT} SliceItem
*/
public RemoteInput getRemoteInput() {
return (RemoteInput) mObj;
}
/**
* @return The color held by this {@link #FORMAT_INT} SliceItem
*/
public int getInt() {
return (Integer) mObj;
}
/**
* @return The slice held by this {@link #FORMAT_ACTION} or {@link #FORMAT_SLICE} SliceItem
*/
public Slice getSlice() {
if (FORMAT_ACTION.equals(getFormat())) {
return ((Pair<PendingIntent, Slice>) mObj).second;
}
return (Slice) mObj;
}
/**
* @return The long held by this {@link #FORMAT_LONG} SliceItem
*/
public long getLong() {
return (Long) mObj;
}
/**
* @deprecated replaced by {@link #getLong()}
* @removed
*/
@Deprecated
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(@Slice.SliceHint String hint) {
return ArrayUtils.contains(mHints, hint);
}
/**
* @hide
*/
public SliceItem(Parcel in) {
mHints = in.readStringArray();
mFormat = in.readString();
mSubType = in.readString();
mObj = readObj(mFormat, in);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeStringArray(mHints);
dest.writeString(mFormat);
dest.writeString(mSubType);
writeObj(dest, flags, mObj, mFormat);
}
/**
* @hide
*/
public boolean hasHints(@Slice.SliceHint String[] hints) {
if (hints == null) return true;
for (String hint : hints) {
if (!TextUtils.isEmpty(hint) && !ArrayUtils.contains(mHints, hint)) {
return false;
}
}
return true;
}
/**
* @hide
*/
public boolean hasAnyHints(@Slice.SliceHint String[] hints) {
if (hints == null) return false;
for (String hint : hints) {
if (ArrayUtils.contains(mHints, hint)) {
return true;
}
}
return false;
}
private static String getBaseType(String type) {
int index = type.indexOf('/');
if (index >= 0) {
return type.substring(0, index);
}
return type;
}
private static void writeObj(Parcel dest, int flags, Object obj, String type) {
switch (getBaseType(type)) {
case FORMAT_SLICE:
case FORMAT_IMAGE:
case FORMAT_REMOTE_INPUT:
case FORMAT_BUNDLE:
((Parcelable) obj).writeToParcel(dest, flags);
break;
case FORMAT_ACTION:
((Pair<PendingIntent, Slice>) obj).first.writeToParcel(dest, flags);
((Pair<PendingIntent, Slice>) obj).second.writeToParcel(dest, flags);
break;
case FORMAT_TEXT:
TextUtils.writeToParcel((CharSequence) obj, dest, flags);
break;
case FORMAT_INT:
dest.writeInt((Integer) obj);
break;
case FORMAT_TIMESTAMP:
dest.writeLong((Long) obj);
break;
}
}
private static Object readObj(String type, Parcel in) {
switch (getBaseType(type)) {
case FORMAT_SLICE:
return Slice.CREATOR.createFromParcel(in);
case FORMAT_TEXT:
return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
case FORMAT_IMAGE:
return Icon.CREATOR.createFromParcel(in);
case FORMAT_ACTION:
return new Pair<>(
PendingIntent.CREATOR.createFromParcel(in),
Slice.CREATOR.createFromParcel(in));
case FORMAT_INT:
return in.readInt();
case FORMAT_TIMESTAMP:
return in.readLong();
case FORMAT_REMOTE_INPUT:
return RemoteInput.CREATOR.createFromParcel(in);
case FORMAT_BUNDLE:
return Bundle.CREATOR.createFromParcel(in);
}
throw new RuntimeException("Unsupported type " + type);
}
public static final @android.annotation.NonNull 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];
}
};
}