public class

SliceMetadata

extends java.lang.Object

 java.lang.Object

↳androidx.slice.SliceMetadata

Gradle dependencies

compile group: 'androidx.slice', name: 'slice-view', version: '1.1.0-alpha02'

  • groupId: androidx.slice
  • artifactId: slice-view
  • version: 1.1.0-alpha02

Artifact androidx.slice:slice-view:1.1.0-alpha02 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.slice:slice-view com.android.support:slices-view

Overview

Utility class to parse a Slice and provide access to information around its contents.

Summary

Fields
public static final intLOADED_ALL

Indicates this slice has fully loaded and is not waiting for other content.

public static final intLOADED_NONE

Indicates this slice is empty and waiting for content to be loaded.

public static final intLOADED_PARTIAL

Indicates this slice has some content but is waiting for other content to be loaded.

Methods
public static SliceMetadatafrom(Context context, Slice slice)

Create a SliceMetadata object to provide access to some information around the slice and its contents.

public longgetExpiry()

A slice contains an expiry to indicate when the content in the slice might no longer be valid.

public intgetHeaderType()

public BundlegetHostExtras()

public PendingIntentgetInputRangeAction()

Gets the input range action associated with the header of this slice, if it exists.

public longgetLastUpdatedTime()

public ListContentgetListContent()

public intgetLoadingState()

public SliceActiongetPrimaryAction()

public Pair<java.lang.Integer, java.lang.Integer>getRange()

Gets the range information associated with a progress bar or input range associated with this slice, if it exists.

public intgetRangeValue()

Gets the current value for a progress bar or input range associated with this slice, if it exists, -1 if unknown.

public java.util.List<SliceAction>getSliceActions()

public static java.util.List<SliceAction>getSliceActions(Slice slice)

public java.util.List<java.lang.String>getSliceKeywords()

public java.lang.CharSequencegetSubtitle()

public java.lang.CharSequencegetSummary()

public longgetTimeToExpiry()

public java.lang.CharSequencegetTitle()

public java.util.List<SliceAction>getToggles()

public booleanhasLargeMode()

public booleanisCachedSlice()

Indicates whether this slice was created using SliceUtils.parseSlice(Context, InputStream, String, SliceUtils.SliceActionListener) or through normal binding.

public booleanisErrorSlice()

Indicates whether this slice indicates an error, i.e.

public booleanisExpired()

public booleanisPermissionSlice()

To present a slice from another app, the app must grant uri permissions for the slice.

public booleanisSelection()

public booleanneverExpires()

public booleansendInputRangeAction(int newValue)

Sends the intent to adjust the input range value for the header of this slice, if it exists.

public booleansendToggleAction(SliceAction toggleAction, boolean toggleValue)

Sends the intent to adjust the state of the provided toggle action.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Fields

public static final int LOADED_NONE

Indicates this slice is empty and waiting for content to be loaded.

public static final int LOADED_PARTIAL

Indicates this slice has some content but is waiting for other content to be loaded.

public static final int LOADED_ALL

Indicates this slice has fully loaded and is not waiting for other content.

Methods

public static SliceMetadata from(Context context, Slice slice)

Create a SliceMetadata object to provide access to some information around the slice and its contents.

Parameters:

context: the context to use for the slice.
slice: the slice to extract metadata from.

Returns:

the metadata associated with the provided slice.

public java.lang.CharSequence getTitle()

Returns:

the title associated with this slice, if it exists.

public java.lang.CharSequence getSubtitle()

Returns:

the subtitle associated with this slice, if it exists.

public java.lang.CharSequence getSummary()

Returns:

the summary associated with this slice, if it exists.

public java.util.List<SliceAction> getSliceActions()

Returns:

the group of actions associated with this slice, if they exist.

public SliceAction getPrimaryAction()

Returns:

the primary action for this slice, null if none specified.

public int getHeaderType()

Returns:

the type of row that is used for the header of this slice, -1 if unknown.

public boolean hasLargeMode()

Returns:

whether this slice has content to show when presented in SliceView.MODE_LARGE.

public java.util.List<SliceAction> getToggles()

Returns:

the toggles associated with the header of this slice.

public Bundle getHostExtras()

public boolean sendToggleAction(SliceAction toggleAction, boolean toggleValue)

Sends the intent to adjust the state of the provided toggle action.

Parameters:

toggleAction: the toggle action.
toggleValue: the new value to set the toggle to.

Returns:

whether there was an action to send.

public PendingIntent getInputRangeAction()

Gets the input range action associated with the header of this slice, if it exists.

Returns:

the android.app.PendingIntent for the input range.

public boolean sendInputRangeAction(int newValue)

Sends the intent to adjust the input range value for the header of this slice, if it exists.

Parameters:

newValue: the value to set the input range to.

Returns:

whether there was an action to send.

public Pair<java.lang.Integer, java.lang.Integer> getRange()

Gets the range information associated with a progress bar or input range associated with this slice, if it exists.

Returns:

a pair where the first item is the minimum value of the range and the second item is the maximum value of the range.

public int getRangeValue()

Gets the current value for a progress bar or input range associated with this slice, if it exists, -1 if unknown.

Returns:

the current value of a progress bar or input range associated with this slice.

public boolean isSelection()

Returns:

whether this slice is a selection (a drop-down list) slice.

public java.util.List<java.lang.String> getSliceKeywords()

Returns:

the list of keywords associated with the provided slice, null if no keywords were specified or an empty list if the slice was specified to have no keywords.

public int getLoadingState()

Returns:

the current loading state for this slice.

See also: SliceMetadata.LOADED_NONE, SliceMetadata.LOADED_PARTIAL, SliceMetadata.LOADED_ALL

public long getExpiry()

A slice contains an expiry to indicate when the content in the slice might no longer be valid.

Returns:

the time, measured in milliseconds, between the expiry time of this slice and midnight, January 1, 1970 UTC, or ListBuilder.INFINITY if the slice is not time-sensitive.

public long getLastUpdatedTime()

Returns:

the time, measured in milliseconds, between when the slice was created or last updated, and midnight, January 1, 1970 UTC.

public boolean isPermissionSlice()

To present a slice from another app, the app must grant uri permissions for the slice. If these permissions have not been granted and the app slice is requested then a permission request slice will be returned instead, allowing the user to grant permission. This method can be used to identify if a slice is a permission request.

Returns:

whether this slice represents a permission request.

public boolean isErrorSlice()

Indicates whether this slice indicates an error, i.e. the normal contents of this slice are unavailable and instead the slice contains a message indicating an error.

Returns:

whether this slice represents an error.

public boolean isCachedSlice()

Indicates whether this slice was created using SliceUtils.parseSlice(Context, InputStream, String, SliceUtils.SliceActionListener) or through normal binding.

public static java.util.List<SliceAction> getSliceActions(Slice slice)

Returns:

the group of slice actions associated with the provided slice, if they exist.

public boolean isExpired()

public boolean neverExpires()

public long getTimeToExpiry()

public ListContent getListContent()

Source

/*
 * Copyright 2018 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.Slice.EXTRA_RANGE_VALUE;
import static android.app.slice.Slice.EXTRA_TOGGLE_STATE;
import static android.app.slice.Slice.HINT_ACTIONS;
import static android.app.slice.Slice.HINT_ERROR;
import static android.app.slice.Slice.HINT_KEYWORDS;
import static android.app.slice.Slice.HINT_LAST_UPDATED;
import static android.app.slice.Slice.HINT_LIST_ITEM;
import static android.app.slice.Slice.HINT_PARTIAL;
import static android.app.slice.Slice.HINT_PERMISSION_REQUEST;
import static android.app.slice.Slice.HINT_SHORTCUT;
import static android.app.slice.Slice.HINT_TTL;
import static android.app.slice.Slice.SUBTYPE_MAX;
import static android.app.slice.Slice.SUBTYPE_VALUE;
import static android.app.slice.SliceItem.FORMAT_ACTION;
import static android.app.slice.SliceItem.FORMAT_BUNDLE;
import static android.app.slice.SliceItem.FORMAT_INT;
import static android.app.slice.SliceItem.FORMAT_LONG;
import static android.app.slice.SliceItem.FORMAT_SLICE;
import static android.app.slice.SliceItem.FORMAT_TEXT;

import static androidx.slice.core.SliceHints.HINT_CACHED;
import static androidx.slice.core.SliceHints.SUBTYPE_HOST_EXTRAS;
import static androidx.slice.core.SliceHints.SUBTYPE_MIN;
import static androidx.slice.widget.EventInfo.ROW_TYPE_PROGRESS;
import static androidx.slice.widget.EventInfo.ROW_TYPE_SLIDER;

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.math.MathUtils;
import androidx.core.util.Pair;
import androidx.slice.core.SliceAction;
import androidx.slice.core.SliceActionImpl;
import androidx.slice.core.SliceHints;
import androidx.slice.core.SliceQuery;
import androidx.slice.widget.EventInfo;
import androidx.slice.widget.ListContent;
import androidx.slice.widget.RowContent;
import androidx.slice.widget.SliceView;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;

/**
 * Utility class to parse a {@link Slice} and provide access to information around its contents.
 */
@RequiresApi(19)
public class SliceMetadata {

    /**
     * @hide
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY)
    @IntDef({
            LOADED_NONE, LOADED_PARTIAL, LOADED_ALL
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface SliceLoadingState{}

    /**
     * Indicates this slice is empty and waiting for content to be loaded.
     */
    public static final int LOADED_NONE = 0;
    /**
     * Indicates this slice has some content but is waiting for other content to be loaded.
     */
    public static final int LOADED_PARTIAL = 1;
    /**
     * Indicates this slice has fully loaded and is not waiting for other content.
     */
    public static final int LOADED_ALL = 2;

    @NonNull
    private Slice mSlice;
    @Nullable
    private Context mContext;
    private long mExpiry;
    private long mLastUpdated;
    private ListContent mListContent;
    private RowContent mHeaderContent;
    private SliceAction mPrimaryAction;
    private List<SliceAction> mSliceActions;
    private @EventInfo.SliceRowType int mTemplateType;
    private final Bundle mHostExtras;

    /**
     * Create a SliceMetadata object to provide access to some information around the slice and
     * its contents.
     *
     * @param context the context to use for the slice.
     * @param slice the slice to extract metadata from.
     *
     * @return the metadata associated with the provided slice.
     */
    @NonNull
    public static SliceMetadata from(@Nullable Context context, @NonNull Slice slice) {
        return new SliceMetadata(context, slice);
    }

    /**
     * Create a SliceMetadata object to provide access to some information around the slice and
     * its contents.
     *
     * @param context the context to use for the slice.
     * @param slice the slice to extract metadata from.
     */
    private SliceMetadata(@Nullable Context context, @NonNull Slice slice) {
        mSlice = slice;
        mContext = context;
        SliceItem ttlItem = SliceQuery.find(slice, FORMAT_LONG, HINT_TTL, null);
        if (ttlItem != null) {
            mExpiry = ttlItem.getLong();
        }
        SliceItem updatedItem = SliceQuery.find(slice, FORMAT_LONG, HINT_LAST_UPDATED, null);
        if (updatedItem != null) {
            mLastUpdated = updatedItem.getLong();
        }
        SliceItem hostExtrasItem = SliceQuery.findSubtype(slice, FORMAT_BUNDLE,
                SUBTYPE_HOST_EXTRAS);
        if (hostExtrasItem != null && hostExtrasItem.mObj instanceof Bundle) {
            mHostExtras = (Bundle) hostExtrasItem.mObj;
        } else {
            mHostExtras = Bundle.EMPTY;
        }
        mListContent = new ListContent(slice);
        mHeaderContent = mListContent.getHeader();
        mTemplateType = mListContent.getHeaderTemplateType();
        mPrimaryAction = mListContent.getShortcut(mContext);
        mSliceActions = mListContent.getSliceActions();
        if (mSliceActions == null && mHeaderContent != null
                && SliceQuery.hasHints(mHeaderContent.getSliceItem(), HINT_LIST_ITEM)) {
            // It's not a real header, check it for end items.
            List<SliceItem> items = mHeaderContent.getEndItems();
            List<SliceAction> actions = new ArrayList<>();
            for (int i = 0; i < items.size(); i++) {
                if (SliceQuery.find(items.get(i), FORMAT_ACTION) != null) {
                    actions.add(new SliceActionImpl(items.get(i)));
                }
            }
            if (actions.size() > 0) {
                mSliceActions = actions;
            }
        }
    }

    /**
     * @return the title associated with this slice, if it exists.
     */
    @Nullable
    public CharSequence getTitle() {
        CharSequence title = null;
        if (mHeaderContent != null && mHeaderContent.getTitleItem() != null) {
            title = mHeaderContent.getTitleItem().getText();
        }
        if (TextUtils.isEmpty(title) && mPrimaryAction != null) {
            return mPrimaryAction.getTitle();

        }
        return title;
    }

    /**
     * @return the subtitle associated with this slice, if it exists.
     */
    @Nullable
    public CharSequence getSubtitle() {
        if (mHeaderContent != null && mHeaderContent.getSubtitleItem() != null) {
            return mHeaderContent.getSubtitleItem().getText();
        }
        return null;
    }

    /**
     * @return the summary associated with this slice, if it exists.
     */
    @Nullable
    public CharSequence getSummary() {
        if (mHeaderContent != null && mHeaderContent.getSummaryItem() != null) {
            return mHeaderContent.getSummaryItem().getText();
        }
        return null;
    }

    /**
     * @return the group of actions associated with this slice, if they exist.
     */
    @Nullable
    public List<SliceAction> getSliceActions() {
        return mSliceActions;
    }

    /**
     * @return the primary action for this slice, null if none specified.
     */
    @Nullable
    public SliceAction getPrimaryAction() {
        return mPrimaryAction;
    }

    /**
     * @return the type of row that is used for the header of this slice, -1 if unknown.
     */
    public @EventInfo.SliceRowType int getHeaderType() {
        return mTemplateType;
    }

    /**
     * @return whether this slice has content to show when presented
     * in {@link SliceView#MODE_LARGE}.
     */
    public boolean hasLargeMode() {
        return mListContent.getRowItems().size() > 1;
    }

    /**
     * @return the toggles associated with the header of this slice.
     */
    public List<SliceAction> getToggles() {
        List<SliceAction> toggles = new ArrayList<>();
        // Is it the primary action?
        if (mPrimaryAction != null && mPrimaryAction.isToggle()) {
            toggles.add(mPrimaryAction);
        } else if (mSliceActions != null && mSliceActions.size() > 0) {
            for (int i = 0; i < mSliceActions.size(); i++) {
                SliceAction action = mSliceActions.get(i);
                if (action.isToggle()) {
                    toggles.add(action);
                }
            }
        } else if (mHeaderContent != null) {
            toggles.addAll(mHeaderContent.getToggleItems());
        }
        return toggles;
    }

    @NonNull
    public Bundle getHostExtras() {
        return mHostExtras;
    }

    /**
     * Sends the intent to adjust the state of the provided toggle action.
     *
     * @param toggleAction the toggle action.
     * @param toggleValue the new value to set the toggle to.
     * @return whether there was an action to send.
     */
    public boolean sendToggleAction(SliceAction toggleAction, boolean toggleValue)
            throws PendingIntent.CanceledException {
        if (toggleAction != null) {
            Intent intent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
                    .putExtra(EXTRA_TOGGLE_STATE, toggleValue);
            if (mContext != null) {
                toggleAction.getAction().send(mContext, 0, intent, null, null);
            }
            return true;
        }
        return false;
    }

    /**
     * Gets the input range action associated with the header of this slice, if it exists.
     *
     * @return the {@link android.app.PendingIntent} for the input range.
     */
    @Nullable
    public PendingIntent getInputRangeAction() {
        if (mTemplateType == ROW_TYPE_SLIDER) {
            SliceItem range = mHeaderContent.getRange();
            if (range != null) {
                return range.getAction();
            }
        }
        return null;
    }

    /**
     * Sends the intent to adjust the input range value for the header of this slice, if it exists.
     *
     * @param newValue the value to set the input range to.
     * @return whether there was an action to send.
     */
    public boolean sendInputRangeAction(int newValue) throws PendingIntent.CanceledException {
        if (mTemplateType == ROW_TYPE_SLIDER) {
            SliceItem range = mHeaderContent.getRange();
            if (range != null) {
                // Ensure new value is valid
                Pair<Integer, Integer> validRange = getRange();
                int adjustedValue = MathUtils.clamp(newValue, validRange.first, validRange.second);
                Intent intent = new Intent()
                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
                        .putExtra(EXTRA_RANGE_VALUE, adjustedValue);
                range.fireAction(mContext, intent);
                return true;
            }
        }
        return false;
    }

    /**
     * Gets the range information associated with a progress bar or input range associated with this
     * slice, if it exists.
     *
     * @return a pair where the first item is the minimum value of the range and the second item is
     * the maximum value of the range.
     */
    @Nullable
    public Pair<Integer, Integer> getRange() {
        if (mTemplateType == ROW_TYPE_SLIDER
                || mTemplateType == ROW_TYPE_PROGRESS) {
            SliceItem range = mHeaderContent.getRange();
            SliceItem maxItem = SliceQuery.findSubtype(range, FORMAT_INT, SUBTYPE_MAX);
            SliceItem minItem = SliceQuery.findSubtype(range, FORMAT_INT, SUBTYPE_MIN);
            int max = maxItem != null ? maxItem.getInt() : 100; // default max of range
            int min = minItem != null ? minItem.getInt() : 0; // default min of range
            return new Pair<>(min, max);
        }
        return null;
    }

    /**
     * Gets the current value for a progress bar or input range associated with this slice, if it
     * exists, -1 if unknown.
     *
     * @return the current value of a progress bar or input range associated with this slice.
     */
    @NonNull
    public int getRangeValue() {
        if (mTemplateType == ROW_TYPE_SLIDER
                || mTemplateType == ROW_TYPE_PROGRESS) {
            SliceItem range = mHeaderContent.getRange();
            SliceItem currentItem = SliceQuery.findSubtype(range, FORMAT_INT, SUBTYPE_VALUE);
            return currentItem != null ? currentItem.getInt() : -1;
        }
        return -1;

    }

    /**
     * @return whether this slice is a selection (a drop-down list) slice.
     */
    public boolean isSelection() {
        return (mTemplateType == EventInfo.ROW_TYPE_SELECTION);
    }

    /**
     * @return the list of keywords associated with the provided slice, null if no keywords were
     * specified or an empty list if the slice was specified to have no keywords.
     */
    @Nullable
    public List<String> getSliceKeywords() {
        SliceItem keywordGroup = SliceQuery.find(mSlice, FORMAT_SLICE, HINT_KEYWORDS, null);
        if (keywordGroup != null) {
            List<SliceItem> itemList = SliceQuery.findAll(keywordGroup, FORMAT_TEXT);
            if (itemList != null) {
                ArrayList<String> stringList = new ArrayList<>();
                for (int i = 0; i < itemList.size(); i++) {
                    String keyword = (String) itemList.get(i).getText();
                    if (!TextUtils.isEmpty(keyword)) {
                        stringList.add(keyword);
                    }
                }
                return stringList;
            }
        }
        return null;
    }

    /**
     * @return the current loading state for this slice.
     *
     * @see #LOADED_NONE
     * @see #LOADED_PARTIAL
     * @see #LOADED_ALL
     */
    public int getLoadingState() {
        // Check loading state
        boolean hasHintPartial = SliceQuery.find(mSlice, null, HINT_PARTIAL, null) != null;
        if (!mListContent.isValid()) {
            // Empty slice
            return LOADED_NONE;
        } else if (hasHintPartial) {
            // Slice with specific content to load
            return LOADED_PARTIAL;
        } else {
            // Full slice
            return LOADED_ALL;
        }
    }

    /**
     * A slice contains an expiry to indicate when the content in the slice might no longer be
     * valid.
     *
     * @return the time, measured in milliseconds, between the expiry time of this slice and
     * midnight, January 1, 1970 UTC, or {@link androidx.slice.builders.ListBuilder#INFINITY} if
     * the slice is not time-sensitive.
     */
    public long getExpiry() {
        return mExpiry;
    }

    /**
     * @return the time, measured in milliseconds, between when the slice was created or last
     * updated, and midnight, January 1, 1970 UTC.
     */
    public long getLastUpdatedTime() {
        return mLastUpdated;
    }

    /**
     * To present a slice from another app, the app must grant uri permissions for the slice. If
     * these permissions have not been granted and the app slice is requested then
     * a permission request slice will be returned instead, allowing the user to grant permission.
     * This method can be used to identify if a slice is a permission request.
     *
     * @return whether this slice represents a permission request.
     */
    public boolean isPermissionSlice() {
        return mSlice.hasHint(HINT_PERMISSION_REQUEST);
    }

    /**
     * Indicates whether this slice indicates an error, i.e. the normal contents of this slice are
     * unavailable and instead the slice contains a message indicating an error.
     *
     * @return whether this slice represents an error.
     */
    public boolean isErrorSlice() {
        return mSlice.hasHint(HINT_ERROR);
    }

    /**
     * Indicates whether this slice was created using {@link SliceUtils#parseSlice} or through
     * normal binding.
     */
    public boolean isCachedSlice() {
        return mSlice.hasHint(HINT_CACHED);
    }

    /**
     * @return the group of slice actions associated with the provided slice, if they exist.
     * @hide
     */
    @Nullable
    @RestrictTo(RestrictTo.Scope.LIBRARY)
    public static List<SliceAction> getSliceActions(@NonNull Slice slice) {
        SliceItem actionGroup = SliceQuery.find(slice, FORMAT_SLICE, HINT_ACTIONS, null);
        String[] hints = new String[] {HINT_ACTIONS, HINT_SHORTCUT};
        List<SliceItem> items =  (actionGroup != null)
                ? SliceQuery.findAll(actionGroup, FORMAT_SLICE, hints, null)
                : null;
        if (items != null) {
            List<SliceAction> actions = new ArrayList<>(items.size());
            for (int i = 0; i < items.size(); i++) {
                SliceItem item = items.get(i);
                actions.add(new SliceActionImpl(item));
            }
            return actions;
        }
        return null;
    }

    /**
     * @hide
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY)
    public boolean isExpired() {
        long now = System.currentTimeMillis();
        return mExpiry != 0 && mExpiry != SliceHints.INFINITY && now > mExpiry;
    }

    /**
     * @hide
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY)
    public boolean neverExpires() {
        return mExpiry == SliceHints.INFINITY;
    }

    /**
     * @hide
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY)
    public long getTimeToExpiry() {
        long now = System.currentTimeMillis();
        return (mExpiry == 0 || mExpiry == SliceHints.INFINITY || now > mExpiry)
                ? 0 : mExpiry - now;
    }

    /**
     * @hide
     */
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
    public ListContent getListContent() {
        return mListContent;
    }
}