1 /* 2 * Copyright (C) 2021 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.taskbar; 17 18 import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_RESUMED; 19 import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR; 20 21 import android.animation.Animator; 22 import android.annotation.ColorInt; 23 import android.os.RemoteException; 24 import android.util.Log; 25 import android.view.MotionEvent; 26 import android.view.TaskTransitionSpec; 27 import android.view.WindowManagerGlobal; 28 29 import androidx.annotation.NonNull; 30 31 import com.android.launcher3.BaseQuickstepLauncher; 32 import com.android.launcher3.DeviceProfile; 33 import com.android.launcher3.LauncherState; 34 import com.android.launcher3.QuickstepTransitionManager; 35 import com.android.launcher3.R; 36 import com.android.launcher3.Utilities; 37 import com.android.launcher3.config.FeatureFlags; 38 import com.android.launcher3.logging.InstanceId; 39 import com.android.launcher3.logging.InstanceIdSequence; 40 import com.android.launcher3.model.data.ItemInfoWithIcon; 41 import com.android.launcher3.model.data.WorkspaceItemInfo; 42 import com.android.launcher3.util.OnboardingPrefs; 43 import com.android.quickstep.AnimatedFloat; 44 import com.android.quickstep.RecentsAnimationCallbacks; 45 46 import java.util.Arrays; 47 import java.util.Set; 48 import java.util.stream.Stream; 49 50 /** 51 * A data source which integrates with a Launcher instance 52 */ 53 public class LauncherTaskbarUIController extends TaskbarUIController { 54 55 private static final String TAG = "TaskbarUIController"; 56 57 private final BaseQuickstepLauncher mLauncher; 58 59 private final DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener = 60 this::onStashedInAppChanged; 61 62 // Initialized in init. 63 private AnimatedFloat mTaskbarOverrideBackgroundAlpha; 64 private TaskbarKeyguardController mKeyguardController; 65 private final TaskbarLauncherStateController 66 mTaskbarLauncherStateController = new TaskbarLauncherStateController(); 67 68 private final DeviceProfile.OnDeviceProfileChangeListener mProfileChangeListener = 69 new DeviceProfile.OnDeviceProfileChangeListener() { 70 @Override 71 public void onDeviceProfileChanged(DeviceProfile dp) { 72 mControllers.taskbarViewController.onRotationChanged( 73 mLauncher.getDeviceProfile()); 74 } 75 }; 76 LauncherTaskbarUIController(BaseQuickstepLauncher launcher)77 public LauncherTaskbarUIController(BaseQuickstepLauncher launcher) { 78 mLauncher = launcher; 79 } 80 81 @Override init(TaskbarControllers taskbarControllers)82 protected void init(TaskbarControllers taskbarControllers) { 83 super.init(taskbarControllers); 84 85 mTaskbarLauncherStateController.init(mControllers, mLauncher); 86 mTaskbarOverrideBackgroundAlpha = mControllers.taskbarDragLayerController 87 .getOverrideBackgroundAlpha(); 88 89 mLauncher.setTaskbarUIController(this); 90 mKeyguardController = taskbarControllers.taskbarKeyguardController; 91 92 onLauncherResumedOrPaused(mLauncher.hasBeenResumed(), true /* fromInit */); 93 94 onStashedInAppChanged(mLauncher.getDeviceProfile()); 95 mLauncher.addOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener); 96 mLauncher.addOnDeviceProfileChangeListener(mProfileChangeListener); 97 } 98 99 @Override onDestroy()100 protected void onDestroy() { 101 super.onDestroy(); 102 onLauncherResumedOrPaused(false); 103 mTaskbarLauncherStateController.onDestroy(); 104 105 mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener); 106 mLauncher.setTaskbarUIController(null); 107 mLauncher.removeOnDeviceProfileChangeListener(mProfileChangeListener); 108 updateTaskTransitionSpec(true); 109 } 110 111 @Override isTaskbarTouchable()112 protected boolean isTaskbarTouchable() { 113 return !mTaskbarLauncherStateController.isAnimatingToLauncher(); 114 } 115 116 /** 117 * Should be called from onResume() and onPause(), and animates the Taskbar accordingly. 118 */ onLauncherResumedOrPaused(boolean isResumed)119 public void onLauncherResumedOrPaused(boolean isResumed) { 120 onLauncherResumedOrPaused(isResumed, false /* fromInit */); 121 } 122 onLauncherResumedOrPaused(boolean isResumed, boolean fromInit)123 private void onLauncherResumedOrPaused(boolean isResumed, boolean fromInit) { 124 if (mKeyguardController.isScreenOff()) { 125 if (!isResumed) { 126 return; 127 } else { 128 // Resuming implicitly means device unlocked 129 mKeyguardController.setScreenOn(); 130 } 131 } 132 133 mTaskbarLauncherStateController.updateStateForFlag(FLAG_RESUMED, isResumed); 134 mTaskbarLauncherStateController.applyState( 135 fromInit ? 0 : QuickstepTransitionManager.CONTENT_ALPHA_DURATION); 136 } 137 138 /** 139 * Create Taskbar animation when going from an app to Launcher as part of recents transition. 140 * @param toState If known, the state we will end up in when reaching Launcher. 141 * @param callbacks callbacks to track the recents animation lifecycle. The state change is 142 * automatically reset once the recents animation finishes 143 */ createAnimToLauncher(@onNull LauncherState toState, @NonNull RecentsAnimationCallbacks callbacks, long duration)144 public Animator createAnimToLauncher(@NonNull LauncherState toState, 145 @NonNull RecentsAnimationCallbacks callbacks, long duration) { 146 return mTaskbarLauncherStateController.createAnimToLauncher(toState, callbacks, duration); 147 } 148 149 /** 150 * @param ev MotionEvent in screen coordinates. 151 * @return Whether any Taskbar item could handle the given MotionEvent if given the chance. 152 */ isEventOverAnyTaskbarItem(MotionEvent ev)153 public boolean isEventOverAnyTaskbarItem(MotionEvent ev) { 154 return mControllers.taskbarViewController.isEventOverAnyItem(ev) 155 || mControllers.navbarButtonsViewController.isEventOverAnyItem(ev); 156 } 157 isDraggingItem()158 public boolean isDraggingItem() { 159 return mControllers.taskbarDragController.isDragging(); 160 } 161 162 @Override onStashedInAppChanged()163 protected void onStashedInAppChanged() { 164 onStashedInAppChanged(mLauncher.getDeviceProfile()); 165 } 166 onStashedInAppChanged(DeviceProfile deviceProfile)167 private void onStashedInAppChanged(DeviceProfile deviceProfile) { 168 boolean taskbarStashedInApps = mControllers.taskbarStashController.isStashedInApp(); 169 deviceProfile.isTaskbarPresentInApps = !taskbarStashedInApps; 170 updateTaskTransitionSpec(taskbarStashedInApps); 171 } 172 updateTaskTransitionSpec(boolean taskbarIsHidden)173 private void updateTaskTransitionSpec(boolean taskbarIsHidden) { 174 try { 175 if (taskbarIsHidden) { 176 // Clear custom task transition settings when the taskbar is stashed 177 WindowManagerGlobal.getWindowManagerService().clearTaskTransitionSpec(); 178 } else { 179 // Adjust task transition spec to account for taskbar being visible 180 @ColorInt int taskAnimationBackgroundColor = 181 mLauncher.getColor(R.color.taskbar_background); 182 183 TaskTransitionSpec customTaskAnimationSpec = new TaskTransitionSpec( 184 taskAnimationBackgroundColor, 185 Set.of(ITYPE_EXTRA_NAVIGATION_BAR) 186 ); 187 WindowManagerGlobal.getWindowManagerService() 188 .setTaskTransitionSpec(customTaskAnimationSpec); 189 } 190 } catch (RemoteException e) { 191 // This shouldn't happen but if it does task animations won't look good until the 192 // taskbar stashing state is changed. 193 Log.e(TAG, "Failed to update task transition spec to account for new taskbar state", 194 e); 195 } 196 } 197 198 /** 199 * Sets whether the background behind the taskbar/nav bar should be hidden. 200 */ forceHideBackground(boolean forceHide)201 public void forceHideBackground(boolean forceHide) { 202 mTaskbarOverrideBackgroundAlpha.updateValue(forceHide ? 0 : 1); 203 } 204 205 @Override getAppIconsForEdu()206 public Stream<ItemInfoWithIcon> getAppIconsForEdu() { 207 return Arrays.stream(mLauncher.getAppsView().getAppsStore().getApps()); 208 } 209 210 /** 211 * Starts the taskbar education flow, if the user hasn't seen it yet. 212 */ showEdu()213 public void showEdu() { 214 if (!FeatureFlags.ENABLE_TASKBAR_EDU.get() 215 || Utilities.IS_RUNNING_IN_TEST_HARNESS 216 || mLauncher.getOnboardingPrefs().getBoolean(OnboardingPrefs.TASKBAR_EDU_SEEN)) { 217 return; 218 } 219 mLauncher.getOnboardingPrefs().markChecked(OnboardingPrefs.TASKBAR_EDU_SEEN); 220 221 mControllers.taskbarEduController.showEdu(); 222 } 223 224 /** 225 * Manually ends the taskbar education flow. 226 */ hideEdu()227 public void hideEdu() { 228 if (!FeatureFlags.ENABLE_TASKBAR_EDU.get()) { 229 return; 230 } 231 232 mControllers.taskbarEduController.hideEdu(); 233 } 234 235 @Override onTaskbarIconLaunched(WorkspaceItemInfo item)236 public void onTaskbarIconLaunched(WorkspaceItemInfo item) { 237 InstanceId instanceId = new InstanceIdSequence().newInstanceId(); 238 mLauncher.logAppLaunch(mControllers.taskbarActivityContext.getStatsLogManager(), item, 239 instanceId); 240 } 241 242 @Override setSystemGestureInProgress(boolean inProgress)243 public void setSystemGestureInProgress(boolean inProgress) { 244 super.setSystemGestureInProgress(inProgress); 245 // Launcher's ScrimView will draw the background throughout the gesture. But once the 246 // gesture ends, start drawing taskbar's background again since launcher might stop drawing. 247 forceHideBackground(inProgress); 248 } 249 } 250