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.graphics.Insets; 25 import android.os.Build; 26 import android.os.Bundle; 27 import android.view.WindowInsets; 28 29 import com.android.launcher3.DeviceProfile; 30 import com.android.launcher3.InvariantDeviceProfile; 31 import com.android.launcher3.Launcher; 32 import com.android.launcher3.LauncherAppState; 33 import com.android.launcher3.LauncherState; 34 import com.android.launcher3.R; 35 import com.android.launcher3.util.ResourceBasedOverride; 36 37 import java.util.concurrent.ExecutionException; 38 import java.util.function.Function; 39 import java.util.function.Supplier; 40 41 /** 42 * Class to handle requests from tests 43 */ 44 @TargetApi(Build.VERSION_CODES.Q) 45 public class TestInformationHandler implements ResourceBasedOverride { 46 newInstance(Context context)47 public static TestInformationHandler newInstance(Context context) { 48 return Overrides.getObject(TestInformationHandler.class, 49 context, R.string.test_information_handler_class); 50 } 51 52 protected Context mContext; 53 protected DeviceProfile mDeviceProfile; 54 protected LauncherAppState mLauncherAppState; 55 init(Context context)56 public void init(Context context) { 57 mContext = context; 58 mDeviceProfile = InvariantDeviceProfile.INSTANCE. 59 get(context).getDeviceProfile(context); 60 mLauncherAppState = LauncherAppState.getInstanceNoCreate(); 61 } 62 call(String method)63 public Bundle call(String method) { 64 final Bundle response = new Bundle(); 65 switch (method) { 66 case TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT: { 67 return getLauncherUIProperty(Bundle::putInt, l -> { 68 final float progress = LauncherState.NORMAL.getVerticalProgress(l) 69 - LauncherState.ALL_APPS.getVerticalProgress(l); 70 final float distance = l.getAllAppsController().getShiftRange() * progress; 71 return (int) distance; 72 }); 73 } 74 75 case TestProtocol.REQUEST_IS_LAUNCHER_INITIALIZED: { 76 return getUIProperty(Bundle::putBoolean, t -> isLauncherInitialized(), () -> true); 77 } 78 79 case TestProtocol.REQUEST_FREEZE_APP_LIST: 80 return getLauncherUIProperty(Bundle::putBoolean, l -> { 81 l.getAppsView().getAppsStore().enableDeferUpdates(DEFER_UPDATES_TEST); 82 return true; 83 }); 84 case TestProtocol.REQUEST_UNFREEZE_APP_LIST: 85 return getLauncherUIProperty(Bundle::putBoolean, l -> { 86 l.getAppsView().getAppsStore().disableDeferUpdates(DEFER_UPDATES_TEST); 87 return true; 88 }); 89 90 case TestProtocol.REQUEST_APPS_LIST_SCROLL_Y: { 91 return getLauncherUIProperty(Bundle::putInt, 92 l -> l.getAppsView().getActiveRecyclerView().getCurrentScrollY()); 93 } 94 95 case TestProtocol.REQUEST_WINDOW_INSETS: { 96 return getUIProperty(Bundle::putParcelable, a -> { 97 WindowInsets insets = a.getWindow() 98 .getDecorView().getRootWindowInsets(); 99 return Insets.max( 100 insets.getSystemGestureInsets(), insets.getSystemWindowInsets()); 101 }, this::getCurrentActivity); 102 } 103 104 case TestProtocol.REQUEST_ICON_HEIGHT: { 105 response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, 106 mDeviceProfile.allAppsCellHeightPx); 107 return response; 108 } 109 110 case TestProtocol.REQUEST_MOCK_SENSOR_ROTATION: 111 TestProtocol.sDisableSensorRotation = true; 112 return response; 113 114 default: 115 return null; 116 } 117 } 118 119 protected boolean isLauncherInitialized() { 120 return Launcher.ACTIVITY_TRACKER.getCreatedActivity() == null 121 || LauncherAppState.getInstance(mContext).getModel().isModelLoaded(); 122 } 123 124 protected Activity getCurrentActivity() { 125 return Launcher.ACTIVITY_TRACKER.getCreatedActivity(); 126 } 127 128 /** 129 * Returns the result by getting a Launcher property on UI thread 130 */ 131 public static <T> Bundle getLauncherUIProperty( 132 BundleSetter<T> bundleSetter, Function<Launcher, T> provider) { 133 return getUIProperty(bundleSetter, provider, Launcher.ACTIVITY_TRACKER::getCreatedActivity); 134 } 135 136 /** 137 * Returns the result by getting a generic property on UI thread 138 */ 139 private static <S, T> Bundle getUIProperty( 140 BundleSetter<T> bundleSetter, Function<S, T> provider, Supplier<S> targetSupplier) { 141 try { 142 return MAIN_EXECUTOR.submit(() -> { 143 S target = targetSupplier.get(); 144 if (target == null) { 145 return null; 146 } 147 T value = provider.apply(target); 148 Bundle response = new Bundle(); 149 bundleSetter.set(response, TestProtocol.TEST_INFO_RESPONSE_FIELD, value); 150 return response; 151 }).get(); 152 } catch (ExecutionException | InterruptedException e) { 153 throw new RuntimeException(e); 154 } 155 } 156 157 /** 158 * Generic interface for setting a fiend in bundle 159 * @param <T> the type of value being set 160 */ 161 public interface BundleSetter<T> { 162 163 /** 164 * Sets any generic property to the bundle 165 */ 166 void set(Bundle b, String key, T value); 167 } 168 } 169