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