1 /* 2 * Portions of this code came from frameworks/base/core/java/android/view/ViewConfiguration.java, 3 * which contains the following license text: 4 * 5 * Copyright (C) 2006 The Android Open Source Project 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 */ 20 21 package org.robolectric.shadows; 22 23 import static android.os.Build.VERSION_CODES.Q; 24 import static org.robolectric.util.reflector.Reflector.reflector; 25 26 import android.content.Context; 27 import android.content.res.Resources; 28 import android.util.DisplayMetrics; 29 import android.util.SparseArray; 30 import android.view.ViewConfiguration; 31 import org.robolectric.RuntimeEnvironment; 32 import org.robolectric.annotation.Implementation; 33 import org.robolectric.annotation.Implements; 34 import org.robolectric.annotation.RealObject; 35 import org.robolectric.annotation.Resetter; 36 import org.robolectric.shadow.api.Shadow; 37 import org.robolectric.util.ReflectionHelpers.ClassParameter; 38 import org.robolectric.util.reflector.Accessor; 39 import org.robolectric.util.reflector.ForType; 40 import org.robolectric.util.reflector.Static; 41 42 @SuppressWarnings({"UnusedDeclaration"}) 43 @Implements(ViewConfiguration.class) 44 public class ShadowViewConfiguration { 45 @RealObject ViewConfiguration realViewConfiguration; 46 47 private static final int PRESSED_STATE_DURATION = 125; 48 private static final int LONG_PRESS_TIMEOUT = 500; 49 private static final int TAP_TIMEOUT = 115; 50 private static final int DOUBLE_TAP_TIMEOUT = 300; 51 private static final int TOUCH_SLOP = 16; 52 private static final int PAGING_TOUCH_SLOP = TOUCH_SLOP * 2; 53 private static final int DOUBLE_TAP_SLOP = 100; 54 private static final int WINDOW_TOUCH_SLOP = 16; 55 56 // The previous hardcoded value for draw cache size. Some screenshot tests depend on this value. 57 private static final int MIN_MAXIMUM_DRAWING_CACHE_SIZE = 480 * 800 * 4; 58 59 private int edgeSlop; 60 private int fadingEdgeLength; 61 private int touchSlop; 62 private int pagingTouchSlop; 63 private int doubleTapSlop; 64 private int windowTouchSlop; 65 private int maximumDrawingCacheSize; 66 private static boolean hasPermanentMenuKey = true; 67 68 @Implementation __constructor__(Context context)69 protected void __constructor__(Context context) { 70 Shadow.invokeConstructor( 71 ViewConfiguration.class, 72 realViewConfiguration, 73 ClassParameter.from(Context.class, context)); 74 final Resources resources = context.getResources(); 75 final DisplayMetrics metrics = resources.getDisplayMetrics(); 76 float density = metrics.density; 77 edgeSlop = (int) (density * ViewConfiguration.getEdgeSlop() + 0.5f); 78 fadingEdgeLength = (int) (density * ViewConfiguration.getFadingEdgeLength() + 0.5f); 79 touchSlop = (int) (density * TOUCH_SLOP + 0.5f); 80 pagingTouchSlop = (int) (density * PAGING_TOUCH_SLOP + 0.5f); 81 doubleTapSlop = (int) (density * DOUBLE_TAP_SLOP + 0.5f); 82 windowTouchSlop = (int) (density * WINDOW_TOUCH_SLOP + 0.5f); 83 // Some screenshot tests were misconfigured and try to draw very large views onto small 84 // screens using SW rendering. To avoid breaking these tests, we keep the drawing cache a bit 85 // larger when screens are configured to be arbitrarily small. 86 // TODO(hoisie): Investigate removing this Math.max logic. 87 maximumDrawingCacheSize = 88 Math.max(MIN_MAXIMUM_DRAWING_CACHE_SIZE, 4 * metrics.widthPixels * metrics.heightPixels); 89 if (RuntimeEnvironment.getApiLevel() >= Q && !useRealMinScalingSpan()) { 90 reflector(ViewConfigurationReflector.class, realViewConfiguration).setMinScalingSpan(0); 91 } 92 } 93 94 @Implementation getScaledFadingEdgeLength()95 protected int getScaledFadingEdgeLength() { 96 return fadingEdgeLength; 97 } 98 99 @Implementation getPressedStateDuration()100 protected static int getPressedStateDuration() { 101 return PRESSED_STATE_DURATION; 102 } 103 104 @Implementation getLongPressTimeout()105 protected static int getLongPressTimeout() { 106 return LONG_PRESS_TIMEOUT; 107 } 108 109 @Implementation getTapTimeout()110 protected static int getTapTimeout() { 111 return TAP_TIMEOUT; 112 } 113 114 @Implementation getDoubleTapTimeout()115 protected static int getDoubleTapTimeout() { 116 return DOUBLE_TAP_TIMEOUT; 117 } 118 119 @Implementation getScaledEdgeSlop()120 protected int getScaledEdgeSlop() { 121 return edgeSlop; 122 } 123 124 @Implementation getTouchSlop()125 protected static int getTouchSlop() { 126 return TOUCH_SLOP; 127 } 128 129 @Implementation getScaledTouchSlop()130 protected int getScaledTouchSlop() { 131 return touchSlop; 132 } 133 134 @Implementation getScaledPagingTouchSlop()135 protected int getScaledPagingTouchSlop() { 136 return pagingTouchSlop; 137 } 138 139 @Implementation getScaledDoubleTapSlop()140 protected int getScaledDoubleTapSlop() { 141 return doubleTapSlop; 142 } 143 144 @Implementation getWindowTouchSlop()145 protected static int getWindowTouchSlop() { 146 return WINDOW_TOUCH_SLOP; 147 } 148 149 @Implementation getScaledWindowTouchSlop()150 protected int getScaledWindowTouchSlop() { 151 return windowTouchSlop; 152 } 153 154 @Implementation getScaledMaximumDrawingCacheSize()155 protected int getScaledMaximumDrawingCacheSize() { 156 return maximumDrawingCacheSize; 157 } 158 159 @Implementation hasPermanentMenuKey()160 protected boolean hasPermanentMenuKey() { 161 return hasPermanentMenuKey; 162 } 163 setHasPermanentMenuKey(boolean value)164 public static void setHasPermanentMenuKey(boolean value) { 165 hasPermanentMenuKey = value; 166 } 167 168 @ForType(ViewConfiguration.class) 169 interface ViewConfigurationReflector { 170 171 @Accessor("mMinScalingSpan") setMinScalingSpan(int minScalingSpan)172 void setMinScalingSpan(int minScalingSpan); 173 174 @Static 175 @Accessor("sConfigurations") getStaticCache()176 SparseArray<ViewConfiguration> getStaticCache(); 177 } 178 179 /** 180 * Due to overzealous shadowing, the return value of {@link 181 * ViewConfiguration#getScaledMinimumScalingSpan} was previously 0, when it should be around 170 182 * for an mdpi display with density factor 1. This issue has been fixed, but there may be tests 183 * that emit touch events to trigger pinching/spreading that rely on the previous incorrect 184 * behavior. These tests have an option to use a system property to enable this previous bug. 185 */ useRealMinScalingSpan()186 private static boolean useRealMinScalingSpan() { 187 return Boolean.parseBoolean(System.getProperty("robolectric.useRealMinScalingSpan", "true")); 188 } 189 190 @Resetter reset()191 public static void reset() { 192 SparseArray<ViewConfiguration> configurations = 193 reflector(ViewConfigurationReflector.class).getStaticCache(); 194 configurations.clear(); 195 } 196 } 197