Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / content / public / android / java / src / org / chromium / content / browser / SnapScrollController.java
1 // Copyright 2013 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.DisplayMetrics;
9 import android.view.MotionEvent;
10
11 /**
12  * This objects controls the scroll snapping behavior based on scroll updates.
13  */
14 class SnapScrollController {
15     private static final String TAG = "SnapScrollController";
16     private static final int SNAP_NONE = 0;
17     private static final int SNAP_HORIZ = 1;
18     private static final int SNAP_VERT = 2;
19     private static final int SNAP_BOUND = 16;
20
21     private float mChannelDistance = 16f;
22     private int mSnapScrollMode = SNAP_NONE;
23     private int mFirstTouchX = -1;
24     private int mFirstTouchY = -1;
25     private float mDistanceX = 0;
26     private float mDistanceY = 0;
27
28     SnapScrollController(Context context) {
29         calculateChannelDistance(context);
30     }
31
32     /**
33      * Updates the snap scroll mode based on the given X and Y distance to be moved on scroll.
34      * If the scroll update is above a threshold, the snapping behavior is reset.
35      * @param distanceX X distance for the current scroll update.
36      * @param distanceY Y distance for the current scroll update.
37      */
38     void updateSnapScrollMode(float distanceX, float distanceY) {
39         if (mSnapScrollMode == SNAP_HORIZ || mSnapScrollMode == SNAP_VERT) {
40             mDistanceX += Math.abs(distanceX);
41             mDistanceY += Math.abs(distanceY);
42             if (mSnapScrollMode == SNAP_HORIZ) {
43                 if (mDistanceY > mChannelDistance) {
44                     mSnapScrollMode = SNAP_NONE;
45                 } else if (mDistanceX > mChannelDistance) {
46                     mDistanceX = 0;
47                     mDistanceY = 0;
48                 }
49             } else {
50                 if (mDistanceX > mChannelDistance) {
51                     mSnapScrollMode = SNAP_NONE;
52                 } else if (mDistanceY > mChannelDistance) {
53                     mDistanceX = 0;
54                     mDistanceY = 0;
55                 }
56             }
57         }
58     }
59
60     /**
61      * Sets the snap scroll mode based on the event type.
62      * @param event The received MotionEvent.
63      */
64     void setSnapScrollingMode(MotionEvent event, boolean isScaleGestureDetectionInProgress) {
65         switch(event.getAction()) {
66             case MotionEvent.ACTION_DOWN:
67                 mSnapScrollMode = SNAP_NONE;
68                 mFirstTouchX = (int) event.getX();
69                 mFirstTouchY = (int) event.getY();
70                 break;
71              // Set scrolling mode to SNAP_X if scroll towards x-axis exceeds SNAP_BOUND
72              // and movement towards y-axis is trivial.
73              // Set scrolling mode to SNAP_Y if scroll towards y-axis exceeds SNAP_BOUND
74              // and movement towards x-axis is trivial.
75              // Scrolling mode will remain in SNAP_NONE for other conditions.
76             case MotionEvent.ACTION_MOVE:
77                 if (!isScaleGestureDetectionInProgress &&
78                         mSnapScrollMode == SNAP_NONE) {
79                     int xDiff = (int) Math.abs(event.getX() - mFirstTouchX);
80                     int yDiff = (int) Math.abs(event.getY() - mFirstTouchY);
81                     if (xDiff > SNAP_BOUND && yDiff < SNAP_BOUND) {
82                         mSnapScrollMode = SNAP_HORIZ;
83                     } else if (xDiff < SNAP_BOUND && yDiff > SNAP_BOUND) {
84                         mSnapScrollMode = SNAP_VERT;
85                     }
86                 }
87                 break;
88             case MotionEvent.ACTION_UP:
89             case MotionEvent.ACTION_CANCEL:
90                 mFirstTouchX = -1;
91                 mFirstTouchY = -1;
92                 mDistanceX = 0;
93                 mDistanceY = 0;
94                 break;
95             default:
96                 break;
97         }
98     }
99
100     private void calculateChannelDistance(Context context) {
101         // The channel distance is adjusted for density and screen size.
102         final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
103         final double screenSize = Math.hypot((double) metrics.widthPixels / metrics.densityDpi,
104                 (double) metrics.heightPixels / metrics.densityDpi);
105         if (screenSize < 3.0) {
106             mChannelDistance = 16f;
107         } else if (screenSize < 5.0) {
108             mChannelDistance = 22f;
109         } else if (screenSize < 7.0) {
110             mChannelDistance = 28f;
111         } else {
112             mChannelDistance = 34f;
113         }
114         mChannelDistance = mChannelDistance * metrics.density;
115         if (mChannelDistance < 16f) mChannelDistance = 16f;
116     }
117
118     /**
119      * @return Whether current snap scroll mode is vertical.
120      */
121     boolean isSnapVertical() {
122         return mSnapScrollMode == SNAP_VERT;
123     }
124
125     /**
126      * @return Whether current snap scroll mode is horizontal.
127      */
128     boolean isSnapHorizontal() {
129         return mSnapScrollMode == SNAP_HORIZ;
130     }
131
132     /**
133      * @return Whether currently snapping scrolls.
134      */
135     boolean isSnappingScrolls() {
136         return mSnapScrollMode != SNAP_NONE;
137     }
138 }