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