1 /*
2  * Copyright (C) 2019 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 package com.android.launcher3.testing;
17 
18 import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
19 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
20 
21 import android.annotation.TargetApi;
22 import android.app.Activity;
23 import android.content.Context;
24 import android.content.res.Resources;
25 import android.graphics.Insets;
26 import android.os.Build;
27 import android.os.Bundle;
28 import android.view.WindowInsets;
29 
30 import com.android.launcher3.DeviceProfile;
31 import com.android.launcher3.InvariantDeviceProfile;
32 import com.android.launcher3.Launcher;
33 import com.android.launcher3.LauncherAppState;
34 import com.android.launcher3.LauncherState;
35 import com.android.launcher3.R;
36 import com.android.launcher3.util.ResourceBasedOverride;
37 import com.android.launcher3.widget.picker.WidgetsFullSheet;
38 
39 import java.util.concurrent.ExecutionException;
40 import java.util.function.Function;
41 import java.util.function.Supplier;
42 
43 /**
44  * Class to handle requests from tests
45  */
46 @TargetApi(Build.VERSION_CODES.Q)
47 public class TestInformationHandler implements ResourceBasedOverride {
48 
newInstance(Context context)49     public static TestInformationHandler newInstance(Context context) {
50         return Overrides.getObject(TestInformationHandler.class,
51                 context, R.string.test_information_handler_class);
52     }
53 
54     protected Context mContext;
55     protected DeviceProfile mDeviceProfile;
56     protected LauncherAppState mLauncherAppState;
57 
init(Context context)58     public void init(Context context) {
59         mContext = context;
60         mDeviceProfile = InvariantDeviceProfile.INSTANCE.
61                 get(context).getDeviceProfile(context);
62         mLauncherAppState = LauncherAppState.getInstanceNoCreate();
63     }
64 
call(String method)65     public Bundle call(String method) {
66         return call(method, /*arg=*/ null);
67     }
68 
call(String method, String arg)69     public Bundle call(String method, String arg) {
70         final Bundle response = new Bundle();
71         switch (method) {
72             case TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT: {
73                 return getLauncherUIProperty(Bundle::putInt, l -> {
74                     final float progress = LauncherState.NORMAL.getVerticalProgress(l)
75                             - LauncherState.ALL_APPS.getVerticalProgress(l);
76                     final float distance = l.getAllAppsController().getShiftRange() * progress;
77                     return (int) distance;
78                 });
79             }
80 
81             case TestProtocol.REQUEST_IS_LAUNCHER_INITIALIZED: {
82                 return getUIProperty(Bundle::putBoolean, t -> isLauncherInitialized(), () -> true);
83             }
84 
85             case TestProtocol.REQUEST_FREEZE_APP_LIST:
86                 return getLauncherUIProperty(Bundle::putBoolean, l -> {
87                     l.getAppsView().getAppsStore().enableDeferUpdates(DEFER_UPDATES_TEST);
88                     return true;
89                 });
90             case TestProtocol.REQUEST_UNFREEZE_APP_LIST:
91                 return getLauncherUIProperty(Bundle::putBoolean, l -> {
92                     l.getAppsView().getAppsStore().disableDeferUpdates(DEFER_UPDATES_TEST);
93                     return true;
94                 });
95 
96             case TestProtocol.REQUEST_APPS_LIST_SCROLL_Y: {
97                 return getLauncherUIProperty(Bundle::putInt,
98                         l -> l.getAppsView().getActiveRecyclerView().getCurrentScrollY());
99             }
100 
101             case TestProtocol.REQUEST_WIDGETS_SCROLL_Y: {
102                 return getLauncherUIProperty(Bundle::putInt,
103                         l -> WidgetsFullSheet.getWidgetsView(l).getCurrentScrollY());
104             }
105 
106             case TestProtocol.REQUEST_TARGET_INSETS: {
107                 return getUIProperty(Bundle::putParcelable, activity -> {
108                     WindowInsets insets = activity.getWindow()
109                             .getDecorView().getRootWindowInsets();
110                     return Insets.max(
111                             insets.getSystemGestureInsets(),
112                             insets.getSystemWindowInsets());
113                 }, this::getCurrentActivity);
114             }
115 
116             case TestProtocol.REQUEST_WINDOW_INSETS: {
117                 return getUIProperty(Bundle::putParcelable, activity -> {
118                     WindowInsets insets = activity.getWindow()
119                             .getDecorView().getRootWindowInsets();
120                     return insets.getSystemWindowInsets();
121                 }, this::getCurrentActivity);
122             }
123 
124             case TestProtocol.REQUEST_ICON_HEIGHT: {
125                 response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
126                         mDeviceProfile.allAppsCellHeightPx);
127                 return response;
128             }
129 
130             case TestProtocol.REQUEST_MOCK_SENSOR_ROTATION:
131                 TestProtocol.sDisableSensorRotation = true;
132                 return response;
133 
134             case TestProtocol.REQUEST_IS_TABLET:
135                 response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, mDeviceProfile.isTablet);
136                 return response;
137 
138             case TestProtocol.REQUEST_IS_TWO_PANELS:
139                 response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
140                         mDeviceProfile.isTwoPanels);
141                 return response;
142 
143             case TestProtocol.REQUEST_SET_FORCE_PAUSE_TIMEOUT:
144                 TestProtocol.sForcePauseTimeout = Long.parseLong(arg);
145                 return response;
146 
147             case TestProtocol.REQUEST_GET_HAD_NONTEST_EVENTS:
148                 response.putBoolean(
149                         TestProtocol.TEST_INFO_RESPONSE_FIELD, TestLogging.sHadEventsNotFromTest);
150                 return response;
151 
152             case TestProtocol.REQUEST_START_DRAG_THRESHOLD: {
153                 final Resources resources = mContext.getResources();
154                 response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
155                         resources.getDimensionPixelSize(R.dimen.deep_shortcuts_start_drag_threshold)
156                                 + resources.getDimensionPixelSize(R.dimen.pre_drag_view_scale));
157                 return response;
158             }
159 
160             case TestProtocol.REQUEST_ENABLE_ROTATION:
161                 MAIN_EXECUTOR.submit(() ->
162                         Launcher.ACTIVITY_TRACKER.getCreatedActivity().getRotationHelper()
163                                 .forceAllowRotationForTesting(Boolean.parseBoolean(arg)));
164                 return null;
165 
166             default:
167                 return null;
168         }
169     }
170 
171     protected boolean isLauncherInitialized() {
172         return Launcher.ACTIVITY_TRACKER.getCreatedActivity() == null
173                 || LauncherAppState.getInstance(mContext).getModel().isModelLoaded();
174     }
175 
176     protected Activity getCurrentActivity() {
177         return Launcher.ACTIVITY_TRACKER.getCreatedActivity();
178     }
179 
180     /**
181      * Returns the result by getting a Launcher property on UI thread
182      */
183     public static <T> Bundle getLauncherUIProperty(
184             BundleSetter<T> bundleSetter, Function<Launcher, T> provider) {
185         return getUIProperty(bundleSetter, provider, Launcher.ACTIVITY_TRACKER::getCreatedActivity);
186     }
187 
188     /**
189      * Returns the result by getting a generic property on UI thread
190      */
191     private static <S, T> Bundle getUIProperty(
192             BundleSetter<T> bundleSetter, Function<S, T> provider, Supplier<S> targetSupplier) {
193         try {
194             return MAIN_EXECUTOR.submit(() -> {
195                 S target = targetSupplier.get();
196                 if (target == null) {
197                     return null;
198                 }
199                 T value = provider.apply(target);
200                 Bundle response = new Bundle();
201                 bundleSetter.set(response, TestProtocol.TEST_INFO_RESPONSE_FIELD, value);
202                 return response;
203             }).get();
204         } catch (ExecutionException | InterruptedException e) {
205             throw new RuntimeException(e);
206         }
207     }
208 
209     /**
210      * Generic interface for setting a fiend in bundle
211      *
212      * @param <T> the type of value being set
213      */
214     public interface BundleSetter<T> {
215 
216         /**
217          * Sets any generic property to the bundle
218          */
219         void set(Bundle b, String key, T value);
220     }
221 }
222