1/*
2 * Copyright (C) 2011 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
17package android.support.v4.view;
18
19import android.view.MotionEvent;
20
21/**
22 * Helper for accessing features in {@link MotionEvent} introduced
23 * after API level 4 in a backwards compatible fashion.
24 */
25public class MotionEventCompat {
26    /**
27     * Interface for the full API.
28     */
29    interface MotionEventVersionImpl {
30        public int findPointerIndex(MotionEvent event, int pointerId);
31        public int getPointerId(MotionEvent event, int pointerIndex);
32        public float getX(MotionEvent event, int pointerIndex);
33        public float getY(MotionEvent event, int pointerIndex);
34        public int getPointerCount(MotionEvent event);
35    }
36
37    /**
38     * Interface implementation that doesn't use anything about v4 APIs.
39     */
40    static class BaseMotionEventVersionImpl implements MotionEventVersionImpl {
41        @Override
42        public int findPointerIndex(MotionEvent event, int pointerId) {
43            if (pointerId == 0) {
44                // id 0 == index 0 and vice versa.
45                return 0;
46            }
47            return -1;
48        }
49        @Override
50        public int getPointerId(MotionEvent event, int pointerIndex) {
51            if (pointerIndex == 0) {
52                // index 0 == id 0 and vice versa.
53                return 0;
54            }
55            throw new IndexOutOfBoundsException("Pre-Eclair does not support multiple pointers");
56        }
57        @Override
58        public float getX(MotionEvent event, int pointerIndex) {
59            if (pointerIndex == 0) {
60                return event.getX();
61            }
62            throw new IndexOutOfBoundsException("Pre-Eclair does not support multiple pointers");
63        }
64        @Override
65        public float getY(MotionEvent event, int pointerIndex) {
66            if (pointerIndex == 0) {
67                return event.getY();
68            }
69            throw new IndexOutOfBoundsException("Pre-Eclair does not support multiple pointers");
70        }
71        @Override
72        public int getPointerCount(MotionEvent event) {
73            return 1;
74        }
75    }
76
77    /**
78     * Interface implementation for devices with at least v11 APIs.
79     */
80    static class EclairMotionEventVersionImpl implements MotionEventVersionImpl {
81        @Override
82        public int findPointerIndex(MotionEvent event, int pointerId) {
83            return MotionEventCompatEclair.findPointerIndex(event, pointerId);
84        }
85        @Override
86        public int getPointerId(MotionEvent event, int pointerIndex) {
87            return MotionEventCompatEclair.getPointerId(event, pointerIndex);
88        }
89        @Override
90        public float getX(MotionEvent event, int pointerIndex) {
91            return MotionEventCompatEclair.getX(event, pointerIndex);
92        }
93        @Override
94        public float getY(MotionEvent event, int pointerIndex) {
95            return MotionEventCompatEclair.getY(event, pointerIndex);
96        }
97        @Override
98        public int getPointerCount(MotionEvent event) {
99            return MotionEventCompatEclair.getPointerCount(event);
100        }
101    }
102
103    /**
104     * Select the correct implementation to use for the current platform.
105     */
106    static final MotionEventVersionImpl IMPL;
107    static {
108        if (android.os.Build.VERSION.SDK_INT >= 5) {
109            IMPL = new EclairMotionEventVersionImpl();
110        } else {
111            IMPL = new BaseMotionEventVersionImpl();
112        }
113    }
114
115    // -------------------------------------------------------------------
116
117    /**
118     * Synonym for {@link MotionEvent#ACTION_MASK}.
119     */
120    public static final int ACTION_MASK = 0xff;
121
122    /**
123     * Synonym for {@link MotionEvent#ACTION_POINTER_DOWN}.
124     */
125    public static final int ACTION_POINTER_DOWN = 5;
126
127    /**
128     * Synonym for {@link MotionEvent#ACTION_POINTER_UP}.
129     */
130    public static final int ACTION_POINTER_UP = 6;
131
132    /**
133     * Synonym for {@link MotionEvent#ACTION_HOVER_MOVE}.
134     */
135    public static final int ACTION_HOVER_MOVE = 7;
136
137    /**
138     * Synonym for {@link MotionEvent#ACTION_SCROLL}.
139     */
140    public static final int ACTION_SCROLL = 8;
141
142    /**
143     * Synonym for {@link MotionEvent#ACTION_POINTER_INDEX_MASK}.
144     */
145    public static final int ACTION_POINTER_INDEX_MASK  = 0xff00;
146
147    /**
148     * Synonym for {@link MotionEvent#ACTION_POINTER_INDEX_SHIFT}.
149     */
150    public static final int ACTION_POINTER_INDEX_SHIFT = 8;
151
152    /**
153     * Constant for {@link #getActionMasked}: The pointer is not down but has entered the
154     * boundaries of a window or view.
155     * <p>
156     * This action is always delivered to the window or view under the pointer.
157     * </p><p>
158     * This action is not a touch event so it is delivered to
159     * {@link android.view.View#onGenericMotionEvent(MotionEvent)} rather than
160     * {@link android.view.View#onTouchEvent(MotionEvent)}.
161     * </p>
162     */
163    public static final int ACTION_HOVER_ENTER = 9;
164
165    /**
166     * Constant for {@link #getActionMasked}: The pointer is not down but has exited the
167     * boundaries of a window or view.
168     * <p>
169     * This action is always delivered to the window or view that was previously under the pointer.
170     * </p><p>
171     * This action is not a touch event so it is delivered to
172     * {@link android.view.View#onGenericMotionEvent(MotionEvent)} rather than
173     * {@link android.view.View#onTouchEvent(MotionEvent)}.
174     * </p>
175     */
176    public static final int ACTION_HOVER_EXIT = 10;
177
178    /**
179     * Call {@link MotionEvent#getAction}, returning only the {@link #ACTION_MASK}
180     * portion.
181     */
182    public static int getActionMasked(MotionEvent event) {
183        return event.getAction() & ACTION_MASK;
184    }
185
186    /**
187     * Call {@link MotionEvent#getAction}, returning only the pointer index
188     * portion
189     */
190    public static int getActionIndex(MotionEvent event) {
191        return (event.getAction() & ACTION_POINTER_INDEX_MASK)
192                >> ACTION_POINTER_INDEX_SHIFT;
193    }
194
195    /**
196     * Call {@link MotionEvent#findPointerIndex(int)}.
197     * If running on a pre-{@link android.os.Build.VERSION_CODES#ECLAIR} device,
198     * does nothing and returns -1.
199     */
200    public static int findPointerIndex(MotionEvent event, int pointerId) {
201        return IMPL.findPointerIndex(event, pointerId);
202    }
203
204    /**
205     * Call {@link MotionEvent#getPointerId(int)}.
206     * If running on a pre-{@link android.os.Build.VERSION_CODES#ECLAIR} device,
207     * {@link IndexOutOfBoundsException} is thrown.
208     */
209    public static int getPointerId(MotionEvent event, int pointerIndex) {
210        return IMPL.getPointerId(event, pointerIndex);
211    }
212
213    /**
214     * Call {@link MotionEvent#getX(int)}.
215     * If running on a pre-{@link android.os.Build.VERSION_CODES#ECLAIR} device,
216     * {@link IndexOutOfBoundsException} is thrown.
217     */
218    public static float getX(MotionEvent event, int pointerIndex) {
219        return IMPL.getX(event, pointerIndex);
220    }
221
222    /**
223     * Call {@link MotionEvent#getY(int)}.
224     * If running on a pre-{@link android.os.Build.VERSION_CODES#ECLAIR} device,
225     * {@link IndexOutOfBoundsException} is thrown.
226     */
227    public static float getY(MotionEvent event, int pointerIndex) {
228        return IMPL.getY(event, pointerIndex);
229    }
230
231    /**
232     * The number of pointers of data contained in this event.  Always
233     * >= 1.
234     */
235    public static int getPointerCount(MotionEvent event) {
236        return IMPL.getPointerCount(event);
237    }
238}
239