Gradle dependencies
compile group: 'androidx.slice', name: 'slice-core', version: '1.1.0-alpha02'
- groupId: androidx.slice
- artifactId: slice-core
- version: 1.1.0-alpha02
Artifact androidx.slice:slice-core:1.1.0-alpha02 it located at Google repository (https://maven.google.com/)
Androidx artifact mapping:
androidx.slice:slice-core com.android.support:slices-core
Overview
A SliceItem is a single unit in the tree structure of a 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:
The hints that a SliceItem are a set of strings which annotate
the content. The hints that are guaranteed to be understood by the system
are defined on Slice.
Summary
Fields |
---|
protected java.lang.String | mHints |
Constructors |
---|
public | SliceItem()
Used by VersionedParcelable. |
public | SliceItem(Bundle in)
|
public | SliceItem(java.lang.Object obj, java.lang.String format, java.lang.String subType, java.util.List<java.lang.String> hints)
|
public | SliceItem(java.lang.Object obj, java.lang.String format, java.lang.String subType, java.lang.String hints[])
|
public | SliceItem(PendingIntent intent, Slice slice, java.lang.String format, java.lang.String subType, java.lang.String hints[])
|
Methods |
---|
public void | addHint(java.lang.String hint)
|
public static ParcelableSpan | createSensitiveSpan()
Creates a span object that identifies content that should be redacted when acquired using
SliceItem.getRedactedText(). |
public void | fireAction(Context context, Intent i)
Trigger the action on this SliceItem. |
public boolean | fireActionInternal(Context context, Intent i)
|
public PendingIntent | getAction()
|
public java.lang.String | getFormat()
Get the format of this SliceItem. |
public java.lang.String | getHintArray()
|
public java.util.List<java.lang.String> | getHints()
Gets all hints associated with this SliceItem. |
public IconCompat | getIcon()
|
public int | getInt()
|
public long | getLong()
|
public java.lang.CharSequence | getRedactedText()
Get the same content as SliceItem.getText() except with content that should be excluded from
persistent logs because it was tagged with SliceItem.createSensitiveSpan(). |
public RemoteInput | getRemoteInput()
|
public java.lang.CharSequence | getSanitizedText()
|
public Slice | getSlice()
|
public java.lang.String | getSubType()
Get the sub-type of this SliceItem. |
public java.lang.CharSequence | getText()
|
public boolean | hasAnyHints(java.lang.String hints[])
|
public boolean | hasHint(java.lang.String hint)
|
public boolean | hasHints(java.lang.String hints[])
|
public void | onPostParceling()
Called immediately after this object has been deserialized, can be used
to handle any custom fields that cannot be easily annotated. |
public void | onPreParceling(boolean isStream)
Called immediately before this object is going to be serialized, can be used
to handle any custom fields that cannot be easily annotated. |
public Bundle | toBundle()
|
public java.lang.String | toString()
|
public java.lang.String | toString(java.lang.String indent)
|
public static java.lang.String | typeToString(java.lang.String format)
|
from java.lang.Object | clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait |
Fields
protected java.lang.String
mHintsConstructors
public
SliceItem(java.lang.Object obj, java.lang.String format, java.lang.String subType, java.lang.String hints[])
public
SliceItem(java.lang.Object obj, java.lang.String format, java.lang.String subType, java.util.List<java.lang.String> hints)
Used by VersionedParcelable.
public
SliceItem(PendingIntent intent,
Slice slice, java.lang.String format, java.lang.String subType, java.lang.String hints[])
public
SliceItem(Bundle in)
Methods
public java.util.List<java.lang.String>
getHints()
Gets all hints associated with this SliceItem.
Returns:
Array of hints.
public java.lang.String
getHintArray()
public void
addHint(java.lang.String hint)
public java.lang.String
getFormat()
Get the format of this SliceItem.
The format will be one of the following types supported by the platform:
See also: ()
public java.lang.String
getSubType()
Get the sub-type of this SliceItem.
Subtypes provide additional information about the type of this information beyond basic
interpretations inferred by SliceItem.getFormat(). For example a slice may contain
many items, but only some of them may be
.
See also: SliceItem.getFormat()
public java.lang.CharSequence
getText()
Returns:
The text held by this SliceItem
public java.lang.CharSequence
getSanitizedText()
Returns:
The text held by this SliceItem with
ony spans that are unsupported by the androidx Slice renderer removed.
public java.lang.CharSequence
getRedactedText()
Get the same content as SliceItem.getText() except with content that should be excluded from
persistent logs because it was tagged with SliceItem.createSensitiveSpan().
Returns:
The text held by this SliceItem
Returns:
The icon held by this SliceItem
public PendingIntent
getAction()
Returns:
The pending intent held by this
SliceItem
public void
fireAction(Context context, Intent i)
Trigger the action on this SliceItem.
Parameters:
context: The Context to use when sending the PendingIntent.
i: The intent to use when sending the PendingIntent.
public boolean
fireActionInternal(Context context, Intent i)
public RemoteInput
getRemoteInput()
Returns:
The remote input held by this
SliceItem
Returns:
The color held by this SliceItem
Returns:
The slice held by this or
SliceItem
Returns:
The long held by this
SliceItem
public boolean
hasHint(java.lang.String hint)
Parameters:
hint: The hint to check for
Returns:
true if this item contains the given hint
Returns:
public boolean
hasHints(java.lang.String hints[])
public boolean
hasAnyHints(java.lang.String hints[])
public static java.lang.String
typeToString(java.lang.String format)
public java.lang.String
toString()
Returns:
A string representation of this slice item.
public java.lang.String
toString(java.lang.String indent)
Returns:
A string representation of this slice item.
public void
onPreParceling(boolean isStream)
Called immediately before this object is going to be serialized, can be used
to handle any custom fields that cannot be easily annotated.
public void
onPostParceling()
Called immediately after this object has been deserialized, can be used
to handle any custom fields that cannot be easily annotated.
public static ParcelableSpan
createSensitiveSpan()
Creates a span object that identifies content that should be redacted when acquired using
SliceItem.getRedactedText().
Source
/*
* 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 androidx.slice;
import static android.app.slice.SliceItem.FORMAT_ACTION;
import static android.app.slice.SliceItem.FORMAT_BUNDLE;
import static android.app.slice.SliceItem.FORMAT_IMAGE;
import static android.app.slice.SliceItem.FORMAT_INT;
import static android.app.slice.SliceItem.FORMAT_LONG;
import static android.app.slice.SliceItem.FORMAT_REMOTE_INPUT;
import static android.app.slice.SliceItem.FORMAT_SLICE;
import static android.app.slice.SliceItem.FORMAT_TEXT;
import static androidx.slice.Slice.appendHints;
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Parcelable;
import android.text.Annotation;
import android.text.ParcelableSpan;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.style.AlignmentSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.RelativeSizeSpan;
import android.text.style.StyleSpan;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.annotation.StringDef;
import androidx.core.graphics.drawable.IconCompat;
import androidx.core.util.Pair;
import androidx.versionedparcelable.CustomVersionedParcelable;
import androidx.versionedparcelable.NonParcelField;
import androidx.versionedparcelable.ParcelField;
import androidx.versionedparcelable.VersionedParcelize;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
/**
* A SliceItem is a single unit in the tree structure of a {@link Slice}.
* <p>
* 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 android.app.slice.SliceItem#FORMAT_SLICE}</li>
* <li>{@link android.app.slice.SliceItem#FORMAT_TEXT}</li>
* <li>{@link android.app.slice.SliceItem#FORMAT_IMAGE}</li>
* <li>{@link android.app.slice.SliceItem#FORMAT_ACTION}</li>
* <li>{@link android.app.slice.SliceItem#FORMAT_INT}</li>
* <li>{@link android.app.slice.SliceItem#FORMAT_LONG}</li>
* <p>
* 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}.
*/
@VersionedParcelize(allowSerialization = true, ignoreParcelables = true, isCustom = true)
@RequiresApi(19)
public final class SliceItem extends CustomVersionedParcelable {
private static final String HINTS = "hints";
private static final String FORMAT = "format";
private static final String SUBTYPE = "subtype";
private static final String OBJ = "obj";
private static final String OBJ_2 = "obj_2";
private static final String SLICE_CONTENT = "androidx.slice.content";
private static final String SLICE_CONTENT_SENSITIVE = "sensitive";
/**
* @hide
*/
@RestrictTo(Scope.LIBRARY)
@StringDef({FORMAT_SLICE, FORMAT_TEXT, FORMAT_IMAGE, FORMAT_ACTION, FORMAT_INT,
FORMAT_LONG, FORMAT_REMOTE_INPUT, FORMAT_LONG, FORMAT_BUNDLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SliceType {
}
/**
* @hide
*/
@RestrictTo(Scope.LIBRARY)
@ParcelField(value = 1, defaultValue = "androidx.slice.Slice.NO_HINTS")
protected @Slice.SliceHint String[] mHints = Slice.NO_HINTS;
@ParcelField(value = 2, defaultValue = FORMAT_TEXT)
String mFormat = FORMAT_TEXT;
@ParcelField(value = 3, defaultValue = "null")
String mSubType = null;
@NonParcelField
Object mObj;
@NonParcelField
CharSequence mSanitizedText;
@ParcelField(4)
SliceItemHolder mHolder;
/**
* @hide
*/
@RestrictTo(Scope.LIBRARY_GROUP)
public SliceItem(Object obj, @SliceType String format, String subType,
@Slice.SliceHint String[] hints) {
mHints = hints;
mFormat = format;
mSubType = subType;
mObj = obj;
}
/**
* @hide
*/
@RestrictTo(Scope.LIBRARY_GROUP)
public SliceItem(Object obj, @SliceType String format, String subType,
@Slice.SliceHint List<String> hints) {
this (obj, format, subType, hints.toArray(new String[hints.size()]));
}
/**
* Used by VersionedParcelable.
* @hide
*/
@RestrictTo(Scope.LIBRARY_GROUP)
public SliceItem() {
}
/**
* @hide
*/
@RestrictTo(Scope.LIBRARY_GROUP)
public SliceItem(PendingIntent intent, Slice slice, String format, String subType,
@Slice.SliceHint String[] hints) {
this(new Pair<Object, Slice>(intent, slice), format, subType, hints);
}
/**
* @hide
*/
@RestrictTo(Scope.LIBRARY_GROUP)
public SliceItem(ActionHandler action, Slice slice, String format, String subType,
@Slice.SliceHint String[] hints) {
this(new Pair<Object, Slice>(action, 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);
}
/**
* @hide
*/
@RestrictTo(Scope.LIBRARY)
public @NonNull @Slice.SliceHint String[] getHintArray() {
return mHints;
}
/**
* @hide
*/
@RestrictTo(Scope.LIBRARY_GROUP)
public void addHint(@Slice.SliceHint String hint) {
mHints = ArrayUtils.appendElement(String.class, mHints, hint);
}
/**
* Get the format of this SliceItem.
* <p>
* The format will be one of the following types supported by the platform:
* <li>{@link android.app.slice.SliceItem#FORMAT_SLICE}</li>
* <li>{@link android.app.slice.SliceItem#FORMAT_TEXT}</li>
* <li>{@link android.app.slice.SliceItem#FORMAT_IMAGE}</li>
* <li>{@link android.app.slice.SliceItem#FORMAT_ACTION}</li>
* <li>{@link android.app.slice.SliceItem#FORMAT_INT}</li>
* <li>{@link android.app.slice.SliceItem#FORMAT_LONG}</li>
* <li>{@link android.app.slice.SliceItem#FORMAT_REMOTE_INPUT}</li>
* @see #getSubType() ()
*/
public @SliceType 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 android.app.slice.SliceItem#FORMAT_TEXT} items, but only some of them may be
* {@link android.app.slice.Slice#SUBTYPE_MESSAGE}.
* @see #getFormat()
*/
public String getSubType() {
return mSubType;
}
/**
* @return The text held by this {@link android.app.slice.SliceItem#FORMAT_TEXT} SliceItem
*/
public CharSequence getText() {
return (CharSequence) mObj;
}
/**
* @hide
* @return The text held by this {@link android.app.slice.SliceItem#FORMAT_TEXT} SliceItem with
* ony spans that are unsupported by the androidx Slice renderer removed.
*/
@RestrictTo(Scope.LIBRARY_GROUP_PREFIX)
public CharSequence getSanitizedText() {
if (mSanitizedText == null) mSanitizedText = sanitizeText(getText());
return mSanitizedText;
}
/**
* Get the same content as {@link #getText()} except with content that should be excluded from
* persistent logs because it was tagged with {@link #createSensitiveSpan()}.
*
* @return The text held by this {@link android.app.slice.SliceItem#FORMAT_TEXT} SliceItem
*/
@Nullable
public CharSequence getRedactedText() {
return redactSensitiveText(getText());
}
/**
* @return The icon held by this {@link android.app.slice.SliceItem#FORMAT_IMAGE} SliceItem
*/
public IconCompat getIcon() {
return (IconCompat) mObj;
}
/**
* @return The pending intent held by this {@link android.app.slice.SliceItem#FORMAT_ACTION}
* SliceItem
*/
@SuppressWarnings("unchecked")
public PendingIntent getAction() {
Object action = ((Pair<Object, Slice>) mObj).first;
if (action instanceof PendingIntent) {
return (PendingIntent) action;
}
return null;
}
/**
* Trigger the action on this SliceItem.
* @param context The Context to use when sending the PendingIntent.
* @param i The intent to use when sending the PendingIntent.
*/
public void fireAction(@Nullable Context context, @Nullable Intent i)
throws PendingIntent.CanceledException {
fireActionInternal(context, i);
}
/**
* @hide
*/
@SuppressWarnings("unchecked")
@RestrictTo(Scope.LIBRARY_GROUP_PREFIX)
public boolean fireActionInternal(@Nullable Context context, @Nullable Intent i)
throws PendingIntent.CanceledException {
Object action = ((Pair<Object, Slice>) mObj).first;
if (action instanceof PendingIntent) {
((PendingIntent) action).send(context, 0, i, null, null);
return false;
} else {
((ActionHandler) action).onAction(this, context, i);
return true;
}
}
/**
* @return The remote input held by this {@link android.app.slice.SliceItem#FORMAT_REMOTE_INPUT}
* SliceItem
* @hide
*/
@RequiresApi(20)
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public RemoteInput getRemoteInput() {
return (RemoteInput) mObj;
}
/**
* @return The color held by this {@link android.app.slice.SliceItem#FORMAT_INT} SliceItem
*/
public int getInt() {
return (Integer) mObj;
}
/**
* @return The slice held by this {@link android.app.slice.SliceItem#FORMAT_ACTION} or
* {@link android.app.slice.SliceItem#FORMAT_SLICE} SliceItem
*/
@SuppressWarnings("unchecked")
public Slice getSlice() {
if (FORMAT_ACTION.equals(getFormat())) {
return ((Pair<Object, Slice>) mObj).second;
}
return (Slice) mObj;
}
/**
* @return The long held by this {@link android.app.slice.SliceItem#FORMAT_LONG}
* SliceItem
*/
public long getLong() {
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
*/
@RestrictTo(Scope.LIBRARY)
public SliceItem(Bundle in) {
mHints = in.getStringArray(HINTS);
mFormat = in.getString(FORMAT);
mSubType = in.getString(SUBTYPE);
mObj = readObj(mFormat, in);
}
/**
* @hide
* @return
*/
@RestrictTo(Scope.LIBRARY)
public Bundle toBundle() {
Bundle b = new Bundle();
b.putStringArray(HINTS, mHints);
b.putString(FORMAT, mFormat);
b.putString(SUBTYPE, mSubType);
writeObj(b, mObj, mFormat);
return b;
}
/**
* @hide
*/
@RestrictTo(Scope.LIBRARY)
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
*/
@RestrictTo(Scope.LIBRARY_GROUP)
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;
}
@SuppressWarnings("unchecked")
private void writeObj(Bundle dest, Object obj, String type) {
switch (type) {
case FORMAT_IMAGE:
dest.putBundle(OBJ, ((IconCompat) obj).toBundle());
break;
case FORMAT_REMOTE_INPUT:
dest.putParcelable(OBJ, (Parcelable) obj);
break;
case FORMAT_SLICE:
dest.putParcelable(OBJ, ((Slice) obj).toBundle());
break;
case FORMAT_ACTION:
dest.putParcelable(OBJ, (PendingIntent) ((Pair<Object, Slice>) obj).first);
dest.putBundle(OBJ_2, ((Pair<Object, Slice>) obj).second.toBundle());
break;
case FORMAT_TEXT:
dest.putCharSequence(OBJ, (CharSequence) obj);
break;
case FORMAT_INT:
dest.putInt(OBJ, (Integer) mObj);
break;
case FORMAT_LONG:
dest.putLong(OBJ, (Long) mObj);
break;
case FORMAT_BUNDLE:
dest.putBundle(OBJ, (Bundle) mObj);
}
}
private static Object readObj(String type, Bundle in) {
switch (type) {
case FORMAT_IMAGE:
return IconCompat.createFromBundle(in.getBundle(OBJ));
case FORMAT_REMOTE_INPUT:
return in.getParcelable(OBJ);
case FORMAT_SLICE:
return new Slice(in.getBundle(OBJ));
case FORMAT_TEXT:
return in.getCharSequence(OBJ);
case FORMAT_ACTION:
return new Pair<>(
in.getParcelable(OBJ),
new Slice(in.getBundle(OBJ_2)));
case FORMAT_INT:
return in.getInt(OBJ);
case FORMAT_LONG:
return in.getLong(OBJ);
case FORMAT_BUNDLE:
return in.getBundle(OBJ);
}
throw new RuntimeException("Unsupported type " + type);
}
/**
* @hide
*/
@RestrictTo(Scope.LIBRARY)
public static String typeToString(String format) {
switch (format) {
case FORMAT_SLICE:
return "Slice";
case FORMAT_TEXT:
return "Text";
case FORMAT_IMAGE:
return "Image";
case FORMAT_ACTION:
return "Action";
case FORMAT_INT:
return "Int";
case FORMAT_LONG:
return "Long";
case FORMAT_REMOTE_INPUT:
return "RemoteInput";
}
return "Unrecognized format: " + format;
}
/**
* @return A string representation of this slice item.
*/
@Override
public String toString() {
return toString("");
}
/**
* @return A string representation of this slice item.
* @hide
*/
@RestrictTo(Scope.LIBRARY)
@SuppressWarnings("unchecked")
public String toString(String indent) {
StringBuilder sb = new StringBuilder();
sb.append(indent);
sb.append(getFormat());
if (getSubType() != null) {
sb.append('<');
sb.append(getSubType());
sb.append('>');
}
sb.append(' ');
if (mHints.length > 0) {
appendHints(sb, mHints);
sb.append(' ');
}
final String nextIndent = indent + " ";
switch (getFormat()) {
case FORMAT_SLICE:
sb.append("{\n");
sb.append(getSlice().toString(nextIndent));
sb.append('\n').append(indent).append('}');
break;
case FORMAT_ACTION:
// Not using getAction because the action can actually be other types.
Object action = ((Pair<Object, Slice>) mObj).first;
sb.append('[').append(action).append("] ");
sb.append("{\n");
sb.append(getSlice().toString(nextIndent));
sb.append('\n').append(indent).append('}');
break;
case FORMAT_TEXT:
sb.append('"').append(getText()).append('"');
break;
case FORMAT_IMAGE:
sb.append(getIcon());
break;
case FORMAT_INT:
if (android.app.slice.Slice.SUBTYPE_COLOR.equals(getSubType())) {
int color = getInt();
sb.append(String.format("a=0x%02x r=0x%02x g=0x%02x b=0x%02x",
Color.alpha(color), Color.red(color), Color.green(color),
Color.blue(color)));
} else if (android.app.slice.Slice.SUBTYPE_LAYOUT_DIRECTION.equals(getSubType())) {
sb.append(layoutDirectionToString(getInt()));
} else {
sb.append(getInt());
}
break;
case FORMAT_LONG:
if (android.app.slice.Slice.SUBTYPE_MILLIS.equals(getSubType())) {
if (getLong() == -1L) {
sb.append("INFINITY");
} else {
sb.append(DateUtils.getRelativeTimeSpanString(getLong(),
Calendar.getInstance().getTimeInMillis(),
DateUtils.SECOND_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE));
}
} else {
sb.append(getLong()).append('L');
}
break;
default:
sb.append(SliceItem.typeToString(getFormat()));
break;
}
sb.append("\n");
return sb.toString();
}
@Override
public void onPreParceling(boolean isStream) {
mHolder = new SliceItemHolder(mFormat, mObj, isStream);
}
@Override
public void onPostParceling() {
if (mHolder != null) {
mObj = mHolder.getObj(mFormat);
mHolder.release();
} else {
mObj = null;
}
mHolder = null;
}
/**
* Creates a span object that identifies content that should be redacted when acquired using
* {@link #getRedactedText()}.
*/
@NonNull
public static ParcelableSpan createSensitiveSpan() {
return new Annotation(SLICE_CONTENT, SLICE_CONTENT_SENSITIVE);
}
private static String layoutDirectionToString(int layoutDirection) {
switch (layoutDirection) {
case android.util.LayoutDirection.LTR:
return "LTR";
case android.util.LayoutDirection.RTL:
return "RTL";
case android.util.LayoutDirection.INHERIT:
return "INHERIT";
case android.util.LayoutDirection.LOCALE:
return "LOCALE";
default:
return Integer.toString(layoutDirection);
}
}
private static CharSequence redactSensitiveText(CharSequence text) {
if (text instanceof Spannable) {
return redactSpannableText((Spannable) text);
} else if (text instanceof Spanned) {
if (!isRedactionNeeded((Spanned) text)) return text;
Spannable fixedText = new SpannableString(text);
return redactSpannableText(fixedText);
} else {
return text;
}
}
private static boolean isRedactionNeeded(Spanned text) {
for (Annotation span : text.getSpans(0, text.length(), Annotation.class)) {
if (SLICE_CONTENT.equals(span.getKey())
&& SLICE_CONTENT_SENSITIVE.equals(span.getValue())) {
return true;
}
}
return false;
}
private static CharSequence redactSpannableText(Spannable text) {
Spanned out = text;
for (Annotation span : text.getSpans(0, text.length(), Annotation.class)) {
if (!SLICE_CONTENT.equals(span.getKey())
|| !SLICE_CONTENT_SENSITIVE.equals(span.getValue())) {
continue;
}
int spanStart = text.getSpanStart(span);
int spanEnd = text.getSpanEnd(span);
out = new SpannableStringBuilder()
.append(out.subSequence(0, spanStart))
.append(createRedacted(spanEnd - spanStart))
.append(out.subSequence(spanEnd, text.length()));
}
return out;
}
private static String createRedacted(final int n) {
StringBuilder s = new StringBuilder();
for (int i = 0; i < n; i++) {
s.append('*');
}
return s.toString();
}
private static CharSequence sanitizeText(CharSequence text) {
if (text instanceof Spannable) {
fixSpannableText((Spannable) text);
return text;
} else if (text instanceof Spanned) {
if (checkSpannedText((Spanned) text)) return text;
Spannable fixedText = new SpannableString(text);
fixSpannableText(fixedText);
return fixedText;
} else {
return text;
}
}
private static boolean checkSpannedText(Spanned text) {
for (Object span : text.getSpans(0, text.length(), Object.class)) {
if (!checkSpan(span)) return false;
}
return true;
}
private static void fixSpannableText(Spannable text) {
for (Object span : text.getSpans(0, text.length(), Object.class)) {
Object fixedSpan = fixSpan(span);
if (fixedSpan == span) continue;
if (fixedSpan != null) {
int spanStart = text.getSpanStart(span);
int spanEnd = text.getSpanEnd(span);
int spanFlags = text.getSpanFlags(span);
text.setSpan(fixedSpan, spanStart, spanEnd, spanFlags);
}
text.removeSpan(span);
}
}
// TODO: Allow only highlight color in ForegroundColorSpan.
// TODO: Cap smallest/largest sizeChange for RelativeSizeSpans, minding nested ones.
private static boolean checkSpan(Object span) {
return span instanceof AlignmentSpan
|| span instanceof ForegroundColorSpan
|| span instanceof RelativeSizeSpan
|| span instanceof StyleSpan;
}
private static Object fixSpan(Object span) {
return checkSpan(span) ? span : null;
}
/**
* @hide
*/
@RestrictTo(Scope.LIBRARY_GROUP_PREFIX)
public interface ActionHandler {
/**
* Called when a pending intent would be sent on a real slice.
*/
void onAction(SliceItem item, Context context, Intent intent);
}
}