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