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