Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright 2018 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.car.widget;
     18 
     19 import static java.lang.annotation.RetentionPolicy.SOURCE;
     20 
     21 import android.car.drivingstate.CarUxRestrictions;
     22 import android.content.Context;
     23 import android.graphics.drawable.Drawable;
     24 import android.text.TextUtils;
     25 import android.view.View;
     26 import android.view.ViewGroup;
     27 import android.widget.ImageView;
     28 import android.widget.LinearLayout;
     29 import android.widget.RelativeLayout;
     30 import android.widget.SeekBar;
     31 import android.widget.TextView;
     32 
     33 import androidx.annotation.DrawableRes;
     34 import androidx.annotation.IntDef;
     35 import androidx.annotation.Nullable;
     36 import androidx.car.R;
     37 import androidx.car.utils.CarUxRestrictionsUtils;
     38 
     39 import java.lang.annotation.Retention;
     40 import java.util.ArrayList;
     41 import java.util.List;
     42 
     43 /**
     44  * Class to build a list item with {@link SeekBar}.
     45  *
     46  * <p>An item supports primary action and supplemental action(s).
     47  *
     48  * <p>An item visually composes of 3 parts; each part may contain multiple views.
     49  * <ul>
     50  *     <li>{@code Primary Action}: represented by an icon of following types.
     51  *     <ul>
     52  *         <li>Primary Icon - icon size could be large or small.
     53  *         <li>No Icon - no icon is shown.
     54  *         <li>Empty Icon - {@code Seekbar} offsets start space as if there was an icon.
     55  *     </ul>
     56  *     <li>{@code Seekbar}: with optional {@code Text}.
     57  *     <li>{@code Supplemental Action}: presented by an icon of following types; aligned to
     58  *     the end of item.
     59  *     <ul>
     60  *         <li>Supplemental Icon.
     61  *         <li>Supplemental Empty Icon - {@code Seekbar} offsets end space as if there was an icon.
     62  *     </ul>
     63  * </ul>
     64  *
     65  * {@code SeekbarListItem} binds data to {@link ViewHolder} based on components selected.
     66  *
     67  * <p>When conflicting methods are called (e.g. setting primary action to both primary icon and
     68  * no icon), the last called method wins.
     69  *
     70  * {@code minimum value} is set to 0.
     71  */
     72 public class SeekbarListItem extends ListItem<SeekbarListItem.ViewHolder> {
     73 
     74     @Retention(SOURCE)
     75     @IntDef({
     76             PRIMARY_ACTION_TYPE_NO_ICON, PRIMARY_ACTION_TYPE_EMPTY_ICON,
     77             PRIMARY_ACTION_TYPE_SMALL_ICON})
     78     private @interface PrimaryActionType {}
     79 
     80     private static final int PRIMARY_ACTION_TYPE_NO_ICON = 0;
     81     private static final int PRIMARY_ACTION_TYPE_EMPTY_ICON = 1;
     82     private static final int PRIMARY_ACTION_TYPE_SMALL_ICON = 2;
     83 
     84     @Retention(SOURCE)
     85     @IntDef({SUPPLEMENTAL_ACTION_NO_ACTION, SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON,
     86             SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON,
     87             SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON_WITH_DIVIDER})
     88     private @interface SupplementalActionType {}
     89 
     90     private static final int SUPPLEMENTAL_ACTION_NO_ACTION = 0;
     91     private static final int SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON = 1;
     92     private static final int SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON = 2;
     93     private static final int SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON_WITH_DIVIDER = 3;
     94 
     95     private final Context mContext;
     96 
     97     private final List<ViewBinder<ViewHolder>> mBinders = new ArrayList<>();
     98 
     99     @PrimaryActionType private int mPrimaryActionType = PRIMARY_ACTION_TYPE_NO_ICON;
    100     private Drawable mPrimaryActionIconDrawable;
    101     private View.OnClickListener mPrimaryActionIconOnClickListener;
    102 
    103     private String mText;
    104 
    105     private int mMax;
    106     private int mProgress;
    107     private int mSecondaryProgress;
    108     private SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener;
    109 
    110     @SupplementalActionType private int mSupplementalActionType = SUPPLEMENTAL_ACTION_NO_ACTION;
    111     private Drawable mSupplementalIconDrawable;
    112     private View.OnClickListener mSupplementalIconOnClickListener;
    113     private boolean mShowSupplementalIconDivider;
    114 
    115     /**
    116      * Creates a {@link SeekbarListItem.ViewHolder}.
    117      */
    118     public static ViewHolder createViewHolder(View itemView) {
    119         return new ViewHolder(itemView);
    120     }
    121 
    122     public SeekbarListItem(Context context) {
    123         mContext = context;
    124         markDirty();
    125     }
    126 
    127     /**
    128      * Used by {@link ListItemAdapter} to choose layout to inflate for view holder.
    129      */
    130     @Override
    131     public int getViewType() {
    132         return ListItemAdapter.LIST_ITEM_TYPE_SEEKBAR;
    133     }
    134 
    135     /**
    136      * Sets max value of seekbar.
    137      */
    138     public void setMax(int max) {
    139         mMax = max;
    140         markDirty();
    141     }
    142 
    143     /**
    144      * Sets progress of seekbar.
    145      */
    146     public void setProgress(int progress) {
    147         mProgress = progress;
    148         markDirty();
    149     }
    150 
    151     /**
    152      * Sets secondary progress of seekbar.
    153      */
    154     public void setSecondaryProgress(int secondaryProgress) {
    155         mSecondaryProgress = secondaryProgress;
    156         markDirty();
    157     }
    158 
    159     /**
    160      * Sets {@link SeekBar.OnSeekBarChangeListener}.
    161      */
    162     public void setOnSeekBarChangeListener(SeekBar.OnSeekBarChangeListener listener) {
    163         mOnSeekBarChangeListener = listener;
    164         markDirty();
    165     }
    166 
    167     /**
    168      * Sets text that sits on top of seekbar.
    169      */
    170     public void setText(String text) {
    171         mText = text;
    172         markDirty();
    173     }
    174 
    175     /**
    176      * Calculates the layout params for views in {@link ViewHolder}.
    177      */
    178     @Override
    179     protected void resolveDirtyState() {
    180         mBinders.clear();
    181 
    182         // Create binders that adjust layout params of each view.
    183         setItemLayoutHeight();
    184         setPrimaryAction();
    185         setSeekBarAndText();
    186         setSupplementalAction();
    187     }
    188 
    189     /**
    190      * Hides all views in {@link ViewHolder} then applies ViewBinders to adjust view layout params.
    191      */
    192     @Override
    193     protected void onBind(ViewHolder viewHolder) {
    194         // Hide all subviews then apply view binders to adjust subviews.
    195         hideSubViews(viewHolder);
    196         for (ViewBinder binder : mBinders) {
    197             binder.bind(viewHolder);
    198         }
    199     }
    200 
    201     private void hideSubViews(ViewHolder vh) {
    202         View[] subviews = new View[] {
    203                 vh.getPrimaryIcon(),
    204                 // SeekBar is always visible.
    205                 vh.getText(),
    206                 vh.getSupplementalIcon(), vh.getSupplementalIconDivider(),
    207         };
    208         for (View v : subviews) {
    209             v.setVisibility(View.GONE);
    210         }
    211     }
    212 
    213     private void setItemLayoutHeight() {
    214         int minHeight = mContext.getResources().getDimensionPixelSize(
    215                     R.dimen.car_single_line_list_item_height);
    216         mBinders.add(vh -> {
    217             vh.itemView.setMinimumHeight(minHeight);
    218             vh.getContainerLayout().setMinimumHeight(minHeight);
    219 
    220             ViewGroup.LayoutParams layoutParams = vh.itemView.getLayoutParams();
    221             layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
    222             vh.itemView.requestLayout();
    223         });
    224     }
    225 
    226     private void setPrimaryAction() {
    227         setPrimaryActionLayout();
    228         setPrimaryActionContent();
    229     }
    230 
    231     private void setSeekBarAndText() {
    232         setSeekBarAndTextContent();
    233         setSeekBarAndTextLayout();
    234     }
    235 
    236     private void setSupplementalAction() {
    237         setSupplementalActionLayout();
    238         setSupplementalActionContent();
    239     }
    240 
    241     private void setPrimaryActionLayout() {
    242         switch (mPrimaryActionType) {
    243             case PRIMARY_ACTION_TYPE_NO_ICON:
    244             case PRIMARY_ACTION_TYPE_EMPTY_ICON:
    245                 // Do nothing.
    246                 break;
    247             case PRIMARY_ACTION_TYPE_SMALL_ICON:
    248                 int startMargin = mContext.getResources().getDimensionPixelSize(
    249                         R.dimen.car_keyline_1);
    250                 int iconSize = mContext.getResources().getDimensionPixelSize(
    251                         R.dimen.car_primary_icon_size);
    252                 mBinders.add(vh -> {
    253                     RelativeLayout.LayoutParams layoutParams =
    254                             (RelativeLayout.LayoutParams) vh.getPrimaryIcon().getLayoutParams();
    255                     // Icon size.
    256                     layoutParams.height = layoutParams.width = iconSize;
    257                     // Start margin.
    258                     layoutParams.addRule(RelativeLayout.ALIGN_PARENT_START);
    259                     layoutParams.setMarginStart(startMargin);
    260 
    261                     if (!TextUtils.isEmpty(mText)) {
    262                         // Set icon top margin so that the icon remains in the same position it
    263                         // would've been in for non-long-text item, namely so that the center
    264                         // line of icon matches that of line item.
    265                         int itemHeight = mContext.getResources().getDimensionPixelSize(
    266                                 R.dimen.car_double_line_list_item_height);
    267                         layoutParams.removeRule(RelativeLayout.CENTER_VERTICAL);
    268                         layoutParams.topMargin = (itemHeight - iconSize) / 2;
    269                     } else {
    270                         layoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
    271                         layoutParams.topMargin = 0;
    272                     }
    273 
    274                     vh.getPrimaryIcon().requestLayout();
    275                 });
    276                 break;
    277             default:
    278                 throw new IllegalStateException("Unknown primary action type.");
    279         }
    280     }
    281 
    282     private void setPrimaryActionContent() {
    283         switch (mPrimaryActionType) {
    284             case PRIMARY_ACTION_TYPE_NO_ICON:
    285             case PRIMARY_ACTION_TYPE_EMPTY_ICON:
    286                 // Do nothing.
    287                 break;
    288             case PRIMARY_ACTION_TYPE_SMALL_ICON:
    289                 mBinders.add(vh -> {
    290                     vh.getPrimaryIcon().setVisibility(View.VISIBLE);
    291                     vh.getPrimaryIcon().setImageDrawable(mPrimaryActionIconDrawable);
    292                     vh.getPrimaryIcon().setOnClickListener(
    293                             mPrimaryActionIconOnClickListener);
    294                     vh.getPrimaryIcon().setClickable(
    295                             mPrimaryActionIconOnClickListener != null);
    296                 });
    297                 break;
    298             default:
    299                 throw new IllegalStateException("Unknown primary action type.");
    300         }
    301     }
    302 
    303     private void setSeekBarAndTextContent() {
    304         mBinders.add(vh -> {
    305             vh.getSeekBar().setMax(mMax);
    306             vh.getSeekBar().setProgress(mProgress);
    307             vh.getSeekBar().setSecondaryProgress(mSecondaryProgress);
    308             vh.getSeekBar().setOnSeekBarChangeListener(mOnSeekBarChangeListener);
    309 
    310             if (!TextUtils.isEmpty(mText)) {
    311                 vh.getText().setVisibility(View.VISIBLE);
    312                 vh.getText().setText(mText);
    313                 vh.getText().setTextAppearance(getTitleTextAppearance());
    314             }
    315         });
    316     }
    317 
    318     private void setSeekBarAndTextLayout() {
    319         mBinders.add(vh -> {
    320             // SeekBar is below text with a gap.
    321             ViewGroup.MarginLayoutParams seekBarLayoutParams =
    322                     (ViewGroup.MarginLayoutParams) vh.getSeekBar().getLayoutParams();
    323             seekBarLayoutParams.topMargin = TextUtils.isEmpty(mText)
    324                     ? 0
    325                     : mContext.getResources().getDimensionPixelSize(R.dimen.car_padding_1);
    326             vh.getSeekBar().requestLayout();
    327 
    328             // Set start and end margin of text and seek bar.
    329             setViewStartMargin(vh.getSeekBarContainer());
    330             setViewEndMargin(vh.getSeekBarContainer());
    331 
    332             RelativeLayout.LayoutParams containerLayoutParams =
    333                     (RelativeLayout.LayoutParams) vh.getSeekBarContainer().getLayoutParams();
    334             containerLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
    335         });
    336     }
    337 
    338     // Helper method to set start margin of seekbar/text.
    339     private void setViewStartMargin(View v) {
    340         int startMarginResId;
    341         switch (mPrimaryActionType) {
    342             case PRIMARY_ACTION_TYPE_NO_ICON:
    343                 startMarginResId = R.dimen.car_keyline_1;
    344                 break;
    345             case PRIMARY_ACTION_TYPE_EMPTY_ICON:
    346             case PRIMARY_ACTION_TYPE_SMALL_ICON:
    347                 startMarginResId = R.dimen.car_keyline_3;
    348                 break;
    349             default:
    350                 throw new IllegalStateException("Unknown primary action type.");
    351         }
    352         ViewGroup.MarginLayoutParams layoutParams =
    353                 (ViewGroup.MarginLayoutParams) v.getLayoutParams();
    354         layoutParams.setMarginStart(
    355                 mContext.getResources().getDimensionPixelSize(startMarginResId));
    356         v.requestLayout();
    357     }
    358 
    359     // Helper method to set end margin of seekbar/text.
    360     private void setViewEndMargin(View v) {
    361         RelativeLayout.LayoutParams layoutParams =
    362                 (RelativeLayout.LayoutParams) v.getLayoutParams();
    363         int endMargin = 0;
    364         switch (mSupplementalActionType) {
    365             case SUPPLEMENTAL_ACTION_NO_ACTION:
    366                 // Aligned to parent end with margin.
    367                 layoutParams.addRule(RelativeLayout.ALIGN_PARENT_END);
    368                 layoutParams.removeRule(RelativeLayout.START_OF);
    369                 layoutParams.setMarginEnd(mContext.getResources().getDimensionPixelSize(
    370                                       R.dimen.car_keyline_1));
    371                 break;
    372             case SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON:
    373                 // Align to start of divider with padding.
    374                 layoutParams.addRule(RelativeLayout.START_OF, R.id.supplemental_icon_divider);
    375                 layoutParams.removeRule(RelativeLayout.ALIGN_PARENT_END);
    376                 layoutParams.setMarginEnd(mContext.getResources().getDimensionPixelSize(
    377                                       R.dimen.car_padding_4));
    378                 break;
    379             case SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON_WITH_DIVIDER:
    380                 // Align to parent end with a margin as if the icon and an optional divider were
    381                 // present. We do this by setting
    382 
    383                 // Add divider padding to icon, and width of divider.
    384                 endMargin += mContext.getResources().getDimensionPixelSize(
    385                          R.dimen.car_padding_4);
    386                 endMargin += mContext.getResources().getDimensionPixelSize(
    387                          R.dimen.car_vertical_line_divider_width);
    388                 // Fall through.
    389             case SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON:
    390                 // Add view padding, width of icon, and icon end margin.
    391                 endMargin += mContext.getResources().getDimensionPixelSize(
    392                          R.dimen.car_padding_4);
    393                 endMargin += mContext.getResources().getDimensionPixelSize(
    394                          R.dimen.car_primary_icon_size);
    395                 endMargin += mContext.getResources().getDimensionPixelSize(
    396                          R.dimen.car_keyline_1);
    397 
    398                 layoutParams.addRule(RelativeLayout.ALIGN_PARENT_END);
    399                 layoutParams.removeRule(RelativeLayout.START_OF);
    400                 layoutParams.setMarginEnd(endMargin);
    401                 break;
    402             default:
    403                 throw new IllegalStateException("Unknown supplemental action type.");
    404         }
    405         v.requestLayout();
    406     }
    407 
    408     private void setSupplementalActionLayout() {
    409         int keyline1 = mContext.getResources().getDimensionPixelSize(R.dimen.car_keyline_1);
    410         int padding4 = mContext.getResources().getDimensionPixelSize(R.dimen.car_padding_4);
    411         mBinders.add(vh -> {
    412             RelativeLayout.LayoutParams iconLayoutParams =
    413                     (RelativeLayout.LayoutParams) vh.getSupplementalIcon().getLayoutParams();
    414             // Align to parent end with margin.
    415             iconLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_END);
    416             iconLayoutParams.setMarginEnd(keyline1);
    417             iconLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
    418 
    419             vh.getSupplementalIcon().requestLayout();
    420 
    421             // Divider aligns to the start of supplemental icon with margin.
    422             RelativeLayout.LayoutParams dividerLayoutParams =
    423                     (RelativeLayout.LayoutParams) vh.getSupplementalIconDivider()
    424                             .getLayoutParams();
    425             dividerLayoutParams.addRule(RelativeLayout.START_OF, R.id.supplemental_icon);
    426             dividerLayoutParams.setMarginEnd(padding4);
    427             dividerLayoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
    428 
    429             vh.getSupplementalIconDivider().requestLayout();
    430         });
    431     }
    432 
    433     private void setSupplementalActionContent() {
    434         switch (mSupplementalActionType) {
    435             case SUPPLEMENTAL_ACTION_NO_ACTION:
    436             case SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON_WITH_DIVIDER:
    437             case SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON:
    438                 break;
    439             case SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON:
    440                 mBinders.add(vh -> {
    441                     vh.getSupplementalIcon().setVisibility(View.VISIBLE);
    442                     if (mShowSupplementalIconDivider) {
    443                         vh.getSupplementalIconDivider().setVisibility(View.VISIBLE);
    444                     }
    445 
    446                     vh.getSupplementalIcon().setImageDrawable(mSupplementalIconDrawable);
    447 
    448                     vh.getSupplementalIcon().setOnClickListener(
    449                             mSupplementalIconOnClickListener);
    450                     vh.getSupplementalIcon().setClickable(
    451                             mSupplementalIconOnClickListener != null);
    452                 });
    453                 break;
    454             default:
    455                 throw new IllegalStateException("Unknown supplemental action type.");
    456         }
    457     }
    458 
    459     /**
    460      * Sets {@code Primary Action} to be represented by an icon.
    461      *
    462      * @param iconResId the resource identifier of the drawable.
    463      */
    464     public void setPrimaryActionIcon(@DrawableRes int iconResId) {
    465         setPrimaryActionIcon(mContext.getDrawable(iconResId));
    466     }
    467 
    468     /**
    469      * Sets {@code Primary Action} to be represented by an icon.
    470      *
    471      * @param drawable the Drawable to set, or null to clear the content.
    472      */
    473     public void setPrimaryActionIcon(Drawable drawable) {
    474         mPrimaryActionType = PRIMARY_ACTION_TYPE_SMALL_ICON;
    475         mPrimaryActionIconDrawable = drawable;
    476         markDirty();
    477     }
    478 
    479     /**
    480      * Sets an {@code OnClickListener} for the icon representing the {@code Primary Action}.
    481      *
    482      * @param onClickListener the listener to be set for the primary action icon.
    483      */
    484     public void setPrimaryActionIconListener(View.OnClickListener onClickListener) {
    485         mPrimaryActionIconOnClickListener = onClickListener;
    486         markDirty();
    487     }
    488 
    489     /**
    490      * Sets {@code Primary Action} to be empty icon.
    491      *
    492      * {@code Seekbar} would have a start margin as if {@code Primary Action} were set as icon.
    493      */
    494     public void setPrimaryActionEmptyIcon() {
    495         mPrimaryActionType = PRIMARY_ACTION_TYPE_EMPTY_ICON;
    496 
    497         markDirty();
    498     }
    499 
    500     /**
    501      * Sets {@code Supplemental Action} to be represented by an {@code Supplemental Icon}.
    502      */
    503     public void setSupplementalIcon(@DrawableRes int iconResId,
    504             boolean showSupplementalIconDivider) {
    505         setSupplementalIconInfo(mContext.getDrawable(iconResId), showSupplementalIconDivider);
    506     }
    507 
    508     /**
    509      * Sets {@code Supplemental Action} to be represented by an {@code Supplemental Icon}.
    510      */
    511     public void setSupplementalIcon(Drawable drawable, boolean showSupplementalIconDivider) {
    512         setSupplementalIconInfo(drawable, showSupplementalIconDivider);
    513     }
    514 
    515     /**
    516      * Sets {@code OnClickListener} for a {@code Supplemental Icon}.
    517      */
    518     public void setSupplementalIconListener(View.OnClickListener listener) {
    519         mSupplementalIconOnClickListener = listener;
    520 
    521         markDirty();
    522     }
    523 
    524     private void setSupplementalIconInfo(Drawable drawable, boolean showSupplementalIconDivider) {
    525         mSupplementalActionType = SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON;
    526 
    527         mSupplementalIconDrawable = drawable;
    528         mShowSupplementalIconDivider = showSupplementalIconDivider;
    529 
    530         markDirty();
    531     }
    532 
    533     /**
    534      * Sets {@code Supplemental Action} to be represented by an {@code Supplemental Icon}.
    535      *
    536      * @deprecated use either {@link #setSupplementalIcon(Drawable, boolean)} or
    537      * {@link #setSupplementalIcon(int, boolean)} and
    538      * {@link #setSupplementalIconListener(android.view.View.OnClickListener)}.
    539      */
    540     @Deprecated
    541     public void setSupplementalIcon(Drawable drawable, boolean showSupplementalIconDivider,
    542             @Nullable  View.OnClickListener listener) {
    543         mSupplementalActionType = SUPPLEMENTAL_ACTION_SUPPLEMENTAL_ICON;
    544 
    545         mSupplementalIconDrawable = drawable;
    546         mShowSupplementalIconDivider = showSupplementalIconDivider;
    547         mSupplementalIconOnClickListener = listener;
    548 
    549         markDirty();
    550     }
    551 
    552     /**
    553      * Sets {@code Supplemental Action} to be empty icon.
    554      *
    555      * {@code Seekbar} would have an end margin as if {@code Supplemental Action} were set.
    556      */
    557     public void setSupplementalEmptyIcon(boolean seekbarOffsetDividerWidth) {
    558         mSupplementalActionType = seekbarOffsetDividerWidth
    559                 ? SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON_WITH_DIVIDER
    560                 : SUPPLEMENTAL_ACTION_SUPPLEMENTAL_EMPTY_ICON;
    561         markDirty();
    562     }
    563 
    564     /**
    565      * Holds views of SeekbarListItem.
    566      */
    567     public static class ViewHolder extends ListItem.ViewHolder {
    568 
    569         private RelativeLayout mContainerLayout;
    570 
    571         private ImageView mPrimaryIcon;
    572 
    573         private LinearLayout mSeekBarContainer;
    574         private TextView mText;
    575         private SeekBar mSeekBar;
    576 
    577         private View mSupplementalIconDivider;
    578         private ImageView mSupplementalIcon;
    579 
    580         public ViewHolder(View itemView) {
    581             super(itemView);
    582 
    583             mContainerLayout = itemView.findViewById(R.id.container);
    584 
    585             mPrimaryIcon = itemView.findViewById(R.id.primary_icon);
    586 
    587             mSeekBarContainer = itemView.findViewById(R.id.seek_bar_container);
    588             mText = itemView.findViewById(R.id.text);
    589             mSeekBar = itemView.findViewById(R.id.seek_bar);
    590 
    591             mSupplementalIcon = itemView.findViewById(R.id.supplemental_icon);
    592             mSupplementalIconDivider = itemView.findViewById(R.id.supplemental_icon_divider);
    593 
    594             int minTouchSize = itemView.getContext().getResources()
    595                     .getDimensionPixelSize(R.dimen.car_touch_target_size);
    596 
    597             MinTouchTargetHelper.ensureThat(mSupplementalIcon)
    598                     .hasMinTouchSize(minTouchSize);
    599         }
    600 
    601         @Override
    602         protected void complyWithUxRestrictions(CarUxRestrictions restrictions) {
    603             CarUxRestrictionsUtils.comply(itemView.getContext(), restrictions, getText());
    604         }
    605 
    606         public RelativeLayout getContainerLayout() {
    607             return mContainerLayout;
    608         }
    609 
    610         public ImageView getPrimaryIcon() {
    611             return mPrimaryIcon;
    612         }
    613 
    614         public LinearLayout getSeekBarContainer() {
    615             return mSeekBarContainer;
    616         }
    617 
    618         public TextView getText() {
    619             return mText;
    620         }
    621 
    622         public SeekBar getSeekBar() {
    623             return mSeekBar;
    624         }
    625 
    626         public ImageView getSupplementalIcon() {
    627             return mSupplementalIcon;
    628         }
    629 
    630         public View getSupplementalIconDivider() {
    631             return mSupplementalIconDivider;
    632         }
    633     }
    634 }
    635