Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package androidx.slice.widget;
     18 
     19 import static androidx.slice.widget.SliceView.MODE_SMALL;
     20 
     21 import android.content.Context;
     22 import android.os.Build;
     23 import android.util.AttributeSet;
     24 import android.view.MotionEvent;
     25 import android.view.View;
     26 import android.widget.FrameLayout;
     27 
     28 import androidx.annotation.RestrictTo;
     29 import androidx.recyclerview.widget.LinearLayoutManager;
     30 import androidx.recyclerview.widget.RecyclerView;
     31 import androidx.slice.SliceItem;
     32 
     33 import java.util.ArrayList;
     34 import java.util.Collections;
     35 import java.util.List;
     36 
     37 /**
     38  * @hide
     39  */
     40 @RestrictTo(RestrictTo.Scope.LIBRARY)
     41 public class LargeTemplateView extends SliceChildView {
     42 
     43     private SliceView mParent;
     44     private final View mForeground;
     45     private final LargeSliceAdapter mAdapter;
     46     private final RecyclerView mRecyclerView;
     47     private boolean mIsScrollable;
     48     private ListContent mListContent;
     49     private List<SliceItem> mDisplayedItems = new ArrayList<>();
     50     private int mDisplayedItemsHeight = 0;
     51     private int[] mLoc = new int[2];
     52 
     53     public LargeTemplateView(Context context) {
     54         super(context);
     55         mRecyclerView = new RecyclerView(getContext());
     56         mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
     57         mAdapter = new LargeSliceAdapter(context);
     58         mRecyclerView.setAdapter(mAdapter);
     59         addView(mRecyclerView);
     60 
     61         mForeground = new View(getContext());
     62         mForeground.setBackground(SliceViewUtil.getDrawable(getContext(),
     63                 android.R.attr.selectableItemBackground));
     64         addView(mForeground);
     65 
     66         FrameLayout.LayoutParams lp = (LayoutParams) mForeground.getLayoutParams();
     67         lp.width = LayoutParams.MATCH_PARENT;
     68         lp.height = LayoutParams.MATCH_PARENT;
     69         mForeground.setLayoutParams(lp);
     70     }
     71 
     72     @Override
     73     public void onAttachedToWindow() {
     74         super.onAttachedToWindow();
     75         mParent = (SliceView) getParent();
     76         mAdapter.setParents(mParent, this);
     77     }
     78 
     79     @Override
     80     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     81         int height = MeasureSpec.getSize(heightMeasureSpec);
     82         if (mDisplayedItems.size() > 0 && mDisplayedItemsHeight > height) {
     83             // Need to resize
     84             updateDisplayedItems(height);
     85         }
     86         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     87     }
     88 
     89     /**
     90      * Called when the foreground view handling touch feedback should be activated.
     91      * @param event the event to handle.
     92      */
     93     public void onForegroundActivated(MotionEvent event) {
     94         if (mParent != null && !mParent.isSliceViewClickable()) {
     95             // Only show highlight if clickable
     96             mForeground.setPressed(false);
     97             return;
     98         }
     99         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    100             mForeground.getLocationOnScreen(mLoc);
    101             final int x = (int) (event.getRawX() - mLoc[0]);
    102             final int y = (int) (event.getRawY() - mLoc[1]);
    103             mForeground.getBackground().setHotspot(x, y);
    104         }
    105         int action = event.getActionMasked();
    106         if (action == MotionEvent.ACTION_DOWN) {
    107             mForeground.setPressed(true);
    108         } else if (action == MotionEvent.ACTION_CANCEL
    109                 || action == MotionEvent.ACTION_UP
    110                 || action == MotionEvent.ACTION_MOVE) {
    111             mForeground.setPressed(false);
    112         }
    113     }
    114 
    115     @Override
    116     public void setMode(int newMode) {
    117         super.setMode(newMode);
    118         updateDisplayedItems(getMeasuredHeight());
    119     }
    120 
    121     @Override
    122     public int getActualHeight() {
    123         return mDisplayedItemsHeight;
    124     }
    125 
    126     @Override
    127     public int getSmallHeight() {
    128         if (mListContent == null || mListContent.getHeaderItem() == null) {
    129             return 0;
    130         }
    131         SliceItem headerItem = mListContent.getHeaderItem();
    132         return mListContent.getHeight(getContext(), headerItem, true /* isHeader */,
    133                 0 /* rowIndex */, 1 /* rowCount */, MODE_SMALL);
    134     }
    135 
    136     @Override
    137     public void setTint(int tint) {
    138         super.setTint(tint);
    139         populate();
    140     }
    141 
    142     @Override
    143     public void setSliceActionListener(SliceView.OnSliceActionListener observer) {
    144         mObserver = observer;
    145         if (mAdapter != null) {
    146             mAdapter.setSliceObserver(mObserver);
    147         }
    148     }
    149 
    150     @Override
    151     public void setSliceActions(List<SliceItem> actions) {
    152         mAdapter.setSliceActions(actions);
    153     }
    154 
    155     @Override
    156     public void setSliceContent(ListContent sliceContent) {
    157         mListContent = sliceContent;
    158         populate();
    159     }
    160 
    161     @Override
    162     public void setStyle(AttributeSet attrs, int defStyleAttrs, int defStyleRes) {
    163         super.setStyle(attrs, defStyleAttrs, defStyleRes);
    164         mAdapter.setStyle(attrs, defStyleAttrs, defStyleRes);
    165     }
    166 
    167     @Override
    168     public void setShowLastUpdated(boolean showLastUpdated) {
    169         super.setShowLastUpdated(showLastUpdated);
    170         mAdapter.setShowLastUpdated(showLastUpdated);
    171     }
    172 
    173     @Override
    174     public void setLastUpdated(long lastUpdated) {
    175         super.setLastUpdated(lastUpdated);
    176         mAdapter.setLastUpdated(lastUpdated);
    177     }
    178 
    179     private void populate() {
    180         if (mListContent == null) {
    181             resetView();
    182             return;
    183         }
    184         updateDisplayedItems(getMeasuredHeight());
    185     }
    186 
    187     /**
    188      * Whether or not the content in this template should be scrollable.
    189      */
    190     public void setScrollable(boolean isScrollable) {
    191         mIsScrollable = isScrollable;
    192         updateDisplayedItems(getMeasuredHeight());
    193     }
    194 
    195     private void updateDisplayedItems(int height) {
    196         if (mListContent == null) {
    197             return;
    198         }
    199         if (!mIsScrollable) {
    200             // If we're not scrollable we must cap the number of items we're displaying such
    201             // that they fit in the available space
    202             if (height == 0) {
    203                 // Not measured, use default
    204                 mDisplayedItems = mListContent.getItemsForNonScrollingList(-1);
    205             } else {
    206                 mDisplayedItems = mListContent.getItemsForNonScrollingList(height);
    207             }
    208         } else {
    209             mDisplayedItems = mListContent.getRowItems();
    210         }
    211         mDisplayedItemsHeight = mListContent.getListHeight(getContext(), mDisplayedItems);
    212         int mode = getMode();
    213         if (mode == SliceView.MODE_LARGE) {
    214             mAdapter.setSliceItems(mDisplayedItems, mTintColor, mode);
    215         } else if (mode == MODE_SMALL) {
    216             mAdapter.setSliceItems(
    217                     Collections.singletonList(mDisplayedItems.get(0)), mTintColor, mode);
    218         }
    219     }
    220 
    221     @Override
    222     public void resetView() {
    223         mDisplayedItemsHeight = 0;
    224         mDisplayedItems.clear();
    225         mAdapter.setSliceItems(null, -1, getMode());
    226         mListContent = null;
    227     }
    228 }
    229