• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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.photoeditor.actions;
18 
19 import android.content.Context;
20 import android.util.AttributeSet;
21 import android.view.MotionEvent;
22 
23 /**
24  * View that handles touch-events to track flipping directions and angles.
25  */
26 class FlipView extends FullscreenToolView {
27 
28     /**
29      * Listens to flip changes.
30      */
31     public interface OnFlipChangeListener {
32 
onAngleChanged(float horizontalDegrees, float verticalDegrees, boolean fromUser)33         void onAngleChanged(float horizontalDegrees, float verticalDegrees, boolean fromUser);
34 
onStartTrackingTouch()35         void onStartTrackingTouch();
36 
onStopTrackingTouch()37         void onStopTrackingTouch();
38     }
39 
40     private static final float FIXED_DIRECTION_THRESHOLD = 20;
41 
42     private OnFlipChangeListener listener;
43     private float maxFlipSpan;
44     private float touchStartX;
45     private float touchStartY;
46     private float currentHorizontalDegrees;
47     private float currentVerticalDegrees;
48     private float lastHorizontalDegrees;
49     private float lastVerticalDegrees;
50     private boolean fixedDirection;
51     private boolean fixedDirectionHorizontal;
52 
FlipView(Context context, AttributeSet attrs)53     public FlipView(Context context, AttributeSet attrs) {
54         super(context, attrs);
55     }
56 
setOnFlipChangeListener(OnFlipChangeListener listener)57     public void setOnFlipChangeListener(OnFlipChangeListener listener) {
58         this.listener = listener;
59     }
60 
setFlippedAngles(float horizontalDegrees, float verticalDegrees)61     public void setFlippedAngles(float horizontalDegrees, float verticalDegrees) {
62         refreshAngle(horizontalDegrees, verticalDegrees, false);
63     }
64 
65     /**
66      * Sets allowed degrees for every flip before flipping the view.
67      */
setFlipSpan(float degrees)68     public void setFlipSpan(float degrees) {
69         // Flip-span limits allowed flipping degrees of every flip for usability purpose; the max.
70         // flipped angles could be accumulated and larger than allowed flip-span.
71         maxFlipSpan = degrees;
72     }
73 
calculateAngle(boolean flipHorizontal, float x, float y)74     private float calculateAngle(boolean flipHorizontal, float x, float y) {
75         // Use partial length along the moving direction to calculate the flip angle.
76         float maxDistance = (flipHorizontal ? getWidth() : getHeight()) * 0.35f;
77         float moveDistance = flipHorizontal ? (x - touchStartX) : (touchStartY - y);
78 
79         if (Math.abs(moveDistance) > maxDistance) {
80             moveDistance = (moveDistance > 0) ? maxDistance : -maxDistance;
81 
82             if (flipHorizontal) {
83                 touchStartX = x - moveDistance;
84             } else {
85                 touchStartY = moveDistance + y;
86             }
87         }
88         return (moveDistance / maxDistance) * maxFlipSpan;
89     }
90 
91     @Override
onTouchEvent(MotionEvent ev)92     public boolean onTouchEvent(MotionEvent ev) {
93         super.onTouchEvent(ev);
94 
95         if (isEnabled()) {
96             switch (ev.getAction()) {
97                 case MotionEvent.ACTION_DOWN:
98                     fixedDirection = false;
99                     lastHorizontalDegrees = currentHorizontalDegrees;
100                     lastVerticalDegrees = currentVerticalDegrees;
101                     touchStartX = ev.getX();
102                     touchStartY = ev.getY();
103 
104                     if (listener != null) {
105                         listener.onStartTrackingTouch();
106                     }
107                     break;
108 
109                 case MotionEvent.ACTION_MOVE:
110                     // Allow only one direction for flipping during movements, and make the
111                     // direction fixed once it exceeds threshold.
112                     float x = ev.getX();
113                     float y = ev.getY();
114                     boolean flipHorizontal = fixedDirection ? fixedDirectionHorizontal
115                             : (Math.abs(x - touchStartX) >= Math.abs(y - touchStartY));
116                     float degrees = calculateAngle(flipHorizontal, x, y);
117                     if (!fixedDirection && (Math.abs(degrees) > FIXED_DIRECTION_THRESHOLD)) {
118                         fixedDirection = true;
119                         fixedDirectionHorizontal = flipHorizontal;
120                     }
121 
122                     if (flipHorizontal) {
123                         refreshAngle(lastHorizontalDegrees + degrees, lastVerticalDegrees, true);
124                     } else {
125                         refreshAngle(lastHorizontalDegrees, lastVerticalDegrees + degrees, true);
126                     }
127                    break;
128 
129                 case MotionEvent.ACTION_CANCEL:
130                 case MotionEvent.ACTION_UP:
131                     if (listener != null) {
132                         listener.onStopTrackingTouch();
133                     }
134                     break;
135             }
136         }
137         return true;
138     }
139 
refreshAngle(float horizontalDegrees, float verticalDegrees, boolean fromUser)140     private void refreshAngle(float horizontalDegrees, float verticalDegrees, boolean fromUser) {
141         currentHorizontalDegrees = horizontalDegrees;
142         currentVerticalDegrees = verticalDegrees;
143         if (listener != null) {
144             listener.onAngleChanged(horizontalDegrees, verticalDegrees, fromUser);
145         }
146     }
147 }
148