StatePanelTrack.java revision f5eedf1635eba7edfa7d41fd4e991cced978c4b2
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.gallery3d.filtershow.state;
18
19import android.animation.LayoutTransition;
20import android.content.Context;
21import android.content.res.TypedArray;
22import android.graphics.Point;
23import android.graphics.Rect;
24import android.util.AttributeSet;
25import android.util.Log;
26import android.view.GestureDetector;
27import android.view.MotionEvent;
28import android.view.View;
29import android.view.ViewGroup;
30import android.widget.Adapter;
31import android.widget.LinearLayout;
32import com.android.gallery3d.R;
33import com.android.gallery3d.filtershow.FilterShowActivity;
34import com.android.gallery3d.filtershow.editors.ImageOnlyEditor;
35import com.android.gallery3d.filtershow.filters.FilterRepresentation;
36import com.android.gallery3d.filtershow.imageshow.MasterImage;
37
38public class StatePanelTrack extends LinearLayout implements PanelTrack {
39
40    private static final String LOGTAG = "StatePanelTrack";
41    private Point mTouchPoint;
42    private StateView mCurrentView;
43    private StateView mCurrentSelectedView;
44    private boolean mExited = false;
45    private boolean mStartedDrag = false;
46    private StateAdapter mAdapter;
47    private DragListener mDragListener = new DragListener(this);
48    private float mDeleteSlope = 0.2f;
49    private GestureDetector mGestureDetector;
50    private int mElemWidth;
51    private int mElemHeight;
52    private int mElemSize;
53    private int mElemEndSize;
54    private int mEndElemWidth;
55    private int mEndElemHeight;
56    private long mTouchTime;
57    private int mMaxTouchDelay = 300; // 300ms delay for touch
58    private static final boolean ALLOWS_DRAG = false;
59
60    public StatePanelTrack(Context context, AttributeSet attrs) {
61        super(context, attrs);
62        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.StatePanelTrack);
63        mElemSize = a.getDimensionPixelSize(R.styleable.StatePanelTrack_elemSize, 0);
64        mElemEndSize = a.getDimensionPixelSize(R.styleable.StatePanelTrack_elemEndSize, 0);
65        if (getOrientation() == LinearLayout.HORIZONTAL) {
66            mElemWidth = mElemSize;
67            mElemHeight = LayoutParams.MATCH_PARENT;
68            mEndElemWidth = mElemEndSize;
69            mEndElemHeight = LayoutParams.MATCH_PARENT;
70        } else {
71            mElemWidth = LayoutParams.MATCH_PARENT;
72            mElemHeight = mElemSize;
73            mEndElemWidth = LayoutParams.MATCH_PARENT;
74            mEndElemHeight = mElemEndSize;
75        }
76        GestureDetector.SimpleOnGestureListener simpleOnGestureListener
77                = new GestureDetector.SimpleOnGestureListener(){
78            @Override
79            public void onLongPress(MotionEvent e) {
80                longPress(e);
81            }
82            @Override
83            public boolean onDoubleTap(MotionEvent e) {
84                addDuplicate(e);
85                return true;
86            }
87        };
88        mGestureDetector = new GestureDetector(context, simpleOnGestureListener);
89    }
90
91    private void addDuplicate(MotionEvent e) {
92        if (mCurrentSelectedView == null) {
93            return;
94        }
95        int pos = findChild(mCurrentSelectedView);
96        if (pos != -1) {
97            mAdapter.insert(new State(mCurrentSelectedView.getState()), pos);
98            fillContent(true);
99        }
100    }
101
102    private void longPress(MotionEvent e) {
103        View view = findChildAt((int) e.getX(), (int) e.getY());
104        if (view == null) {
105            return;
106        }
107        if (view instanceof StateView) {
108            StateView stateView = (StateView) view;
109            stateView.setDuplicateButton(true);
110        }
111    }
112
113    public void setAdapter(StateAdapter adapter) {
114        mAdapter = adapter;
115        mAdapter.setListener(this);
116        mAdapter.setOrientation(getOrientation());
117        fillContent(false);
118        requestLayout();
119    }
120
121    public StateView findChildWithState(State state) {
122        for (int i = 0; i < getChildCount(); i++) {
123            StateView view = (StateView) getChildAt(i);
124            if (view.getState() == state) {
125                return view;
126            }
127        }
128        return null;
129    }
130
131    public void fillContent(boolean animate) {
132        if (!animate) {
133            this.setLayoutTransition(null);
134        }
135        int n = mAdapter.getCount();
136        for (int i = 0; i < getChildCount(); i++) {
137            StateView child = (StateView) getChildAt(i);
138            child.resetPosition();
139            if (!mAdapter.contains(child.getState())) {
140                removeView(child);
141            }
142        }
143        LayoutParams params = new LayoutParams(mElemWidth, mElemHeight);
144        for (int i = 0; i < n; i++) {
145            State s = mAdapter.getItem(i);
146            if (findChildWithState(s) == null) {
147                View view = mAdapter.getView(i, null, this);
148                addView(view, i, params);
149            }
150        }
151
152        for (int i = 0; i < n; i++) {
153            State state = mAdapter.getItem(i);
154            StateView view = (StateView) getChildAt(i);
155            view.setState(state);
156            if (i == 0) {
157                view.setType(StateView.BEGIN);
158            } else if (i == n - 1) {
159                view.setType(StateView.END);
160            } else {
161                view.setType(StateView.DEFAULT);
162            }
163            view.resetPosition();
164        }
165
166        if (!animate) {
167            this.setLayoutTransition(new LayoutTransition());
168        }
169    }
170
171    public void onTouch(MotionEvent event, StateView view) {
172        if (!view.isDraggable()) {
173            return;
174        }
175        mCurrentView = view;
176        if (mCurrentSelectedView == mCurrentView) {
177            return;
178        }
179        if (mCurrentSelectedView != null) {
180            mCurrentSelectedView.setSelected(false);
181        }
182        // We changed the current view -- let's reset the
183        // gesture detector.
184        MotionEvent cancelEvent = MotionEvent.obtain(event);
185        cancelEvent.setAction(MotionEvent.ACTION_CANCEL);
186        mGestureDetector.onTouchEvent(cancelEvent);
187        mCurrentSelectedView = mCurrentView;
188        // We have to send the event to the gesture detector
189        mGestureDetector.onTouchEvent(event);
190        mTouchTime = System.currentTimeMillis();
191    }
192
193    @Override
194    public boolean onInterceptTouchEvent(MotionEvent event) {
195        if (mCurrentView != null) {
196            return true;
197        }
198        return false;
199    }
200
201    @Override
202    public boolean onTouchEvent(MotionEvent event) {
203        if (mCurrentView == null) {
204            return false;
205        }
206        if (mTouchTime == 0) {
207            mTouchTime = System.currentTimeMillis();
208        }
209        mGestureDetector.onTouchEvent(event);
210        if (mTouchPoint == null) {
211            mTouchPoint = new Point();
212            mTouchPoint.x = (int) event.getX();
213            mTouchPoint.y = (int) event.getY();
214        }
215
216        if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
217            float translation = event.getY() - mTouchPoint.y;
218            float alpha = 1.0f - (Math.abs(translation) / mCurrentView.getHeight());
219            if (getOrientation() == LinearLayout.VERTICAL) {
220                translation = event.getX() - mTouchPoint.x;
221                alpha = 1.0f - (Math.abs(translation) / mCurrentView.getWidth());
222                mCurrentView.setTranslationX(translation);
223            } else {
224                mCurrentView.setTranslationY(translation);
225            }
226            mCurrentView.setBackgroundAlpha(alpha);
227            if (ALLOWS_DRAG && alpha < 0.7) {
228                setOnDragListener(mDragListener);
229                DragShadowBuilder shadowBuilder = new DragShadowBuilder(mCurrentView);
230                mCurrentView.startDrag(null, shadowBuilder, mCurrentView, 0);
231                mStartedDrag = true;
232            }
233        }
234        if (!mExited && mCurrentView != null
235                && mCurrentView.getBackgroundAlpha() > mDeleteSlope
236                && event.getActionMasked() == MotionEvent.ACTION_UP
237                && System.currentTimeMillis() - mTouchTime < mMaxTouchDelay) {
238            FilterRepresentation representation = mCurrentView.getState().getFilterRepresentation();
239            if (representation != MasterImage.getImage().getCurrentFilterRepresentation()) {
240                FilterShowActivity activity = (FilterShowActivity) getContext();
241                activity.showRepresentation(representation);
242            }
243            if (representation.getEditorId() != ImageOnlyEditor.ID) {
244                mCurrentView.setSelected(true);
245            }
246        }
247        if (event.getActionMasked() == MotionEvent.ACTION_UP
248                || (!mStartedDrag && event.getActionMasked() == MotionEvent.ACTION_CANCEL)) {
249            checkEndState();
250        }
251        return true;
252    }
253
254    public void checkEndState() {
255        mTouchPoint = null;
256        mTouchTime = 0;
257        if (mExited || mCurrentView.getBackgroundAlpha() < mDeleteSlope) {
258            int origin = findChild(mCurrentView);
259            if (origin != -1) {
260                State current = mAdapter.getItem(origin);
261                FilterRepresentation currentRep = MasterImage.getImage().getCurrentFilterRepresentation();
262                FilterRepresentation removedRep = current.getFilterRepresentation();
263                mAdapter.remove(current);
264                fillContent(true);
265                if (currentRep != null && removedRep != null
266                        && currentRep.getFilterClass() == removedRep.getFilterClass()) {
267                    FilterShowActivity activity = (FilterShowActivity) getContext();
268                    activity.backToMain();
269                    return;
270                }
271            }
272        } else {
273            mCurrentView.setBackgroundAlpha(1.0f);
274            mCurrentView.setTranslationX(0);
275            mCurrentView.setTranslationY(0);
276        }
277        if (mCurrentSelectedView != null) {
278            mCurrentSelectedView.invalidate();
279        }
280        if (mCurrentView != null) {
281            mCurrentView.invalidate();
282        }
283        mCurrentView = null;
284        mExited = false;
285        mStartedDrag = false;
286    }
287
288    public View findChildAt(int x, int y) {
289        Rect frame = new Rect();
290        int scrolledXInt = getScrollX() + x;
291        int scrolledYInt = getScrollY() + y;
292        for (int i = 0; i < getChildCount(); i++) {
293            View child = getChildAt(i);
294            child.getHitRect(frame);
295            if (frame.contains(scrolledXInt, scrolledYInt)) {
296                return child;
297            }
298        }
299        return null;
300    }
301
302    public int findChild(View view) {
303        for (int i = 0; i < getChildCount(); i++) {
304            View child = getChildAt(i);
305            if (child == view) {
306                return i;
307            }
308        }
309        return -1;
310    }
311
312    public StateView getCurrentView() {
313        return mCurrentView;
314    }
315
316    public void setCurrentView(View currentView) {
317        mCurrentView = (StateView) currentView;
318    }
319
320    public void setExited(boolean value) {
321        mExited = value;
322    }
323
324    public Point getTouchPoint() {
325        return mTouchPoint;
326    }
327
328    public Adapter getAdapter() {
329        return mAdapter;
330    }
331}
332