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