Files
lawnchair/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
Tony Wickham c3963a7ff4 Fixes to VoiceInteractionWindowController
- Make temporary taskbar background behind assistant non-touchable
  (fail-safe in case the window isn't removed for some reason)
- Give temporary taskbar background a different window title and
  add a couple more dump statements to help debugging
- Only show the taskbar background for persistent taskbar; transient
  taskbar can skip most of the special casing and just hide it
- Fix bug where we weren't drawing the separate taskbar background in 3
  button mode
- Fix bug where we weren't actually synchronizing
  separateWindowForTaskbarBackground with TaskbarDragLayer, since the
  former wasn't attached to the window yet; now we wait until it is
  attached before calling synchronizeNextDraw()
- Also added dump logs for TaskbarDragLayerController alpha channels

Test: manual in 3 button and gesture nav (with and without
FORCE_PERSISTENT_TASKBAR enabled)
Fixes: 243652789
Bug: 262664266

Change-Id: I865871e57dd4cb255a916317a7e5d35cfde97df5
2023-01-31 00:58:22 +00:00

224 lines
9.0 KiB
Kotlin

/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.taskbar
import android.animation.AnimatorSet
import android.graphics.Canvas
import android.view.View
import android.view.ViewTreeObserver
import android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
import com.android.launcher3.util.DisplayController
import com.android.launcher3.views.BaseDragLayer
import com.android.systemui.animation.ViewRootSync
import java.io.PrintWriter
private const val TASKBAR_ICONS_FADE_DURATION = 300L
private const val STASHED_HANDLE_FADE_DURATION = 180L
private const val TEMP_BACKGROUND_WINDOW_TITLE = "VoiceInteractionTaskbarBackground"
/**
* Controls Taskbar behavior while Voice Interaction Window (assistant) is showing. Specifically:
* - We always hide the taskbar icons or stashed handle, whichever is currently showing.
* - For persistent taskbar, we also move the taskbar background to a new window/layer
* (TYPE_APPLICATION_OVERLAY) which is behind the assistant.
* - For transient taskbar, we hide the real taskbar background (if it's showing).
*/
class VoiceInteractionWindowController(val context: TaskbarActivityContext) :
TaskbarControllers.LoggableTaskbarController, TaskbarControllers.BackgroundRendererController {
private val isSeparateBackgroundEnabled = !DisplayController.isTransientTaskbar(context)
private val taskbarBackgroundRenderer = TaskbarBackgroundRenderer(context)
private val nonTouchableInsetsComputer =
ViewTreeObserver.OnComputeInternalInsetsListener {
it.touchableRegion.setEmpty()
it.setTouchableInsets(TOUCHABLE_INSETS_REGION)
}
// Initialized in init.
private lateinit var controllers: TaskbarControllers
// Only initialized if isSeparateBackgroundEnabled
private var separateWindowForTaskbarBackground: BaseDragLayer<TaskbarActivityContext>? = null
private var separateWindowLayoutParams: WindowManager.LayoutParams? = null
private var isVoiceInteractionWindowVisible: Boolean = false
private var pendingAttachedToWindowListener: View.OnAttachStateChangeListener? = null
fun init(controllers: TaskbarControllers) {
this.controllers = controllers
if (!isSeparateBackgroundEnabled) {
return
}
separateWindowForTaskbarBackground =
object : BaseDragLayer<TaskbarActivityContext>(context, null, 0) {
override fun recreateControllers() {
mControllers = emptyArray()
}
override fun draw(canvas: Canvas) {
super.draw(canvas)
if (controllers.taskbarStashController.isTaskbarVisibleAndNotStashing) {
taskbarBackgroundRenderer.draw(canvas)
}
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
viewTreeObserver.addOnComputeInternalInsetsListener(nonTouchableInsetsComputer)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
viewTreeObserver.removeOnComputeInternalInsetsListener(
nonTouchableInsetsComputer
)
}
}
separateWindowForTaskbarBackground?.recreateControllers()
separateWindowForTaskbarBackground?.setWillNotDraw(false)
separateWindowLayoutParams =
context.createDefaultWindowLayoutParams(
TYPE_APPLICATION_OVERLAY,
TEMP_BACKGROUND_WINDOW_TITLE
)
separateWindowLayoutParams?.isSystemApplicationOverlay = true
}
fun onDestroy() {
setIsVoiceInteractionWindowVisible(visible = false, skipAnim = true)
separateWindowForTaskbarBackground?.removeOnAttachStateChangeListener(
pendingAttachedToWindowListener
)
}
fun setIsVoiceInteractionWindowVisible(visible: Boolean, skipAnim: Boolean) {
if (isVoiceInteractionWindowVisible == visible) {
return
}
isVoiceInteractionWindowVisible = visible
// Fade out taskbar icons and stashed handle.
val taskbarIconAlpha = if (isVoiceInteractionWindowVisible) 0f else 1f
val fadeTaskbarIcons =
controllers.taskbarViewController.taskbarIconAlpha
.get(TaskbarViewController.ALPHA_INDEX_ASSISTANT_INVOKED)
.animateToValue(taskbarIconAlpha)
.setDuration(TASKBAR_ICONS_FADE_DURATION)
val fadeStashedHandle =
controllers.stashedHandleViewController.stashedHandleAlpha
.get(StashedHandleViewController.ALPHA_INDEX_ASSISTANT_INVOKED)
.animateToValue(taskbarIconAlpha)
.setDuration(STASHED_HANDLE_FADE_DURATION)
val animSet = AnimatorSet()
animSet.play(fadeTaskbarIcons)
animSet.play(fadeStashedHandle)
if (!isSeparateBackgroundEnabled) {
val fadeTaskbarBackground =
controllers.taskbarDragLayerController.assistantBgTaskbar
.animateToValue(taskbarIconAlpha)
.setDuration(TASKBAR_ICONS_FADE_DURATION)
animSet.play(fadeTaskbarBackground)
}
animSet.start()
if (skipAnim) {
animSet.end()
}
if (isSeparateBackgroundEnabled) {
moveTaskbarBackgroundToAppropriateLayer(skipAnim)
}
}
/**
* Either:
*
* Hides the TaskbarDragLayer background and creates a new window to draw just that background.
*
* OR
*
* Removes the temporary window and show the TaskbarDragLayer background again.
*/
private fun moveTaskbarBackgroundToAppropriateLayer(skipAnim: Boolean) {
val moveToLowerLayer = isVoiceInteractionWindowVisible
val onWindowsSynchronized =
if (moveToLowerLayer) {
// First add the temporary window, then hide the overlapping taskbar background.
context.addWindowView(
separateWindowForTaskbarBackground,
separateWindowLayoutParams
);
{ controllers.taskbarDragLayerController.setIsBackgroundDrawnElsewhere(true) }
} else {
// First reapply the original taskbar background, then remove the temporary window.
controllers.taskbarDragLayerController.setIsBackgroundDrawnElsewhere(false);
{ context.removeWindowView(separateWindowForTaskbarBackground) }
}
if (skipAnim) {
onWindowsSynchronized()
} else {
separateWindowForTaskbarBackground?.runWhenAttachedToWindow {
ViewRootSync.synchronizeNextDraw(
separateWindowForTaskbarBackground!!,
context.dragLayer,
onWindowsSynchronized
)
}
}
}
private fun View.runWhenAttachedToWindow(onAttachedToWindow: () -> Unit) {
if (isAttachedToWindow) {
onAttachedToWindow()
return
}
removeOnAttachStateChangeListener(pendingAttachedToWindowListener)
pendingAttachedToWindowListener =
object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View?) {
onAttachedToWindow()
removeOnAttachStateChangeListener(this)
pendingAttachedToWindowListener = null
}
override fun onViewDetachedFromWindow(v: View?) {}
}
addOnAttachStateChangeListener(pendingAttachedToWindowListener)
}
override fun setCornerRoundness(cornerRoundness: Float) {
if (!isSeparateBackgroundEnabled) {
return
}
taskbarBackgroundRenderer.setCornerRoundness(cornerRoundness)
separateWindowForTaskbarBackground?.invalidate()
}
override fun dumpLogs(prefix: String, pw: PrintWriter) {
pw.println(prefix + "VoiceInteractionWindowController:")
pw.println("$prefix\tisSeparateBackgroundEnabled=$isSeparateBackgroundEnabled")
pw.println("$prefix\tisVoiceInteractionWindowVisible=$isVoiceInteractionWindowVisible")
pw.println(
"$prefix\tisSeparateTaskbarBackgroundAttachedToWindow=" +
"${separateWindowForTaskbarBackground?.isAttachedToWindow}"
)
}
}