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