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