1/*
2 * Copyright (C) 2014 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.content.res.ColorStateList;
20import android.graphics.PorterDuff;
21import android.graphics.Rect;
22import android.graphics.drawable.Drawable;
23import android.os.Build;
24import android.view.View;
25import android.view.ViewParent;
26import android.view.WindowInsets;
27
28class ViewCompatLollipop {
29
30    private static ThreadLocal<Rect> sThreadLocalRect;
31
32    public static void setTransitionName(View view, String transitionName) {
33        view.setTransitionName(transitionName);
34    }
35
36    public static String getTransitionName(View view) {
37        return view.getTransitionName();
38    }
39
40    public static void requestApplyInsets(View view) {
41        view.requestApplyInsets();
42    }
43
44    public static void setElevation(View view, float elevation) {
45        view.setElevation(elevation);
46    }
47
48    public static float getElevation(View view) {
49        return view.getElevation();
50    }
51
52    public static void setTranslationZ(View view, float translationZ) {
53        view.setTranslationZ(translationZ);
54    }
55
56    public static float getTranslationZ(View view) {
57        return view.getTranslationZ();
58    }
59
60    public static void setOnApplyWindowInsetsListener(View view,
61            final OnApplyWindowInsetsListener listener) {
62        if (listener == null) {
63            view.setOnApplyWindowInsetsListener(null);
64        } else {
65            view.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
66                @Override
67                public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
68                    // Wrap the framework insets in our wrapper
69                    WindowInsetsCompatApi21 insets = new WindowInsetsCompatApi21(windowInsets);
70                    // Give the listener a chance to use the wrapped insets
71                    insets = (WindowInsetsCompatApi21) listener.onApplyWindowInsets(view, insets);
72                    // Return the unwrapped insets
73                    return insets.unwrap();
74                }
75            });
76        }
77    }
78
79    public static boolean isImportantForAccessibility(View view) {
80        return view.isImportantForAccessibility();
81    }
82
83    static ColorStateList getBackgroundTintList(View view) {
84        return view.getBackgroundTintList();
85    }
86
87    static void setBackgroundTintList(View view, ColorStateList tintList) {
88        view.setBackgroundTintList(tintList);
89
90        if (Build.VERSION.SDK_INT == 21) {
91            // Work around a bug in L that did not update the state of the background
92            // after applying the tint
93            Drawable background = view.getBackground();
94            boolean hasTint = (view.getBackgroundTintList() != null)
95                    && (view.getBackgroundTintMode() != null);
96            if ((background != null) && hasTint) {
97                if (background.isStateful()) {
98                    background.setState(view.getDrawableState());
99                }
100                view.setBackground(background);
101            }
102        }
103    }
104
105    static PorterDuff.Mode getBackgroundTintMode(View view) {
106        return view.getBackgroundTintMode();
107    }
108
109    static void setBackgroundTintMode(View view, PorterDuff.Mode mode) {
110        view.setBackgroundTintMode(mode);
111
112        if (Build.VERSION.SDK_INT == 21) {
113            // Work around a bug in L that did not update the state of the background
114            // after applying the tint
115            Drawable background = view.getBackground();
116            boolean hasTint = (view.getBackgroundTintList() != null)
117                    && (view.getBackgroundTintMode() != null);
118            if ((background != null) && hasTint) {
119                if (background.isStateful()) {
120                    background.setState(view.getDrawableState());
121                }
122                view.setBackground(background);
123            }
124        }
125    }
126
127    public static WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
128        if (insets instanceof WindowInsetsCompatApi21) {
129            // First unwrap the compat version so that we have the framework instance
130            WindowInsets unwrapped = ((WindowInsetsCompatApi21) insets).unwrap();
131            // Now call onApplyWindowInsets
132            WindowInsets result = v.onApplyWindowInsets(unwrapped);
133
134            if (result != unwrapped) {
135                // ...and return a newly wrapped compat insets instance if different
136                insets = new WindowInsetsCompatApi21(result);
137            }
138        }
139        return insets;
140    }
141
142    public static WindowInsetsCompat dispatchApplyWindowInsets(View v, WindowInsetsCompat insets) {
143        if (insets instanceof WindowInsetsCompatApi21) {
144            // First unwrap the compat version so that we have the framework instance
145            WindowInsets unwrapped = ((WindowInsetsCompatApi21) insets).unwrap();
146            // Now call dispatchApplyWindowInsets
147            WindowInsets result = v.dispatchApplyWindowInsets(unwrapped);
148
149            if (result != unwrapped) {
150                // ...and return a newly wrapped compat insets instance if different
151                insets = new WindowInsetsCompatApi21(result);
152            }
153        }
154        return insets;
155    }
156
157    public static void setNestedScrollingEnabled(View view, boolean enabled) {
158        view.setNestedScrollingEnabled(enabled);
159    }
160
161    public static boolean isNestedScrollingEnabled(View view) {
162        return view.isNestedScrollingEnabled();
163    }
164
165    public static boolean startNestedScroll(View view, int axes) {
166        return view.startNestedScroll(axes);
167    }
168
169    public static void stopNestedScroll(View view) {
170        view.stopNestedScroll();
171    }
172
173    public static boolean hasNestedScrollingParent(View view) {
174        return view.hasNestedScrollingParent();
175    }
176
177    public static boolean dispatchNestedScroll(View view, int dxConsumed, int dyConsumed,
178            int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
179        return view.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
180                offsetInWindow);
181    }
182
183    public static boolean dispatchNestedPreScroll(View view, int dx, int dy, int[] consumed,
184            int[] offsetInWindow) {
185        return view.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
186    }
187
188    public static boolean dispatchNestedFling(View view, float velocityX, float velocityY,
189            boolean consumed) {
190        return view.dispatchNestedFling(velocityX, velocityY, consumed);
191    }
192
193    public static boolean dispatchNestedPreFling(View view, float velocityX, float velocityY) {
194        return view.dispatchNestedPreFling(velocityX, velocityY);
195    }
196
197    public static float getZ(View view) {
198        return view.getZ();
199    }
200
201    public static void setZ(View view, float z) {
202        view.setZ(z);
203    }
204
205    static void offsetTopAndBottom(final View view, final int offset) {
206        final Rect parentRect = getEmptyTempRect();
207        boolean needInvalidateWorkaround = false;
208
209        final ViewParent parent = view.getParent();
210        if (parent instanceof View) {
211            final View p = (View) parent;
212            parentRect.set(p.getLeft(), p.getTop(), p.getRight(), p.getBottom());
213            // If the view currently does not currently intersect the parent (and is therefore
214            // not displayed) we may need need to invalidate
215            needInvalidateWorkaround = !parentRect.intersects(view.getLeft(), view.getTop(),
216                    view.getRight(), view.getBottom());
217        }
218
219        // Now offset, invoking the API 11+ implementation (which contains it's own workarounds)
220        ViewCompatHC.offsetTopAndBottom(view, offset);
221
222        // The view has now been offset, so let's intersect the Rect and invalidate where
223        // the View is now displayed
224        if (needInvalidateWorkaround && parentRect.intersect(view.getLeft(), view.getTop(),
225                view.getRight(), view.getBottom())) {
226            ((View) parent).invalidate(parentRect);
227        }
228    }
229
230    static void offsetLeftAndRight(final View view, final int offset) {
231        final Rect parentRect = getEmptyTempRect();
232        boolean needInvalidateWorkaround = false;
233
234        final ViewParent parent = view.getParent();
235        if (parent instanceof View) {
236            final View p = (View) parent;
237            parentRect.set(p.getLeft(), p.getTop(), p.getRight(), p.getBottom());
238            // If the view currently does not currently intersect the parent (and is therefore
239            // not displayed) we may need need to invalidate
240            needInvalidateWorkaround = !parentRect.intersects(view.getLeft(), view.getTop(),
241                    view.getRight(), view.getBottom());
242        }
243
244        // Now offset, invoking the API 11+ implementation (which contains it's own workarounds)
245        ViewCompatHC.offsetLeftAndRight(view, offset);
246
247        // The view has now been offset, so let's intersect the Rect and invalidate where
248        // the View is now displayed
249        if (needInvalidateWorkaround && parentRect.intersect(view.getLeft(), view.getTop(),
250                view.getRight(), view.getBottom())) {
251            ((View) parent).invalidate(parentRect);
252        }
253    }
254
255    private static Rect getEmptyTempRect() {
256        if (sThreadLocalRect == null) {
257            sThreadLocalRect = new ThreadLocal<>();
258        }
259        Rect rect = sThreadLocalRect.get();
260        if (rect == null) {
261            rect = new Rect();
262            sThreadLocalRect.set(rect);
263        }
264        rect.setEmpty();
265        return rect;
266    }
267}
268