18a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk/*
28a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * Copyright 2017 The Android Open Source Project
38a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk *
48a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * Licensed under the Apache License, Version 2.0 (the "License");
58a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * you may not use this file except in compliance with the License.
68a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * You may obtain a copy of the License at
78a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk *
88a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk *      http://www.apache.org/licenses/LICENSE-2.0
98a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk *
108a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * Unless required by applicable law or agreed to in writing, software
118a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * distributed under the License is distributed on an "AS IS" BASIS,
128a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * See the License for the specific language governing permissions and
148a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * limitations under the License.
158a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk */
168a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk
1785ef1446b82c8783a50af92c4cb1389fe0d0e907Aurimas Liutikaspackage androidx.slice.widget;
188a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk
19dcb5e2f13fa8471f62a7f4625b4dc6e449358cbdJason Monkimport static android.app.slice.Slice.HINT_HORIZONTAL;
200929cbe8a1ad88cb18e0cd3b50abba21840bc592Mady Mellorimport static android.app.slice.Slice.HINT_SUMMARY;
210c76d3038c814e26f15b16ce2e09e28bcbcedcc6Jason Monkimport static android.app.slice.Slice.SUBTYPE_MESSAGE;
220c76d3038c814e26f15b16ce2e09e28bcbcedcc6Jason Monkimport static android.app.slice.Slice.SUBTYPE_SOURCE;
2398ae4f80b7244070c20d5c3a16245df5cd0c5df8Jason Monkimport static android.app.slice.SliceItem.FORMAT_INT;
240c76d3038c814e26f15b16ce2e09e28bcbcedcc6Jason Monkimport static android.app.slice.SliceItem.FORMAT_TEXT;
250c76d3038c814e26f15b16ce2e09e28bcbcedcc6Jason Monk
2685ef1446b82c8783a50af92c4cb1389fe0d0e907Aurimas Liutikasimport static androidx.slice.widget.SliceView.MODE_LARGE;
277c19f268c4f3b016861816c350a13e7aa5743fc6Mady Mellor
28c1334184900d93f73e879d02a32715a5579aa55fMady Mellorimport android.app.slice.Slice;
298a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.content.Context;
30dc05204762b444dde1db9029aed2819fd72476f7Mady Mellorimport android.util.AttributeSet;
318a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.view.LayoutInflater;
326975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellorimport android.view.MotionEvent;
338a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.view.View;
348a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.view.ViewGroup;
358a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkimport android.view.ViewGroup.LayoutParams;
368a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk
3763fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellorimport androidx.annotation.RestrictTo;
38f68dde011de8fb4d616a09292524869f56d94110Jason Monkimport androidx.collection.ArrayMap;
3963fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellorimport androidx.recyclerview.widget.RecyclerView;
4085ef1446b82c8783a50af92c4cb1389fe0d0e907Aurimas Liutikasimport androidx.slice.SliceItem;
4185ef1446b82c8783a50af92c4cb1389fe0d0e907Aurimas Liutikasimport androidx.slice.core.SliceQuery;
4285ef1446b82c8783a50af92c4cb1389fe0d0e907Aurimas Liutikasimport androidx.slice.view.R;
438a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk
4463fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellorimport java.util.ArrayList;
45f68dde011de8fb4d616a09292524869f56d94110Jason Monkimport java.util.Iterator;
4663fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellorimport java.util.List;
4763fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor
488a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk/**
498a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk * @hide
508a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk */
518a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk@RestrictTo(RestrictTo.Scope.LIBRARY)
528a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monkpublic class LargeSliceAdapter extends RecyclerView.Adapter<LargeSliceAdapter.SliceViewHolder> {
538a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk
54dc05204762b444dde1db9029aed2819fd72476f7Mady Mellor    static final int TYPE_DEFAULT       = 1;
55dc05204762b444dde1db9029aed2819fd72476f7Mady Mellor    static final int TYPE_HEADER        = 2; // TODO: headers shouldn't scroll off
56dc05204762b444dde1db9029aed2819fd72476f7Mady Mellor    static final int TYPE_GRID          = 3;
57dc05204762b444dde1db9029aed2819fd72476f7Mady Mellor    static final int TYPE_MESSAGE       = 4;
58dc05204762b444dde1db9029aed2819fd72476f7Mady Mellor    static final int TYPE_MESSAGE_LOCAL = 5;
598a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk
6063fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor    static final int HEADER_INDEX = 0;
6163fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor
628a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    private final IdGenerator mIdGen = new IdGenerator();
638a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    private final Context mContext;
648a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    private List<SliceWrapper> mSlices = new ArrayList<>();
65abd7ffd938056760f750af041b0e6f02d2cb6e1eMady Mellor    private SliceView.OnSliceActionListener mSliceObserver;
66dc05204762b444dde1db9029aed2819fd72476f7Mady Mellor    private int mColor;
67dc05204762b444dde1db9029aed2819fd72476f7Mady Mellor    private AttributeSet mAttrs;
6877d3fe9d9b671c0b3853fec6bc21b91d8418afc9Mady Mellor    private int mDefStyleAttr;
6977d3fe9d9b671c0b3853fec6bc21b91d8418afc9Mady Mellor    private int mDefStyleRes;
70b794b5b0f4bcd000e098265a6ec63d4b0cf3852fMady Mellor    private List<SliceItem> mSliceActions;
7163fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor    private boolean mShowLastUpdated;
7263fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor    private long mLastUpdated;
736975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor    private SliceView mParent;
746975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor    private LargeTemplateView mTemplateView;
758a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk
768a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    public LargeSliceAdapter(Context context) {
778a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        mContext = context;
788a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        setHasStableIds(true);
798a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    }
808a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk
816975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor    /**
826975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor     * Sets the SliceView parent and the template parent.
836975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor     */
846975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor    public void setParents(SliceView parent, LargeTemplateView templateView) {
856975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor        mParent = parent;
866975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor        mTemplateView = templateView;
876975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor    }
886975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor
896975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor    /**
906975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor     * Sets the observer to pass down to child views.
916975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor     */
92abd7ffd938056760f750af041b0e6f02d2cb6e1eMady Mellor    public void setSliceObserver(SliceView.OnSliceActionListener observer) {
93238b9b6aad4ef0c63acbecfa89605a3d753931feMady Mellor        mSliceObserver = observer;
94238b9b6aad4ef0c63acbecfa89605a3d753931feMady Mellor    }
95238b9b6aad4ef0c63acbecfa89605a3d753931feMady Mellor
968a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    /**
97b794b5b0f4bcd000e098265a6ec63d4b0cf3852fMady Mellor     * Sets the actions to display for this slice, this adjusts what's displayed in the header item.
98b794b5b0f4bcd000e098265a6ec63d4b0cf3852fMady Mellor     */
99b794b5b0f4bcd000e098265a6ec63d4b0cf3852fMady Mellor    public void setSliceActions(List<SliceItem> actions) {
100b794b5b0f4bcd000e098265a6ec63d4b0cf3852fMady Mellor        mSliceActions = actions;
10163fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor        notifyHeaderChanged();
102b794b5b0f4bcd000e098265a6ec63d4b0cf3852fMady Mellor    }
103b794b5b0f4bcd000e098265a6ec63d4b0cf3852fMady Mellor
104b794b5b0f4bcd000e098265a6ec63d4b0cf3852fMady Mellor    /**
1058a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk     * Set the {@link SliceItem}'s to be displayed in the adapter and the accent color.
1068a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk     */
1070929cbe8a1ad88cb18e0cd3b50abba21840bc592Mady Mellor    public void setSliceItems(List<SliceItem> slices, int color, int mode) {
108df26970a23ca28c8582178e3dd0e5502d4030293Mady Mellor        if (slices == null) {
109df26970a23ca28c8582178e3dd0e5502d4030293Mady Mellor            mSlices.clear();
110df26970a23ca28c8582178e3dd0e5502d4030293Mady Mellor        } else {
111df26970a23ca28c8582178e3dd0e5502d4030293Mady Mellor            mIdGen.resetUsage();
11206ab4aa38f82b8e2d9af769f7c777253f2497f93Jake Wharton            mSlices = new ArrayList<>(slices.size());
11306ab4aa38f82b8e2d9af769f7c777253f2497f93Jake Wharton            for (SliceItem s : slices) {
1140929cbe8a1ad88cb18e0cd3b50abba21840bc592Mady Mellor                mSlices.add(new SliceWrapper(s, mIdGen, mode));
11506ab4aa38f82b8e2d9af769f7c777253f2497f93Jake Wharton            }
116df26970a23ca28c8582178e3dd0e5502d4030293Mady Mellor        }
1178a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        mColor = color;
1188a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        notifyDataSetChanged();
1198a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    }
1208a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk
121dc05204762b444dde1db9029aed2819fd72476f7Mady Mellor    /**
122dc05204762b444dde1db9029aed2819fd72476f7Mady Mellor     * Sets the attribute set to use for views in the list.
123dc05204762b444dde1db9029aed2819fd72476f7Mady Mellor     */
12477d3fe9d9b671c0b3853fec6bc21b91d8418afc9Mady Mellor    public void setStyle(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
125dc05204762b444dde1db9029aed2819fd72476f7Mady Mellor        mAttrs = attrs;
12677d3fe9d9b671c0b3853fec6bc21b91d8418afc9Mady Mellor        mDefStyleAttr = defStyleAttr;
12777d3fe9d9b671c0b3853fec6bc21b91d8418afc9Mady Mellor        mDefStyleRes = defStyleRes;
128dc05204762b444dde1db9029aed2819fd72476f7Mady Mellor        notifyDataSetChanged();
129dc05204762b444dde1db9029aed2819fd72476f7Mady Mellor    }
130dc05204762b444dde1db9029aed2819fd72476f7Mady Mellor
13163fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor    /**
13263fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor     * Sets whether the last updated time should be shown on the slice.
13363fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor     */
13463fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor    public void setShowLastUpdated(boolean showLastUpdated) {
13563fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor        mShowLastUpdated = showLastUpdated;
13663fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor        notifyHeaderChanged();
13763fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor    }
13863fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor
13963fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor    /**
14063fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor     * Sets when the slice was last updated.
14163fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor     */
14263fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor    public void setLastUpdated(long lastUpdated) {
14363fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor        mLastUpdated = lastUpdated;
14463fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor        notifyHeaderChanged();
14563fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor    }
14663fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor
1478a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    @Override
1488a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    public SliceViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
1498a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        View v = inflateForType(viewType);
1508a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        v.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
1518a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        return new SliceViewHolder(v);
1528a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    }
1538a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk
1548a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    @Override
1558a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    public int getItemViewType(int position) {
1568a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        return mSlices.get(position).mType;
1578a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    }
1588a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk
1598a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    @Override
1608a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    public long getItemId(int position) {
1618a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        return mSlices.get(position).mId;
1628a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    }
1638a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk
1648a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    @Override
1658a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    public int getItemCount() {
1668a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        return mSlices.size();
1678a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    }
1688a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk
1698a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    @Override
1708a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    public void onBindViewHolder(SliceViewHolder holder, int position) {
1718a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        SliceWrapper slice = mSlices.get(position);
1726975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor        holder.bind(slice.mItem, position);
1738a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    }
1748a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk
17563fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor    private void notifyHeaderChanged() {
17663fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor        if (getItemCount() > 0) {
17763fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor            notifyItemChanged(HEADER_INDEX);
17863fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor        }
17963fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor    }
18063fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor
1818a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    private View inflateForType(int viewType) {
1827c19f268c4f3b016861816c350a13e7aa5743fc6Mady Mellor        View v = new RowView(mContext);
1838a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        switch (viewType) {
1848a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk            case TYPE_GRID:
1857c19f268c4f3b016861816c350a13e7aa5743fc6Mady Mellor                v = LayoutInflater.from(mContext).inflate(R.layout.abc_slice_grid, null);
1867c19f268c4f3b016861816c350a13e7aa5743fc6Mady Mellor                break;
1878a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk            case TYPE_MESSAGE:
1887c19f268c4f3b016861816c350a13e7aa5743fc6Mady Mellor                v = LayoutInflater.from(mContext).inflate(R.layout.abc_slice_message, null);
1897c19f268c4f3b016861816c350a13e7aa5743fc6Mady Mellor                break;
1908a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk            case TYPE_MESSAGE_LOCAL:
1917c19f268c4f3b016861816c350a13e7aa5743fc6Mady Mellor                v = LayoutInflater.from(mContext).inflate(R.layout.abc_slice_message_local,
1928a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk                        null);
1937c19f268c4f3b016861816c350a13e7aa5743fc6Mady Mellor                break;
1948a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        }
1957c19f268c4f3b016861816c350a13e7aa5743fc6Mady Mellor        return v;
1968a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    }
1978a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk
1988a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    protected static class SliceWrapper {
1998a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        private final SliceItem mItem;
2008a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        private final int mType;
2018a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        private final long mId;
2028a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk
2030929cbe8a1ad88cb18e0cd3b50abba21840bc592Mady Mellor        public SliceWrapper(SliceItem item, IdGenerator idGen, int mode) {
2048a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk            mItem = item;
2050c76d3038c814e26f15b16ce2e09e28bcbcedcc6Jason Monk            mType = getFormat(item);
2060929cbe8a1ad88cb18e0cd3b50abba21840bc592Mady Mellor            mId = idGen.getId(item, mode);
2078a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        }
2088a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk
2090c76d3038c814e26f15b16ce2e09e28bcbcedcc6Jason Monk        public static int getFormat(SliceItem item) {
2100c76d3038c814e26f15b16ce2e09e28bcbcedcc6Jason Monk            if (SUBTYPE_MESSAGE.equals(item.getSubType())) {
2118a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk                // TODO: Better way to determine me or not? Something more like Messaging style.
2120c76d3038c814e26f15b16ce2e09e28bcbcedcc6Jason Monk                if (SliceQuery.findSubtype(item, null, SUBTYPE_SOURCE) != null) {
2138a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk                    return TYPE_MESSAGE;
2148a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk                } else {
2158a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk                    return TYPE_MESSAGE_LOCAL;
2168a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk                }
2178a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk            }
218dcb5e2f13fa8471f62a7f4625b4dc6e449358cbdJason Monk            if (item.hasHint(HINT_HORIZONTAL)) {
2198a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk                return TYPE_GRID;
2208a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk            }
221c1334184900d93f73e879d02a32715a5579aa55fMady Mellor            if (!item.hasHint(Slice.HINT_LIST_ITEM)) {
222c1334184900d93f73e879d02a32715a5579aa55fMady Mellor                return TYPE_HEADER;
223c1334184900d93f73e879d02a32715a5579aa55fMady Mellor            }
2248a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk            return TYPE_DEFAULT;
2258a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        }
2268a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    }
2278a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk
2288a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    /**
2298a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk     * A {@link RecyclerView.ViewHolder} for presenting slices in {@link LargeSliceAdapter}.
2308a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk     */
2316975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor    public class SliceViewHolder extends RecyclerView.ViewHolder implements View.OnTouchListener,
2326975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor            View.OnClickListener {
2336975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor        public final SliceChildView mSliceChildView;
2348a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk
2358a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        public SliceViewHolder(View itemView) {
2368a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk            super(itemView);
2376975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor            mSliceChildView = itemView instanceof SliceChildView ? (SliceChildView) itemView : null;
2386975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor        }
2396975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor
2406975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor        void bind(SliceItem item, int position) {
2416975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor            if (mSliceChildView == null || item == null) {
2426975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor                return;
2436975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor            }
2446975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor            // Click listener used to pipe click events to parent
2456975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor            mSliceChildView.setOnClickListener(this);
2466975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor            // Touch listener used to pipe events to touch feedback drawable
2476975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor            mSliceChildView.setOnTouchListener(this);
2486975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor
2496975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor            final boolean isHeader = position == HEADER_INDEX;
250432265f1922d8d6bf208825855b2d16160599199Mady Mellor            int mode = mParent != null ? mParent.getMode() : MODE_LARGE;
251432265f1922d8d6bf208825855b2d16160599199Mady Mellor            mSliceChildView.setMode(mode);
2526975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor            mSliceChildView.setTint(mColor);
25377d3fe9d9b671c0b3853fec6bc21b91d8418afc9Mady Mellor            mSliceChildView.setStyle(mAttrs, mDefStyleAttr, mDefStyleRes);
25465d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            mSliceChildView.setSliceItem(item, isHeader, position, getItemCount(), mSliceObserver);
2550929cbe8a1ad88cb18e0cd3b50abba21840bc592Mady Mellor            mSliceChildView.setSliceActions(isHeader ? mSliceActions : null);
2560929cbe8a1ad88cb18e0cd3b50abba21840bc592Mady Mellor            mSliceChildView.setLastUpdated(isHeader ? mLastUpdated : -1);
2570929cbe8a1ad88cb18e0cd3b50abba21840bc592Mady Mellor            mSliceChildView.setShowLastUpdated(isHeader && mShowLastUpdated);
2580929cbe8a1ad88cb18e0cd3b50abba21840bc592Mady Mellor            if (mSliceChildView instanceof RowView) {
259ba2ea6ccfa7239f89ed0a301d0c24afb46036976Mady Mellor                ((RowView) mSliceChildView).setSingleItem(getItemCount() == 1);
2606975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor            }
2616975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor            int[] info = new int[2];
2626975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor            info[0] = ListContent.getRowType(mContext, item, isHeader, mSliceActions);
2636975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor            info[1] = position;
2646975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor            mSliceChildView.setTag(info);
2656975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor        }
2666975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor
2676975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor        @Override
2686975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor        public void onClick(View v) {
2696975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor            if (mParent != null) {
2706975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor                mParent.setClickInfo((int[]) v.getTag());
2716975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor                mParent.performClick();
2726975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor            }
2736975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor        }
2746975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor
2756975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor        @Override
2766975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor        public boolean onTouch(View v, MotionEvent event) {
2776975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor            if (mTemplateView != null) {
2786975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor                mTemplateView.onForegroundActivated(event);
2796975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor            }
2806975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor            return false;
2818a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        }
2828a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    }
2838a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk
2848a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    private static class IdGenerator {
2858a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        private long mNextLong = 0;
2868a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        private final ArrayMap<String, Long> mCurrentIds = new ArrayMap<>();
2878a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        private final ArrayMap<String, Integer> mUsedIds = new ArrayMap<>();
2888a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk
2890929cbe8a1ad88cb18e0cd3b50abba21840bc592Mady Mellor        public long getId(SliceItem item, int mode) {
2908a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk            String str = genString(item);
2910929cbe8a1ad88cb18e0cd3b50abba21840bc592Mady Mellor            SliceItem summary = SliceQuery.find(item, null, HINT_SUMMARY, null);
2920929cbe8a1ad88cb18e0cd3b50abba21840bc592Mady Mellor            if (summary != null) {
2930929cbe8a1ad88cb18e0cd3b50abba21840bc592Mady Mellor                str += mode; // mode matters
2940929cbe8a1ad88cb18e0cd3b50abba21840bc592Mady Mellor            }
2958a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk            if (!mCurrentIds.containsKey(str)) {
2968a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk                mCurrentIds.put(str, mNextLong++);
2978a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk            }
2988a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk            long id = mCurrentIds.get(str);
29906ab4aa38f82b8e2d9af769f7c777253f2497f93Jake Wharton            Integer usedIdIndex = mUsedIds.get(str);
30006ab4aa38f82b8e2d9af769f7c777253f2497f93Jake Wharton            int index = usedIdIndex != null ? usedIdIndex : 0;
3018a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk            mUsedIds.put(str, index + 1);
3028a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk            return id + index * 10000;
3038a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        }
3048a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk
3058a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        private String genString(SliceItem item) {
3062a7d0fcd09ed39bbeda29e024c47fca617050094Jason Monk            final StringBuilder builder = new StringBuilder();
307f68dde011de8fb4d616a09292524869f56d94110Jason Monk            Iterator<SliceItem> items = SliceQuery.stream(item);
308f68dde011de8fb4d616a09292524869f56d94110Jason Monk            while (items.hasNext()) {
309f68dde011de8fb4d616a09292524869f56d94110Jason Monk                SliceItem i = items.next();
310f68dde011de8fb4d616a09292524869f56d94110Jason Monk                builder.append(i.getFormat());
311f68dde011de8fb4d616a09292524869f56d94110Jason Monk                builder.append(i.getHints());
312f68dde011de8fb4d616a09292524869f56d94110Jason Monk                switch (i.getFormat()) {
313f68dde011de8fb4d616a09292524869f56d94110Jason Monk                    case FORMAT_TEXT:
314f68dde011de8fb4d616a09292524869f56d94110Jason Monk                        builder.append(i.getText());
315f68dde011de8fb4d616a09292524869f56d94110Jason Monk                        break;
316f68dde011de8fb4d616a09292524869f56d94110Jason Monk                    case FORMAT_INT:
317f68dde011de8fb4d616a09292524869f56d94110Jason Monk                        builder.append(i.getInt());
318f68dde011de8fb4d616a09292524869f56d94110Jason Monk                        break;
3198a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk                }
320f68dde011de8fb4d616a09292524869f56d94110Jason Monk            }
3218a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk            return builder.toString();
3228a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        }
3238a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk
3248a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        public void resetUsage() {
3258a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk            mUsedIds.clear();
3268a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk        }
3278a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk    }
3288a452e96e2308fe9515aa91b8e5b369eeefc25e7Jason Monk}
329