blob: aba9251e4bb867ba35ee61219b5522a0ac4f0e67 [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;
20import android.support.v4.view.ViewCompat;
21import android.support.v7.widget.GridLayoutManager.SpanSizeLookup;
22import android.support.v7.widget.RecyclerView;
23import android.support.v7.widget.RecyclerView.ItemDecoration;
24import android.support.v7.widget.RecyclerView.State;
25import android.support.v7.widget.RecyclerView.ViewHolder;
26import android.support.v7.widget.helper.ItemTouchHelper;
Jason Monk5db8a412015-10-21 15:16:23 -070027import android.view.LayoutInflater;
Jason Monkb53b6c52016-02-24 17:25:49 -050028import android.view.MotionEvent;
Jason Monk5db8a412015-10-21 15:16:23 -070029import 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 Monk5db8a412015-10-21 15:16:23 -070032import com.android.systemui.R;
Jason Monk62b63a02016-02-02 15:15:31 -050033import com.android.systemui.qs.QSIconView;
34import com.android.systemui.qs.QSTileView;
35import com.android.systemui.qs.customize.TileAdapter.Holder;
36import com.android.systemui.qs.customize.TileQueryHelper.TileInfo;
37import com.android.systemui.qs.customize.TileQueryHelper.TileStateListener;
Jason Monk5db8a412015-10-21 15:16:23 -070038import com.android.systemui.statusbar.phone.QSTileHost;
Jason Monk5db8a412015-10-21 15:16:23 -070039
40import java.util.ArrayList;
Jason Monk5db8a412015-10-21 15:16:23 -070041import java.util.List;
42
Jason Monk62b63a02016-02-02 15:15:31 -050043public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileStateListener {
Jason Monk5db8a412015-10-21 15:16:23 -070044
Jason Monk62b63a02016-02-02 15:15:31 -050045 private static final long DRAG_LENGTH = 100;
46 private static final float DRAG_SCALE = 1.2f;
47 public static final long MOVE_DURATION = 150;
Jason Monk5db8a412015-10-21 15:16:23 -070048
Jason Monk62b63a02016-02-02 15:15:31 -050049 private static final int TYPE_TILE = 0;
50 private static final int TYPE_EDIT = 1;
51
Jason Monk5db8a412015-10-21 15:16:23 -070052 private final Context mContext;
53
Jason Monk62b63a02016-02-02 15:15:31 -050054 private final List<TileInfo> mTiles = new ArrayList<>();
Jason Monkb53b6c52016-02-24 17:25:49 -050055 private final ItemTouchHelper mItemTouchHelper;
Jason Monk62b63a02016-02-02 15:15:31 -050056 private int mDividerIndex;
57 private List<String> mCurrentSpecs;
58 private List<TileInfo> mOtherTiles;
59 private List<TileInfo> mAllTiles;
Jason Monk5db8a412015-10-21 15:16:23 -070060
Jason Monk62b63a02016-02-02 15:15:31 -050061 private Holder mCurrentDrag;
62
63 public TileAdapter(Context context) {
Jason Monk5db8a412015-10-21 15:16:23 -070064 mContext = context;
Jason Monkb53b6c52016-02-24 17:25:49 -050065 mItemTouchHelper = new ItemTouchHelper(mCallbacks);
Jason Monk62b63a02016-02-02 15:15:31 -050066 setHasStableIds(true);
Jason Monk5db8a412015-10-21 15:16:23 -070067 }
68
69 @Override
70 public long getItemId(int position) {
Jason Monk62b63a02016-02-02 15:15:31 -050071 return mTiles.get(position) != null ? mAllTiles.indexOf(mTiles.get(position)) : -1;
72 }
73
Jason Monkb53b6c52016-02-24 17:25:49 -050074 public ItemTouchHelper getItemTouchHelper() {
75 return mItemTouchHelper;
Jason Monk62b63a02016-02-02 15:15:31 -050076 }
77
78 public ItemDecoration getItemDecoration() {
79 return mDecoration;
80 }
81
82 public void saveSpecs(QSTileHost host) {
83 List<String> newSpecs = new ArrayList<>();
84 for (int i = 0; mTiles.get(i) != null; i++) {
85 newSpecs.add(mTiles.get(i).spec);
86 }
87 host.changeTiles(mCurrentSpecs, newSpecs);
88 setTileSpecs(newSpecs);
89 }
90
91 public void setTileSpecs(List<String> currentSpecs) {
92 mCurrentSpecs = currentSpecs;
93 recalcSpecs();
Jason Monk5db8a412015-10-21 15:16:23 -070094 }
95
96 @Override
Jason Monk62b63a02016-02-02 15:15:31 -050097 public void onTilesChanged(List<TileInfo> tiles) {
98 mAllTiles = tiles;
99 recalcSpecs();
Jason Monk5db8a412015-10-21 15:16:23 -0700100 }
101
Jason Monk62b63a02016-02-02 15:15:31 -0500102 private void recalcSpecs() {
103 if (mCurrentSpecs == null || mAllTiles == null) {
104 return;
Jason Monk5db8a412015-10-21 15:16:23 -0700105 }
Jason Monk62b63a02016-02-02 15:15:31 -0500106 mOtherTiles = new ArrayList<TileInfo>(mAllTiles);
107 mTiles.clear();
108 for (int i = 0; i < mCurrentSpecs.size(); i++) {
Jason Monkcb654cb2016-02-25 15:43:07 -0500109 final TileInfo tile = getAndRemoveOther(mCurrentSpecs.get(i));
110 if (tile != null) {
111 mTiles.add(tile);
112 }
Jason Monk5db8a412015-10-21 15:16:23 -0700113 }
Jason Monk62b63a02016-02-02 15:15:31 -0500114 mTiles.add(null);
115 mTiles.addAll(mOtherTiles);
116 mDividerIndex = mTiles.indexOf(null);
117 notifyDataSetChanged();
118 }
Jason Monk5db8a412015-10-21 15:16:23 -0700119
Jason Monk62b63a02016-02-02 15:15:31 -0500120 private TileInfo getAndRemoveOther(String s) {
121 for (int i = 0; i < mOtherTiles.size(); i++) {
122 if (mOtherTiles.get(i).spec.equals(s)) {
123 return mOtherTiles.remove(i);
Jason Monk5db8a412015-10-21 15:16:23 -0700124 }
Jason Monk62b63a02016-02-02 15:15:31 -0500125 }
126 return null;
127 }
128
129 @Override
130 public int getItemViewType(int position) {
131 if (mTiles.get(position) == null) {
132 return TYPE_EDIT;
133 }
134 return TYPE_TILE;
135 }
136
137 @Override
138 public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
139 final Context context = parent.getContext();
140 LayoutInflater inflater = LayoutInflater.from(context);
141 if (viewType == 1) {
142 return new Holder(inflater.inflate(R.layout.qs_customize_divider, parent, false));
143 }
144 FrameLayout frame = (FrameLayout) inflater.inflate(R.layout.qs_customize_tile_frame, parent,
145 false);
146 frame.addView(new QSTileView(context, new QSIconView(context)));
147 return new Holder(frame);
148 }
149
150 @Override
151 public int getItemCount() {
152 return mTiles.size();
153 }
154
155 @Override
Jason Monkb53b6c52016-02-24 17:25:49 -0500156 public void onBindViewHolder(final Holder holder, int position) {
Jason Monk62b63a02016-02-02 15:15:31 -0500157 if (holder.getItemViewType() == TYPE_EDIT) return;
158
159 TileInfo info = mTiles.get(position);
160 holder.mTileView.onStateChanged(info.state);
161 }
162
163 public SpanSizeLookup getSizeLookup() {
164 return mSizeLookup;
165 }
166
167 public class Holder extends ViewHolder {
168 private QSTileView mTileView;
169
170 public Holder(View itemView) {
171 super(itemView);
172 if (itemView instanceof FrameLayout) {
173 mTileView = (QSTileView) ((FrameLayout) itemView).getChildAt(0);
Jason Monk04fd2492016-02-29 14:29:26 -0500174 mTileView.setBackground(null);
Jason Monk1aec93f2016-03-01 09:39:30 -0500175 mTileView.getIcon().disableAnimation();
Jason Monk5db8a412015-10-21 15:16:23 -0700176 }
Jason Monk5db8a412015-10-21 15:16:23 -0700177 }
178
Jason Monk62b63a02016-02-02 15:15:31 -0500179 public void startDrag() {
180 itemView.animate()
181 .setDuration(DRAG_LENGTH)
182 .scaleX(DRAG_SCALE)
183 .scaleY(DRAG_SCALE);
184 mTileView.findViewById(R.id.tile_label).animate()
185 .setDuration(DRAG_LENGTH)
186 .alpha(0);
187 }
188
189 public void stopDrag() {
190 itemView.animate()
191 .setDuration(DRAG_LENGTH)
192 .scaleX(1)
193 .scaleY(1);
194 mTileView.findViewById(R.id.tile_label).animate()
195 .setDuration(DRAG_LENGTH)
196 .alpha(1);
Jason Monk5db8a412015-10-21 15:16:23 -0700197 }
198 }
199
Jason Monk62b63a02016-02-02 15:15:31 -0500200 private final SpanSizeLookup mSizeLookup = new SpanSizeLookup() {
Jason Monk5db8a412015-10-21 15:16:23 -0700201 @Override
Jason Monk62b63a02016-02-02 15:15:31 -0500202 public int getSpanSize(int position) {
203 return getItemViewType(position) == TYPE_EDIT ? 3 : 1;
204 }
205 };
206
207 private final ItemDecoration mDecoration = new ItemDecoration() {
208 // TODO: Move this to resource.
209 private final ColorDrawable mDrawable = new ColorDrawable(0xff384248);
210
211 @Override
212 public void onDraw(Canvas c, RecyclerView parent, State state) {
213 super.onDraw(c, parent, state);
214
215 final int childCount = parent.getChildCount();
216 final int width = parent.getWidth();
217 final int bottom = parent.getBottom();
218 for (int i = 0; i < childCount; i++) {
219 final View child = parent.getChildAt(i);
220 final ViewHolder holder = parent.getChildViewHolder(child);
221 if (holder.getAdapterPosition() < mDividerIndex) {
Jason Monk5db8a412015-10-21 15:16:23 -0700222 continue;
223 }
Jason Monk62b63a02016-02-02 15:15:31 -0500224
225 final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
226 .getLayoutParams();
227 final int top = child.getTop() + params.topMargin +
228 Math.round(ViewCompat.getTranslationY(child));
229 // Draw full width, in case there aren't tiles all the way across.
230 mDrawable.setBounds(0, top, width, bottom);
231 mDrawable.draw(c);
232 break;
Jason Monk5db8a412015-10-21 15:16:23 -0700233 }
Jason Monk62b63a02016-02-02 15:15:31 -0500234 }
235 };
236
237 private final ItemTouchHelper.Callback mCallbacks = new ItemTouchHelper.Callback() {
238
239 @Override
240 public boolean isLongPressDragEnabled() {
241 return true;
Jason Monk5db8a412015-10-21 15:16:23 -0700242 }
243
244 @Override
Jason Monk62b63a02016-02-02 15:15:31 -0500245 public boolean isItemViewSwipeEnabled() {
246 return false;
Jason Monk5db8a412015-10-21 15:16:23 -0700247 }
Jason Monk5db8a412015-10-21 15:16:23 -0700248
Jason Monk62b63a02016-02-02 15:15:31 -0500249 @Override
250 public void onSelectedChanged(ViewHolder viewHolder, int actionState) {
251 super.onSelectedChanged(viewHolder, actionState);
252 if (mCurrentDrag != null) {
253 mCurrentDrag.stopDrag();
254 }
255 if (viewHolder != null) {
256 mCurrentDrag = (Holder) viewHolder;
257 mCurrentDrag.startDrag();
258 }
259 }
260
261 @Override
262 public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) {
263 if (viewHolder.getItemViewType() == TYPE_EDIT) {
264 return makeMovementFlags(0, 0);
265 }
266 int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.RIGHT
267 | ItemTouchHelper.LEFT;
268 return makeMovementFlags(dragFlags, 0);
269 }
270
271 @Override
272 public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder, ViewHolder target) {
273 int from = viewHolder.getAdapterPosition();
274 int to = target.getAdapterPosition();
275 if (to > mDividerIndex) {
Jason Monkb53b6c52016-02-24 17:25:49 -0500276 if (from >= mDividerIndex) {
Jason Monk62b63a02016-02-02 15:15:31 -0500277 return false;
278 }
279 }
280 if (target.getItemViewType() == TYPE_EDIT && from < mDividerIndex) {
281 to++;
282 }
283 move(from, to, mTiles);
284 mDividerIndex = mTiles.indexOf(null);
285 notifyItemMoved(from, to);
286 return true;
287 }
288
289 private <T> void move(int from, int to, List<T> list) {
290 list.add(from > to ? to : to + 1, list.get(from));
291 list.remove(from > to ? from + 1 : from);
292 }
293
294 @Override
295 public void onSwiped(ViewHolder viewHolder, int direction) {
296 }
297 };
Jason Monk5db8a412015-10-21 15:16:23 -0700298}