1 /* 2 * Copyright (C) 2023 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 android.animation.AnimatorSet 19 import android.annotation.SuppressLint 20 import android.view.View 21 import androidx.annotation.VisibleForTesting 22 import androidx.core.animation.doOnEnd 23 import com.android.app.animation.Interpolators 24 import com.android.launcher3.LauncherPrefs 25 import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING 26 import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING_IN_DESKTOP_MODE 27 import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE 28 import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_DIVIDER_MENU_OPEN 29 import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_PINNED 30 import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_UNPINNED 31 import com.android.launcher3.taskbar.TaskbarDividerPopupView.Companion.createAndPopulate 32 import java.io.PrintWriter 33 34 /** Controls taskbar pinning through a popup view. */ 35 class TaskbarPinningController( 36 private val context: TaskbarActivityContext, 37 private val isInDesktopModeProvider: () -> Boolean, 38 ) : TaskbarControllers.LoggableTaskbarController { 39 40 private lateinit var controllers: TaskbarControllers 41 private lateinit var taskbarSharedState: TaskbarSharedState 42 private lateinit var launcherPrefs: LauncherPrefs 43 private val statsLogManager = context.statsLogManager 44 @VisibleForTesting var isAnimatingTaskbarPinning = false 45 @VisibleForTesting lateinit var onCloseCallback: (preferenceChanged: Boolean) -> Unit 46 47 @SuppressLint("VisibleForTests") initnull48 fun init(taskbarControllers: TaskbarControllers, sharedState: TaskbarSharedState) { 49 controllers = taskbarControllers 50 taskbarSharedState = sharedState 51 launcherPrefs = context.launcherPrefs 52 onCloseCallback = 53 fun(didPreferenceChange: Boolean) { 54 statsLogManager.logger().log(LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE) 55 context.dragLayer.post { context.onPopupVisibilityChanged(false) } 56 57 if (!didPreferenceChange) { 58 return 59 } 60 val shouldPinTaskbar = 61 if (isInDesktopModeProvider()) { 62 !launcherPrefs.get(TASKBAR_PINNING_IN_DESKTOP_MODE) 63 } else { 64 !launcherPrefs.get(TASKBAR_PINNING) 65 } 66 67 val animateToValue = 68 if (shouldPinTaskbar) { 69 statsLogManager.logger().log(LAUNCHER_TASKBAR_PINNED) 70 PINNING_PERSISTENT 71 } else { 72 statsLogManager.logger().log(LAUNCHER_TASKBAR_UNPINNED) 73 PINNING_TRANSIENT 74 } 75 76 taskbarSharedState.taskbarWasPinned = animateToValue == PINNING_TRANSIENT 77 animateTaskbarPinning(animateToValue) 78 } 79 } 80 showPinningViewnull81 fun showPinningView(view: View) { 82 context.isTaskbarWindowFullscreen = true 83 view.post { 84 val popupView = getPopupView(view) 85 popupView.requestFocus() 86 popupView.onCloseCallback = onCloseCallback 87 context.onPopupVisibilityChanged(true) 88 popupView.show() 89 statsLogManager.logger().log(LAUNCHER_TASKBAR_DIVIDER_MENU_OPEN) 90 } 91 } 92 93 @VisibleForTesting getPopupViewnull94 fun getPopupView(view: View): TaskbarDividerPopupView<*> { 95 return createAndPopulate(view, context) 96 } 97 98 @VisibleForTesting animateTaskbarPinningnull99 fun animateTaskbarPinning(animateToValue: Float) { 100 val taskbarViewController = controllers.taskbarViewController 101 val animatorSet = 102 getAnimatorSetForTaskbarPinningAnimation(animateToValue).apply { 103 doOnEnd { recreateTaskbarAndUpdatePinningValue() } 104 duration = PINNING_ANIMATION_DURATION 105 } 106 controllers.taskbarOverlayController.hideWindow() 107 updateIsAnimatingTaskbarPinningAndNotifyTaskbarDragLayer(true) 108 taskbarViewController.animateAwayNotificationDotsDuringTaskbarPinningAnimation() 109 animatorSet.start() 110 } 111 112 @VisibleForTesting getAnimatorSetForTaskbarPinningAnimationnull113 fun getAnimatorSetForTaskbarPinningAnimation(animateToValue: Float): AnimatorSet { 114 val animatorSet = AnimatorSet() 115 val taskbarViewController = controllers.taskbarViewController 116 val dragLayerController = controllers.taskbarDragLayerController 117 118 animatorSet.playTogether( 119 dragLayerController.taskbarBackgroundProgress.animateToValue(animateToValue), 120 taskbarViewController.taskbarIconTranslationYForPinning.animateToValue(animateToValue), 121 taskbarViewController.taskbarIconScaleForPinning.animateToValue(animateToValue), 122 taskbarViewController.taskbarIconTranslationXForPinning.animateToValue(animateToValue) 123 ) 124 125 animatorSet.interpolator = Interpolators.EMPHASIZED 126 return animatorSet 127 } 128 updateIsAnimatingTaskbarPinningAndNotifyTaskbarDragLayernull129 private fun updateIsAnimatingTaskbarPinningAndNotifyTaskbarDragLayer(isAnimating: Boolean) { 130 isAnimatingTaskbarPinning = isAnimating 131 context.dragLayer.setAnimatingTaskbarPinning(isAnimating) 132 } 133 134 @VisibleForTesting recreateTaskbarAndUpdatePinningValuenull135 fun recreateTaskbarAndUpdatePinningValue() { 136 updateIsAnimatingTaskbarPinningAndNotifyTaskbarDragLayer(false) 137 if (isInDesktopModeProvider()) { 138 launcherPrefs.put( 139 TASKBAR_PINNING_IN_DESKTOP_MODE, 140 !launcherPrefs.get(TASKBAR_PINNING_IN_DESKTOP_MODE) 141 ) 142 } else { 143 launcherPrefs.put(TASKBAR_PINNING, !launcherPrefs.get(TASKBAR_PINNING)) 144 } 145 } 146 dumpLogsnull147 override fun dumpLogs(prefix: String, pw: PrintWriter) { 148 pw.println(prefix + "TaskbarPinningController:") 149 pw.println("$prefix\tisAnimatingTaskbarPinning=$isAnimatingTaskbarPinning") 150 pw.println("$prefix\tTASKBAR_PINNING shared pref =" + launcherPrefs.get(TASKBAR_PINNING)) 151 pw.println( 152 "$prefix\tTASKBAR_PINNING_IN_DESKTOP_MODE shared pref in desktop mode =" + 153 launcherPrefs.get(TASKBAR_PINNING_IN_DESKTOP_MODE) 154 ) 155 } 156 157 companion object { 158 const val PINNING_PERSISTENT = 1f 159 const val PINNING_TRANSIENT = 0f 160 const val PINNING_ANIMATION_DURATION = 600L 161 } 162 } 163