16b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor/*
26b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor * Copyright 2017 The Android Open Source Project
36b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor *
46b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor * Licensed under the Apache License, Version 2.0 (the "License");
56b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor * you may not use this file except in compliance with the License.
66b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor * You may obtain a copy of the License at
76b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor *
86b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor *      http://www.apache.org/licenses/LICENSE-2.0
96b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor *
106b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor * Unless required by applicable law or agreed to in writing, software
116b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor * distributed under the License is distributed on an "AS IS" BASIS,
126b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor * See the License for the specific language governing permissions and
146b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor * limitations under the License.
156b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor */
166b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor
1785ef1446b82c8783a50af92c4cb1389fe0d0e907Aurimas Liutikaspackage androidx.slice.widget;
186b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor
196b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellorimport static android.app.slice.Slice.HINT_ACTIONS;
208a2763f580c71e17a0b13e682c73012e902b4232Mady Mellorimport static android.app.slice.Slice.HINT_HORIZONTAL;
216b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellorimport static android.app.slice.Slice.HINT_LIST_ITEM;
2253380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellorimport static android.app.slice.Slice.HINT_SEE_MORE;
23af76b3bd62a6b218bb44917b1dddfa1ee4803149Mady Mellorimport static android.app.slice.Slice.HINT_SHORTCUT;
246b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellorimport static android.app.slice.Slice.SUBTYPE_COLOR;
256b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellorimport static android.app.slice.SliceItem.FORMAT_ACTION;
266b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellorimport static android.app.slice.SliceItem.FORMAT_INT;
276b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellorimport static android.app.slice.SliceItem.FORMAT_SLICE;
28968346929453133f937381a2e685bc4e47788e2cMady Mellorimport static android.app.slice.SliceItem.FORMAT_TEXT;
296b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor
300707dbe9fa3225cc921a2305a611272f0dee1ca8Mady Mellorimport static androidx.slice.core.SliceHints.HINT_KEYWORDS;
3163fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellorimport static androidx.slice.core.SliceHints.HINT_LAST_UPDATED;
3263fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellorimport static androidx.slice.core.SliceHints.HINT_TTL;
3365d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellorimport static androidx.slice.widget.SliceView.MODE_LARGE;
3465d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellorimport static androidx.slice.widget.SliceView.MODE_SMALL;
35bb51b5909dd8d5b233cd675fbc6fe74c42f48d3cMady Mellor
368a2763f580c71e17a0b13e682c73012e902b4232Mady Mellorimport android.content.Context;
3765d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellorimport android.content.res.TypedArray;
3865d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellorimport android.util.AttributeSet;
396b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor
40bb51b5909dd8d5b233cd675fbc6fe74c42f48d3cMady Mellorimport androidx.annotation.NonNull;
41bb51b5909dd8d5b233cd675fbc6fe74c42f48d3cMady Mellorimport androidx.annotation.Nullable;
42bb51b5909dd8d5b233cd675fbc6fe74c42f48d3cMady Mellorimport androidx.annotation.RestrictTo;
4385ef1446b82c8783a50af92c4cb1389fe0d0e907Aurimas Liutikasimport androidx.slice.Slice;
4485ef1446b82c8783a50af92c4cb1389fe0d0e907Aurimas Liutikasimport androidx.slice.SliceItem;
45e7f1c6cf75ce36c633075445bd676572a9fe0f47Mady Mellorimport androidx.slice.SliceMetadata;
46ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellorimport androidx.slice.core.SliceAction;
47ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellorimport androidx.slice.core.SliceActionImpl;
4885ef1446b82c8783a50af92c4cb1389fe0d0e907Aurimas Liutikasimport androidx.slice.core.SliceQuery;
4965d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellorimport androidx.slice.view.R;
506b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor
51c50cc30733ad1058f98523c25f7413745638332aMady Mellorimport java.util.ArrayList;
52c50cc30733ad1058f98523c25f7413745638332aMady Mellorimport java.util.List;
53c50cc30733ad1058f98523c25f7413745638332aMady Mellor
546b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor/**
556b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor * Extracts information required to present content in a list format from a slice.
566b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor * @hide
576b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor */
586b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
596b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellorpublic class ListContent {
606b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor
6165d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor    private Slice mSlice;
62968346929453133f937381a2e685bc4e47788e2cMady Mellor    private SliceItem mHeaderItem;
636b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor    private SliceItem mColorItem;
6453380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor    private SliceItem mSeeMoreItem;
656b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor    private ArrayList<SliceItem> mRowItems = new ArrayList<>();
66968346929453133f937381a2e685bc4e47788e2cMady Mellor    private List<SliceItem> mSliceActions;
678a2763f580c71e17a0b13e682c73012e902b4232Mady Mellor    private Context mContext;
686b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor
6965d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor    private int mHeaderTitleSize;
7065d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor    private int mHeaderSubtitleSize;
7165d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor    private int mVerticalHeaderTextPadding;
7265d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor    private int mTitleSize;
7365d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor    private int mSubtitleSize;
7465d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor    private int mVerticalTextPadding;
7565d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor    private int mGridTitleSize;
7665d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor    private int mGridSubtitleSize;
7765d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor    private int mVerticalGridTextPadding;
7865d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor    private int mGridTopPadding;
7965d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor    private int mGridBottomPadding;
8065d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor
8165d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor    public ListContent(Context context, Slice slice, AttributeSet attrs, int defStyleAttr,
8265d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            int defStyleRes) {
8365d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor        mSlice = slice;
848a2763f580c71e17a0b13e682c73012e902b4232Mady Mellor        mContext = context;
8565d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor
8665d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor        // TODO: duplicated code from SliceChildView; could do something better
8765d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor        // Some of this information will impact the size calculations for slice content.
8865d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SliceView,
8965d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor                defStyleAttr, defStyleRes);
9065d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor        try {
9165d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            mHeaderTitleSize = (int) a.getDimension(
9265d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor                    R.styleable.SliceView_headerTitleSize, 0);
9365d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            mHeaderSubtitleSize = (int) a.getDimension(
9465d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor                    R.styleable.SliceView_headerSubtitleSize, 0);
9565d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            mVerticalHeaderTextPadding = (int) a.getDimension(
9665d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor                    R.styleable.SliceView_headerTextVerticalPadding, 0);
9765d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor
9865d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            mTitleSize = (int) a.getDimension(R.styleable.SliceView_titleSize, 0);
9965d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            mSubtitleSize = (int) a.getDimension(
10065d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor                    R.styleable.SliceView_subtitleSize, 0);
10165d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            mVerticalTextPadding = (int) a.getDimension(
10265d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor                    R.styleable.SliceView_textVerticalPadding, 0);
10365d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor
10465d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            mGridTitleSize = (int) a.getDimension(R.styleable.SliceView_gridTitleSize, 0);
10565d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            mGridSubtitleSize = (int) a.getDimension(
10665d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor                    R.styleable.SliceView_gridSubtitleSize, 0);
10765d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            int defaultVerticalGridPadding = context.getResources().getDimensionPixelSize(
10865d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor                    R.dimen.abc_slice_grid_text_inner_padding);
10965d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            mVerticalGridTextPadding = (int) a.getDimension(
11065d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor                    R.styleable.SliceView_gridTextVerticalPadding, defaultVerticalGridPadding);
11165d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            mGridTopPadding = (int) a.getDimension(R.styleable.SliceView_gridTopPadding, 0);
11265d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            mGridBottomPadding = (int) a.getDimension(R.styleable.SliceView_gridTopPadding, 0);
11365d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor        } finally {
11465d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            a.recycle();
11565d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor        }
11665d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor
1176b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor        populate(slice);
1186b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor    }
1196b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor
1206b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor    /**
1216b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor     * @return whether this row has content that is valid to display.
1226b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor     */
1238a2763f580c71e17a0b13e682c73012e902b4232Mady Mellor    private boolean populate(Slice slice) {
1246b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor        mColorItem = SliceQuery.findSubtype(slice, FORMAT_INT, SUBTYPE_COLOR);
125968346929453133f937381a2e685bc4e47788e2cMady Mellor        // Find slice actions
126e7f1c6cf75ce36c633075445bd676572a9fe0f47Mady Mellor        mSliceActions = SliceMetadata.getSliceActions(slice);
127968346929453133f937381a2e685bc4e47788e2cMady Mellor        // Find header
128968346929453133f937381a2e685bc4e47788e2cMady Mellor        mHeaderItem = findHeaderItem(slice);
129968346929453133f937381a2e685bc4e47788e2cMady Mellor        if (mHeaderItem != null) {
130968346929453133f937381a2e685bc4e47788e2cMady Mellor            mRowItems.add(mHeaderItem);
131968346929453133f937381a2e685bc4e47788e2cMady Mellor        }
13253380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        mSeeMoreItem = getSeeMoreItem(slice);
1336b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor        // Filter + create row items
1346b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor        List<SliceItem> children = slice.getItems();
1356b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor        for (int i = 0; i < children.size(); i++) {
1366b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor            final SliceItem child = children.get(i);
1376b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor            final String format = child.getFormat();
1380707dbe9fa3225cc921a2305a611272f0dee1ca8Mady Mellor            boolean isNonRowContent = child.hasAnyHints(HINT_ACTIONS, HINT_SEE_MORE, HINT_KEYWORDS,
13963fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor                    HINT_TTL, HINT_LAST_UPDATED);
14063fb9955b209f1bb9d19e41df9784bfbdf63defeMady Mellor            if (!isNonRowContent && (FORMAT_ACTION.equals(format) || FORMAT_SLICE.equals(format))) {
141968346929453133f937381a2e685bc4e47788e2cMady Mellor                if (mHeaderItem == null && !child.hasHint(HINT_LIST_ITEM)) {
142968346929453133f937381a2e685bc4e47788e2cMady Mellor                    mHeaderItem = child;
1436b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor                    mRowItems.add(0, child);
144968346929453133f937381a2e685bc4e47788e2cMady Mellor                } else if (child.hasHint(HINT_LIST_ITEM)) {
1456b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor                    mRowItems.add(child);
1466b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor                }
1476b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor            }
1486b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor        }
149968346929453133f937381a2e685bc4e47788e2cMady Mellor        // Ensure we have something for the header -- use first row
150968346929453133f937381a2e685bc4e47788e2cMady Mellor        if (mHeaderItem == null && mRowItems.size() >= 1) {
151968346929453133f937381a2e685bc4e47788e2cMady Mellor            mHeaderItem = mRowItems.get(0);
152968346929453133f937381a2e685bc4e47788e2cMady Mellor        }
1536b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor        return isValid();
1546b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor    }
1556b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor
1566b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor    /**
1578dc56e6039af468e05c213ef6562c9c6624aaf8aMady Mellor     * Expects the provided list of items to be filtered (i.e. only things that can be turned into
1588dc56e6039af468e05c213ef6562c9c6624aaf8aMady Mellor     * GridContent or RowContent) and in order (i.e. first item could be a header).
1598dc56e6039af468e05c213ef6562c9c6624aaf8aMady Mellor     *
16053380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor     * @return the total height of all the rows contained in the provided list.
1618a2763f580c71e17a0b13e682c73012e902b4232Mady Mellor     */
16265d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor    public int getListHeight(Context context, List<SliceItem> listItems) {
163246bd7a721591efc55557e6018a8127be0ae3a5cMady Mellor        if (listItems == null) {
164246bd7a721591efc55557e6018a8127be0ae3a5cMady Mellor            return 0;
165246bd7a721591efc55557e6018a8127be0ae3a5cMady Mellor        }
1668a2763f580c71e17a0b13e682c73012e902b4232Mady Mellor        int height = 0;
1678dc56e6039af468e05c213ef6562c9c6624aaf8aMady Mellor        boolean hasRealHeader = false;
168ba2ea6ccfa7239f89ed0a301d0c24afb46036976Mady Mellor        SliceItem maybeHeader = null;
169246bd7a721591efc55557e6018a8127be0ae3a5cMady Mellor        if (!listItems.isEmpty()) {
170ba2ea6ccfa7239f89ed0a301d0c24afb46036976Mady Mellor            maybeHeader = listItems.get(0);
1718dc56e6039af468e05c213ef6562c9c6624aaf8aMady Mellor            hasRealHeader = !maybeHeader.hasAnyHints(HINT_LIST_ITEM, HINT_HORIZONTAL);
1728dc56e6039af468e05c213ef6562c9c6624aaf8aMady Mellor        }
173ba2ea6ccfa7239f89ed0a301d0c24afb46036976Mady Mellor        if (listItems.size() == 1 && !maybeHeader.hasHint(HINT_HORIZONTAL)) {
17465d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            return getHeight(context, maybeHeader, true /* isHeader */, 0, 1, MODE_LARGE);
175ba2ea6ccfa7239f89ed0a301d0c24afb46036976Mady Mellor        }
17665d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor        int rowCount = listItems.size();
17753380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        for (int i = 0; i < listItems.size(); i++) {
17865d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            height += getHeight(context, listItems.get(i), i == 0 && hasRealHeader /* isHeader */,
17965d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor                    i, rowCount, MODE_LARGE);
18053380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        }
18153380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        return height;
18253380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor    }
18353380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor
18453380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor    /**
18553380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor     * Returns a list of items that can be displayed in the provided height. If this list
18653380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor     * has a {@link #getSeeMoreItem()} this will be returned in the list if appropriate.
18753380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor     *
18853380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor     * @param height the height to restrict the items, -1 to use default sizings for non-scrolling
18953380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor     *               templates.
19053380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor     * @return the list of items that can be displayed in the provided  height.
19153380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor     */
19253380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor    @NonNull
19353380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor    public List<SliceItem> getItemsForNonScrollingList(int height) {
19453380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        ArrayList<SliceItem> visibleItems = new ArrayList<>();
19553380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        if (mRowItems == null || mRowItems.size() == 0) {
19653380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor            return visibleItems;
19753380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        }
19853380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        final int idealItemCount = hasHeader() ? 4 : 3;
19953380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        final int minItemCount = hasHeader() ? 2 : 1;
20053380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        int visibleHeight = 0;
20153380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        // Need to show see more
20253380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        if (mSeeMoreItem != null) {
20353380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor            RowContent rc = new RowContent(mContext, mSeeMoreItem, false /* isHeader */);
20453380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor            visibleHeight += rc.getActualHeight();
20553380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        }
20665d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor        int rowCount = mRowItems.size();
20765d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor        for (int i = 0; i < rowCount; i++) {
20865d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            int itemHeight = getHeight(mContext, mRowItems.get(i), i == 0 /* isHeader */,
20965d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor                    i, rowCount, MODE_LARGE);
21053380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor            if ((height == -1 && i > idealItemCount)
21153380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor                    || (height > 0 && visibleHeight + itemHeight > height)) {
21253380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor                break;
2138a2763f580c71e17a0b13e682c73012e902b4232Mady Mellor            } else {
21453380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor                visibleHeight += itemHeight;
21553380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor                visibleItems.add(mRowItems.get(i));
2168a2763f580c71e17a0b13e682c73012e902b4232Mady Mellor            }
2178a2763f580c71e17a0b13e682c73012e902b4232Mady Mellor        }
21853380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        if (mSeeMoreItem != null && visibleItems.size() >= minItemCount) {
21953380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor            // Only add see more if we're at least showing one item and it's not the header
22053380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor            visibleItems.add(mSeeMoreItem);
22153380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        }
22253380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        if (visibleItems.size() == 0) {
22353380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor            // Didn't have enough space to show anything; should still show something
22453380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor            visibleItems.add(mRowItems.get(0));
22553380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        }
22653380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        return visibleItems;
22753380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor    }
22853380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor
22965d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor    /**
23065d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor     * Determines the height of the provided {@link SliceItem}.
23165d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor     */
23265d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor    public int getHeight(Context context, SliceItem item, boolean isHeader, int index,
23365d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            int count, int mode) {
23453380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        if (item.hasHint(HINT_HORIZONTAL)) {
23553380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor            GridContent gc = new GridContent(context, item);
23665d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            int topPadding = gc.isAllImages() && index == 0 ? mGridTopPadding : 0;
23765d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            int bottomPadding = gc.isAllImages() && index == count - 1 ? mGridBottomPadding : 0;
23865d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            int height = mode == MODE_SMALL ? gc.getSmallHeight() : gc.getActualHeight();
23965d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            return height + topPadding + bottomPadding;
24053380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        } else {
24153380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor            RowContent rc = new RowContent(context, item, isHeader);
24265d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor            return mode == MODE_SMALL ? rc.getSmallHeight() : rc.getActualHeight();
24353380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        }
2448a2763f580c71e17a0b13e682c73012e902b4232Mady Mellor    }
2458a2763f580c71e17a0b13e682c73012e902b4232Mady Mellor
2468a2763f580c71e17a0b13e682c73012e902b4232Mady Mellor    /**
2476b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor     * @return whether this list has content that is valid to display.
2486b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor     */
2496b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor    public boolean isValid() {
250968346929453133f937381a2e685bc4e47788e2cMady Mellor        return mRowItems.size() > 0;
2516b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor    }
2526b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor
2536b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor    @Nullable
25465d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor    public Slice getSlice() {
25565d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor        return mSlice;
25665d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor    }
25765d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor
25865d77ea1ec28f1a623a1a7f8e624cf08c27d5faeMady Mellor    @Nullable
2596b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor    public SliceItem getColorItem() {
2606b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor        return mColorItem;
2616b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor    }
2626b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor
2636b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor    @Nullable
264968346929453133f937381a2e685bc4e47788e2cMady Mellor    public SliceItem getHeaderItem() {
265968346929453133f937381a2e685bc4e47788e2cMady Mellor        return mHeaderItem;
266968346929453133f937381a2e685bc4e47788e2cMady Mellor    }
267968346929453133f937381a2e685bc4e47788e2cMady Mellor
268968346929453133f937381a2e685bc4e47788e2cMady Mellor    @Nullable
269968346929453133f937381a2e685bc4e47788e2cMady Mellor    public List<SliceItem> getSliceActions() {
270968346929453133f937381a2e685bc4e47788e2cMady Mellor        return mSliceActions;
2716b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor    }
2726b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor
27353380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor    @Nullable
27453380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor    public SliceItem getSeeMoreItem() {
27553380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        return mSeeMoreItem;
27653380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor    }
27753380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor
278246bd7a721591efc55557e6018a8127be0ae3a5cMady Mellor    @NonNull
2796b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor    public ArrayList<SliceItem> getRowItems() {
2806b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor        return mRowItems;
2816b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor    }
2826b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor
2836b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor    /**
284968346929453133f937381a2e685bc4e47788e2cMady Mellor     * @return whether this list has an explicit header (i.e. row item without HINT_LIST_ITEM)
2856b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor     */
2866b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor    public boolean hasHeader() {
287968346929453133f937381a2e685bc4e47788e2cMady Mellor        return mHeaderItem != null && isValidHeader(mHeaderItem);
2886b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor    }
2896b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor
290c50cc30733ad1058f98523c25f7413745638332aMady Mellor    /**
291ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor     * @return the type of template that the header represents.
292ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor     */
293ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor    public int getHeaderTemplateType() {
2946975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor        return getRowType(mContext, mHeaderItem, true, mSliceActions);
2956975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor    }
2966975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor
2976975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor    /**
2986975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor     * The type of template that the provided row item represents.
2996975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor     *
3006975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor     * @param context context used for this slice.
3016975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor     * @param rowItem the row item to determine the template type of.
3026975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor     * @param isHeader whether this row item is used as a header.
3036975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor     * @param actions the actions associated with this slice, only matter if this row is the header.
3046975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor     * @return the type of template the provided row item represents.
3056975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor     */
3066975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor    public static int getRowType(Context context, SliceItem rowItem, boolean isHeader,
3076975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor                                 List<SliceItem> actions) {
3086975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor        if (rowItem != null) {
3096975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor            if (rowItem.hasHint(HINT_HORIZONTAL)) {
310ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                return EventInfo.ROW_TYPE_GRID;
311ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor            } else {
3126975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor                RowContent rc = new RowContent(context, rowItem, isHeader);
313ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                SliceItem actionItem = rc.getPrimaryAction();
314ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                SliceAction primaryAction = null;
315ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                if (actionItem != null) {
316ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                    primaryAction = new SliceActionImpl(actionItem);
317ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                }
318ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                if (rc.getRange() != null) {
319ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                    return FORMAT_ACTION.equals(rc.getRange().getFormat())
320ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                            ? EventInfo.ROW_TYPE_SLIDER
321ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                            : EventInfo.ROW_TYPE_PROGRESS;
322ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                } else if (primaryAction != null && primaryAction.isToggle()) {
323ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                    return EventInfo.ROW_TYPE_TOGGLE;
3246975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor                } else if (isHeader && actions != null) {
3256975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor                    for (int i = 0; i < actions.size(); i++) {
3266975e4e9eba6772c9d7d38374109a90dd4f390cdMady Mellor                        if (new SliceActionImpl(actions.get(i)).isToggle()) {
327ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                            return EventInfo.ROW_TYPE_TOGGLE;
328ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                        }
329ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                    }
330ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                    return EventInfo.ROW_TYPE_LIST;
331ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                } else {
332ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                    return rc.getToggleItems().size() > 0
333ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                            ? EventInfo.ROW_TYPE_TOGGLE
334ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                            : EventInfo.ROW_TYPE_LIST;
335ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                }
336ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor            }
337ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor        }
338ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor        return EventInfo.ROW_TYPE_LIST;
339ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor    }
340ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor
341ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor    /**
342c50cc30733ad1058f98523c25f7413745638332aMady Mellor     * @return the primary action for this list; i.e. action on the header or first row.
343c50cc30733ad1058f98523c25f7413745638332aMady Mellor     */
344c50cc30733ad1058f98523c25f7413745638332aMady Mellor    @Nullable
345c50cc30733ad1058f98523c25f7413745638332aMady Mellor    public SliceItem getPrimaryAction() {
346ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor        if (mHeaderItem != null) {
347ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor            if (mHeaderItem.hasHint(HINT_HORIZONTAL)) {
348ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                GridContent gc = new GridContent(mContext, mHeaderItem);
349ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                return gc.getContentIntent();
350ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor            } else {
351ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                RowContent rc = new RowContent(mContext, mHeaderItem, false);
352ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor                return rc.getPrimaryAction();
353ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor            }
354ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor        }
355ca12087a732c2e40be512a8d9d25e0aec8bf92d8Mady Mellor        return null;
356c50cc30733ad1058f98523c25f7413745638332aMady Mellor    }
357c50cc30733ad1058f98523c25f7413745638332aMady Mellor
358968346929453133f937381a2e685bc4e47788e2cMady Mellor    @Nullable
359968346929453133f937381a2e685bc4e47788e2cMady Mellor    private static SliceItem findHeaderItem(@NonNull Slice slice) {
360968346929453133f937381a2e685bc4e47788e2cMady Mellor        // See if header is specified
361bb51b5909dd8d5b233cd675fbc6fe74c42f48d3cMady Mellor        String[] nonHints = new String[] {HINT_LIST_ITEM, HINT_SHORTCUT, HINT_ACTIONS,
362378ff19cef9998493bc715c58d923150cfd3d816Mady Mellor                HINT_KEYWORDS, HINT_TTL, HINT_LAST_UPDATED, HINT_HORIZONTAL};
363b794b5b0f4bcd000e098265a6ec63d4b0cf3852fMady Mellor        SliceItem header = SliceQuery.find(slice, FORMAT_SLICE, null, nonHints);
364968346929453133f937381a2e685bc4e47788e2cMady Mellor        if (header != null && isValidHeader(header)) {
365968346929453133f937381a2e685bc4e47788e2cMady Mellor            return header;
3666b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor        }
367968346929453133f937381a2e685bc4e47788e2cMady Mellor        return null;
3686b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor    }
3696b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor
37053380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor    @Nullable
37153380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor    private static SliceItem getSeeMoreItem(@NonNull Slice slice) {
37253380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        SliceItem item = SliceQuery.find(slice, null, HINT_SEE_MORE, null);
373378ff19cef9998493bc715c58d923150cfd3d816Mady Mellor        if (item != null) {
37453380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor            if (FORMAT_SLICE.equals(item.getFormat())) {
37553380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor                List<SliceItem> items = item.getSlice().getItems();
37653380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor                if (items.size() == 1 && FORMAT_ACTION.equals(items.get(0).getFormat())) {
37753380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor                    return items.get(0);
37853380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor                }
37953380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor                return item;
38053380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor            }
38153380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        }
38253380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor        return null;
38353380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor    }
38453380fecd1a1537e913578d1e53aa516d3f6e58eMady Mellor
38592952f5bbd2b743091af437767b0b48e48d292b5Mady Mellor    /**
38692952f5bbd2b743091af437767b0b48e48d292b5Mady Mellor     * @return whether the provided slice item is a valid header.
38792952f5bbd2b743091af437767b0b48e48d292b5Mady Mellor     */
38892952f5bbd2b743091af437767b0b48e48d292b5Mady Mellor    public static boolean isValidHeader(SliceItem sliceItem) {
389bb51b5909dd8d5b233cd675fbc6fe74c42f48d3cMady Mellor        if (FORMAT_SLICE.equals(sliceItem.getFormat()) && !sliceItem.hasAnyHints(HINT_LIST_ITEM,
39092952f5bbd2b743091af437767b0b48e48d292b5Mady Mellor                HINT_ACTIONS, HINT_KEYWORDS, HINT_SEE_MORE)) {
391968346929453133f937381a2e685bc4e47788e2cMady Mellor             // Minimum valid header is a slice with text
392968346929453133f937381a2e685bc4e47788e2cMady Mellor            SliceItem item = SliceQuery.find(sliceItem, FORMAT_TEXT, (String) null, null);
393968346929453133f937381a2e685bc4e47788e2cMady Mellor            return item != null;
3946b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor        }
395968346929453133f937381a2e685bc4e47788e2cMady Mellor        return false;
3966b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor    }
3976b5cd6162737bc3a885d58c22549e5b17beded1aMady Mellor}
398