blob: 03c2a0bc505aaafeebbe5a94b0dba451d2235d38 [file] [log] [blame]
Jason Monk5db8a412015-10-21 15:16:23 -07001/*
Jason Monk62b63a02016-02-02 15:15:31 -05002 * Copyright (C) 2016 The Android Open Source Project
Jason Monk5db8a412015-10-21 15:16:23 -07003 *
Jason Monk62b63a02016-02-02 15:15:31 -05004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5 * except in compliance with the License. You may obtain a copy of the License at
Jason Monk5db8a412015-10-21 15:16:23 -07006 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
Jason Monk62b63a02016-02-02 15:15:31 -05009 * Unless required by applicable law or agreed to in writing, software distributed under the
10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11 * KIND, either express or implied. See the License for the specific language governing
12 * permissions and limitations under the License.
Jason Monk5db8a412015-10-21 15:16:23 -070013 */
14
15package com.android.systemui.qs.customize;
16
Jason Monk5db8a412015-10-21 15:16:23 -070017import android.content.Context;
Jason Monk62b63a02016-02-02 15:15:31 -050018import android.graphics.Canvas;
19import android.graphics.drawable.ColorDrawable;
Jason Monk1c2fea82016-03-11 11:33:36 -050020import android.os.Handler;
Jason Monk62b63a02016-02-02 15:15:31 -050021import android.support.v4.view.ViewCompat;
22import android.support.v7.widget.GridLayoutManager.SpanSizeLookup;
23import android.support.v7.widget.RecyclerView;
24import android.support.v7.widget.RecyclerView.ItemDecoration;
25import android.support.v7.widget.RecyclerView.State;
26import android.support.v7.widget.RecyclerView.ViewHolder;
27import android.support.v7.widget.helper.ItemTouchHelper;
Jason Monk5db8a412015-10-21 15:16:23 -070028import android.view.LayoutInflater;
29import android.view.View;
Jason Monk5db8a412015-10-21 15:16:23 -070030import android.view.ViewGroup;
Jason Monk62b63a02016-02-02 15:15:31 -050031import android.widget.FrameLayout;
Jason Monk24dbd512016-03-16 14:56:42 -040032import android.widget.TextView;
Jason Monk5db8a412015-10-21 15:16:23 -070033import com.android.systemui.R;
Jason Monk62b63a02016-02-02 15:15:31 -050034import com.android.systemui.qs.QSIconView;
35import com.android.systemui.qs.QSTileView;
36import com.android.systemui.qs.customize.TileAdapter.Holder;
37import com.android.systemui.qs.customize.TileQueryHelper.TileInfo;
38import com.android.systemui.qs.customize.TileQueryHelper.TileStateListener;
Jason Monk5db8a412015-10-21 15:16:23 -070039import com.android.systemui.statusbar.phone.QSTileHost;
Jason Monk5db8a412015-10-21 15:16:23 -070040
41import java.util.ArrayList;
Jason Monk5db8a412015-10-21 15:16:23 -070042import java.util.List;
43
Jason Monk62b63a02016-02-02 15:15:31 -050044public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileStateListener {
Jason Monk5db8a412015-10-21 15:16:23 -070045
Jason Monk62b63a02016-02-02 15:15:31 -050046 private static final long DRAG_LENGTH = 100;
47 private static final float DRAG_SCALE = 1.2f;
48 public static final long MOVE_DURATION = 150;
Jason Monk5db8a412015-10-21 15:16:23 -070049
Jason Monk62b63a02016-02-02 15:15:31 -050050 private static final int TYPE_TILE = 0;
51 private static final int TYPE_EDIT = 1;
52
Jason Monk5db8a412015-10-21 15:16:23 -070053 private final Context mContext;
54
Jason Monk1c2fea82016-03-11 11:33:36 -050055 private final Handler mHandler = new Handler();
Jason Monk62b63a02016-02-02 15:15:31 -050056 private final List<TileInfo> mTiles = new ArrayList<>();
Jason Monkb53b6c52016-02-24 17:25:49 -050057 private final ItemTouchHelper mItemTouchHelper;
Jason Monk62b63a02016-02-02 15:15:31 -050058 private int mDividerIndex;
59 private List<String> mCurrentSpecs;
60 private List<TileInfo> mOtherTiles;
61 private List<TileInfo> mAllTiles;
Jason Monk5db8a412015-10-21 15:16:23 -070062
Jason Monk62b63a02016-02-02 15:15:31 -050063 private Holder mCurrentDrag;
64
65 public TileAdapter(Context context) {
Jason Monk5db8a412015-10-21 15:16:23 -070066 mContext = context;
Jason Monkb53b6c52016-02-24 17:25:49 -050067 mItemTouchHelper = new ItemTouchHelper(mCallbacks);
Jason Monk62b63a02016-02-02 15:15:31 -050068 setHasStableIds(true);
Jason Monk5db8a412015-10-21 15:16:23 -070069 }
70
71 @Override
72 public long getItemId(int position) {
Jason Monk62b63a02016-02-02 15:15:31 -050073 return mTiles.get(position) != null ? mAllTiles.indexOf(mTiles.get(position)) : -1;
74 }
75
Jason Monkb53b6c52016-02-24 17:25:49 -050076 public ItemTouchHelper getItemTouchHelper() {
77 return mItemTouchHelper;
Jason Monk62b63a02016-02-02 15:15:31 -050078 }
79
80 public ItemDecoration getItemDecoration() {
81 return mDecoration;
82 }
83
84 public void saveSpecs(QSTileHost host) {
85 List<String> newSpecs = new ArrayList<>();
86 for (int i = 0; mTiles.get(i) != null; i++) {
87 newSpecs.add(mTiles.get(i).spec);
88 }
89 host.changeTiles(mCurrentSpecs, newSpecs);
90 setTileSpecs(newSpecs);
91 }
92
93 public void setTileSpecs(List<String> currentSpecs) {
94 mCurrentSpecs = currentSpecs;
95 recalcSpecs();
Jason Monk5db8a412015-10-21 15:16:23 -070096 }
97
98 @Override
Jason Monk62b63a02016-02-02 15:15:31 -050099 public void onTilesChanged(List<TileInfo> tiles) {
100 mAllTiles = tiles;
101 recalcSpecs();
Jason Monk5db8a412015-10-21 15:16:23 -0700102 }
103
Jason Monk62b63a02016-02-02 15:15:31 -0500104 private void recalcSpecs() {
105 if (mCurrentSpecs == null || mAllTiles == null) {
106 return;
Jason Monk5db8a412015-10-21 15:16:23 -0700107 }
Jason Monk62b63a02016-02-02 15:15:31 -0500108 mOtherTiles = new ArrayList<TileInfo>(mAllTiles);
109 mTiles.clear();
110 for (int i = 0; i < mCurrentSpecs.size(); i++) {
Jason Monkcb654cb2016-02-25 15:43:07 -0500111 final TileInfo tile = getAndRemoveOther(mCurrentSpecs.get(i));
112 if (tile != null) {
113 mTiles.add(tile);
114 }
Jason Monk5db8a412015-10-21 15:16:23 -0700115 }
Jason Monk62b63a02016-02-02 15:15:31 -0500116 mTiles.add(null);
117 mTiles.addAll(mOtherTiles);
118 mDividerIndex = mTiles.indexOf(null);
119 notifyDataSetChanged();
120 }
Jason Monk5db8a412015-10-21 15:16:23 -0700121
Jason Monk62b63a02016-02-02 15:15:31 -0500122 private TileInfo getAndRemoveOther(String s) {
123 for (int i = 0; i < mOtherTiles.size(); i++) {
124 if (mOtherTiles.get(i).spec.equals(s)) {
125 return mOtherTiles.remove(i);
Jason Monk5db8a412015-10-21 15:16:23 -0700126 }
Jason Monk62b63a02016-02-02 15:15:31 -0500127 }
128 return null;
129 }
130
131 @Override
132 public int getItemViewType(int position) {
133 if (mTiles.get(position) == null) {
134 return TYPE_EDIT;
135 }
136 return TYPE_TILE;
137 }
138
139 @Override
140 public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
141 final Context context = parent.getContext();
142 LayoutInflater inflater = LayoutInflater.from(context);
143 if (viewType == 1) {
144 return new Holder(inflater.inflate(R.layout.qs_customize_divider, parent, false));
145 }
146 FrameLayout frame = (FrameLayout) inflater.inflate(R.layout.qs_customize_tile_frame, parent,
147 false);
148 frame.addView(new QSTileView(context, new QSIconView(context)));
149 return new Holder(frame);
150 }
151
152 @Override
153 public int getItemCount() {
154 return mTiles.size();
155 }
156
157 @Override
Jason Monkb53b6c52016-02-24 17:25:49 -0500158 public void onBindViewHolder(final Holder holder, int position) {
Jason Monk24dbd512016-03-16 14:56:42 -0400159 if (holder.getItemViewType() == TYPE_EDIT) {
160 ((TextView) holder.itemView.findViewById(android.R.id.title)).setText(
161 mCurrentDrag != null ? R.string.drag_to_remove_tiles
162 : R.string.drag_to_add_tiles);
163 return;
164 }
Jason Monk62b63a02016-02-02 15:15:31 -0500165
166 TileInfo info = mTiles.get(position);
167 holder.mTileView.onStateChanged(info.state);
168 }
169
170 public SpanSizeLookup getSizeLookup() {
171 return mSizeLookup;
172 }
173
174 public class Holder extends ViewHolder {
175 private QSTileView mTileView;
176
177 public Holder(View itemView) {
178 super(itemView);
179 if (itemView instanceof FrameLayout) {
180 mTileView = (QSTileView) ((FrameLayout) itemView).getChildAt(0);
Jason Monk04fd2492016-02-29 14:29:26 -0500181 mTileView.setBackground(null);
Jason Monk1aec93f2016-03-01 09:39:30 -0500182 mTileView.getIcon().disableAnimation();
Jason Monk5db8a412015-10-21 15:16:23 -0700183 }
Jason Monk5db8a412015-10-21 15:16:23 -0700184 }
185
Jason Monk62b63a02016-02-02 15:15:31 -0500186 public void startDrag() {
187 itemView.animate()
188 .setDuration(DRAG_LENGTH)
189 .scaleX(DRAG_SCALE)
190 .scaleY(DRAG_SCALE);
191 mTileView.findViewById(R.id.tile_label).animate()
192 .setDuration(DRAG_LENGTH)
193 .alpha(0);
194 }
195
196 public void stopDrag() {
197 itemView.animate()
198 .setDuration(DRAG_LENGTH)
199 .scaleX(1)
200 .scaleY(1);
201 mTileView.findViewById(R.id.tile_label).animate()
202 .setDuration(DRAG_LENGTH)
203 .alpha(1);
Jason Monk5db8a412015-10-21 15:16:23 -0700204 }
205 }
206
Jason Monk62b63a02016-02-02 15:15:31 -0500207 private final SpanSizeLookup mSizeLookup = new SpanSizeLookup() {
Jason Monk5db8a412015-10-21 15:16:23 -0700208 @Override
Jason Monk62b63a02016-02-02 15:15:31 -0500209 public int getSpanSize(int position) {
210 return getItemViewType(position) == TYPE_EDIT ? 3 : 1;
211 }
212 };
213
214 private final ItemDecoration mDecoration = new ItemDecoration() {
215 // TODO: Move this to resource.
216 private final ColorDrawable mDrawable = new ColorDrawable(0xff384248);
217
218 @Override
219 public void onDraw(Canvas c, RecyclerView parent, State state) {
220 super.onDraw(c, parent, state);
221
222 final int childCount = parent.getChildCount();
223 final int width = parent.getWidth();
224 final int bottom = parent.getBottom();
225 for (int i = 0; i < childCount; i++) {
226 final View child = parent.getChildAt(i);
227 final ViewHolder holder = parent.getChildViewHolder(child);
228 if (holder.getAdapterPosition() < mDividerIndex) {
Jason Monk5db8a412015-10-21 15:16:23 -0700229 continue;
230 }
Jason Monk62b63a02016-02-02 15:15:31 -0500231
232 final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
233 .getLayoutParams();
234 final int top = child.getTop() + params.topMargin +
235 Math.round(ViewCompat.getTranslationY(child));
236 // Draw full width, in case there aren't tiles all the way across.
237 mDrawable.setBounds(0, top, width, bottom);
238 mDrawable.draw(c);
239 break;
Jason Monk5db8a412015-10-21 15:16:23 -0700240 }
Jason Monk62b63a02016-02-02 15:15:31 -0500241 }
242 };
243
244 private final ItemTouchHelper.Callback mCallbacks = new ItemTouchHelper.Callback() {
245
246 @Override
247 public boolean isLongPressDragEnabled() {
248 return true;
Jason Monk5db8a412015-10-21 15:16:23 -0700249 }
250
251 @Override
Jason Monk62b63a02016-02-02 15:15:31 -0500252 public boolean isItemViewSwipeEnabled() {
253 return false;
Jason Monk5db8a412015-10-21 15:16:23 -0700254 }
Jason Monk5db8a412015-10-21 15:16:23 -0700255
Jason Monk62b63a02016-02-02 15:15:31 -0500256 @Override
257 public void onSelectedChanged(ViewHolder viewHolder, int actionState) {
258 super.onSelectedChanged(viewHolder, actionState);
259 if (mCurrentDrag != null) {
260 mCurrentDrag.stopDrag();
Jason Monk24dbd512016-03-16 14:56:42 -0400261 mCurrentDrag = null;
Jason Monk62b63a02016-02-02 15:15:31 -0500262 }
263 if (viewHolder != null) {
264 mCurrentDrag = (Holder) viewHolder;
265 mCurrentDrag.startDrag();
266 }
Jason Monk1c2fea82016-03-11 11:33:36 -0500267 mHandler.post(new Runnable() {
268 @Override
269 public void run() {
270 notifyItemChanged(mDividerIndex);
271 }
272 });
Jason Monk62b63a02016-02-02 15:15:31 -0500273 }
274
275 @Override
276 public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) {
277 if (viewHolder.getItemViewType() == TYPE_EDIT) {
278 return makeMovementFlags(0, 0);
279 }
280 int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.RIGHT
281 | ItemTouchHelper.LEFT;
282 return makeMovementFlags(dragFlags, 0);
283 }
284
285 @Override
286 public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder, ViewHolder target) {
287 int from = viewHolder.getAdapterPosition();
288 int to = target.getAdapterPosition();
289 if (to > mDividerIndex) {
Jason Monkb53b6c52016-02-24 17:25:49 -0500290 if (from >= mDividerIndex) {
Jason Monk62b63a02016-02-02 15:15:31 -0500291 return false;
292 }
293 }
Jason Monk62b63a02016-02-02 15:15:31 -0500294 move(from, to, mTiles);
295 mDividerIndex = mTiles.indexOf(null);
296 notifyItemMoved(from, to);
297 return true;
298 }
299
300 private <T> void move(int from, int to, List<T> list) {
301 list.add(from > to ? to : to + 1, list.get(from));
302 list.remove(from > to ? from + 1 : from);
303 }
304
305 @Override
306 public void onSwiped(ViewHolder viewHolder, int direction) {
307 }
308 };
Jason Monk5db8a412015-10-21 15:16:23 -0700309}