1/* 2 * Copyright (C) 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 17package androidx.car.drawer; 18 19import android.app.Activity; 20import android.car.drivingstate.CarUxRestrictions; 21import android.content.Context; 22import android.graphics.PorterDuff; 23import android.graphics.drawable.Drawable; 24import android.os.Bundle; 25import android.view.LayoutInflater; 26import android.view.View; 27import android.view.ViewGroup; 28 29import androidx.annotation.Nullable; 30import androidx.car.R; 31import androidx.car.utils.CarUxRestrictionsHelper; 32import androidx.car.widget.PagedListView; 33import androidx.recyclerview.widget.RecyclerView; 34 35/** 36 * Base adapter for displaying items in the car navigation drawer, which uses a 37 * {@link PagedListView}. 38 * 39 * <p>Subclasses can optionally set the title that will be displayed when displaying the contents 40 * of the drawer via {@link #setTitle(CharSequence)}. The title can be updated at any point later 41 * on. The title of the root adapter will also be the main title showed in the toolbar when the 42 * drawer is closed. See {@link CarDrawerController#setRootAdapter(CarDrawerAdapter)} for more 43 * information. 44 * 45 * <p>This class also takes care of implementing the PageListView.ItemCamp contract and subclasses 46 * should implement {@link #getActualItemCount()}. 47 * 48 * <p>To enable support for {@link CarUxRestrictions}, call {@link #start()} in your 49 * {@code Activity}'s {@link android.app.Activity#onCreate(Bundle)}, and {@link #stop()} in 50 * {@link Activity#onStop()}. 51 */ 52public abstract class CarDrawerAdapter extends RecyclerView.Adapter<DrawerItemViewHolder> 53 implements PagedListView.ItemCap, DrawerItemClickListener { 54 private final boolean mShowDisabledListOnEmpty; 55 private final Drawable mEmptyListDrawable; 56 private int mMaxItems = PagedListView.ItemCap.UNLIMITED; 57 private CharSequence mTitle; 58 private TitleChangeListener mTitleChangeListener; 59 60 private final CarUxRestrictionsHelper mUxRestrictionsHelper; 61 private CarUxRestrictions mCurrentUxRestrictions; 62 63 /** 64 * Enables support for {@link CarUxRestrictions}. 65 * 66 * <p>This method can be called from {@code Activity}'s {@link Activity#onStart()}, or at the 67 * time of construction. 68 * 69 * <p>This method must be accompanied with a matching {@link #stop()} to avoid leak. 70 */ 71 public void start() { 72 mUxRestrictionsHelper.start(); 73 } 74 75 /** 76 * Disables support for {@link CarUxRestrictions}, and frees up resources. 77 * 78 * <p>This method should be called from {@code Activity}'s {@link Activity#onStop()}, or at the 79 * time of this adapter being discarded. 80 */ 81 public void stop() { 82 mUxRestrictionsHelper.stop(); 83 } 84 85 /** 86 * Interface for a class that will be notified a new title has been set on this adapter. 87 */ 88 interface TitleChangeListener { 89 /** 90 * Called when {@link #setTitle(CharSequence)} has been called and the title has been 91 * changed. 92 */ 93 void onTitleChanged(@Nullable CharSequence newTitle); 94 } 95 96 protected CarDrawerAdapter(Context context, boolean showDisabledListOnEmpty) { 97 mShowDisabledListOnEmpty = showDisabledListOnEmpty; 98 99 mEmptyListDrawable = context.getDrawable(R.drawable.ic_list_view_disable); 100 mEmptyListDrawable.setColorFilter(context.getColor(R.color.car_tint), 101 PorterDuff.Mode.SRC_IN); 102 103 mUxRestrictionsHelper = 104 new CarUxRestrictionsHelper(context, carUxRestrictions -> { 105 mCurrentUxRestrictions = carUxRestrictions; 106 notifyDataSetChanged(); 107 }); 108 } 109 110 /** Returns the title set via {@link #setTitle(CharSequence)}. */ 111 CharSequence getTitle() { 112 return mTitle; 113 } 114 115 /** Updates the title to display in the toolbar for this Adapter. */ 116 public final void setTitle(@Nullable CharSequence title) { 117 mTitle = title; 118 119 if (mTitleChangeListener != null) { 120 mTitleChangeListener.onTitleChanged(mTitle); 121 } 122 } 123 124 /** Sets a listener to be notified whenever the title of this adapter has been changed. */ 125 void setTitleChangeListener(@Nullable TitleChangeListener listener) { 126 mTitleChangeListener = listener; 127 } 128 129 @Override 130 public final void setMaxItems(int maxItems) { 131 mMaxItems = maxItems; 132 } 133 134 @Override 135 public final int getItemCount() { 136 if (shouldShowDisabledListItem()) { 137 return 1; 138 } 139 return mMaxItems >= 0 ? Math.min(mMaxItems, getActualItemCount()) : getActualItemCount(); 140 } 141 142 /** 143 * Returns the absolute number of items that can be displayed in the list. 144 * 145 * <p>A class should implement this method to supply the number of items to be displayed. 146 * Returning 0 from this method will cause an empty list icon to be displayed in the drawer. 147 * 148 * <p>A class should override this method rather than {@link #getItemCount()} because that 149 * method is handling the logic of when to display the empty list icon. It will return 1 when 150 * {@link #getActualItemCount()} returns 0. 151 * 152 * @return The number of items to be displayed in the list. 153 */ 154 protected abstract int getActualItemCount(); 155 156 @Override 157 public final int getItemViewType(int position) { 158 if (shouldShowDisabledListItem()) { 159 return R.layout.car_drawer_list_item_empty; 160 } 161 162 return usesSmallLayout(position) 163 ? R.layout.car_drawer_list_item_small 164 : R.layout.car_drawer_list_item_normal; 165 } 166 167 /** 168 * Used to indicate the layout used for the Drawer item at given position. Subclasses can 169 * override this to use normal layout which includes text element below title. 170 * 171 * <p>A small layout is presented by the layout {@code R.layout.car_drawer_list_item_small}. 172 * Otherwise, the layout {@code R.layout.car_drawer_list_item_normal} will be used. 173 * 174 * @param position Adapter position of item. 175 * @return Whether the item at this position will use a small layout (default) or normal layout. 176 */ 177 protected boolean usesSmallLayout(int position) { 178 return true; 179 } 180 181 @Override 182 public final DrawerItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 183 View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false); 184 return new DrawerItemViewHolder(view); 185 } 186 187 @Override 188 public final void onBindViewHolder(DrawerItemViewHolder holder, int position) { 189 // Car may not be initialized thus current UXR will not be available. 190 if (mCurrentUxRestrictions != null) { 191 holder.complyWithUxRestrictions(mCurrentUxRestrictions); 192 } 193 194 if (shouldShowDisabledListItem()) { 195 holder.getTitle().setText(null); 196 holder.getIcon().setImageDrawable(mEmptyListDrawable); 197 holder.setItemClickListener(null); 198 } else { 199 holder.setItemClickListener(this); 200 populateViewHolder(holder, position); 201 } 202 } 203 204 /** 205 * Whether or not this adapter should be displaying an empty list icon. The icon is shown if it 206 * has been configured to show and there are no items to be displayed. 207 */ 208 private boolean shouldShowDisabledListItem() { 209 return mShowDisabledListOnEmpty && getActualItemCount() == 0; 210 } 211 212 /** 213 * Subclasses should set all elements in {@code holder} to populate the drawer-item. If some 214 * element is not used, it should be nulled out since these ViewHolder/View's are recycled. 215 */ 216 protected abstract void populateViewHolder(DrawerItemViewHolder holder, int position); 217 218 /** 219 * Called when this adapter has been popped off the stack and is no longer needed. Subclasses 220 * can override to do any necessary cleanup. 221 */ 222 public void cleanup() {} 223} 224