public class

SliceAdapter

extends RecyclerView.Adapter<SliceAdapter.SliceViewHolder>

implements androidx.slice.widget.SliceActionView.SliceActionLoadingListener

 java.lang.Object

androidx.recyclerview.widget.RecyclerView.Adapter<SliceAdapter.SliceViewHolder>

↳androidx.slice.widget.SliceAdapter

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

Summary

Constructors
publicSliceAdapter(Context context)

Methods
public abstract intgetItemCount()

Returns the total number of items in the data set held by the adapter.

public longgetItemId(int position)

Return the stable ID for the item at position.

public intgetItemViewType(int position)

Return the view type of the item at position for the purposes of view recycling.

public java.util.Set<SliceItem>getLoadingActions()

Returns the currently loading actions.

public voidnotifyHeaderChanged()

Notifies that content in the header of this adapter has changed.

public abstract voidonBindViewHolder(RecyclerView.ViewHolder holder, int position)

Called by RecyclerView to display the data at the specified position.

public abstract RecyclerView.ViewHolderonCreateViewHolder(ViewGroup parent, int viewType)

Called when RecyclerView needs a new RecyclerView.ViewHolder of the given type to represent an item.

public voidonSliceActionLoading(SliceItem actionItem, int position)

public voidsetAllowTwoLines(boolean allowTwoLines)

Sets whether this slice can have 2 lines of subtitle text in the first row.

public voidsetInsets(int l, int t, int r, int b)

Sets the insets (padding) for slice view.

public voidsetLastUpdated(long lastUpdated)

Sets when the slice was last updated.

public voidsetLoadingActions(java.util.Set<SliceItem> actions)

Indicates that no actions should be loading and updates the views.

public voidsetParents(SliceView parent, TemplateView templateView)

Sets the SliceView parent and the template parent.

public voidsetPolicy(SliceViewPolicy p)

Sets the policy information to use for views in this adapter.

public voidsetShowLastUpdated(boolean showLastUpdated)

Sets whether the last updated time should be shown on the slice.

public voidsetSliceActions(java.util.List<SliceAction> actions)

Sets the actions to display for this slice, this adjusts what's displayed in the header item.

public voidsetSliceItems(java.util.List<SliceContent> slices, int color, int mode)

Set the SliceItem's to be displayed in the adapter and the accent color.

public voidsetSliceObserver(SliceView.OnSliceActionListener observer)

Sets the observer to pass down to child views.

public voidsetStyle(SliceStyle style)

Sets the style information to use for views in this adapter.

from RecyclerView.Adapter<VH>bindViewHolder, createViewHolder, findRelativeAdapterPositionIn, getStateRestorationPolicy, hasObservers, hasStableIds, notifyDataSetChanged, notifyItemChanged, notifyItemChanged, notifyItemInserted, notifyItemMoved, notifyItemRangeChanged, notifyItemRangeChanged, notifyItemRangeInserted, notifyItemRangeRemoved, notifyItemRemoved, onAttachedToRecyclerView, onBindViewHolder, onDetachedFromRecyclerView, onFailedToRecycleView, onViewAttachedToWindow, onViewDetachedFromWindow, onViewRecycled, registerAdapterDataObserver, setHasStableIds, setStateRestorationPolicy, unregisterAdapterDataObserver
from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public SliceAdapter(Context context)

Methods

public void setParents(SliceView parent, TemplateView templateView)

Sets the SliceView parent and the template parent.

public void setInsets(int l, int t, int r, int b)

Sets the insets (padding) for slice view. SliceAdapter will handle determining if a child needs a particular padding, i.e. if it's the first row then the top inset will be applied to it whereas subsequent rows would get a top inset of 0.

public void setSliceObserver(SliceView.OnSliceActionListener observer)

Sets the observer to pass down to child views.

public void setSliceActions(java.util.List<SliceAction> actions)

Sets the actions to display for this slice, this adjusts what's displayed in the header item.

public void setSliceItems(java.util.List<SliceContent> slices, int color, int mode)

Set the SliceItem's to be displayed in the adapter and the accent color.

public void setStyle(SliceStyle style)

Sets the style information to use for views in this adapter.

public void setPolicy(SliceViewPolicy p)

Sets the policy information to use for views in this adapter.

public void setShowLastUpdated(boolean showLastUpdated)

Sets whether the last updated time should be shown on the slice.

public void setLastUpdated(long lastUpdated)

Sets when the slice was last updated.

public void setLoadingActions(java.util.Set<SliceItem> actions)

Indicates that no actions should be loading and updates the views.

public java.util.Set<SliceItem> getLoadingActions()

Returns the currently loading actions.

public void onSliceActionLoading(SliceItem actionItem, int position)

public void setAllowTwoLines(boolean allowTwoLines)

Sets whether this slice can have 2 lines of subtitle text in the first row.

public void notifyHeaderChanged()

Notifies that content in the header of this adapter has changed.

public abstract RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)

Called when RecyclerView needs a new RecyclerView.ViewHolder of the given type to represent an item.

This new ViewHolder should be constructed with a new View that can represent the items of the given type. You can either create a new View manually or inflate it from an XML layout file.

The new ViewHolder will be used to display items of the adapter using RecyclerView.Adapter. Since it will be re-used to display different items in the data set, it is a good idea to cache references to sub views of the View to avoid unnecessary View calls.

Parameters:

parent: The ViewGroup into which the new View will be added after it is bound to an adapter position.
viewType: The view type of the new View.

Returns:

A new ViewHolder that holds a View of the given view type.

See also: RecyclerView.Adapter.getItemViewType(int), RecyclerView.Adapter

public int getItemViewType(int position)

Return the view type of the item at position for the purposes of view recycling.

The default implementation of this method returns 0, making the assumption of a single view type for the adapter. Unlike ListView adapters, types need not be contiguous. Consider using id resources to uniquely identify item view types.

Parameters:

position: position to query

Returns:

integer value identifying the type of the view needed to represent the item at position. Type codes need not be contiguous.

public long getItemId(int position)

Return the stable ID for the item at position. If RecyclerView.Adapter.hasStableIds() would return false this method should return RecyclerView.NO_ID. The default implementation of this method returns RecyclerView.NO_ID.

Parameters:

position: Adapter position to query

Returns:

the stable ID of the item at position

public abstract int getItemCount()

Returns the total number of items in the data set held by the adapter.

Returns:

The total number of items in this adapter.

public abstract void onBindViewHolder(RecyclerView.ViewHolder holder, int position)

Called by RecyclerView to display the data at the specified position. This method should update the contents of the RecyclerView.ViewHolder.itemView to reflect the item at the given position.

Note that unlike , RecyclerView will not call this method again if the position of the item changes in the data set unless the item itself is invalidated or the new position cannot be determined. For this reason, you should only use the position parameter while acquiring the related data item inside this method and should not keep a copy of it. If you need the position of an item later on (e.g. in a click listener), use RecyclerView.ViewHolder.getBindingAdapterPosition() which will have the updated adapter position. Override RecyclerView.Adapter instead if Adapter can handle efficient partial bind.

Parameters:

holder: The ViewHolder which should be updated to represent the contents of the item at the given position in the data set.
position: The position of the item within the adapter's data set.

Source

/*
 * Copyright 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.widget;

import static android.app.slice.Slice.HINT_HORIZONTAL;
import static android.app.slice.Slice.SUBTYPE_MESSAGE;
import static android.app.slice.Slice.SUBTYPE_SOURCE;
import static android.app.slice.SliceItem.FORMAT_ACTION;
import static android.app.slice.SliceItem.FORMAT_SLICE;

import android.app.slice.Slice;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;

import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.collection.ArrayMap;
import androidx.recyclerview.widget.RecyclerView;
import androidx.slice.SliceItem;
import androidx.slice.core.SliceAction;
import androidx.slice.core.SliceQuery;
import androidx.slice.view.R;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @hide
 */
@RestrictTo(RestrictTo.Scope.LIBRARY)
@RequiresApi(19)
public class SliceAdapter extends RecyclerView.Adapter<SliceAdapter.SliceViewHolder>
        implements SliceActionView.SliceActionLoadingListener {

    static final int TYPE_DEFAULT       = 1;
    static final int TYPE_HEADER        = 2; // TODO: headers shouldn't scroll off
    static final int TYPE_GRID          = 3;
    static final int TYPE_MESSAGE       = 4;
    static final int TYPE_MESSAGE_LOCAL = 5;

    static final int HEADER_INDEX = 0;

    private final IdGenerator mIdGen = new IdGenerator();
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    final Context mContext;
    private List<SliceWrapper> mSlices = new ArrayList<>();
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    SliceView.OnSliceActionListener mSliceObserver;
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    int mTintColor;
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    SliceStyle mSliceStyle;
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    List<SliceAction> mSliceActions;
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    boolean mShowLastUpdated;
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    long mLastUpdated;
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    SliceView mParent;
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    TemplateView mTemplateView;
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    int mInsetStart;
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    int mInsetTop;
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    int mInsetEnd;
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    int mInsetBottom;
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    Set<SliceItem> mLoadingActions = new HashSet<>();
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    boolean mAllowTwoLines;
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    SliceViewPolicy mPolicy;

    public SliceAdapter(Context context) {
        mContext = context;
        setHasStableIds(true);
    }

    /**
     * Sets the SliceView parent and the template parent.
     */
    public void setParents(SliceView parent, TemplateView templateView) {
        mParent = parent;
        mTemplateView = templateView;
    }

    /**
     * Sets the insets (padding) for slice view. SliceAdapter will handle determining
     * if a child needs a particular padding, i.e. if it's the first row then the top inset
     * will be applied to it whereas subsequent rows would get a top inset of 0.
     */
    public void setInsets(int l, int t, int r, int b) {
        mInsetStart = l;
        mInsetTop = t;
        mInsetEnd = r;
        mInsetBottom = b;
    }

    /**
     * Sets the observer to pass down to child views.
     */
    public void setSliceObserver(SliceView.OnSliceActionListener observer) {
        mSliceObserver = observer;
    }

    /**
     * Sets the actions to display for this slice, this adjusts what's displayed in the header item.
     */
    public void setSliceActions(List<SliceAction> actions) {
        mSliceActions = actions;
        notifyHeaderChanged();
    }

    /**
     * Set the {@link SliceItem}'s to be displayed in the adapter and the accent color.
     */
    public void setSliceItems(List<SliceContent> slices, int color, int mode) {
        if (slices == null) {
            mLoadingActions.clear();
            mSlices.clear();
        } else {
            mIdGen.resetUsage();
            mSlices = new ArrayList<>(slices.size());
            for (SliceContent s : slices) {
                mSlices.add(new SliceWrapper(s, mIdGen, mode));
            }
        }
        mTintColor = color;
        notifyDataSetChanged();
    }

    /**
     * Sets the style information to use for views in this adapter.
     */
    public void setStyle(SliceStyle style) {
        mSliceStyle = style;
        notifyDataSetChanged();
    }

    /**
     * Sets the policy information to use for views in this adapter.
     */
    public void setPolicy(SliceViewPolicy p) {
        mPolicy = p;
    }

    /**
     * Sets whether the last updated time should be shown on the slice.
     */
    public void setShowLastUpdated(boolean showLastUpdated) {
        if (mShowLastUpdated != showLastUpdated) {
            mShowLastUpdated = showLastUpdated;
            notifyHeaderChanged();
        }
    }

    /**
     * Sets when the slice was last updated.
     */
    public void setLastUpdated(long lastUpdated) {
        if (mLastUpdated != lastUpdated) {
            mLastUpdated = lastUpdated;
            notifyHeaderChanged();
        }
    }

    /**
     * Indicates that no actions should be loading and updates the views.
     */
    public void setLoadingActions(Set<SliceItem> actions) {
        if (actions == null) {
            mLoadingActions.clear();
        } else {
            mLoadingActions = actions;
        }
        notifyDataSetChanged();
    }

    /**
     * Returns the currently loading actions.
     */
    public Set<SliceItem> getLoadingActions() {
        return mLoadingActions;
    }

    @Override
    public void onSliceActionLoading(SliceItem actionItem, int position) {
        mLoadingActions.add(actionItem);
        if (getItemCount() > position) {
            notifyItemChanged(position);
        } else {
            notifyDataSetChanged();
        }
    }

    /**
     * Sets whether this slice can have 2 lines of subtitle text in the first row.
     */
    public void setAllowTwoLines(boolean allowTwoLines) {
        mAllowTwoLines = allowTwoLines;
        notifyHeaderChanged();
    }

    /**
     * Notifies that content in the header of this adapter has changed.
     */
    public void notifyHeaderChanged() {
        if (getItemCount() > 0) {
            notifyItemChanged(HEADER_INDEX);
        }
    }

    @Override
    public SliceViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = inflateForType(viewType);
        v.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
        return new SliceViewHolder(v);
    }

    @Override
    public int getItemViewType(int position) {
        return mSlices.get(position).mType;
    }

    @Override
    public long getItemId(int position) {
        return mSlices.get(position).mId;
    }

    @Override
    public int getItemCount() {
        return mSlices.size();
    }

    @Override
    public void onBindViewHolder(SliceViewHolder holder, int position) {
        SliceWrapper slice = mSlices.get(position);
        holder.bind(slice.mItem, position);
    }

    private View inflateForType(int viewType) {
        switch (viewType) {
            case TYPE_GRID:
                return LayoutInflater.from(mContext).inflate(R.layout.abc_slice_grid, null);
            case TYPE_MESSAGE:
                return LayoutInflater.from(mContext).inflate(R.layout.abc_slice_message, null);
            case TYPE_MESSAGE_LOCAL:
                return LayoutInflater.from(mContext).inflate(R.layout.abc_slice_message_local,
                        null);
            default:
                return new RowView(mContext);
        }
    }

    protected static class SliceWrapper {
        final SliceContent mItem;
        final int mType;
        final long mId;

        public SliceWrapper(SliceContent item, IdGenerator idGen, int mode) {
            mItem = item;
            mType = getFormat(item.getSliceItem());
            mId = idGen.getId(item.getSliceItem());
        }

        public static int getFormat(SliceItem item) {
            if (SUBTYPE_MESSAGE.equals(item.getSubType())) {
                // TODO: Better way to determine me or not? Something more like Messaging style.
                if (SliceQuery.findSubtype(item, null, SUBTYPE_SOURCE) != null) {
                    return TYPE_MESSAGE;
                } else {
                    return TYPE_MESSAGE_LOCAL;
                }
            }
            if (item.hasHint(HINT_HORIZONTAL)) {
                return TYPE_GRID;
            }
            if (!item.hasHint(Slice.HINT_LIST_ITEM)) {
                return TYPE_HEADER;
            }
            return TYPE_DEFAULT;
        }
    }

    /**
     * A {@link RecyclerView.ViewHolder} for presenting slices in {@link SliceAdapter}.
     */
    public class SliceViewHolder extends RecyclerView.ViewHolder implements View.OnTouchListener,
            View.OnClickListener {
        public final SliceChildView mSliceChildView;

        public SliceViewHolder(View itemView) {
            super(itemView);
            mSliceChildView = itemView instanceof SliceChildView ? (SliceChildView) itemView : null;
        }

        void bind(SliceContent item, int position) {
            if (mSliceChildView == null || item == null) {
                return;
            }

            RowStyle rowStyle = mSliceStyle.getRowStyle(item.getSliceItem());

            // Click listener used to pipe click events to parent
            mSliceChildView.setOnClickListener(this);
            // Touch listener used to pipe events to touch feedback drawable
            mSliceChildView.setOnTouchListener(this);
            mSliceChildView.setSliceActionLoadingListener(SliceAdapter.this);

            final boolean isHeader = item instanceof RowContent
                    ? ((RowContent) item).getIsHeader() : position == HEADER_INDEX;
            mSliceChildView.setLoadingActions(mLoadingActions);
            mSliceChildView.setPolicy(mPolicy);
            mSliceChildView.setTint(rowStyle.getTintColor());
            mSliceChildView.setStyle(mSliceStyle, rowStyle);
            mSliceChildView.setShowLastUpdated(isHeader && mShowLastUpdated);
            mSliceChildView.setLastUpdated(isHeader ? mLastUpdated : -1);
            // Only apply top / bottom insets to first / last rows
            int top = position == 0 ? mInsetTop : 0;
            int bottom = position == getItemCount() - 1 ? mInsetBottom : 0;
            mSliceChildView.setInsets(mInsetStart, top, mInsetEnd, bottom);
            mSliceChildView.setAllowTwoLines(mAllowTwoLines);
            mSliceChildView.setSliceActions(isHeader ? mSliceActions : null);
            mSliceChildView.setSliceItem(item, isHeader, position, getItemCount(), mSliceObserver);
            int[] info = new int[2];
            info[0] = ListContent.getRowType(item, isHeader, mSliceActions);
            info[1] = position;
            mSliceChildView.setTag(info);
        }

        @Override
        public void onClick(View v) {
            if (mParent != null) {
                mParent.setClickInfo((int[]) v.getTag());
                mParent.performClick();
            }
        }

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (mTemplateView != null) {
                mTemplateView.onForegroundActivated(event);
            }
            return false;
        }
    }

    private static class IdGenerator {
        private long mNextLong = 0;
        private final ArrayMap<String, Long> mCurrentIds = new ArrayMap<>();
        private final ArrayMap<String, Integer> mUsedIds = new ArrayMap<>();

        IdGenerator() {
        }

        public long getId(SliceItem item) {
            String str = genString(item);
            if (!mCurrentIds.containsKey(str)) {
                mCurrentIds.put(str, mNextLong++);
            }
            long id = mCurrentIds.get(str);
            Integer usedIdIndex = mUsedIds.get(str);
            int index = usedIdIndex != null ? usedIdIndex : 0;
            mUsedIds.put(str, index + 1);
            return id + index * 10000;
        }

        private String genString(SliceItem item) {
            if (FORMAT_SLICE.equals(item.getFormat())
                    || FORMAT_ACTION.equals(item.getFormat())) {
                return String.valueOf(item.getSlice().getItems().size());
            }
            return item.toString();
        }

        public void resetUsage() {
            mUsedIds.clear();
        }
    }
}