1 /* 2 * Copyright (C) 2016 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 17 package androidx.transition; 18 19 import android.graphics.Matrix; 20 import android.graphics.Rect; 21 import android.os.Build; 22 import android.util.Log; 23 import android.util.Property; 24 import android.view.View; 25 26 import androidx.annotation.NonNull; 27 import androidx.annotation.Nullable; 28 import androidx.core.view.ViewCompat; 29 30 import java.lang.reflect.Field; 31 32 /** 33 * Compatibility utilities for platform features of {@link View}. 34 */ 35 class ViewUtils { 36 37 private static final ViewUtilsBase IMPL; 38 private static final String TAG = "ViewUtils"; 39 40 private static Field sViewFlagsField; 41 private static boolean sViewFlagsFieldFetched; 42 private static final int VISIBILITY_MASK = 0x0000000C; 43 44 static { 45 if (Build.VERSION.SDK_INT >= 22) { 46 IMPL = new ViewUtilsApi22(); 47 } else if (Build.VERSION.SDK_INT >= 21) { 48 IMPL = new ViewUtilsApi21(); 49 } else if (Build.VERSION.SDK_INT >= 19) { 50 IMPL = new ViewUtilsApi19(); 51 } else { 52 IMPL = new ViewUtilsBase(); 53 } 54 } 55 56 /** 57 * A {@link Property} for animating transitionAlpha value of a View. 58 */ 59 static final Property<View, Float> TRANSITION_ALPHA = 60 new Property<View, Float>(Float.class, "translationAlpha") { 61 62 @Override 63 public Float get(View view) { 64 return getTransitionAlpha(view); 65 } 66 67 @Override 68 public void set(View view, Float alpha) { 69 setTransitionAlpha(view, alpha); 70 } 71 72 }; 73 74 static final Property<View, Rect> CLIP_BOUNDS = 75 new Property<View, Rect>(Rect.class, "clipBounds") { 76 77 @Override 78 public Rect get(View view) { 79 return ViewCompat.getClipBounds(view); 80 } 81 82 @Override 83 public void set(View view, Rect clipBounds) { 84 ViewCompat.setClipBounds(view, clipBounds); 85 } 86 87 }; 88 89 /** 90 * Backward-compatible {@link View#getOverlay()}. 91 */ 92 static ViewOverlayImpl getOverlay(@NonNull View view) { 93 if (Build.VERSION.SDK_INT >= 18) { 94 return new ViewOverlayApi18(view); 95 } 96 return ViewOverlayApi14.createFrom(view); 97 } 98 99 /** 100 * Backward-compatible {@link View#getWindowId()}. 101 */ 102 static WindowIdImpl getWindowId(@NonNull View view) { 103 if (Build.VERSION.SDK_INT >= 18) { 104 return new WindowIdApi18(view); 105 } 106 return new WindowIdApi14(view.getWindowToken()); 107 } 108 109 static void setTransitionAlpha(@NonNull View view, float alpha) { 110 IMPL.setTransitionAlpha(view, alpha); 111 } 112 113 static float getTransitionAlpha(@NonNull View view) { 114 return IMPL.getTransitionAlpha(view); 115 } 116 117 /** 118 * This method needs to be called before an animation using {@link #setTransitionAlpha(View, 119 * float)} in order to make its behavior backward-compatible. 120 */ 121 static void saveNonTransitionAlpha(@NonNull View view) { 122 IMPL.saveNonTransitionAlpha(view); 123 } 124 125 /** 126 * This method needs to be called after an animation using 127 * {@link #setTransitionAlpha(View, float)} if {@link #saveNonTransitionAlpha(View)} has been 128 * called. 129 */ 130 static void clearNonTransitionAlpha(@NonNull View view) { 131 IMPL.clearNonTransitionAlpha(view); 132 } 133 134 /** 135 * Copy of a hidden platform method, View#setTransitionVisibility. 136 * 137 * <p>Change the visibility of the View without triggering any other changes. This is 138 * important for transitions, where visibility changes should not adjust focus or 139 * trigger a new layout. This is only used when the visibility has already been changed 140 * and we need a transient value during an animation. When the animation completes, 141 * the original visibility value is always restored.</p> 142 * 143 * @param view The target view. 144 * @param visibility One of {@link View#VISIBLE}, {@link View#INVISIBLE}, or 145 * {@link View#GONE}. 146 */ 147 static void setTransitionVisibility(@NonNull View view, int visibility) { 148 fetchViewFlagsField(); 149 if (sViewFlagsField != null) { 150 try { 151 int viewFlags = sViewFlagsField.getInt(view); 152 sViewFlagsField.setInt(view, (viewFlags & ~VISIBILITY_MASK) | visibility); 153 } catch (IllegalAccessException e) { 154 // Do nothing 155 } 156 } 157 } 158 159 /** 160 * Modifies the input matrix such that it maps view-local coordinates to 161 * on-screen coordinates. 162 * 163 * <p>On API Level 21 and above, this includes transformation matrix applied to {@code 164 * ViewRootImpl}, but not on older platforms. This difference is balanced out by the 165 * implementation difference in other related platform APIs and their backport, such as 166 * GhostView.</p> 167 * 168 * @param view target view 169 * @param matrix input matrix to modify 170 */ 171 static void transformMatrixToGlobal(@NonNull View view, @NonNull Matrix matrix) { 172 IMPL.transformMatrixToGlobal(view, matrix); 173 } 174 175 /** 176 * Modifies the input matrix such that it maps on-screen coordinates to 177 * view-local coordinates. 178 * 179 * <p>On API Level 21 and above, this includes transformation matrix applied to {@code 180 * ViewRootImpl}, but not on older platforms. This difference is balanced out by the 181 * implementation difference in other related platform APIs and their backport, such as 182 * GhostView.</p> 183 * 184 * @param view target view 185 * @param matrix input matrix to modify 186 */ 187 static void transformMatrixToLocal(@NonNull View view, @NonNull Matrix matrix) { 188 IMPL.transformMatrixToLocal(view, matrix); 189 } 190 191 /** 192 * Sets the transformation matrix for animation. 193 * 194 * @param v The view 195 * @param m The matrix 196 */ 197 static void setAnimationMatrix(@NonNull View v, @Nullable Matrix m) { 198 IMPL.setAnimationMatrix(v, m); 199 } 200 201 /** 202 * Assign a size and position to this view. 203 * 204 * @param left Left position, relative to parent 205 * @param top Top position, relative to parent 206 * @param right Right position, relative to parent 207 * @param bottom Bottom position, relative to parent 208 */ 209 static void setLeftTopRightBottom(@NonNull View v, int left, int top, int right, int bottom) { 210 IMPL.setLeftTopRightBottom(v, left, top, right, bottom); 211 } 212 213 private static void fetchViewFlagsField() { 214 if (!sViewFlagsFieldFetched) { 215 try { 216 sViewFlagsField = View.class.getDeclaredField("mViewFlags"); 217 sViewFlagsField.setAccessible(true); 218 } catch (NoSuchFieldException e) { 219 Log.i(TAG, "fetchViewFlagsField: "); 220 } 221 sViewFlagsFieldFetched = true; 222 } 223 } 224 225 private ViewUtils() { 226 } 227 } 228