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 android.app.slice.Slice.EXTRA_RANGE_VALUE;
     20 import static android.app.slice.Slice.HINT_NO_TINT;
     21 import static android.app.slice.Slice.HINT_PARTIAL;
     22 import static android.app.slice.Slice.HINT_SHORTCUT;
     23 import static android.app.slice.Slice.SUBTYPE_MAX;
     24 import static android.app.slice.Slice.SUBTYPE_VALUE;
     25 import static android.app.slice.SliceItem.FORMAT_ACTION;
     26 import static android.app.slice.SliceItem.FORMAT_IMAGE;
     27 import static android.app.slice.SliceItem.FORMAT_INT;
     28 import static android.app.slice.SliceItem.FORMAT_LONG;
     29 import static android.app.slice.SliceItem.FORMAT_SLICE;
     30 
     31 import static androidx.slice.core.SliceHints.ICON_IMAGE;
     32 import static androidx.slice.core.SliceHints.SMALL_IMAGE;
     33 import static androidx.slice.core.SliceHints.SUBTYPE_MIN;
     34 import static androidx.slice.widget.EventInfo.ACTION_TYPE_BUTTON;
     35 import static androidx.slice.widget.EventInfo.ACTION_TYPE_TOGGLE;
     36 import static androidx.slice.widget.EventInfo.ROW_TYPE_LIST;
     37 import static androidx.slice.widget.EventInfo.ROW_TYPE_TOGGLE;
     38 import static androidx.slice.widget.SliceView.MODE_SMALL;
     39 
     40 import android.app.PendingIntent.CanceledException;
     41 import android.content.Context;
     42 import android.content.Intent;
     43 import android.graphics.Typeface;
     44 import android.graphics.drawable.Drawable;
     45 import android.text.SpannableString;
     46 import android.text.TextUtils;
     47 import android.text.style.StyleSpan;
     48 import android.util.ArrayMap;
     49 import android.util.Log;
     50 import android.util.TypedValue;
     51 import android.view.LayoutInflater;
     52 import android.view.View;
     53 import android.view.ViewGroup;
     54 import android.widget.Button;
     55 import android.widget.ImageView;
     56 import android.widget.LinearLayout;
     57 import android.widget.ProgressBar;
     58 import android.widget.SeekBar;
     59 import android.widget.TextView;
     60 
     61 import androidx.annotation.ColorInt;
     62 import androidx.annotation.RestrictTo;
     63 import androidx.core.graphics.drawable.DrawableCompat;
     64 import androidx.core.graphics.drawable.IconCompat;
     65 import androidx.slice.SliceItem;
     66 import androidx.slice.core.SliceActionImpl;
     67 import androidx.slice.core.SliceQuery;
     68 import androidx.slice.view.R;
     69 
     70 import java.util.List;
     71 
     72 /**
     73  * Row item is in small template format and can be used to construct list items for use
     74  * with {@link LargeTemplateView}.
     75  *
     76  * @hide
     77  */
     78 @RestrictTo(RestrictTo.Scope.LIBRARY)
     79 public class RowView extends SliceChildView implements View.OnClickListener {
     80 
     81     private static final String TAG = "RowView";
     82 
     83     // The number of items that fit on the right hand side of a small slice
     84     private static final int MAX_END_ITEMS = 3;
     85 
     86     private LinearLayout mRootView;
     87     private LinearLayout mStartContainer;
     88     private LinearLayout mContent;
     89     private TextView mPrimaryText;
     90     private TextView mSecondaryText;
     91     private TextView mLastUpdatedText;
     92     private View mDivider;
     93     private ArrayMap<SliceActionImpl, SliceActionView> mToggles = new ArrayMap<>();
     94     private LinearLayout mEndContainer;
     95     private ProgressBar mRangeBar;
     96     private View mSeeMoreView;
     97 
     98     private int mRowIndex;
     99     private RowContent mRowContent;
    100     private SliceActionImpl mRowAction;
    101     private boolean mIsHeader;
    102     private List<SliceItem> mHeaderActions;
    103     private boolean mIsSingleItem;
    104 
    105     private int mImageSize;
    106     private int mIconSize;
    107     private int mRangeHeight;
    108 
    109     public RowView(Context context) {
    110         super(context);
    111         mIconSize = getContext().getResources().getDimensionPixelSize(R.dimen.abc_slice_icon_size);
    112         mImageSize = getContext().getResources().getDimensionPixelSize(
    113                 R.dimen.abc_slice_small_image_size);
    114         mRootView = (LinearLayout) LayoutInflater.from(context).inflate(
    115                 R.layout.abc_slice_small_template, this, false);
    116         addView(mRootView);
    117 
    118         mStartContainer = (LinearLayout) findViewById(R.id.icon_frame);
    119         mContent = (LinearLayout) findViewById(android.R.id.content);
    120         mPrimaryText = (TextView) findViewById(android.R.id.title);
    121         mSecondaryText = (TextView) findViewById(android.R.id.summary);
    122         mLastUpdatedText = (TextView) findViewById(R.id.last_updated);
    123         mDivider = findViewById(R.id.divider);
    124         mEndContainer = (LinearLayout) findViewById(android.R.id.widget_frame);
    125 
    126         mRangeHeight = context.getResources().getDimensionPixelSize(
    127                 R.dimen.abc_slice_row_range_height);
    128     }
    129 
    130     /**
    131      * Set whether this is the only row in the view, in which case our height is different.
    132      */
    133     public void setSingleItem(boolean isSingleItem) {
    134         mIsSingleItem = isSingleItem;
    135     }
    136 
    137     @Override
    138     public int getSmallHeight() {
    139         // RowView is in small format when it is the header of a list and displays at max height.
    140         return mRowContent != null && mRowContent.isValid() ? mRowContent.getSmallHeight() : 0;
    141     }
    142 
    143     @Override
    144     public int getActualHeight() {
    145         if (mIsSingleItem) {
    146             return getSmallHeight();
    147         }
    148         return mRowContent != null && mRowContent.isValid() ? mRowContent.getActualHeight() : 0;
    149     }
    150     /**
    151      * @return height row content (i.e. title, subtitle) without the height of the range element.
    152      */
    153     private int getRowContentHeight() {
    154         int rowHeight = (getMode() == MODE_SMALL || mIsSingleItem)
    155                 ? getSmallHeight()
    156                 : getActualHeight();
    157         if (mRangeBar != null) {
    158             rowHeight -= mRangeHeight;
    159         }
    160         return rowHeight;
    161     }
    162 
    163     @Override
    164     public void setTint(@ColorInt int tintColor) {
    165         super.setTint(tintColor);
    166         if (mRowContent != null) {
    167             // TODO -- can be smarter about this
    168             populateViews();
    169         }
    170     }
    171 
    172     @Override
    173     public void setSliceActions(List<SliceItem> actions) {
    174         mHeaderActions = actions;
    175         if (mRowContent != null) {
    176             populateViews();
    177         }
    178     }
    179 
    180     @Override
    181     public void setShowLastUpdated(boolean showLastUpdated) {
    182         super.setShowLastUpdated(showLastUpdated);
    183         if (mRowContent != null) {
    184             populateViews();
    185         }
    186     }
    187 
    188     @Override
    189     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    190         int totalHeight = getMode() == MODE_SMALL ? getSmallHeight() : getActualHeight();
    191         int rowHeight = getRowContentHeight();
    192         if (rowHeight != 0) {
    193             // Might be gone if we have range / progress but nothing else
    194             mRootView.setVisibility(View.VISIBLE);
    195             heightMeasureSpec = MeasureSpec.makeMeasureSpec(rowHeight, MeasureSpec.EXACTLY);
    196             measureChild(mRootView, widthMeasureSpec, heightMeasureSpec);
    197         } else {
    198             mRootView.setVisibility(View.GONE);
    199         }
    200         if (mRangeBar != null) {
    201             int rangeMeasureSpec = MeasureSpec.makeMeasureSpec(mRangeHeight, MeasureSpec.EXACTLY);
    202             measureChild(mRangeBar, widthMeasureSpec, rangeMeasureSpec);
    203         }
    204 
    205         int totalHeightSpec = MeasureSpec.makeMeasureSpec(totalHeight, MeasureSpec.EXACTLY);
    206         super.onMeasure(widthMeasureSpec, totalHeightSpec);
    207     }
    208 
    209     @Override
    210     protected void onLayout(boolean changed, int l, int t, int r, int b) {
    211         mRootView.layout(0, 0, mRootView.getMeasuredWidth(), getRowContentHeight());
    212         if (mRangeBar != null) {
    213             mRangeBar.layout(0, getRowContentHeight(), mRangeBar.getMeasuredWidth(),
    214                     getRowContentHeight() + mRangeHeight);
    215         }
    216     }
    217 
    218     /**
    219      * This is called when RowView is being used as a component in a large template.
    220      */
    221     @Override
    222     public void setSliceItem(SliceItem slice, boolean isHeader, int index,
    223             int rowCount, SliceView.OnSliceActionListener observer) {
    224         setSliceActionListener(observer);
    225         mRowIndex = index;
    226         mIsHeader = ListContent.isValidHeader(slice);
    227         mHeaderActions = null;
    228         mRowContent = new RowContent(getContext(), slice, mIsHeader);
    229         populateViews();
    230     }
    231 
    232     private void populateViews() {
    233         resetView();
    234         if (mRowContent.isDefaultSeeMore()) {
    235             showSeeMore();
    236             return;
    237         }
    238         CharSequence contentDescr = mRowContent.getContentDescription();
    239         if (contentDescr != null) {
    240             mContent.setContentDescription(contentDescr);
    241         }
    242         final SliceItem startItem = mRowContent.getStartItem();
    243         boolean showStart = startItem != null && mRowIndex > 0;
    244         if (showStart) {
    245             showStart = addItem(startItem, mTintColor, true /* isStart */);
    246         }
    247         mStartContainer.setVisibility(showStart ? View.VISIBLE : View.GONE);
    248 
    249         final SliceItem titleItem = mRowContent.getTitleItem();
    250         if (titleItem != null) {
    251             mPrimaryText.setText(titleItem.getText());
    252         }
    253         mPrimaryText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mIsHeader
    254                 ? mHeaderTitleSize
    255                 : mTitleSize);
    256         mPrimaryText.setTextColor(mTitleColor);
    257         mPrimaryText.setVisibility(titleItem != null ? View.VISIBLE : View.GONE);
    258 
    259         final SliceItem subtitleItem = getMode() == MODE_SMALL
    260                 ? mRowContent.getSummaryItem()
    261                 : mRowContent.getSubtitleItem();
    262         addSubtitle(subtitleItem);
    263 
    264         SliceItem primaryAction = mRowContent.getPrimaryAction();
    265         if (primaryAction != null && primaryAction != startItem) {
    266             mRowAction = new SliceActionImpl(primaryAction);
    267             if (mRowAction.isToggle()) {
    268                 // If primary action is a toggle, add it and we're done
    269                 addAction(mRowAction, mTintColor, mEndContainer, false /* isStart */);
    270                 // TODO: if start item is tappable, touch feedback should exclude it
    271                 setViewClickable(mRootView, true);
    272                 return;
    273             }
    274         }
    275 
    276         final SliceItem range = mRowContent.getRange();
    277         if (range != null) {
    278             if (mRowAction != null) {
    279                 setViewClickable(mRootView, true);
    280             }
    281             addRange(range);
    282             return;
    283         }
    284 
    285         // If we're here we can can show end items; check for top level actions first
    286         List<SliceItem> endItems = mRowContent.getEndItems();
    287         if (mHeaderActions != null && mHeaderActions.size() > 0) {
    288             // Use these if we have them instead
    289             endItems = mHeaderActions;
    290         }
    291         // If we're here we might be able to show end items
    292         int itemCount = 0;
    293         boolean firstItemIsADefaultToggle = false;
    294         boolean hasEndItemAction = false;
    295         for (int i = 0; i < endItems.size(); i++) {
    296             final SliceItem endItem = endItems.get(i);
    297             if (itemCount < MAX_END_ITEMS) {
    298                 if (addItem(endItem, mTintColor, false /* isStart */)) {
    299                     if (SliceQuery.find(endItem, FORMAT_ACTION) != null) {
    300                         hasEndItemAction = true;
    301                     }
    302                     itemCount++;
    303                     if (itemCount == 1) {
    304                         firstItemIsADefaultToggle = !mToggles.isEmpty()
    305                                 && SliceQuery.find(endItem.getSlice(), FORMAT_IMAGE) == null;
    306                     }
    307                 }
    308             }
    309         }
    310 
    311         // If there is a row action and the first end item is a default toggle, show the divider.
    312         mDivider.setVisibility(mRowAction != null && firstItemIsADefaultToggle
    313                 ? View.VISIBLE : View.GONE);
    314         boolean hasStartAction = startItem != null
    315                 && SliceQuery.find(startItem, FORMAT_ACTION) != null;
    316 
    317         if (mRowAction != null) {
    318             // If there are outside actions make only the content bit clickable
    319             // TODO: if start item is an image touch feedback should include it
    320             setViewClickable((hasEndItemAction || hasStartAction) ? mContent : mRootView, true);
    321         } else if (hasEndItemAction != hasStartAction && (itemCount == 1 || hasStartAction)) {
    322             // Single action; make whole row clickable for it
    323             if (!mToggles.isEmpty()) {
    324                 mRowAction = mToggles.keySet().iterator().next();
    325             } else {
    326                 mRowAction = new SliceActionImpl(hasEndItemAction ? endItems.get(0) : startItem);
    327             }
    328             setViewClickable(mRootView, true);
    329         }
    330     }
    331 
    332     private void addSubtitle(final SliceItem subtitleItem) {
    333         CharSequence subtitleTimeString = null;
    334         if (mShowLastUpdated && mLastUpdated != -1) {
    335             subtitleTimeString = getResources().getString(R.string.abc_slice_updated,
    336                     SliceViewUtil.getRelativeTimeString(mLastUpdated));
    337         }
    338         CharSequence subtitle = subtitleItem != null ? subtitleItem.getText() : null;
    339         boolean subtitleExists = !TextUtils.isEmpty(subtitle)
    340                         || (subtitleItem != null && subtitleItem.hasHint(HINT_PARTIAL));
    341         if (subtitleExists) {
    342             mSecondaryText.setText(subtitle);
    343             mSecondaryText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mIsHeader
    344                     ? mHeaderSubtitleSize
    345                     : mSubtitleSize);
    346             mSecondaryText.setTextColor(mSubtitleColor);
    347             int verticalPadding = mIsHeader ? mVerticalHeaderTextPadding : mVerticalTextPadding;
    348             mSecondaryText.setPadding(0, verticalPadding, 0, 0);
    349         }
    350         if (subtitleTimeString != null) {
    351             if (!TextUtils.isEmpty(subtitle)) {
    352                 subtitleTimeString = " \u00B7 " + subtitleTimeString;
    353             }
    354             SpannableString sp = new SpannableString(subtitleTimeString);
    355             sp.setSpan(new StyleSpan(Typeface.ITALIC), 0, subtitleTimeString.length(), 0);
    356             mLastUpdatedText.setText(sp);
    357             mLastUpdatedText.setTextSize(TypedValue.COMPLEX_UNIT_PX,
    358                     mIsHeader ? mHeaderSubtitleSize : mSubtitleSize);
    359             mLastUpdatedText.setTextColor(mSubtitleColor);
    360         }
    361         mLastUpdatedText.setVisibility(TextUtils.isEmpty(subtitleTimeString) ? GONE : VISIBLE);
    362         mSecondaryText.setVisibility(subtitleExists ? VISIBLE : GONE);
    363     }
    364 
    365     private void addRange(final SliceItem range) {
    366         final boolean isSeekBar = FORMAT_ACTION.equals(range.getFormat());
    367         final ProgressBar progressBar = isSeekBar
    368                 ? new SeekBar(getContext())
    369                 : new ProgressBar(getContext(), null, android.R.attr.progressBarStyleHorizontal);
    370         if (mTintColor != -1) {
    371             Drawable drawable = DrawableCompat.wrap(progressBar.getProgressDrawable());
    372             DrawableCompat.setTint(drawable, mTintColor);
    373             progressBar.setProgressDrawable(drawable);
    374         }
    375         // TODO: Need to handle custom accessibility for min
    376         SliceItem min = SliceQuery.findSubtype(range, FORMAT_INT, SUBTYPE_MIN);
    377         int minValue = 0;
    378         if (min != null) {
    379             minValue = min.getInt();
    380         }
    381         SliceItem max = SliceQuery.findSubtype(range, FORMAT_INT, SUBTYPE_MAX);
    382         if (max != null) {
    383             progressBar.setMax(max.getInt() - minValue);
    384         }
    385         SliceItem progress = SliceQuery.findSubtype(range, FORMAT_INT, SUBTYPE_VALUE);
    386         if (progress != null) {
    387             progressBar.setProgress(progress.getInt() - minValue);
    388         }
    389         progressBar.setVisibility(View.VISIBLE);
    390         addView(progressBar);
    391         mRangeBar = progressBar;
    392         if (isSeekBar) {
    393             SliceItem thumb = mRowContent.getInputRangeThumb();
    394             SeekBar seekBar = (SeekBar) mRangeBar;
    395             if (thumb != null) {
    396                 seekBar.setThumb(thumb.getIcon().loadDrawable(getContext()));
    397             }
    398             if (mTintColor != -1) {
    399                 Drawable drawable = DrawableCompat.wrap(seekBar.getThumb());
    400                 DrawableCompat.setTint(drawable, mTintColor);
    401                 seekBar.setThumb(drawable);
    402             }
    403             final int finalMinValue = minValue;
    404             seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
    405                 @Override
    406                 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    407                     progress += finalMinValue;
    408                     try {
    409                         // TODO: sending this PendingIntent should be rate limited.
    410                         range.fireAction(getContext(),
    411                                 new Intent().putExtra(EXTRA_RANGE_VALUE, progress));
    412                     } catch (CanceledException e) { }
    413                 }
    414 
    415                 @Override
    416                 public void onStartTrackingTouch(SeekBar seekBar) { }
    417 
    418                 @Override
    419                 public void onStopTrackingTouch(SeekBar seekBar) { }
    420             });
    421         }
    422     }
    423 
    424     /**
    425      * Add an action view to the container.
    426      */
    427     private void addAction(final SliceActionImpl actionContent, int color, ViewGroup container,
    428                            boolean isStart) {
    429         SliceActionView sav = new SliceActionView(getContext());
    430         container.addView(sav);
    431 
    432         boolean isToggle = actionContent.isToggle();
    433         int actionType = isToggle ? ACTION_TYPE_TOGGLE : ACTION_TYPE_BUTTON;
    434         int rowType = isToggle ? ROW_TYPE_TOGGLE : ROW_TYPE_LIST;
    435         EventInfo info = new EventInfo(getMode(), actionType, rowType, mRowIndex);
    436         if (isStart) {
    437             info.setPosition(EventInfo.POSITION_START, 0, 1);
    438         }
    439         sav.setAction(actionContent, info, mObserver, color);
    440         if (isToggle) {
    441             mToggles.put(actionContent, sav);
    442         }
    443     }
    444 
    445     /**
    446      * Adds simple items to a container. Simple items include actions with icons, images, or
    447      * timestamps.
    448      */
    449     private boolean addItem(SliceItem sliceItem, int color, boolean isStart) {
    450         IconCompat icon = null;
    451         int imageMode = 0;
    452         SliceItem timeStamp = null;
    453         ViewGroup container = isStart ? mStartContainer : mEndContainer;
    454         if (FORMAT_SLICE.equals(sliceItem.getFormat())
    455                 || FORMAT_ACTION.equals(sliceItem.getFormat())) {
    456             if (sliceItem.hasHint(HINT_SHORTCUT)) {
    457                 addAction(new SliceActionImpl(sliceItem), color, container, isStart);
    458                 return true;
    459             } else {
    460                 sliceItem = sliceItem.getSlice().getItems().get(0);
    461             }
    462         }
    463 
    464         if (FORMAT_IMAGE.equals(sliceItem.getFormat())) {
    465             icon = sliceItem.getIcon();
    466             imageMode = sliceItem.hasHint(HINT_NO_TINT) ? SMALL_IMAGE : ICON_IMAGE;
    467         } else if (FORMAT_LONG.equals(sliceItem.getFormat())) {
    468             timeStamp = sliceItem;
    469         }
    470         View addedView = null;
    471         if (icon != null) {
    472             boolean isIcon = imageMode == ICON_IMAGE;
    473             ImageView iv = new ImageView(getContext());
    474             iv.setImageDrawable(icon.loadDrawable(getContext()));
    475             if (isIcon && color != -1) {
    476                 iv.setColorFilter(color);
    477             }
    478             container.addView(iv);
    479             LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) iv.getLayoutParams();
    480             lp.width = mImageSize;
    481             lp.height = mImageSize;
    482             iv.setLayoutParams(lp);
    483             int p = isIcon ? mIconSize / 2 : 0;
    484             iv.setPadding(p, p, p, p);
    485             addedView = iv;
    486         } else if (timeStamp != null) {
    487             TextView tv = new TextView(getContext());
    488             tv.setText(SliceViewUtil.getRelativeTimeString(sliceItem.getTimestamp()));
    489             tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, mSubtitleSize);
    490             tv.setTextColor(mSubtitleColor);
    491             container.addView(tv);
    492             addedView = tv;
    493         }
    494         return addedView != null;
    495     }
    496 
    497     private void showSeeMore() {
    498         Button b = (Button) LayoutInflater.from(getContext()).inflate(
    499                 R.layout.abc_slice_row_show_more, this, false);
    500         b.setOnClickListener(new View.OnClickListener() {
    501             @Override
    502             public void onClick(View v) {
    503                 try {
    504                     if (mObserver != null) {
    505                         EventInfo info = new EventInfo(getMode(), EventInfo.ACTION_TYPE_SEE_MORE,
    506                                 EventInfo.ROW_TYPE_LIST, mRowIndex);
    507                         mObserver.onSliceAction(info, mRowContent.getSlice());
    508                     }
    509                     mRowContent.getSlice().fireAction(null, null);
    510                 } catch (CanceledException e) {
    511                     Log.e(TAG, "PendingIntent for slice cannot be sent", e);
    512                 }
    513             }
    514         });
    515         if (mTintColor != -1) {
    516             b.setTextColor(mTintColor);
    517         }
    518         mSeeMoreView = b;
    519         mRootView.addView(mSeeMoreView);
    520     }
    521 
    522     @Override
    523     public void onClick(View view) {
    524         if (mRowAction != null && mRowAction.getActionItem() != null) {
    525             // Check if it's a row click for a toggle, in this case need to update the UI
    526             if (mRowAction.isToggle() && !(view instanceof SliceActionView)) {
    527                 SliceActionView sav = mToggles.get(mRowAction);
    528                 if (sav != null) {
    529                     sav.toggle();
    530                 }
    531             } else {
    532                 try {
    533                     mRowAction.getActionItem().fireAction(null, null);
    534                     if (mObserver != null) {
    535                         EventInfo info = new EventInfo(getMode(), EventInfo.ACTION_TYPE_CONTENT,
    536                                 EventInfo.ROW_TYPE_LIST, mRowIndex);
    537                         mObserver.onSliceAction(info, mRowAction.getSliceItem());
    538                     }
    539                 } catch (CanceledException e) {
    540                     Log.e(TAG, "PendingIntent for slice cannot be sent", e);
    541                 }
    542             }
    543         }
    544     }
    545 
    546     private void setViewClickable(View layout, boolean isClickable) {
    547         layout.setOnClickListener(isClickable ? this : null);
    548         layout.setBackground(isClickable
    549                 ? SliceViewUtil.getDrawable(getContext(), android.R.attr.selectableItemBackground)
    550                 : null);
    551         layout.setClickable(isClickable);
    552     }
    553 
    554     @Override
    555     public void resetView() {
    556         mRootView.setVisibility(VISIBLE);
    557         setViewClickable(mRootView, false);
    558         setViewClickable(mContent, false);
    559         mStartContainer.removeAllViews();
    560         mEndContainer.removeAllViews();
    561         mPrimaryText.setText(null);
    562         mSecondaryText.setText(null);
    563         mLastUpdatedText.setText(null);
    564         mLastUpdatedText.setVisibility(GONE);
    565         mToggles.clear();
    566         mRowAction = null;
    567         mDivider.setVisibility(GONE);
    568         if (mRangeBar != null) {
    569             removeView(mRangeBar);
    570         }
    571         if (mSeeMoreView != null) {
    572             mRootView.removeView(mSeeMoreView);
    573         }
    574     }
    575 }
    576