TileAdapter.java revision b53b6c566f8e4754406cd1c3fddfa16a27515a7e
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.MotionEvent; 29import android.view.View; 30import android.view.View.OnTouchListener; 31import android.view.ViewGroup; 32import android.widget.FrameLayout; 33import com.android.systemui.R; 34import 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; 39import com.android.systemui.statusbar.phone.QSTileHost; 40 41import java.util.ArrayList; 42import java.util.List; 43 44public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileStateListener { 45 46 private static final long DRAG_LENGTH = 100; 47 private static final float DRAG_SCALE = 1.2f; 48 public static final long MOVE_DURATION = 150; 49 50 private static final int TYPE_TILE = 0; 51 private static final int TYPE_EDIT = 1; 52 53 private final Context mContext; 54 55 private final List<TileInfo> mTiles = new ArrayList<>(); 56 private final ItemTouchHelper mItemTouchHelper; 57 private int mDividerIndex; 58 private List<String> mCurrentSpecs; 59 private List<TileInfo> mOtherTiles; 60 private List<TileInfo> mAllTiles; 61 62 private Holder mCurrentDrag; 63 64 public TileAdapter(Context context) { 65 mContext = context; 66 mItemTouchHelper = new ItemTouchHelper(mCallbacks); 67 setHasStableIds(true); 68 } 69 70 @Override 71 public long getItemId(int position) { 72 return mTiles.get(position) != null ? mAllTiles.indexOf(mTiles.get(position)) : -1; 73 } 74 75 public ItemTouchHelper getItemTouchHelper() { 76 return mItemTouchHelper; 77 } 78 79 public ItemDecoration getItemDecoration() { 80 return mDecoration; 81 } 82 83 public void saveSpecs(QSTileHost host) { 84 List<String> newSpecs = new ArrayList<>(); 85 for (int i = 0; mTiles.get(i) != null; i++) { 86 newSpecs.add(mTiles.get(i).spec); 87 } 88 host.changeTiles(mCurrentSpecs, newSpecs); 89 setTileSpecs(newSpecs); 90 } 91 92 public void setTileSpecs(List<String> currentSpecs) { 93 mCurrentSpecs = currentSpecs; 94 recalcSpecs(); 95 } 96 97 @Override 98 public void onTilesChanged(List<TileInfo> tiles) { 99 mAllTiles = tiles; 100 recalcSpecs(); 101 } 102 103 private void recalcSpecs() { 104 if (mCurrentSpecs == null || mAllTiles == null) { 105 return; 106 } 107 mOtherTiles = new ArrayList<TileInfo>(mAllTiles); 108 mTiles.clear(); 109 for (int i = 0; i < mCurrentSpecs.size(); i++) { 110 mTiles.add(getAndRemoveOther(mCurrentSpecs.get(i))); 111 } 112 mTiles.add(null); 113 mTiles.addAll(mOtherTiles); 114 mDividerIndex = mTiles.indexOf(null); 115 notifyDataSetChanged(); 116 } 117 118 private TileInfo getAndRemoveOther(String s) { 119 for (int i = 0; i < mOtherTiles.size(); i++) { 120 if (mOtherTiles.get(i).spec.equals(s)) { 121 return mOtherTiles.remove(i); 122 } 123 } 124 return null; 125 } 126 127 @Override 128 public int getItemViewType(int position) { 129 if (mTiles.get(position) == null) { 130 return TYPE_EDIT; 131 } 132 return TYPE_TILE; 133 } 134 135 @Override 136 public Holder onCreateViewHolder(ViewGroup parent, int viewType) { 137 final Context context = parent.getContext(); 138 LayoutInflater inflater = LayoutInflater.from(context); 139 if (viewType == 1) { 140 return new Holder(inflater.inflate(R.layout.qs_customize_divider, parent, false)); 141 } 142 FrameLayout frame = (FrameLayout) inflater.inflate(R.layout.qs_customize_tile_frame, parent, 143 false); 144 frame.addView(new QSTileView(context, new QSIconView(context))); 145 return new Holder(frame); 146 } 147 148 @Override 149 public int getItemCount() { 150 return mTiles.size(); 151 } 152 153 @Override 154 public void onBindViewHolder(final Holder holder, int position) { 155 if (holder.getItemViewType() == TYPE_EDIT) return; 156 157 TileInfo info = mTiles.get(position); 158 holder.mTileView.onStateChanged(info.state); 159 holder.mTileView.setOnTouchListener(new OnTouchListener() { 160 @Override 161 public boolean onTouch(View v, MotionEvent event) { 162 mItemTouchHelper.startDrag(holder); 163 return true; 164 } 165 }); 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 } 180 } 181 182 public void startDrag() { 183 itemView.animate() 184 .setDuration(DRAG_LENGTH) 185 .scaleX(DRAG_SCALE) 186 .scaleY(DRAG_SCALE); 187 mTileView.findViewById(R.id.tile_label).animate() 188 .setDuration(DRAG_LENGTH) 189 .alpha(0); 190 } 191 192 public void stopDrag() { 193 itemView.animate() 194 .setDuration(DRAG_LENGTH) 195 .scaleX(1) 196 .scaleY(1); 197 mTileView.findViewById(R.id.tile_label).animate() 198 .setDuration(DRAG_LENGTH) 199 .alpha(1); 200 } 201 } 202 203 private final SpanSizeLookup mSizeLookup = new SpanSizeLookup() { 204 @Override 205 public int getSpanSize(int position) { 206 return getItemViewType(position) == TYPE_EDIT ? 3 : 1; 207 } 208 }; 209 210 private final ItemDecoration mDecoration = new ItemDecoration() { 211 // TODO: Move this to resource. 212 private final ColorDrawable mDrawable = new ColorDrawable(0xff384248); 213 214 @Override 215 public void onDraw(Canvas c, RecyclerView parent, State state) { 216 super.onDraw(c, parent, state); 217 218 final int childCount = parent.getChildCount(); 219 final int width = parent.getWidth(); 220 final int bottom = parent.getBottom(); 221 for (int i = 0; i < childCount; i++) { 222 final View child = parent.getChildAt(i); 223 final ViewHolder holder = parent.getChildViewHolder(child); 224 if (holder.getAdapterPosition() < mDividerIndex) { 225 continue; 226 } 227 228 final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child 229 .getLayoutParams(); 230 final int top = child.getTop() + params.topMargin + 231 Math.round(ViewCompat.getTranslationY(child)); 232 // Draw full width, in case there aren't tiles all the way across. 233 mDrawable.setBounds(0, top, width, bottom); 234 mDrawable.draw(c); 235 break; 236 } 237 } 238 }; 239 240 private final ItemTouchHelper.Callback mCallbacks = new ItemTouchHelper.Callback() { 241 242 @Override 243 public boolean isLongPressDragEnabled() { 244 return true; 245 } 246 247 @Override 248 public boolean isItemViewSwipeEnabled() { 249 return false; 250 } 251 252 @Override 253 public void onSelectedChanged(ViewHolder viewHolder, int actionState) { 254 super.onSelectedChanged(viewHolder, actionState); 255 if (mCurrentDrag != null) { 256 mCurrentDrag.stopDrag(); 257 } 258 if (viewHolder != null) { 259 mCurrentDrag = (Holder) viewHolder; 260 mCurrentDrag.startDrag(); 261 } 262 } 263 264 @Override 265 public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) { 266 if (viewHolder.getItemViewType() == TYPE_EDIT) { 267 return makeMovementFlags(0, 0); 268 } 269 int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.RIGHT 270 | ItemTouchHelper.LEFT; 271 return makeMovementFlags(dragFlags, 0); 272 } 273 274 @Override 275 public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder, ViewHolder target) { 276 int from = viewHolder.getAdapterPosition(); 277 int to = target.getAdapterPosition(); 278 if (to > mDividerIndex) { 279 if (from >= mDividerIndex) { 280 return false; 281 } 282 } 283 if (target.getItemViewType() == TYPE_EDIT && from < mDividerIndex) { 284 to++; 285 } 286 move(from, to, mTiles); 287 mDividerIndex = mTiles.indexOf(null); 288 notifyItemMoved(from, to); 289 return true; 290 } 291 292 private <T> void move(int from, int to, List<T> list) { 293 list.add(from > to ? to : to + 1, list.get(from)); 294 list.remove(from > to ? from + 1 : from); 295 } 296 297 @Override 298 public void onSwiped(ViewHolder viewHolder, int direction) { 299 } 300 }; 301} 302