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