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