1/* 2 * Copyright (C) 2017 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 17package com.android.launcher3.uioverrides; 18 19import static android.view.View.VISIBLE; 20import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; 21import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON; 22import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; 23import static com.android.launcher3.LauncherState.ALL_APPS; 24import static com.android.launcher3.LauncherState.NORMAL; 25import static com.android.launcher3.LauncherState.OVERVIEW; 26import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN; 27import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN; 28import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; 29 30import android.animation.AnimatorSet; 31import android.animation.ValueAnimator; 32import android.app.Activity; 33import android.content.Context; 34import android.os.CancellationSignal; 35import android.util.Base64; 36 37import com.android.launcher3.AbstractFloatingView; 38import com.android.launcher3.DeviceProfile; 39import com.android.launcher3.Launcher; 40import com.android.launcher3.LauncherAppTransitionManagerImpl; 41import com.android.launcher3.LauncherState; 42import com.android.launcher3.LauncherStateManager; 43import com.android.launcher3.LauncherStateManager.StateHandler; 44import com.android.launcher3.Utilities; 45import com.android.launcher3.anim.AnimatorPlaybackController; 46import com.android.launcher3.util.TouchController; 47import com.android.quickstep.OverviewInteractionState; 48import com.android.quickstep.RecentsModel; 49import com.android.quickstep.util.RemoteAnimationTargetSet; 50import com.android.quickstep.util.RemoteFadeOutAnimationListener; 51import com.android.quickstep.views.RecentsView; 52import com.android.systemui.shared.system.ActivityCompat; 53import com.android.systemui.shared.system.WindowManagerWrapper; 54 55import java.io.ByteArrayOutputStream; 56import java.io.PrintWriter; 57import java.util.zip.Deflater; 58 59public class UiFactory { 60 61 public static TouchController[] createTouchControllers(Launcher launcher) { 62 boolean swipeUpEnabled = OverviewInteractionState.getInstance(launcher) 63 .isSwipeUpGestureEnabled(); 64 if (!swipeUpEnabled) { 65 return new TouchController[] { 66 launcher.getDragController(), 67 new OverviewToAllAppsTouchController(launcher), 68 new LauncherTaskViewController(launcher)}; 69 } 70 if (launcher.getDeviceProfile().isVerticalBarLayout()) { 71 return new TouchController[] { 72 launcher.getDragController(), 73 new OverviewToAllAppsTouchController(launcher), 74 new LandscapeEdgeSwipeController(launcher), 75 new LauncherTaskViewController(launcher)}; 76 } else { 77 return new TouchController[] { 78 launcher.getDragController(), 79 new PortraitStatesTouchController(launcher), 80 new LauncherTaskViewController(launcher)}; 81 } 82 } 83 84 public static void setOnTouchControllersChangedListener(Context context, Runnable listener) { 85 OverviewInteractionState.getInstance(context).setOnSwipeUpSettingChangedListener(listener); 86 } 87 88 public static StateHandler[] getStateHandler(Launcher launcher) { 89 return new StateHandler[] {launcher.getAllAppsController(), launcher.getWorkspace(), 90 new RecentsViewStateController(launcher), new BackButtonAlphaHandler(launcher)}; 91 } 92 93 /** 94 * Sets the back button visibility based on the current state/window focus. 95 */ 96 public static void onLauncherStateOrFocusChanged(Launcher launcher) { 97 boolean shouldBackButtonBeHidden = launcher != null 98 && launcher.getStateManager().getState().hideBackButton 99 && launcher.hasWindowFocus(); 100 if (shouldBackButtonBeHidden) { 101 // Show the back button if there is a floating view visible. 102 shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenViewWithType(launcher, 103 TYPE_ALL & ~TYPE_HIDE_BACK_BUTTON) == null; 104 } 105 OverviewInteractionState.getInstance(launcher) 106 .setBackButtonAlpha(shouldBackButtonBeHidden ? 0 : 1, true /* animate */); 107 } 108 109 public static void resetOverview(Launcher launcher) { 110 RecentsView recents = launcher.getOverviewPanel(); 111 recents.reset(); 112 } 113 114 public static void onCreate(Launcher launcher) { 115 if (!launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) { 116 launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() { 117 @Override 118 public void onStateSetImmediately(LauncherState state) { 119 } 120 121 @Override 122 public void onStateTransitionStart(LauncherState toState) { 123 } 124 125 @Override 126 public void onStateTransitionComplete(LauncherState finalState) { 127 boolean swipeUpEnabled = OverviewInteractionState.getInstance(launcher) 128 .isSwipeUpGestureEnabled(); 129 LauncherState prevState = launcher.getStateManager().getLastState(); 130 131 if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled 132 && finalState == ALL_APPS && prevState == NORMAL))) { 133 launcher.getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply(); 134 launcher.getStateManager().removeStateListener(this); 135 } 136 } 137 }); 138 } 139 140 if (!launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)) { 141 launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() { 142 @Override 143 public void onStateSetImmediately(LauncherState state) { 144 } 145 146 @Override 147 public void onStateTransitionStart(LauncherState toState) { 148 } 149 150 @Override 151 public void onStateTransitionComplete(LauncherState finalState) { 152 LauncherState prevState = launcher.getStateManager().getLastState(); 153 154 if (finalState == ALL_APPS && prevState == OVERVIEW) { 155 launcher.getSharedPrefs().edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply(); 156 launcher.getStateManager().removeStateListener(this); 157 } 158 } 159 }); 160 } 161 } 162 163 public static void onStart(Context context) { 164 RecentsModel model = RecentsModel.getInstance(context); 165 if (model != null) { 166 model.onStart(); 167 } 168 } 169 170 public static void onLauncherStateOrResumeChanged(Launcher launcher) { 171 LauncherState state = launcher.getStateManager().getState(); 172 DeviceProfile profile = launcher.getDeviceProfile(); 173 WindowManagerWrapper.getInstance().setShelfHeight( 174 state != ALL_APPS && launcher.isUserActive() && !profile.isVerticalBarLayout(), 175 profile.hotseatBarSizePx); 176 177 if (state == NORMAL) { 178 launcher.<RecentsView>getOverviewPanel().setSwipeDownShouldLaunchApp(false); 179 } 180 } 181 182 public static void onTrimMemory(Context context, int level) { 183 RecentsModel model = RecentsModel.getInstance(context); 184 if (model != null) { 185 model.onTrimMemory(level); 186 } 187 } 188 189 public static void useFadeOutAnimationForLauncherStart(Launcher launcher, 190 CancellationSignal cancellationSignal) { 191 LauncherAppTransitionManagerImpl appTransitionManager = 192 (LauncherAppTransitionManagerImpl) launcher.getAppTransitionManager(); 193 appTransitionManager.setRemoteAnimationProvider((targets) -> { 194 195 // On the first call clear the reference. 196 cancellationSignal.cancel(); 197 198 ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0); 199 fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(targets)); 200 AnimatorSet anim = new AnimatorSet(); 201 anim.play(fadeAnimation); 202 return anim; 203 }, cancellationSignal); 204 } 205 206 public static boolean dumpActivity(Activity activity, PrintWriter writer) { 207 if (!Utilities.IS_DEBUG_DEVICE) { 208 return false; 209 } 210 ByteArrayOutputStream out = new ByteArrayOutputStream(); 211 if (!(new ActivityCompat(activity).encodeViewHierarchy(out))) { 212 return false; 213 } 214 215 Deflater deflater = new Deflater(); 216 deflater.setInput(out.toByteArray()); 217 deflater.finish(); 218 219 out.reset(); 220 byte[] buffer = new byte[1024]; 221 while (!deflater.finished()) { 222 int count = deflater.deflate(buffer); // returns the generated code... index 223 out.write(buffer, 0, count); 224 } 225 226 writer.println("--encoded-view-dump-v0--"); 227 writer.println(Base64.encodeToString( 228 out.toByteArray(), Base64.NO_WRAP | Base64.NO_PADDING)); 229 return true; 230 } 231 232 public static void prepareToShowOverview(Launcher launcher) { 233 RecentsView overview = launcher.getOverviewPanel(); 234 if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) { 235 SCALE_PROPERTY.set(overview, 1.33f); 236 } 237 } 238 239 private static class LauncherTaskViewController extends TaskViewTouchController<Launcher> { 240 241 public LauncherTaskViewController(Launcher activity) { 242 super(activity); 243 } 244 245 @Override 246 protected boolean isRecentsInteractive() { 247 return mActivity.isInState(OVERVIEW); 248 } 249 250 @Override 251 protected void onUserControlledAnimationCreated(AnimatorPlaybackController animController) { 252 mActivity.getStateManager().setCurrentUserControlledAnimation(animController); 253 } 254 } 255} 256