1 /* 2 * Copyright (C) 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 com.android.car.settings.quicksettings; 18 19 import android.annotation.Nullable; 20 import android.content.Context; 21 import android.graphics.drawable.Drawable; 22 import android.text.TextUtils; 23 import android.view.LayoutInflater; 24 import android.view.View; 25 import android.view.ViewGroup; 26 import android.widget.ImageView; 27 import android.widget.SeekBar; 28 import android.widget.TextView; 29 30 import androidx.recyclerview.widget.GridLayoutManager; 31 import androidx.recyclerview.widget.RecyclerView; 32 33 import com.android.car.settings.R; 34 35 import java.util.ArrayList; 36 import java.util.List; 37 38 /** 39 * Controls the content in quick setting grid view. 40 */ 41 public class QuickSettingGridAdapter 42 extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements StateChangedListener { 43 private static final int SEEKBAR_VIEWTYPE = 0; 44 private static final int TILE_VIEWTYPE = 1; 45 private final int mColumnCount; 46 private final Context mContext; 47 private final LayoutInflater mInflater; 48 private final List<Tile> mTiles = new ArrayList<>(); 49 private final List<SeekbarTile> mSeekbarTiles = new ArrayList<>(); 50 private final QsSpanSizeLookup mQsSpanSizeLookup = new QsSpanSizeLookup(); 51 QuickSettingGridAdapter(Context context)52 public QuickSettingGridAdapter(Context context) { 53 mContext = context; 54 mInflater = LayoutInflater.from(context); 55 mColumnCount = mContext.getResources().getInteger(R.integer.quick_setting_column_count); 56 } 57 getGridLayoutManager()58 GridLayoutManager getGridLayoutManager() { 59 GridLayoutManager gridLayoutManager = new GridLayoutManager(mContext, 60 mContext.getResources().getInteger(R.integer.quick_setting_column_count)); 61 gridLayoutManager.setSpanSizeLookup(mQsSpanSizeLookup); 62 return gridLayoutManager; 63 } 64 65 /** 66 * Represents an UI tile in the quick setting grid. 67 */ 68 interface Tile extends View.OnClickListener { 69 70 /** 71 * A state to indicate how we want to render icon, this is independent of what to show 72 * in text. 73 */ 74 enum State { 75 OFF, ON 76 } 77 78 /** 79 * Called when activity owning this tile's onStart() gets called. 80 */ start()81 void start(); 82 83 /** 84 * Called when activity owning this tile's onStop() gets called. 85 */ stop()86 void stop(); 87 getIcon()88 Drawable getIcon(); 89 90 @Nullable getText()91 String getText(); 92 getState()93 State getState(); 94 95 /** 96 * Returns {@code true} if this tile should be displayed. 97 */ isAvailable()98 boolean isAvailable(); 99 100 /** 101 * Returns a listener to call when this tile is clicked and held. Returns {@code null} if 102 * no action should be performed. 103 */ 104 @Nullable getOnLongClickListener()105 View.OnLongClickListener getOnLongClickListener(); 106 } 107 108 interface SeekbarTile extends SeekBar.OnSeekBarChangeListener { 109 /** 110 * Called when activity owning this tile's onStart() gets called. 111 */ start()112 void start(); 113 114 /** 115 * Called when activity owning this tile's onStop() gets called. 116 */ stop()117 void stop(); 118 getMax()119 int getMax(); 120 getCurrent()121 int getCurrent(); 122 } 123 addSeekbarTile(SeekbarTile seekbarTile)124 QuickSettingGridAdapter addSeekbarTile(SeekbarTile seekbarTile) { 125 mSeekbarTiles.add(seekbarTile); 126 return this; 127 } 128 addTile(Tile tile)129 QuickSettingGridAdapter addTile(Tile tile) { 130 if (tile.isAvailable()) { 131 mTiles.add(tile); 132 } 133 return this; 134 } 135 start()136 void start() { 137 for (SeekbarTile tile : mSeekbarTiles) { 138 tile.start(); 139 } 140 for (Tile tile : mTiles) { 141 tile.start(); 142 } 143 } 144 stop()145 void stop() { 146 for (SeekbarTile tile : mSeekbarTiles) { 147 tile.stop(); 148 } 149 for (Tile tile : mTiles) { 150 tile.stop(); 151 } 152 } 153 154 @Override onCreateViewHolder(ViewGroup parent, int viewType)155 public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 156 switch (viewType) { 157 case SEEKBAR_VIEWTYPE: 158 return new BrightnessViewHolder(mInflater.inflate( 159 R.layout.brightness_tile, parent, /* attachToRoot= */ false)); 160 case TILE_VIEWTYPE: 161 return new TileViewHolder(mInflater.inflate( 162 R.layout.tile, parent, /* attachToRoot= */ false)); 163 default: 164 throw new RuntimeException("unknown viewType: " + viewType); 165 } 166 } 167 168 @Override onBindViewHolder(RecyclerView.ViewHolder holder, int position)169 public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 170 switch (holder.getItemViewType()) { 171 case SEEKBAR_VIEWTYPE: 172 SeekbarTile seekbarTile = mSeekbarTiles.get(position); 173 SeekBar seekbar = ((BrightnessViewHolder) holder).mSeekBar; 174 seekbar.setMax(seekbarTile.getMax()); 175 seekbar.setProgress(seekbarTile.getCurrent()); 176 seekbar.setOnSeekBarChangeListener(seekbarTile); 177 break; 178 case TILE_VIEWTYPE: 179 Tile tile = mTiles.get(position - mSeekbarTiles.size()); 180 TileViewHolder vh = (TileViewHolder) holder; 181 vh.itemView.setOnClickListener(tile); 182 View.OnLongClickListener onLongClickListener = tile.getOnLongClickListener(); 183 if (onLongClickListener != null) { 184 vh.itemView.setOnLongClickListener(onLongClickListener); 185 } else { 186 vh.itemView.setOnLongClickListener(null); 187 } 188 vh.mIcon.setImageDrawable(tile.getIcon()); 189 switch (tile.getState()) { 190 case ON: 191 vh.mIcon.setEnabled(true); 192 vh.mIconBackground.setEnabled(true); 193 break; 194 case OFF: 195 vh.mIcon.setEnabled(false); 196 vh.mIconBackground.setEnabled(false); 197 break; 198 default: 199 } 200 String textString = tile.getText(); 201 if (!TextUtils.isEmpty(textString)) { 202 vh.mText.setText(textString); 203 } 204 break; 205 default: 206 } 207 } 208 209 private class BrightnessViewHolder extends RecyclerView.ViewHolder { 210 private final SeekBar mSeekBar; 211 BrightnessViewHolder(View view)212 BrightnessViewHolder(View view) { 213 super(view); 214 mSeekBar = (SeekBar) view.findViewById(R.id.seekbar); 215 } 216 } 217 218 private class TileViewHolder extends RecyclerView.ViewHolder { 219 private final View mIconBackground; 220 private final ImageView mIcon; 221 private final TextView mText; 222 TileViewHolder(View view)223 TileViewHolder(View view) { 224 super(view); 225 mIconBackground = view.findViewById(R.id.icon_background); 226 mIcon = view.findViewById(R.id.tile_icon); 227 mText = view.findViewById(R.id.tile_text); 228 } 229 } 230 231 class QsSpanSizeLookup extends GridLayoutManager.SpanSizeLookup { 232 233 /** 234 * Each list item takes a full row, and each tile takes only 1 span. 235 */ 236 @Override getSpanSize(int position)237 public int getSpanSize(int position) { 238 return position < mSeekbarTiles.size() ? mColumnCount : 1; 239 } 240 241 @Override getSpanIndex(int position, int spanCount)242 public int getSpanIndex(int position, int spanCount) { 243 return position < mSeekbarTiles.size() 244 ? 1 : (position - mSeekbarTiles.size()) % mColumnCount; 245 } 246 } 247 248 @Override getItemViewType(int position)249 public int getItemViewType(int position) { 250 return position < mSeekbarTiles.size() ? SEEKBAR_VIEWTYPE : TILE_VIEWTYPE; 251 } 252 253 @Override getItemCount()254 public int getItemCount() { 255 return mTiles.size() + mSeekbarTiles.size(); 256 } 257 258 @Override onStateChanged()259 public void onStateChanged() { 260 notifyDataSetChanged(); 261 } 262 } 263