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