blob: 4fb4bde59c48d64a90219a58cec3aa2c0e0ce668 [file] [log] [blame]
/*
* Copyright 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.slice.widget;
import static android.app.slice.Slice.HINT_ACTIONS;
import static android.app.slice.Slice.HINT_LIST_ITEM;
import static android.app.slice.Slice.HINT_SEE_MORE;
import static android.app.slice.Slice.HINT_SHORTCUT;
import static android.app.slice.Slice.HINT_TITLE;
import static android.app.slice.Slice.SUBTYPE_COLOR;
import static android.app.slice.Slice.SUBTYPE_CONTENT_DESCRIPTION;
import static android.app.slice.SliceItem.FORMAT_ACTION;
import static android.app.slice.SliceItem.FORMAT_IMAGE;
import static android.app.slice.SliceItem.FORMAT_INT;
import static android.app.slice.SliceItem.FORMAT_SLICE;
import static android.app.slice.SliceItem.FORMAT_TEXT;
import static android.app.slice.SliceItem.FORMAT_TIMESTAMP;
import static androidx.slice.builders.ListBuilder.ICON_IMAGE;
import static androidx.slice.builders.ListBuilder.LARGE_IMAGE;
import static androidx.slice.builders.ListBuilder.SMALL_IMAGE;
import static androidx.slice.core.SliceHints.HINT_KEY_WORDS;
import android.app.slice.Slice;
import android.content.Context;
import android.content.res.Resources;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.slice.SliceItem;
import androidx.slice.builders.ListBuilder;
import androidx.slice.core.SliceQuery;
import androidx.slice.view.R;
/**
* Extracts information required to present content in a grid format from a slice.
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class GridContent {
private boolean mAllImages;
private SliceItem mColorItem;
private SliceItem mPrimaryAction;
private ArrayList<CellContent> mGridContent = new ArrayList<>();
private SliceItem mSeeMoreItem;
private int mMaxCellLineCount;
private boolean mHasImage;
private @ListBuilder.ImageMode int mLargestImageMode;
private SliceItem mContentDescr;
private int mBigPicMinHeight;
private int mBigPicMaxHeight;
private int mAllImagesHeight;
private int mImageTextHeight;
private int mMaxHeight;
private int mMinHeight;
public GridContent(Context context, SliceItem gridItem) {
populate(gridItem);
Resources res = context.getResources();
mBigPicMinHeight = res.getDimensionPixelSize(R.dimen.abc_slice_big_pic_min_height);
mBigPicMaxHeight = res.getDimensionPixelSize(R.dimen.abc_slice_big_pic_max_height);
mAllImagesHeight = res.getDimensionPixelSize(R.dimen.abc_slice_grid_image_only_height);
mImageTextHeight = res.getDimensionPixelSize(R.dimen.abc_slice_grid_image_text_height);
mMinHeight = res.getDimensionPixelSize(R.dimen.abc_slice_grid_min_height);
mMaxHeight = res.getDimensionPixelSize(R.dimen.abc_slice_grid_max_height);
}
/**
* @return whether this grid has content that is valid to display.
*/
private boolean populate(SliceItem gridItem) {
mColorItem = SliceQuery.findSubtype(gridItem, FORMAT_INT, SUBTYPE_COLOR);
mSeeMoreItem = SliceQuery.find(gridItem, null, HINT_SEE_MORE, null);
if (mSeeMoreItem != null && FORMAT_SLICE.equals(mSeeMoreItem.getFormat())) {
mSeeMoreItem = mSeeMoreItem.getSlice().getItems().get(0);
}
String[] hints = new String[] {HINT_SHORTCUT, HINT_TITLE};
mPrimaryAction = SliceQuery.find(gridItem, FORMAT_SLICE, hints,
new String[] {HINT_ACTIONS} /* nonHints */);
mAllImages = true;
if (FORMAT_SLICE.equals(gridItem.getFormat())) {
List<SliceItem> items = gridItem.getSlice().getItems().get(0).getSlice().getItems();
items = filterAndProcessItems(items);
// Check if it it's only one item that is a slice
if (items.size() == 1 && items.get(0).getFormat().equals(FORMAT_SLICE)) {
items = items.get(0).getSlice().getItems();
items = filterAndProcessItems(items);
}
for (int i = 0; i < items.size(); i++) {
SliceItem item = items.get(i);
CellContent cc = new CellContent(item);
processContent(cc);
}
} else {
CellContent cc = new CellContent(gridItem);
processContent(cc);
}
return isValid();
}
private void processContent(CellContent cc) {
if (cc.isValid()) {
mGridContent.add(cc);
if (!cc.isImageOnly()) {
mAllImages = false;
}
mMaxCellLineCount = Math.max(mMaxCellLineCount, cc.getTextCount());
mHasImage |= cc.hasImage();
mLargestImageMode = Math.max(mLargestImageMode, cc.getImageMode());
}
}
/**
* @return the list of cell content for this grid.
*/
@NonNull
public ArrayList<CellContent> getGridContent() {
return mGridContent;
}
/**
* @return the color to tint content in this grid.
*/
@Nullable
public SliceItem getColorItem() {
return mColorItem;
}
/**
* @return the content intent item for this grid.
*/
@Nullable
public SliceItem getContentIntent() {
return mPrimaryAction;
}
/**
* @return the see more item to use when not all items in the grid can be displayed.
*/
@Nullable
public SliceItem getSeeMoreItem() {
return mSeeMoreItem;
}
/**
* @return content description for this row.
*/
@Nullable
public CharSequence getContentDescription() {
return mContentDescr != null ? mContentDescr.getText() : null;
}
/**
* @return whether this grid has content that is valid to display.
*/
public boolean isValid() {
return mGridContent.size() > 0;
}
/**
* @return whether the contents of this grid is just images.
*/
public boolean isAllImages() {
return mAllImages;
}
/**
* Filters non-cell items out of the list of items and finds content description.
*/
private List<SliceItem> filterAndProcessItems(List<SliceItem> items) {
List<SliceItem> filteredItems = new ArrayList<>();
for (int i = 0; i < items.size(); i++) {
SliceItem item = items.get(i);
if (SUBTYPE_CONTENT_DESCRIPTION.equals(item.getSubType())) {
mContentDescr = item;
} else if (item.hasHint(HINT_LIST_ITEM) && !item.hasAnyHints(HINT_SHORTCUT,
HINT_SEE_MORE, HINT_KEY_WORDS)) {
filteredItems.add(item);
}
}
return filteredItems;
}
/**
* @return the max number of lines of text in the cells of this grid row.
*/
public int getMaxCellLineCount() {
return mMaxCellLineCount;
}
/**
* @return whether this row contains an image.
*/
public boolean hasImage() {
return mHasImage;
}
/**
* @return the height to display a grid row at when it is used as a small template.
*/
public int getSmallHeight() {
return getHeight(true /* isSmall */);
}
/**
* @return the height the content in this template requires to be displayed.
*/
public int getActualHeight() {
return getHeight(false /* isSmall */);
}
private int getHeight(boolean isSmall) {
if (!isValid()) {
return 0;
}
if (mAllImages) {
return mGridContent.size() == 1
? isSmall ? mBigPicMinHeight : mBigPicMaxHeight
: mLargestImageMode == ICON_IMAGE ? mMinHeight : mAllImagesHeight;
} else {
boolean twoLines = getMaxCellLineCount() > 1;
boolean hasImage = hasImage();
return (twoLines && !isSmall)
? hasImage ? mMaxHeight : mMinHeight
: mLargestImageMode == ICON_IMAGE ? mMinHeight : mImageTextHeight;
}
}
/**
* Extracts information required to present content in a cell.
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static class CellContent {
private SliceItem mContentIntent;
private ArrayList<SliceItem> mCellItems = new ArrayList<>();
private SliceItem mContentDescr;
private int mTextCount;
private boolean mHasImage;
private int mImageMode = -1;
public CellContent(SliceItem cellItem) {
populate(cellItem);
}
/**
* @return whether this row has content that is valid to display.
*/
public boolean populate(SliceItem cellItem) {
final String format = cellItem.getFormat();
if (!cellItem.hasHint(HINT_SHORTCUT)
&& (FORMAT_SLICE.equals(format) || FORMAT_ACTION.equals(format))) {
List<SliceItem> items = cellItem.getSlice().getItems();
// If we've only got one item that's a slice / action use those items instead
if (items.size() == 1 && (FORMAT_ACTION.equals(items.get(0).getFormat())
|| FORMAT_SLICE.equals(items.get(0).getFormat()))) {
mContentIntent = items.get(0);
items = items.get(0).getSlice().getItems();
}
if (FORMAT_ACTION.equals(format)) {
mContentIntent = cellItem;
}
mTextCount = 0;
int imageCount = 0;
for (int i = 0; i < items.size(); i++) {
final SliceItem item = items.get(i);
final String itemFormat = item.getFormat();
if (SUBTYPE_CONTENT_DESCRIPTION.equals(item.getSubType())) {
mContentDescr = item;
} else if (mTextCount < 2 && (FORMAT_TEXT.equals(itemFormat)
|| FORMAT_TIMESTAMP.equals(itemFormat))) {
mTextCount++;
mCellItems.add(item);
} else if (imageCount < 1 && FORMAT_IMAGE.equals(item.getFormat())) {
if (item.hasHint(Slice.HINT_NO_TINT)) {
mImageMode = item.hasHint(Slice.HINT_LARGE)
? LARGE_IMAGE
: SMALL_IMAGE;
} else {
mImageMode = ICON_IMAGE;
}
imageCount++;
mHasImage = true;
mCellItems.add(item);
}
}
} else if (isValidCellContent(cellItem)) {
mCellItems.add(cellItem);
}
return isValid();
}
/**
* @return the action to activate when this cell is tapped.
*/
public SliceItem getContentIntent() {
return mContentIntent;
}
/**
* @return the slice items to display in this cell.
*/
public ArrayList<SliceItem> getCellItems() {
return mCellItems;
}
/**
* @return whether this is content that is valid to show in a grid cell.
*/
private boolean isValidCellContent(SliceItem cellItem) {
final String format = cellItem.getFormat();
boolean isSpecial = SUBTYPE_CONTENT_DESCRIPTION.equals(cellItem.getSubType())
|| cellItem.hasHint(HINT_KEY_WORDS);
return !isSpecial
&& (FORMAT_TEXT.equals(format)
|| FORMAT_TIMESTAMP.equals(format)
|| FORMAT_IMAGE.equals(format));
}
/**
* @return whether this grid has content that is valid to display.
*/
public boolean isValid() {
return mCellItems.size() > 0 && mCellItems.size() <= 3;
}
/**
* @return whether this cell contains just an image.
*/
public boolean isImageOnly() {
return mCellItems.size() == 1 && FORMAT_IMAGE.equals(mCellItems.get(0).getFormat());
}
/**
* @return number of text items in this cell.
*/
public int getTextCount() {
return mTextCount;
}
/**
* @return whether this cell contains an image.
*/
public boolean hasImage() {
return mHasImage;
}
/**
* @return the mode of the image.
*/
public int getImageMode() {
return mImageMode;
}
@Nullable
public CharSequence getContentDescription() {
return mContentDescr != null ? mContentDescr.getText() : null;
}
}
}