ViewCompat.java revision c95beb648f59c89c6bd7b0eed0a8b266a1b287e2
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.graphics.Rect;
20import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
21import android.view.View;
22import android.view.accessibility.AccessibilityEvent;
23
24/**
25 * Helper for accessing features in {@link View} introduced after API
26 * level 4 in a backwards compatible fashion.
27 */
28public class ViewCompat {
29    /**
30     * Always allow a user to over-scroll this view, provided it is a
31     * view that can scroll.
32     */
33    public static final int OVER_SCROLL_ALWAYS = 0;
34
35    /**
36     * Allow a user to over-scroll this view only if the content is large
37     * enough to meaningfully scroll, provided it is a view that can scroll.
38     */
39    public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1;
40
41    /**
42     * Never allow a user to over-scroll this view.
43     */
44    public static final int OVER_SCROLL_NEVER = 2;
45
46    interface ViewCompatImpl {
47        public boolean canScrollHorizontally(View v, int direction);
48        public boolean canScrollVertically(View v, int direction);
49        public int getOverScrollMode(View v);
50        public void setOverScrollMode(View v, int mode);
51        public void onInitializeAccessibilityEvent(View v, AccessibilityEvent event);
52        public void onPopulateAccessibilityEvent(View v, AccessibilityEvent event);
53        public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info);
54        public void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate);
55        public boolean hasTransientState(View view);
56        public void setHasTransientState(View view, boolean hasTransientState);
57    }
58
59    static class BaseViewCompatImpl implements ViewCompatImpl {
60        public boolean canScrollHorizontally(View v, int direction) {
61            return false;
62        }
63        public boolean canScrollVertically(View v, int direction) {
64            return false;
65        }
66        public int getOverScrollMode(View v) {
67            return OVER_SCROLL_NEVER;
68        }
69        public void setOverScrollMode(View v, int mode) {
70            // Do nothing; API doesn't exist
71        }
72        public void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate) {
73            // Do nothing; API doesn't exist
74        }
75        public void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) {
76            // Do nothing; API doesn't exist
77        }
78        public void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) {
79         // Do nothing; API doesn't exist
80        }
81        public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info) {
82            // Do nothing; API doesn't exist
83        }
84        public boolean hasTransientState(View view) {
85            // A view can't have transient state if transient state wasn't supported.
86            return false;
87        }
88        public void setHasTransientState(View view, boolean hasTransientState) {
89            // Do nothing; API doesn't exist
90        }
91    }
92
93    static class GBViewCompatImpl extends BaseViewCompatImpl {
94        @Override
95        public int getOverScrollMode(View v) {
96            return ViewCompatGingerbread.getOverScrollMode(v);
97        }
98        @Override
99        public void setOverScrollMode(View v, int mode) {
100            ViewCompatGingerbread.setOverScrollMode(v, mode);
101        }
102    }
103
104    static class ICSViewCompatImpl extends GBViewCompatImpl {
105        @Override
106        public boolean canScrollHorizontally(View v, int direction) {
107            return ViewCompatICS.canScrollHorizontally(v, direction);
108        }
109        @Override
110        public boolean canScrollVertically(View v, int direction) {
111            return ViewCompatICS.canScrollVertically(v, direction);
112        }
113        @Override
114        public void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) {
115            ViewCompatICS.onPopulateAccessibilityEvent(v, event);
116        }
117        @Override
118        public void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) {
119            ViewCompatICS.onInitializeAccessibilityEvent(v, event);
120        }
121        @Override
122        public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info) {
123            ViewCompatICS.onInitializeAccessibilityNodeInfo(v, info.getInfo());
124        }
125        @Override
126        public void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate) {
127            ViewCompatICS.setAccessibilityDelegate(v, delegate.getBridge());
128        }
129    }
130
131    static class JBViewCompatImpl extends ICSViewCompatImpl {
132        @Override
133        public boolean hasTransientState(View view) {
134            return ViewCompatJB.hasTransientState(view);
135        }
136        @Override
137        public void setHasTransientState(View view, boolean hasTransientState) {
138            ViewCompatJB.setHasTransientState(view, hasTransientState);
139        }
140    }
141
142    static final ViewCompatImpl IMPL;
143    static {
144        final int version = android.os.Build.VERSION.SDK_INT;
145        if (version >= 16 || android.os.Build.VERSION.CODENAME.equals("JellyBean")) {
146            IMPL = new JBViewCompatImpl();
147        } else if (version >= 14) {
148            IMPL = new ICSViewCompatImpl();
149        } else if (version >= 9) {
150            IMPL = new GBViewCompatImpl();
151        } else {
152            IMPL = new BaseViewCompatImpl();
153        }
154    }
155
156    /**
157     * Check if this view can be scrolled horizontally in a certain direction.
158     *
159     * @param v The View against which to invoke the method.
160     * @param direction Negative to check scrolling left, positive to check scrolling right.
161     * @return true if this view can be scrolled in the specified direction, false otherwise.
162     */
163    public static boolean canScrollHorizontally(View v, int direction) {
164        return IMPL.canScrollHorizontally(v, direction);
165    }
166
167    /**
168     * Check if this view can be scrolled vertically in a certain direction.
169     *
170     * @param v The View against which to invoke the method.
171     * @param direction Negative to check scrolling up, positive to check scrolling down.
172     * @return true if this view can be scrolled in the specified direction, false otherwise.
173     */
174    public static boolean canScrollVertically(View v, int direction) {
175        return IMPL.canScrollVertically(v, direction);
176    }
177
178    /**
179     * Returns the over-scroll mode for this view. The result will be
180     * one of {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}
181     * (allow over-scrolling only if the view content is larger than the container),
182     * or {@link #OVER_SCROLL_NEVER}.
183     *
184     * @param v The View against which to invoke the method.
185     * @return This view's over-scroll mode.
186     */
187    public static int getOverScrollMode(View v) {
188        return IMPL.getOverScrollMode(v);
189    }
190
191    /**
192     * Set the over-scroll mode for this view. Valid over-scroll modes are
193     * {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS}
194     * (allow over-scrolling only if the view content is larger than the container),
195     * or {@link #OVER_SCROLL_NEVER}.
196     *
197     * Setting the over-scroll mode of a view will have an effect only if the
198     * view is capable of scrolling.
199     *
200     * @param v The View against which to invoke the method.
201     * @param overScrollMode The new over-scroll mode for this view.
202     */
203    public static void setOverScrollMode(View v, int overScrollMode) {
204        IMPL.setOverScrollMode(v, overScrollMode);
205    }
206
207    /**
208     * Called from {@link View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)}
209     * giving a chance to this View to populate the accessibility event with its
210     * text content. While this method is free to modify event
211     * attributes other than text content, doing so should normally be performed in
212     * {@link View#onInitializeAccessibilityEvent(AccessibilityEvent)}.
213     * <p>
214     * Example: Adding formatted date string to an accessibility event in addition
215     *          to the text added by the super implementation:
216     * <pre> public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
217     *     super.onPopulateAccessibilityEvent(event);
218     *     final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY;
219     *     String selectedDateUtterance = DateUtils.formatDateTime(mContext,
220     *         mCurrentDate.getTimeInMillis(), flags);
221     *     event.getText().add(selectedDateUtterance);
222     * }</pre>
223     * <p>
224     * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling
225     * {@link View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its
226     * {@link android.view.View.AccessibilityDelegate#onPopulateAccessibilityEvent(View,
227     *  AccessibilityEvent)}
228     * is responsible for handling this call.
229     * </p>
230     * <p class="note"><strong>Note:</strong> Always call the super implementation before adding
231     * information to the event, in case the default implementation has basic information to add.
232     * </p>
233     *
234     * @param v The View against which to invoke the method.
235     * @param event The accessibility event which to populate.
236     *
237     * @see View#sendAccessibilityEvent(int)
238     * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
239     */
240    public static void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) {
241        IMPL.onPopulateAccessibilityEvent(v, event);
242    }
243
244    /**
245     * Initializes an {@link AccessibilityEvent} with information about
246     * this View which is the event source. In other words, the source of
247     * an accessibility event is the view whose state change triggered firing
248     * the event.
249     * <p>
250     * Example: Setting the password property of an event in addition
251     *          to properties set by the super implementation:
252     * <pre> public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
253     *     super.onInitializeAccessibilityEvent(event);
254     *     event.setPassword(true);
255     * }</pre>
256     * <p>
257     * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling
258     * {@link View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its
259     * {@link android.view.View.AccessibilityDelegate#onInitializeAccessibilityEvent(View,
260     *  AccessibilityEvent)}
261     * is responsible for handling this call.
262     * </p>
263     * <p class="note"><strong>Note:</strong> Always call the super implementation before adding
264     * information to the event, in case the default implementation has basic information to add.
265     * </p>
266     *
267     * @param v The View against which to invoke the method.
268     * @param event The event to initialize.
269     *
270     * @see View#sendAccessibilityEvent(int)
271     * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
272     */
273    public static void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) {
274        IMPL.onInitializeAccessibilityEvent(v, event);
275    }
276
277    /**
278     * Initializes an {@link android.view.accessibility.AccessibilityNodeInfo} with information
279     * about this view. The base implementation sets:
280     * <ul>
281     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setParent(View)},</li>
282     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setBoundsInParent(Rect)},</li>
283     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setBoundsInScreen(Rect)},</li>
284     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setPackageName(CharSequence)},</li>
285     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setClassName(CharSequence)},</li>
286     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setContentDescription(CharSequence)},</li>
287     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setEnabled(boolean)},</li>
288     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setClickable(boolean)},</li>
289     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setFocusable(boolean)},</li>
290     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setFocused(boolean)},</li>
291     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setLongClickable(boolean)},</li>
292     * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setSelected(boolean)},</li>
293     * </ul>
294     * <p>
295     * Subclasses should override this method, call the super implementation,
296     * and set additional attributes.
297     * </p>
298     * <p>
299     * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling
300     * {@link View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its
301     * {@link android.view.View.AccessibilityDelegate#onInitializeAccessibilityNodeInfo(View,
302     *  android.view.accessibility.AccessibilityNodeInfo)}
303     * is responsible for handling this call.
304     * </p>
305     *
306     * @param v The View against which to invoke the method.
307     * @param info The instance to initialize.
308     */
309    public static void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info) {
310        IMPL.onInitializeAccessibilityNodeInfo(v, info);
311    }
312
313    /**
314     * Sets a delegate for implementing accessibility support via compositon as
315     * opposed to inheritance. The delegate's primary use is for implementing
316     * backwards compatible widgets. For more details see
317     * {@link android.view.View.AccessibilityDelegate}.
318     *
319     * @param v The View against which to invoke the method.
320     * @param delegate The delegate instance.
321     *
322     * @see android.view.View.AccessibilityDelegate
323     */
324    public static void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate) {
325        IMPL.setAccessibilityDelegate(v, delegate);
326    }
327
328    /**
329     * Indicates whether the view is currently tracking transient state that the
330     * app should not need to concern itself with saving and restoring, but that
331     * the framework should take special note to preserve when possible.
332     *
333     * @param view View to check for transient state
334     * @return true if the view has transient state
335     */
336    public static boolean hasTransientState(View view) {
337        return IMPL.hasTransientState(view);
338    }
339
340    /**
341     * Set whether this view is currently tracking transient state that the
342     * framework should attempt to preserve when possible.
343     *
344     * @param view View tracking transient state
345     * @param hasTransientState true if this view has transient state
346     */
347    public static void setHasTransientState(View view, boolean hasTransientState) {
348        IMPL.setHasTransientState(view, hasTransientState);
349    }
350}
351