blob: f2c3e61cfe02f633e9e5c5155dd14bdaa083b250 [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 Monk39c98e62016-03-16 09:18:35 -040017import android.app.AlertDialog;
18import android.app.AlertDialog.Builder;
Jason Monk96defbe2016-03-29 16:51:03 -040019import android.content.ComponentName;
Jason Monk5db8a412015-10-21 15:16:23 -070020import android.content.Context;
Jason Monk39c98e62016-03-16 09:18:35 -040021import android.content.DialogInterface;
Andrew Sappersteinc11df252016-06-19 19:12:11 -070022import android.content.res.TypedArray;
Jason Monk62b63a02016-02-02 15:15:31 -050023import android.graphics.Canvas;
24import android.graphics.drawable.ColorDrawable;
Jason Monk1c2fea82016-03-11 11:33:36 -050025import android.os.Handler;
Jason Monk62b63a02016-02-02 15:15:31 -050026import android.support.v4.view.ViewCompat;
27import android.support.v7.widget.GridLayoutManager.SpanSizeLookup;
28import android.support.v7.widget.RecyclerView;
29import android.support.v7.widget.RecyclerView.ItemDecoration;
30import android.support.v7.widget.RecyclerView.State;
31import android.support.v7.widget.RecyclerView.ViewHolder;
32import android.support.v7.widget.helper.ItemTouchHelper;
Jason Monk5db8a412015-10-21 15:16:23 -070033import android.view.LayoutInflater;
34import android.view.View;
Jason Monk39c98e62016-03-16 09:18:35 -040035import android.view.View.OnClickListener;
36import android.view.View.OnLayoutChangeListener;
Jason Monk5db8a412015-10-21 15:16:23 -070037import android.view.ViewGroup;
Jason Monk39c98e62016-03-16 09:18:35 -040038import android.view.accessibility.AccessibilityManager;
Jason Monk62b63a02016-02-02 15:15:31 -050039import android.widget.FrameLayout;
Jason Monk24dbd512016-03-16 14:56:42 -040040import android.widget.TextView;
Jason Monk0c0df792016-06-17 13:15:34 -040041
Jason Monk96defbe2016-03-29 16:51:03 -040042import com.android.internal.logging.MetricsLogger;
Tamas Berghammercbd3f0c2016-06-22 15:21:38 +010043import com.android.internal.logging.nano.MetricsProto;
Jason Monk5db8a412015-10-21 15:16:23 -070044import com.android.systemui.R;
Jason Monk62b63a02016-02-02 15:15:31 -050045import com.android.systemui.qs.QSIconView;
Jason Monk62b63a02016-02-02 15:15:31 -050046import com.android.systemui.qs.customize.TileAdapter.Holder;
47import com.android.systemui.qs.customize.TileQueryHelper.TileInfo;
48import com.android.systemui.qs.customize.TileQueryHelper.TileStateListener;
Jason Monk96defbe2016-03-29 16:51:03 -040049import com.android.systemui.qs.external.CustomTile;
Jason Monk5db8a412015-10-21 15:16:23 -070050import com.android.systemui.statusbar.phone.QSTileHost;
Jason Monk39c98e62016-03-16 09:18:35 -040051import com.android.systemui.statusbar.phone.SystemUIDialog;
Jason Monk5db8a412015-10-21 15:16:23 -070052
53import java.util.ArrayList;
Jason Monk5db8a412015-10-21 15:16:23 -070054import java.util.List;
55
Jason Monk62b63a02016-02-02 15:15:31 -050056public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileStateListener {
Jason Monk5db8a412015-10-21 15:16:23 -070057
Jason Monk62b63a02016-02-02 15:15:31 -050058 private static final long DRAG_LENGTH = 100;
59 private static final float DRAG_SCALE = 1.2f;
60 public static final long MOVE_DURATION = 150;
Jason Monk5db8a412015-10-21 15:16:23 -070061
Jason Monk62b63a02016-02-02 15:15:31 -050062 private static final int TYPE_TILE = 0;
63 private static final int TYPE_EDIT = 1;
Jason Monk39c98e62016-03-16 09:18:35 -040064 private static final int TYPE_ACCESSIBLE_DROP = 2;
Jason Monk0b349ad2016-04-04 11:18:03 -040065 private static final int TYPE_DIVIDER = 4;
66
67 private static final long EDIT_ID = 10000;
68 private static final long DIVIDER_ID = 20000;
Jason Monk62b63a02016-02-02 15:15:31 -050069
Jason Monk5db8a412015-10-21 15:16:23 -070070 private final Context mContext;
71
Jason Monk1c2fea82016-03-11 11:33:36 -050072 private final Handler mHandler = new Handler();
Jason Monk62b63a02016-02-02 15:15:31 -050073 private final List<TileInfo> mTiles = new ArrayList<>();
Jason Monkb53b6c52016-02-24 17:25:49 -050074 private final ItemTouchHelper mItemTouchHelper;
Andrew Sappersteinc11df252016-06-19 19:12:11 -070075 private final ItemDecoration mDecoration;
Jason Monk39c98e62016-03-16 09:18:35 -040076 private final AccessibilityManager mAccessibilityManager;
Jason Monk0b349ad2016-04-04 11:18:03 -040077 private int mEditIndex;
78 private int mTileDividerIndex;
Jason Monk39c98e62016-03-16 09:18:35 -040079 private boolean mNeedsFocus;
Jason Monk62b63a02016-02-02 15:15:31 -050080 private List<String> mCurrentSpecs;
81 private List<TileInfo> mOtherTiles;
82 private List<TileInfo> mAllTiles;
Jason Monk5db8a412015-10-21 15:16:23 -070083
Jason Monk62b63a02016-02-02 15:15:31 -050084 private Holder mCurrentDrag;
Jason Monk39c98e62016-03-16 09:18:35 -040085 private boolean mAccessibilityMoving;
86 private int mAccessibilityFromIndex;
Jason Monk63554ed2016-04-29 14:58:29 -040087 private QSTileHost mHost;
Jason Monk62b63a02016-02-02 15:15:31 -050088
89 public TileAdapter(Context context) {
Jason Monk5db8a412015-10-21 15:16:23 -070090 mContext = context;
Jason Monk39c98e62016-03-16 09:18:35 -040091 mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
Jason Monkb53b6c52016-02-24 17:25:49 -050092 mItemTouchHelper = new ItemTouchHelper(mCallbacks);
Andrew Sappersteinc11df252016-06-19 19:12:11 -070093 mDecoration = new TileItemDecoration(context);
Jason Monk5db8a412015-10-21 15:16:23 -070094 }
95
Jason Monk63554ed2016-04-29 14:58:29 -040096 public void setHost(QSTileHost host) {
97 mHost = host;
98 }
99
Jason Monkb53b6c52016-02-24 17:25:49 -0500100 public ItemTouchHelper getItemTouchHelper() {
101 return mItemTouchHelper;
Jason Monk62b63a02016-02-02 15:15:31 -0500102 }
103
104 public ItemDecoration getItemDecoration() {
105 return mDecoration;
106 }
107
108 public void saveSpecs(QSTileHost host) {
109 List<String> newSpecs = new ArrayList<>();
Jason Monk89e6f012016-04-06 14:32:54 -0400110 for (int i = 0; i < mTiles.size() && mTiles.get(i) != null; i++) {
Jason Monk62b63a02016-02-02 15:15:31 -0500111 newSpecs.add(mTiles.get(i).spec);
112 }
113 host.changeTiles(mCurrentSpecs, newSpecs);
Jason Monk63554ed2016-04-29 14:58:29 -0400114 mCurrentSpecs = newSpecs;
Jason Monk62b63a02016-02-02 15:15:31 -0500115 }
116
117 public void setTileSpecs(List<String> currentSpecs) {
Jason Monkb213ac42016-06-20 10:27:27 -0400118 if (currentSpecs.equals(mCurrentSpecs)) {
119 return;
120 }
Jason Monk62b63a02016-02-02 15:15:31 -0500121 mCurrentSpecs = currentSpecs;
122 recalcSpecs();
Jason Monk5db8a412015-10-21 15:16:23 -0700123 }
124
125 @Override
Jason Monk62b63a02016-02-02 15:15:31 -0500126 public void onTilesChanged(List<TileInfo> tiles) {
127 mAllTiles = tiles;
128 recalcSpecs();
Jason Monk5db8a412015-10-21 15:16:23 -0700129 }
130
Jason Monk62b63a02016-02-02 15:15:31 -0500131 private void recalcSpecs() {
132 if (mCurrentSpecs == null || mAllTiles == null) {
133 return;
Jason Monk5db8a412015-10-21 15:16:23 -0700134 }
Jason Monk62b63a02016-02-02 15:15:31 -0500135 mOtherTiles = new ArrayList<TileInfo>(mAllTiles);
136 mTiles.clear();
137 for (int i = 0; i < mCurrentSpecs.size(); i++) {
Jason Monkcb654cb2016-02-25 15:43:07 -0500138 final TileInfo tile = getAndRemoveOther(mCurrentSpecs.get(i));
139 if (tile != null) {
140 mTiles.add(tile);
141 }
Jason Monk5db8a412015-10-21 15:16:23 -0700142 }
Jason Monk62b63a02016-02-02 15:15:31 -0500143 mTiles.add(null);
Jason Monk0b349ad2016-04-04 11:18:03 -0400144 for (int i = 0; i < mOtherTiles.size(); i++) {
145 final TileInfo tile = mOtherTiles.get(i);
146 if (tile.isSystem) {
147 mOtherTiles.remove(i--);
148 mTiles.add(tile);
149 }
150 }
Jason Monk17d03992016-05-03 11:37:49 -0400151 mTileDividerIndex = mTiles.size();
Jason Monkdd036592016-05-19 13:59:41 -0400152 mTiles.add(null);
Jason Monk62b63a02016-02-02 15:15:31 -0500153 mTiles.addAll(mOtherTiles);
Jason Monkdd036592016-05-19 13:59:41 -0400154 updateDividerLocations();
Jason Monk62b63a02016-02-02 15:15:31 -0500155 notifyDataSetChanged();
156 }
Jason Monk5db8a412015-10-21 15:16:23 -0700157
Jason Monk62b63a02016-02-02 15:15:31 -0500158 private TileInfo getAndRemoveOther(String s) {
159 for (int i = 0; i < mOtherTiles.size(); i++) {
160 if (mOtherTiles.get(i).spec.equals(s)) {
161 return mOtherTiles.remove(i);
Jason Monk5db8a412015-10-21 15:16:23 -0700162 }
Jason Monk62b63a02016-02-02 15:15:31 -0500163 }
164 return null;
165 }
166
167 @Override
168 public int getItemViewType(int position) {
Jason Monk0b349ad2016-04-04 11:18:03 -0400169 if (mAccessibilityMoving && position == mEditIndex - 1) {
Jason Monk39c98e62016-03-16 09:18:35 -0400170 return TYPE_ACCESSIBLE_DROP;
171 }
Jason Monk0b349ad2016-04-04 11:18:03 -0400172 if (position == mTileDividerIndex) {
173 return TYPE_DIVIDER;
174 }
Jason Monk62b63a02016-02-02 15:15:31 -0500175 if (mTiles.get(position) == null) {
176 return TYPE_EDIT;
177 }
178 return TYPE_TILE;
179 }
180
181 @Override
182 public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
183 final Context context = parent.getContext();
184 LayoutInflater inflater = LayoutInflater.from(context);
Jason Monk0b349ad2016-04-04 11:18:03 -0400185 if (viewType == TYPE_DIVIDER) {
186 return new Holder(inflater.inflate(R.layout.qs_customize_tile_divider, parent, false));
187 }
Jason Monk39c98e62016-03-16 09:18:35 -0400188 if (viewType == TYPE_EDIT) {
Jason Monk62b63a02016-02-02 15:15:31 -0500189 return new Holder(inflater.inflate(R.layout.qs_customize_divider, parent, false));
190 }
191 FrameLayout frame = (FrameLayout) inflater.inflate(R.layout.qs_customize_tile_frame, parent,
192 false);
Jason Monk0b349ad2016-04-04 11:18:03 -0400193 frame.addView(new CustomizeTileView(context, new QSIconView(context)));
Jason Monk62b63a02016-02-02 15:15:31 -0500194 return new Holder(frame);
195 }
196
197 @Override
198 public int getItemCount() {
199 return mTiles.size();
200 }
201
202 @Override
Jason Monk17d03992016-05-03 11:37:49 -0400203 public boolean onFailedToRecycleView(Holder holder) {
204 holder.clearDrag();
205 return true;
206 }
207
208 @Override
209 public void onBindViewHolder(final Holder holder, int position) {
Jason Monk0b349ad2016-04-04 11:18:03 -0400210 if (holder.getItemViewType() == TYPE_DIVIDER) {
Jason Monkdd036592016-05-19 13:59:41 -0400211 holder.itemView.setVisibility(mTileDividerIndex < mTiles.size() - 1 ? View.VISIBLE
212 : View.INVISIBLE);
Jason Monk0b349ad2016-04-04 11:18:03 -0400213 return;
214 }
Jason Monk24dbd512016-03-16 14:56:42 -0400215 if (holder.getItemViewType() == TYPE_EDIT) {
216 ((TextView) holder.itemView.findViewById(android.R.id.title)).setText(
217 mCurrentDrag != null ? R.string.drag_to_remove_tiles
218 : R.string.drag_to_add_tiles);
219 return;
220 }
Jason Monk39c98e62016-03-16 09:18:35 -0400221 if (holder.getItemViewType() == TYPE_ACCESSIBLE_DROP) {
222 holder.mTileView.setClickable(true);
223 holder.mTileView.setFocusable(true);
224 holder.mTileView.setFocusableInTouchMode(true);
225 holder.mTileView.setVisibility(View.VISIBLE);
226 holder.mTileView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
227 holder.mTileView.setContentDescription(mContext.getString(
228 R.string.accessibility_qs_edit_position_label, position + 1));
229 holder.mTileView.setOnClickListener(new OnClickListener() {
230 @Override
231 public void onClick(View v) {
Jason Monk17d03992016-05-03 11:37:49 -0400232 selectPosition(holder.getAdapterPosition(), v);
Jason Monk39c98e62016-03-16 09:18:35 -0400233 }
234 });
235 if (mNeedsFocus) {
236 // Wait for this to get laid out then set its focus.
Jason Monk0c0df792016-06-17 13:15:34 -0400237 // Ensure that tile gets laid out so we get the callback.
238 holder.mTileView.requestLayout();
Jason Monk39c98e62016-03-16 09:18:35 -0400239 holder.mTileView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
240 @Override
241 public void onLayoutChange(View v, int left, int top, int right, int bottom,
242 int oldLeft, int oldTop, int oldRight, int oldBottom) {
243 holder.mTileView.removeOnLayoutChangeListener(this);
244 holder.mTileView.requestFocus();
245 }
246 });
247 mNeedsFocus = false;
248 }
249 return;
250 }
Jason Monk62b63a02016-02-02 15:15:31 -0500251
252 TileInfo info = mTiles.get(position);
Jason Monk39c98e62016-03-16 09:18:35 -0400253
Jason Monk0b349ad2016-04-04 11:18:03 -0400254 if (position > mEditIndex) {
Jason Monk39c98e62016-03-16 09:18:35 -0400255 info.state.contentDescription = mContext.getString(
256 R.string.accessibility_qs_edit_add_tile_label, info.state.label);
257 } else if (mAccessibilityMoving) {
258 info.state.contentDescription = mContext.getString(
259 R.string.accessibility_qs_edit_position_label, position + 1);
260 } else {
261 info.state.contentDescription = mContext.getString(
262 R.string.accessibility_qs_edit_tile_label, position + 1, info.state.label);
263 }
Jason Monk62b63a02016-02-02 15:15:31 -0500264 holder.mTileView.onStateChanged(info.state);
Jason Monk0b349ad2016-04-04 11:18:03 -0400265 holder.mTileView.setAppLabel(info.appLabel);
Jason Monkb213ac42016-06-20 10:27:27 -0400266 holder.mTileView.setShowAppLabel(position > mEditIndex && !info.isSystem);
Jason Monk39c98e62016-03-16 09:18:35 -0400267
268 if (mAccessibilityManager.isTouchExplorationEnabled()) {
Jason Monk0b349ad2016-04-04 11:18:03 -0400269 final boolean selectable = !mAccessibilityMoving || position < mEditIndex;
Jason Monk39c98e62016-03-16 09:18:35 -0400270 holder.mTileView.setClickable(selectable);
271 holder.mTileView.setFocusable(selectable);
272 holder.mTileView.setImportantForAccessibility(selectable
273 ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
274 : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
275 if (selectable) {
276 holder.mTileView.setOnClickListener(new OnClickListener() {
277 @Override
278 public void onClick(View v) {
Jason Monk17d03992016-05-03 11:37:49 -0400279 int position = holder.getAdapterPosition();
Jason Monk39c98e62016-03-16 09:18:35 -0400280 if (mAccessibilityMoving) {
281 selectPosition(position, v);
282 } else {
Jason Monk0b349ad2016-04-04 11:18:03 -0400283 if (position < mEditIndex) {
Jason Monk39c98e62016-03-16 09:18:35 -0400284 showAccessibilityDialog(position, v);
285 } else {
286 startAccessibleDrag(position);
287 }
288 }
289 }
290 });
291 }
292 }
293 }
294
295 private void selectPosition(int position, View v) {
296 // Remove the placeholder.
Jason Monk39c98e62016-03-16 09:18:35 -0400297 mAccessibilityMoving = false;
Jason Monkad5db292016-05-25 10:54:06 -0400298 mTiles.remove(mEditIndex--);
Jason Monka466d162016-05-04 15:10:01 -0400299 notifyItemRemoved(mEditIndex - 1);
Jason Monkc8969632016-08-05 10:08:11 -0400300 // Don't remove items when the last position is selected.
301 if (position == mEditIndex) position--;
302
Jason Monkad5db292016-05-25 10:54:06 -0400303 move(mAccessibilityFromIndex, position, v);
Jason Monkad5db292016-05-25 10:54:06 -0400304 notifyDataSetChanged();
Jason Monk39c98e62016-03-16 09:18:35 -0400305 }
306
307 private void showAccessibilityDialog(final int position, final View v) {
Jason Monkb213ac42016-06-20 10:27:27 -0400308 final TileInfo info = mTiles.get(position);
Jason Monk39c98e62016-03-16 09:18:35 -0400309 CharSequence[] options = new CharSequence[] {
310 mContext.getString(R.string.accessibility_qs_edit_move_tile, info.state.label),
311 mContext.getString(R.string.accessibility_qs_edit_remove_tile, info.state.label),
312 };
313 AlertDialog dialog = new Builder(mContext)
314 .setItems(options, new DialogInterface.OnClickListener() {
315 @Override
316 public void onClick(DialogInterface dialog, int which) {
317 if (which == 0) {
318 startAccessibleDrag(position);
319 } else {
Jason Monkb213ac42016-06-20 10:27:27 -0400320 move(position, info.isSystem ? mEditIndex : mTileDividerIndex, v);
321 notifyItemChanged(mTileDividerIndex);
322 notifyDataSetChanged();
Jason Monk39c98e62016-03-16 09:18:35 -0400323 }
324 }
325 }).setNegativeButton(android.R.string.cancel, null)
326 .create();
327 SystemUIDialog.setShowForAllUsers(dialog, true);
328 SystemUIDialog.applyFlags(dialog);
329 dialog.show();
330 }
331
332 private void startAccessibleDrag(int position) {
333 mAccessibilityMoving = true;
334 mNeedsFocus = true;
335 mAccessibilityFromIndex = position;
336 // Add placeholder for last slot.
Jason Monk0b349ad2016-04-04 11:18:03 -0400337 mTiles.add(mEditIndex++, null);
Jason Monka466d162016-05-04 15:10:01 -0400338 notifyDataSetChanged();
Jason Monk62b63a02016-02-02 15:15:31 -0500339 }
340
341 public SpanSizeLookup getSizeLookup() {
342 return mSizeLookup;
343 }
344
Jason Monk39c98e62016-03-16 09:18:35 -0400345 private boolean move(int from, int to, View v) {
Jason Monkb213ac42016-06-20 10:27:27 -0400346 if (to == from) {
347 return true;
Jason Monk39c98e62016-03-16 09:18:35 -0400348 }
349 CharSequence fromLabel = mTiles.get(from).state.label;
350 move(from, to, mTiles);
Jason Monk0b349ad2016-04-04 11:18:03 -0400351 updateDividerLocations();
Jason Monk39c98e62016-03-16 09:18:35 -0400352 CharSequence announcement;
Jason Monk0b349ad2016-04-04 11:18:03 -0400353 if (to >= mEditIndex) {
Jason Monk96defbe2016-03-29 16:51:03 -0400354 MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_REMOVE_SPEC,
355 strip(mTiles.get(to)));
356 MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_REMOVE,
357 from);
Jason Monk39c98e62016-03-16 09:18:35 -0400358 announcement = mContext.getString(R.string.accessibility_qs_edit_tile_removed,
359 fromLabel);
Jason Monk0b349ad2016-04-04 11:18:03 -0400360 } else if (from >= mEditIndex) {
Jason Monk96defbe2016-03-29 16:51:03 -0400361 MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_ADD_SPEC,
362 strip(mTiles.get(to)));
363 MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_ADD,
364 to);
Jason Monk39c98e62016-03-16 09:18:35 -0400365 announcement = mContext.getString(R.string.accessibility_qs_edit_tile_added,
366 fromLabel, (to + 1));
367 } else {
Jason Monk96defbe2016-03-29 16:51:03 -0400368 MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_MOVE_SPEC,
369 strip(mTiles.get(to)));
370 MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_QS_EDIT_MOVE,
371 to);
Jason Monk39c98e62016-03-16 09:18:35 -0400372 announcement = mContext.getString(R.string.accessibility_qs_edit_tile_moved,
373 fromLabel, (to + 1));
374 }
375 v.announceForAccessibility(announcement);
Jason Monk63554ed2016-04-29 14:58:29 -0400376 saveSpecs(mHost);
Jason Monk39c98e62016-03-16 09:18:35 -0400377 return true;
378 }
379
Jason Monk0b349ad2016-04-04 11:18:03 -0400380 private void updateDividerLocations() {
381 // The first null is the edit tiles label, the second null is the tile divider.
382 // If there is no second null, then there are no non-system tiles.
383 mEditIndex = -1;
384 mTileDividerIndex = mTiles.size();
385 for (int i = 0; i < mTiles.size(); i++) {
386 if (mTiles.get(i) == null) {
387 if (mEditIndex == -1) {
388 mEditIndex = i;
389 } else {
390 mTileDividerIndex = i;
391 }
392 }
393 }
Jason Monk42317ea2016-04-14 10:53:42 -0400394 if (mTiles.size() - 1 == mTileDividerIndex) {
Jason Monkdd036592016-05-19 13:59:41 -0400395 notifyItemChanged(mTileDividerIndex);
Jason Monk0b349ad2016-04-04 11:18:03 -0400396 }
397 }
398
Jason Monkdd036592016-05-19 13:59:41 -0400399 private static String strip(TileInfo tileInfo) {
Jason Monk96defbe2016-03-29 16:51:03 -0400400 String spec = tileInfo.spec;
401 if (spec.startsWith(CustomTile.PREFIX)) {
402 ComponentName component = CustomTile.getComponentFromSpec(spec);
403 return component.getPackageName();
404 }
405 return spec;
406 }
407
Jason Monk39c98e62016-03-16 09:18:35 -0400408 private <T> void move(int from, int to, List<T> list) {
Jason Monkdd036592016-05-19 13:59:41 -0400409 list.add(to, list.remove(from));
Jason Monk17d03992016-05-03 11:37:49 -0400410 notifyItemMoved(from, to);
Jason Monk39c98e62016-03-16 09:18:35 -0400411 }
412
Jason Monk62b63a02016-02-02 15:15:31 -0500413 public class Holder extends ViewHolder {
Jason Monk0b349ad2016-04-04 11:18:03 -0400414 private CustomizeTileView mTileView;
Jason Monk62b63a02016-02-02 15:15:31 -0500415
416 public Holder(View itemView) {
417 super(itemView);
418 if (itemView instanceof FrameLayout) {
Jason Monk0b349ad2016-04-04 11:18:03 -0400419 mTileView = (CustomizeTileView) ((FrameLayout) itemView).getChildAt(0);
Jason Monk04fd2492016-02-29 14:29:26 -0500420 mTileView.setBackground(null);
Jason Monk1aec93f2016-03-01 09:39:30 -0500421 mTileView.getIcon().disableAnimation();
Jason Monk5db8a412015-10-21 15:16:23 -0700422 }
Jason Monk5db8a412015-10-21 15:16:23 -0700423 }
424
Jason Monk17d03992016-05-03 11:37:49 -0400425 public void clearDrag() {
426 itemView.clearAnimation();
427 mTileView.findViewById(R.id.tile_label).clearAnimation();
428 mTileView.findViewById(R.id.tile_label).setAlpha(1);
429 mTileView.getAppLabel().clearAnimation();
430 mTileView.getAppLabel().setAlpha(.6f);
431 }
432
Jason Monk62b63a02016-02-02 15:15:31 -0500433 public void startDrag() {
434 itemView.animate()
435 .setDuration(DRAG_LENGTH)
436 .scaleX(DRAG_SCALE)
437 .scaleY(DRAG_SCALE);
438 mTileView.findViewById(R.id.tile_label).animate()
439 .setDuration(DRAG_LENGTH)
440 .alpha(0);
Jason Monk0b349ad2016-04-04 11:18:03 -0400441 mTileView.getAppLabel().animate()
442 .setDuration(DRAG_LENGTH)
443 .alpha(0);
Jason Monk62b63a02016-02-02 15:15:31 -0500444 }
445
446 public void stopDrag() {
447 itemView.animate()
448 .setDuration(DRAG_LENGTH)
449 .scaleX(1)
450 .scaleY(1);
451 mTileView.findViewById(R.id.tile_label).animate()
452 .setDuration(DRAG_LENGTH)
453 .alpha(1);
Jason Monk0b349ad2016-04-04 11:18:03 -0400454 mTileView.getAppLabel().animate()
455 .setDuration(DRAG_LENGTH)
456 .alpha(.6f);
Jason Monk5db8a412015-10-21 15:16:23 -0700457 }
458 }
459
Jason Monk62b63a02016-02-02 15:15:31 -0500460 private final SpanSizeLookup mSizeLookup = new SpanSizeLookup() {
Jason Monk5db8a412015-10-21 15:16:23 -0700461 @Override
Jason Monk62b63a02016-02-02 15:15:31 -0500462 public int getSpanSize(int position) {
Jason Monk0b349ad2016-04-04 11:18:03 -0400463 final int type = getItemViewType(position);
464 return type == TYPE_EDIT || type == TYPE_DIVIDER ? 3 : 1;
Jason Monk62b63a02016-02-02 15:15:31 -0500465 }
466 };
467
Andrew Sappersteinc11df252016-06-19 19:12:11 -0700468 private class TileItemDecoration extends ItemDecoration {
469 private final ColorDrawable mDrawable;
470
471 private TileItemDecoration(Context context) {
472 TypedArray ta =
473 context.obtainStyledAttributes(new int[]{android.R.attr.colorSecondary});
474 mDrawable = new ColorDrawable(ta.getColor(0, 0));
475 ta.recycle();
476 }
477
Jason Monk62b63a02016-02-02 15:15:31 -0500478
479 @Override
480 public void onDraw(Canvas c, RecyclerView parent, State state) {
481 super.onDraw(c, parent, state);
482
483 final int childCount = parent.getChildCount();
484 final int width = parent.getWidth();
485 final int bottom = parent.getBottom();
486 for (int i = 0; i < childCount; i++) {
487 final View child = parent.getChildAt(i);
488 final ViewHolder holder = parent.getChildViewHolder(child);
Jason Monkb213ac42016-06-20 10:27:27 -0400489 if (holder.getAdapterPosition() < mEditIndex && !(child instanceof TextView)) {
Jason Monk5db8a412015-10-21 15:16:23 -0700490 continue;
491 }
Jason Monk62b63a02016-02-02 15:15:31 -0500492
493 final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
494 .getLayoutParams();
495 final int top = child.getTop() + params.topMargin +
496 Math.round(ViewCompat.getTranslationY(child));
497 // Draw full width, in case there aren't tiles all the way across.
498 mDrawable.setBounds(0, top, width, bottom);
499 mDrawable.draw(c);
500 break;
Jason Monk5db8a412015-10-21 15:16:23 -0700501 }
Jason Monk62b63a02016-02-02 15:15:31 -0500502 }
503 };
504
505 private final ItemTouchHelper.Callback mCallbacks = new ItemTouchHelper.Callback() {
506
507 @Override
508 public boolean isLongPressDragEnabled() {
509 return true;
Jason Monk5db8a412015-10-21 15:16:23 -0700510 }
511
512 @Override
Jason Monk62b63a02016-02-02 15:15:31 -0500513 public boolean isItemViewSwipeEnabled() {
514 return false;
Jason Monk5db8a412015-10-21 15:16:23 -0700515 }
Jason Monk5db8a412015-10-21 15:16:23 -0700516
Jason Monk62b63a02016-02-02 15:15:31 -0500517 @Override
518 public void onSelectedChanged(ViewHolder viewHolder, int actionState) {
519 super.onSelectedChanged(viewHolder, actionState);
Jason Monkb213ac42016-06-20 10:27:27 -0400520 if (actionState != ItemTouchHelper.ACTION_STATE_DRAG) {
521 viewHolder = null;
522 }
523 if (viewHolder == mCurrentDrag) return;
Jason Monk62b63a02016-02-02 15:15:31 -0500524 if (mCurrentDrag != null) {
Jason Monkb213ac42016-06-20 10:27:27 -0400525 int position = mCurrentDrag.getAdapterPosition();
526 TileInfo info = mTiles.get(position);
527 mCurrentDrag.mTileView.setShowAppLabel(
528 position > mEditIndex && !info.isSystem);
Jason Monk62b63a02016-02-02 15:15:31 -0500529 mCurrentDrag.stopDrag();
Jason Monk24dbd512016-03-16 14:56:42 -0400530 mCurrentDrag = null;
Jason Monk62b63a02016-02-02 15:15:31 -0500531 }
532 if (viewHolder != null) {
533 mCurrentDrag = (Holder) viewHolder;
534 mCurrentDrag.startDrag();
535 }
Jason Monk1c2fea82016-03-11 11:33:36 -0500536 mHandler.post(new Runnable() {
537 @Override
538 public void run() {
Jason Monk0b349ad2016-04-04 11:18:03 -0400539 notifyItemChanged(mEditIndex);
Jason Monk1c2fea82016-03-11 11:33:36 -0500540 }
541 });
Jason Monk62b63a02016-02-02 15:15:31 -0500542 }
543
544 @Override
Jason Monkb213ac42016-06-20 10:27:27 -0400545 public boolean canDropOver(RecyclerView recyclerView, ViewHolder current,
546 ViewHolder target) {
547 return target.getAdapterPosition() <= mEditIndex + 1;
548 }
549
550 @Override
Jason Monk62b63a02016-02-02 15:15:31 -0500551 public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) {
552 if (viewHolder.getItemViewType() == TYPE_EDIT) {
553 return makeMovementFlags(0, 0);
554 }
555 int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.RIGHT
556 | ItemTouchHelper.LEFT;
557 return makeMovementFlags(dragFlags, 0);
558 }
559
560 @Override
561 public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder, ViewHolder target) {
562 int from = viewHolder.getAdapterPosition();
563 int to = target.getAdapterPosition();
Jason Monk39c98e62016-03-16 09:18:35 -0400564 return move(from, to, target.itemView);
Jason Monk62b63a02016-02-02 15:15:31 -0500565 }
566
567 @Override
568 public void onSwiped(ViewHolder viewHolder, int direction) {
569 }
570 };
Jason Monk5db8a412015-10-21 15:16:23 -0700571}