• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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