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