TileAdapter.java revision 24dbd51bdaee076f5b3076bb652686be68aa090a
1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * 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 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * 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. 13 */ 14 15package com.android.systemui.qs.customize; 16 17import android.content.Context; 18import 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; 27import android.view.LayoutInflater; 28import android.view.View; 29import android.view.ViewGroup; 30import android.widget.FrameLayout; 31import android.widget.TextView; 32import com.android.systemui.R; 33import 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; 38import com.android.systemui.statusbar.phone.QSTileHost; 39 40import java.util.ArrayList; 41import java.util.List; 42 43public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileStateListener { 44 45 private static final long DRAG_LENGTH = 100; 46 private static final float DRAG_SCALE = 1.2f; 47 public static final long MOVE_DURATION = 150; 48 49 private static final int TYPE_TILE = 0; 50 private static final int TYPE_EDIT = 1; 51 52 private final Context mContext; 53 54 private final List<TileInfo> mTiles = new ArrayList<>(); 55 private final ItemTouchHelper mItemTouchHelper; 56 private int mDividerIndex; 57 private List<String> mCurrentSpecs; 58 private List<TileInfo> mOtherTiles; 59 private List<TileInfo> mAllTiles; 60 61 private Holder mCurrentDrag; 62 63 public TileAdapter(Context context) { 64 mContext = context; 65 mItemTouchHelper = new ItemTouchHelper(mCallbacks); 66 setHasStableIds(true); 67 } 68 69 @Override 70 public long getItemId(int position) { 71 return mTiles.get(position) != null ? mAllTiles.indexOf(mTiles.get(position)) : -1; 72 } 73 74 public ItemTouchHelper getItemTouchHelper() { 75 return mItemTouchHelper; 76 } 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(); 94 } 95 96 @Override 97 public void onTilesChanged(List<TileInfo> tiles) { 98 mAllTiles = tiles; 99 recalcSpecs(); 100 } 101 102 private void recalcSpecs() { 103 if (mCurrentSpecs == null || mAllTiles == null) { 104 return; 105 } 106 mOtherTiles = new ArrayList<TileInfo>(mAllTiles); 107 mTiles.clear(); 108 for (int i = 0; i < mCurrentSpecs.size(); i++) { 109 final TileInfo tile = getAndRemoveOther(mCurrentSpecs.get(i)); 110 if (tile != null) { 111 mTiles.add(tile); 112 } 113 } 114 mTiles.add(null); 115 mTiles.addAll(mOtherTiles); 116 mDividerIndex = mTiles.indexOf(null); 117 notifyDataSetChanged(); 118 } 119 120 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); 124 } 125 } 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 156 public void onBindViewHolder(final Holder holder, int position) { 157 if (holder.getItemViewType() == TYPE_EDIT) { 158 ((TextView) holder.itemView.findViewById(android.R.id.title)).setText( 159 mCurrentDrag != null ? R.string.drag_to_remove_tiles 160 : R.string.drag_to_add_tiles); 161 return; 162 } 163 164 TileInfo info = mTiles.get(position); 165 holder.mTileView.onStateChanged(info.state); 166 } 167 168 public SpanSizeLookup getSizeLookup() { 169 return mSizeLookup; 170 } 171 172 public class Holder extends ViewHolder { 173 private QSTileView mTileView; 174 175 public Holder(View itemView) { 176 super(itemView); 177 if (itemView instanceof FrameLayout) { 178 mTileView = (QSTileView) ((FrameLayout) itemView).getChildAt(0); 179 mTileView.setBackground(null); 180 mTileView.getIcon().disableAnimation(); 181 } 182 } 183 184 public void startDrag() { 185 itemView.animate() 186 .setDuration(DRAG_LENGTH) 187 .scaleX(DRAG_SCALE) 188 .scaleY(DRAG_SCALE); 189 mTileView.findViewById(R.id.tile_label).animate() 190 .setDuration(DRAG_LENGTH) 191 .alpha(0); 192 } 193 194 public void stopDrag() { 195 itemView.animate() 196 .setDuration(DRAG_LENGTH) 197 .scaleX(1) 198 .scaleY(1); 199 mTileView.findViewById(R.id.tile_label).animate() 200 .setDuration(DRAG_LENGTH) 201 .alpha(1); 202 } 203 } 204 205 private final SpanSizeLookup mSizeLookup = new SpanSizeLookup() { 206 @Override 207 public int getSpanSize(int position) { 208 return getItemViewType(position) == TYPE_EDIT ? 3 : 1; 209 } 210 }; 211 212 private final ItemDecoration mDecoration = new ItemDecoration() { 213 // TODO: Move this to resource. 214 private final ColorDrawable mDrawable = new ColorDrawable(0xff384248); 215 216 @Override 217 public void onDraw(Canvas c, RecyclerView parent, State state) { 218 super.onDraw(c, parent, state); 219 220 final int childCount = parent.getChildCount(); 221 final int width = parent.getWidth(); 222 final int bottom = parent.getBottom(); 223 for (int i = 0; i < childCount; i++) { 224 final View child = parent.getChildAt(i); 225 final ViewHolder holder = parent.getChildViewHolder(child); 226 if (holder.getAdapterPosition() < mDividerIndex) { 227 continue; 228 } 229 230 final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 231 .getLayoutParams(); 232 final int top = child.getTop() + params.topMargin + 233 Math.round(ViewCompat.getTranslationY(child)); 234 // Draw full width, in case there aren't tiles all the way across. 235 mDrawable.setBounds(0, top, width, bottom); 236 mDrawable.draw(c); 237 break; 238 } 239 } 240 }; 241 242 private final ItemTouchHelper.Callback mCallbacks = new ItemTouchHelper.Callback() { 243 244 @Override 245 public boolean isLongPressDragEnabled() { 246 return true; 247 } 248 249 @Override 250 public boolean isItemViewSwipeEnabled() { 251 return false; 252 } 253 254 @Override 255 public void onSelectedChanged(ViewHolder viewHolder, int actionState) { 256 super.onSelectedChanged(viewHolder, actionState); 257 if (mCurrentDrag != null) { 258 mCurrentDrag.stopDrag(); 259 mCurrentDrag = null; 260 } 261 if (viewHolder != null) { 262 mCurrentDrag = (Holder) viewHolder; 263 mCurrentDrag.startDrag(); 264 } 265 notifyItemChanged(mDividerIndex); 266 } 267 268 @Override 269 public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) { 270 if (viewHolder.getItemViewType() == TYPE_EDIT) { 271 return makeMovementFlags(0, 0); 272 } 273 int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.RIGHT 274 | ItemTouchHelper.LEFT; 275 return makeMovementFlags(dragFlags, 0); 276 } 277 278 @Override 279 public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder, ViewHolder target) { 280 int from = viewHolder.getAdapterPosition(); 281 int to = target.getAdapterPosition(); 282 if (to > mDividerIndex) { 283 if (from >= mDividerIndex) { 284 return false; 285 } 286 } 287 move(from, to, mTiles); 288 mDividerIndex = mTiles.indexOf(null); 289 notifyItemMoved(from, to); 290 return true; 291 } 292 293 private <T> void move(int from, int to, List<T> list) { 294 list.add(from > to ? to : to + 1, list.get(from)); 295 list.remove(from > to ? from + 1 : from); 296 } 297 298 @Override 299 public void onSwiped(ViewHolder viewHolder, int direction) { 300 } 301 }; 302} 303