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