Upstream version 5.34.98.0
[platform/framework/web/crosswalk.git] / src / content / public / android / java / src / org / chromium / content / browser / ZoomManager.java
1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.content.browser;
6
7 import android.annotation.TargetApi;
8 import android.content.Context;
9 import android.os.Build;
10 import android.util.Log;
11 import android.view.MotionEvent;
12 import android.view.ScaleGestureDetector;
13
14 /**
15  * ZoomManager is responsible for maintaining the ContentView's current zoom
16  * level state and process scaling-related gestures.
17  */
18 class ZoomManager {
19     private static final String TAG = "ContentViewZoom";
20
21     private final ContentViewCore mContentViewCore;
22
23     // ScaleGestureDetector previous to 4.2.2 failed to record touch event times (b/7626515),
24     // so we record them manually for use when synthesizing pinch gestures.
25     private long mCurrentEventTime;
26
27     private class ScaleGestureListener implements ScaleGestureDetector.OnScaleGestureListener {
28         // Completely silence scaling events. Used in WebView when zoom support
29         // is turned off.
30         private boolean mPermanentlyIgnoreDetectorEvents = false;
31         // Bypass events through the detector to maintain its state. Used when
32         // renderes already handles the touch event.
33         private boolean mTemporarilyIgnoreDetectorEvents = false;
34
35         // Whether any pinch zoom event has been sent to native.
36         private boolean mPinchEventSent;
37
38         long getEventTime(ScaleGestureDetector detector) {
39             // Workaround for b/7626515, fixed in 4.2.2.
40             assert mCurrentEventTime != 0;
41             assert detector.getEventTime() == 0 || detector.getEventTime() == mCurrentEventTime;
42             return mCurrentEventTime;
43         }
44
45         boolean getPermanentlyIgnoreDetectorEvents() {
46             return mPermanentlyIgnoreDetectorEvents;
47         }
48
49         void setPermanentlyIgnoreDetectorEvents(boolean value) {
50             // Note that returning false from onScaleBegin / onScale makes the
51             // gesture detector not to emit further scaling notifications
52             // related to this gesture. Thus, if detector events are enabled in
53             // the middle of the gesture, we don't need to do anything.
54             mPermanentlyIgnoreDetectorEvents = value;
55         }
56
57         void setTemporarilyIgnoreDetectorEvents(boolean value) {
58             mTemporarilyIgnoreDetectorEvents = value;
59         }
60
61         @Override
62         public boolean onScaleBegin(ScaleGestureDetector detector) {
63             if (ignoreDetectorEvents()) return false;
64             mPinchEventSent = false;
65             mContentViewCore.getContentViewGestureHandler().setIgnoreSingleTap(true);
66             return true;
67         }
68
69         @Override
70         public void onScaleEnd(ScaleGestureDetector detector) {
71             if (!mPinchEventSent || !mContentViewCore.isAlive()) return;
72             mContentViewCore.getContentViewGestureHandler().pinchEnd(getEventTime(detector));
73             mPinchEventSent = false;
74         }
75
76         @Override
77         public boolean onScale(ScaleGestureDetector detector) {
78             if (ignoreDetectorEvents()) return false;
79             // It is possible that pinchBegin() was never called when we reach here.
80             // This happens when webkit handles the 2nd touch down event. That causes
81             // ContentView to ignore the onScaleBegin() call. And if webkit does not
82             // handle the touch move events afterwards, we will face a situation
83             // that pinchBy() is called without any pinchBegin().
84             // To solve this problem, we call pinchBegin() here if it is never called.
85             if (!mPinchEventSent) {
86                 mContentViewCore.getContentViewGestureHandler().pinchBegin(getEventTime(detector),
87                         (int) detector.getFocusX(), (int) detector.getFocusY());
88                 mPinchEventSent = true;
89             }
90             mContentViewCore.getContentViewGestureHandler().pinchBy(
91                     getEventTime(detector), (int) detector.getFocusX(), (int) detector.getFocusY(),
92                     detector.getScaleFactor());
93             return true;
94         }
95
96         private boolean ignoreDetectorEvents() {
97             return mPermanentlyIgnoreDetectorEvents ||
98                     mTemporarilyIgnoreDetectorEvents ||
99                     !mContentViewCore.isAlive();
100         }
101     }
102
103     private final ScaleGestureDetector mMultiTouchDetector;
104     private final ScaleGestureListener mMultiTouchListener;
105
106     ZoomManager(final Context context, ContentViewCore contentViewCore) {
107         mContentViewCore = contentViewCore;
108         mMultiTouchListener = new ScaleGestureListener();
109         mMultiTouchDetector = new ScaleGestureDetector(context, mMultiTouchListener);
110
111         // ScaleGestureDetector's "QuickScale" feature was introduced in KitKat.
112         // As ContentViewGestureHandler already implements this feature,
113         // explicitly disable it to prevent double-handling of the gesture.
114         disableQuickScale(mMultiTouchDetector);
115     }
116
117     boolean isScaleGestureDetectionInProgress() {
118         return !mMultiTouchListener.getPermanentlyIgnoreDetectorEvents()
119                 && mMultiTouchDetector.isInProgress();
120     }
121
122     // Passes the touch event to ScaleGestureDetector so that its internal
123     // state won't go wrong, but instructs the listener to ignore the result
124     // of processing, if any.
125     void passTouchEventThrough(MotionEvent event) {
126         mMultiTouchListener.setTemporarilyIgnoreDetectorEvents(true);
127         mCurrentEventTime = event.getEventTime();
128         try {
129             mMultiTouchDetector.onTouchEvent(event);
130         } catch (Exception e) {
131             Log.e(TAG, "ScaleGestureDetector got into a bad state!", e);
132             assert false;
133         }
134     }
135
136     // Passes the touch event to ScaleGestureDetector so that its internal state
137     // won't go wrong. ScaleGestureDetector needs two pointers in a MotionEvent
138     // to recognize a zoom gesture.
139     boolean processTouchEvent(MotionEvent event) {
140         // TODO: Need to deal with multi-touch transition
141         mMultiTouchListener.setTemporarilyIgnoreDetectorEvents(false);
142         mCurrentEventTime = event.getEventTime();
143         try {
144             boolean inGesture = isScaleGestureDetectionInProgress();
145             boolean retVal = mMultiTouchDetector.onTouchEvent(event);
146             if (!inGesture && (event.getActionMasked() == MotionEvent.ACTION_UP
147                     || event.getActionMasked() == MotionEvent.ACTION_CANCEL)) {
148                 return false;
149             }
150             return retVal;
151         } catch (Exception e) {
152             Log.e(TAG, "ScaleGestureDetector got into a bad state!", e);
153             assert false;
154         }
155         return false;
156     }
157
158     void updateMultiTouchSupport(boolean supportsMultiTouchZoom) {
159         mMultiTouchListener.setPermanentlyIgnoreDetectorEvents(!supportsMultiTouchZoom);
160     }
161
162     @TargetApi(Build.VERSION_CODES.KITKAT)
163     private static void disableQuickScale(ScaleGestureDetector scaleGestureDetector) {
164        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;
165        scaleGestureDetector.setQuickScaleEnabled(false);
166      }
167 }