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 17 package com.android.car.list; 18 19 import static java.lang.annotation.RetentionPolicy.SOURCE; 20 21 import android.annotation.CallSuper; 22 import android.annotation.IntDef; 23 import android.annotation.NonNull; 24 import android.support.v7.widget.RecyclerView; 25 import android.support.v7.widget.RecyclerView.ViewHolder; 26 import android.view.View; 27 import android.view.ViewGroup; 28 29 import java.lang.annotation.Retention; 30 import java.util.ArrayList; 31 32 import androidx.car.widget.PagedListView; 33 34 /** 35 * Renders all types of LineItem to a view to be displayed as a row in a list. 36 */ 37 public class TypedPagedListAdapter extends RecyclerView.Adapter<ViewHolder> 38 implements PagedListView.ItemCap { 39 40 private ArrayList<LineItem> mContentList; 41 42 /** 43 * Constructor instance of {@link TypedPagedListAdapter} with an empty content list 44 */ 45 public TypedPagedListAdapter() { 46 this(new ArrayList<>()); 47 } 48 49 /** 50 * Constructor instance of {@link TypedPagedListAdapter} with content list 51 * 52 * @param contentList what is contained in the adapter 53 */ 54 public TypedPagedListAdapter(ArrayList<LineItem> contentList) { 55 mContentList = contentList; 56 } 57 58 /** 59 * Sets current content list to new content list and calls 60 * {@link RecyclerView.Adapter#notifyDataSetChanged()} 61 * 62 * @param contentList New content list 63 */ 64 public void setList(@NonNull ArrayList<LineItem> contentList) { 65 mContentList = contentList; 66 notifyDataSetChanged(); 67 } 68 69 /** 70 * Adds all items to the content list at given index and calls 71 * {@link RecyclerView.Adapter#notifyDataSetChanged()} 72 * 73 * @param index index at which to insert the first element from the specified list 74 * @param lineItems list containing items to add 75 * 76 * @throws IndexOutOfBoundsException thrown when index is out of bounds 77 */ 78 public void addAll(int index, @NonNull ArrayList<LineItem> lineItems) { 79 if (index < 0 || index > mContentList.size()) { 80 throw new IndexOutOfBoundsException( 81 "Index: " + index + ", Size: " + mContentList.size()); 82 } 83 mContentList.addAll(index, lineItems); 84 notifyDataSetChanged(); 85 } 86 87 /** 88 * Removes an item from the content list at the given index and calls 89 * {@link RecyclerView.Adapter#notifyDataSetChanged()} 90 * 91 * @param index the index of the item to be removed 92 */ 93 public void remove(int index) { 94 if (index >= mContentList.size()) { 95 throw new IndexOutOfBoundsException( 96 "Index: " + index + ", Size: " + mContentList.size()); 97 } 98 mContentList.remove(index); 99 notifyItemRemoved(index); 100 } 101 102 /** 103 * Return true if empty, false if not. 104 */ 105 public boolean isEmpty() { 106 return mContentList.isEmpty(); 107 } 108 109 /** 110 * Definition for items that are able to be inserted into the TypedPagedListAdapter 111 * 112 * @paramviewHolder for use with {@link TypedPagedListAdapter} 113 */ 114 public abstract static class LineItem<VH extends RecyclerView.ViewHolder> { 115 @Retention(SOURCE) 116 @IntDef({TEXT_TYPE, 117 TOGGLE_TYPE, 118 ICON_TEXT_TYPE, 119 SEEKBAR_TYPE, 120 ICON_TOGGLE_TYPE, 121 CHECKBOX_TYPE, 122 EDIT_TEXT_TYPE, 123 SINGLE_TEXT_TYPE, 124 SPINNER_TYPE, 125 PASSWORD_TYPE, 126 ACTION_BUTTON_TYPE}) 127 public @interface LineItemType {} 128 129 // with one title and one description 130 static final int TEXT_TYPE = 1; 131 132 // with one tile, one description, and a toggle on the right. 133 static final int TOGGLE_TYPE = 2; 134 135 // with one icon, one tile and one description. 136 static final int ICON_TEXT_TYPE = 3; 137 138 // with one tile and one seekbar. 139 static final int SEEKBAR_TYPE = 4; 140 141 // with one icon, title, description and a toggle. 142 static final int ICON_TOGGLE_TYPE = 5; 143 144 // with one icon, title and a checkbox. 145 static final int CHECKBOX_TYPE = 6; 146 147 // with one title and a EditText. 148 static final int EDIT_TEXT_TYPE = 7; 149 150 // with one title. 151 static final int SINGLE_TEXT_TYPE = 8; 152 153 // with a spinner. 154 static final int SPINNER_TYPE = 9; 155 156 // with a password input window and a checkbox for show password or not. 157 static final int PASSWORD_TYPE = 10; 158 159 // with one title, one description, a start icon, and an end action button with icon 160 static final int ACTION_BUTTON_TYPE = 11; 161 /** 162 * Returns the LineItemType of this LineItem 163 * 164 * @return LineItem's type 165 */ 166 @LineItemType 167 abstract int getType(); 168 169 /** 170 * Called when binding the LineItem to its ViewHolder, sets up onClick to be called 171 * when the item is clicked. 172 * 173 * @param holder The holder for the LineItem 174 */ 175 @CallSuper 176 void bindViewHolder(VH holder) { 177 holder.itemView.setOnClickListener(this::onClick); 178 } 179 180 /** 181 * Returns the description of this LineItem 182 */ 183 public abstract CharSequence getDesc(); 184 185 /** 186 * Returns true if this item is ready to receive touch. If it's set to false, 187 * this item maybe not clickable or temporarily not functional. 188 */ 189 public boolean isEnabled() { 190 return true; 191 } 192 193 /** 194 * Returns true if some component on this item can be clicked. 195 */ 196 public boolean isClickable() { 197 return isExpandable(); 198 } 199 200 /** 201 * Returns true if this item can launch another activity or fragment. 202 */ 203 public abstract boolean isExpandable(); 204 205 /** 206 * Called when the LineItem is clicked, default behavior is nothing. 207 * 208 * @param view Passed in by the view's onClick listener 209 */ 210 public void onClick(View view) { 211 // do nothing 212 } 213 } 214 215 @Override 216 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 217 switch (viewType) { 218 case LineItem.TEXT_TYPE: 219 return TextLineItem.createViewHolder(parent); 220 case LineItem.TOGGLE_TYPE: 221 return ToggleLineItem.createViewHolder(parent); 222 case LineItem.ICON_TEXT_TYPE: 223 return IconTextLineItem.createViewHolder(parent); 224 case LineItem.SEEKBAR_TYPE: 225 return SeekbarLineItem.createViewHolder(parent); 226 case LineItem.ICON_TOGGLE_TYPE: 227 return IconToggleLineItem.createViewHolder(parent); 228 case LineItem.CHECKBOX_TYPE: 229 return CheckBoxLineItem.createViewHolder(parent); 230 case LineItem.EDIT_TEXT_TYPE: 231 return EditTextLineItem.createViewHolder(parent); 232 case LineItem.SINGLE_TEXT_TYPE: 233 return SingleTextLineItem.createViewHolder(parent); 234 case LineItem.SPINNER_TYPE: 235 return SpinnerLineItem.createViewHolder(parent); 236 case LineItem.PASSWORD_TYPE: 237 return PasswordLineItem.createViewHolder(parent); 238 case LineItem.ACTION_BUTTON_TYPE: 239 return ActionIconButtonLineItem.createViewHolder(parent); 240 default: 241 throw new IllegalStateException("ViewType not supported: " + viewType); 242 } 243 } 244 245 @Override 246 public void onBindViewHolder(ViewHolder holder, int position) { 247 mContentList.get(position).bindViewHolder(holder); 248 } 249 250 @Override 251 public int getItemViewType(int position) { 252 return mContentList.get(position).getType(); 253 } 254 255 @Override 256 public int getItemCount() { 257 return mContentList.size(); 258 } 259 260 @Override 261 public void setMaxItems(int maxItems) { 262 // No limit in this list. 263 } 264 } 265