Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 550927be authored by Mykola Podolian's avatar Mykola Podolian Committed by Android (Google) Code Review
Browse files

Merge "Added TransientTaskbarStashController implementation." into main

parents f1e7136c a5cd1a0d
Loading
Loading
Loading
Loading
+4 −21
Original line number Diff line number Diff line
@@ -81,12 +81,7 @@ interface BubbleStashController {
        controllersAfterInitAction: ControllersAfterInitAction
    )

    /** Sets stashed and expanded state of the bubble bar */
    fun updateStashedAndExpandedState(stash: Boolean = false, expand: Boolean = false)

    /**
     * Shows the bubble bar at [getBubbleBarTranslationY] position immediately without animation.
     */
    /** Shows the bubble bar at [bubbleBarTranslationY] position immediately without animation. */
    fun showBubbleBarImmediate()

    /** Shows the bubble bar at [bubbleBarTranslationY] position immediately without animation. */
@@ -120,21 +115,17 @@ interface BubbleStashController {
     * Stashes the bubble bar (transform to the handle view), or just shrink width of the expanded
     * bubble bar based on the controller implementation.
     */
    fun stashBubbleBar() {
        updateStashedAndExpandedState(stash = true, expand = false)
    }
    fun stashBubbleBar()

    /** Shows the bubble bar, and expands bubbles depending on [expandBubbles]. */
    fun showBubbleBar(expandBubbles: Boolean) {
        updateStashedAndExpandedState(stash = false, expandBubbles)
    }
    fun showBubbleBar(expandBubbles: Boolean)

    // TODO(b/354218264): Move to BubbleBarViewAnimator
    /**
     * The difference on the Y axis between the center of the handle and the center of the bubble
     * bar.
     */
    fun getSlideInAnimationDistanceY(): Float
    fun getDiffBetweenHandleAndBarCenters(): Float

    // TODO(b/354218264): Move to BubbleBarViewAnimator
    /** The distance the handle moves as part of the new bubble animation. */
@@ -190,13 +181,5 @@ interface BubbleStashController {

        /** The scale bubble bar animates to when being stashed. */
        const val STASHED_BAR_SCALE = 0.5f

        /** Creates new instance of [BubbleStashController] */
        @JvmStatic
        fun newInstance(
            taskbarHotseatDimensionsProvider: TaskbarHotseatDimensionsProvider
        ): BubbleStashController {
            return PersistentTaskbarStashController(taskbarHotseatDimensionsProvider)
        }
    }
}
+24 −15
Original line number Diff line number Diff line
@@ -33,7 +33,7 @@ import com.android.launcher3.util.MultiPropertyFactory
import com.android.wm.shell.common.bubbles.BubbleBarLocation
import com.android.wm.shell.shared.animation.PhysicsAnimator

class PersistentTaskbarStashController(
class PersistentBubbleStashController(
    private val taskbarHotseatDimensionsProvider: TaskbarHotseatDimensionsProvider,
) : BubbleStashController {

@@ -54,7 +54,7 @@ class PersistentTaskbarStashController(
            }
            if (onHome) {
                // When transition to home we should show collapse the bubble bar
                updateStashedAndExpandedState(stash = false, expand = false)
                updateExpandedState(expand = false)
            }
            animateBubbleBarY()
            bubbleBarViewController.onBubbleBarConfigurationChanged(/* animate= */ true)
@@ -66,7 +66,7 @@ class PersistentTaskbarStashController(
            field = onOverview
            if (!onOverview) {
                // When transition from overview we should show collapse the bubble bar
                updateStashedAndExpandedState(stash = false, expand = false)
                updateExpandedState(expand = false)
            }
            bubbleBarViewController.onBubbleBarConfigurationChanged(/* animate= */ true)
        }
@@ -114,7 +114,8 @@ class PersistentTaskbarStashController(
        this.bubbleBarViewController = bubbleBarViewController
        this.controllersAfterInitAction = controllersAfterInitAction
        bubbleBarTranslationYAnimator = bubbleBarViewController.bubbleBarTranslationY
        bubbleBarAlphaAnimator = bubbleBarViewController.bubbleBarAlpha.get(0)
        // bubble bar has only alpha property, getting it at index 0
        bubbleBarAlphaAnimator = bubbleBarViewController.bubbleBarAlpha.get(/* index= */ 0)
        bubbleBarScaleAnimator = bubbleBarViewController.bubbleBarScale
    }

@@ -131,16 +132,6 @@ class PersistentTaskbarStashController(
        animatorSet.setDuration(BAR_STASH_DURATION).start()
    }

    override fun updateStashedAndExpandedState(stash: Boolean, expand: Boolean) {
        if (bubbleBarViewController.isHiddenForNoBubbles) {
            // If there are no bubbles the bar is invisible, nothing to do here.
            return
        }
        if (bubbleBarViewController.isExpanded != expand) {
            bubbleBarViewController.isExpanded = expand
        }
    }

    override fun showBubbleBarImmediate() = showBubbleBarImmediate(bubbleBarTranslationY)

    override fun showBubbleBarImmediate(bubbleBarTranslationY: Float) {
@@ -154,6 +145,14 @@ class PersistentTaskbarStashController(
        // operation is performed.
    }

    override fun stashBubbleBar() {
        updateExpandedState(expand = false)
    }

    override fun showBubbleBar(expandBubbles: Boolean) {
        updateExpandedState(expandBubbles)
    }

    override fun stashBubbleBarImmediate() {
        // When the bubble bar is shown for the persistent task bar, there is no handle view, so no
        // operation is performed.
@@ -176,7 +175,7 @@ class PersistentTaskbarStashController(
    override fun isEventOverBubbleBarViews(ev: MotionEvent): Boolean =
        bubbleBarViewController.isEventOverAnyItem(ev)

    override fun getSlideInAnimationDistanceY(): Float {
    override fun getDiffBetweenHandleAndBarCenters(): Float {
        // distance from the bottom of the screen and the bubble bar center.
        return -bubbleBarViewController.bubbleBarCollapsedHeight / 2f
    }
@@ -199,6 +198,16 @@ class PersistentTaskbarStashController(
        // no op since does not have a handle view
    }

    private fun updateExpandedState(expand: Boolean) {
        if (bubbleBarViewController.isHiddenForNoBubbles) {
            // If there are no bubbles the bar is invisible, nothing to do here.
            return
        }
        if (bubbleBarViewController.isExpanded != expand) {
            bubbleBarViewController.isExpanded = expand
        }
    }

    /** Animates bubble bar Y accordingly to the showing mode */
    private fun animateBubbleBarY() {
        val animator =
+373 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.bubbles.stashing

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.content.res.Resources
import android.view.MotionEvent
import android.view.View
import androidx.annotation.VisibleForTesting
import com.android.launcher3.R
import com.android.launcher3.anim.AnimatedFloat
import com.android.launcher3.taskbar.StashedHandleViewController
import com.android.launcher3.taskbar.TaskbarInsetsController
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_STASH_DURATION
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_TRANSLATION_DURATION
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.STASHED_BAR_SCALE
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.ControllersAfterInitAction
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.TaskbarHotseatDimensionsProvider
import com.android.launcher3.util.MultiPropertyFactory
import com.android.wm.shell.common.bubbles.BubbleBarLocation
import com.android.wm.shell.shared.animation.PhysicsAnimator

class TransientBubbleStashController(
    private val taskbarHotseatDimensionsProvider: TaskbarHotseatDimensionsProvider,
    resources: Resources
) : BubbleStashController {

    private lateinit var bubbleBarViewController: BubbleBarViewController
    private lateinit var taskbarInsetsController: TaskbarInsetsController
    private lateinit var controllersAfterInitAction: ControllersAfterInitAction

    // stash view properties
    private var bubbleStashedHandleViewController: BubbleStashedHandleViewController? = null
    private var stashHandleViewAlpha: MultiPropertyFactory<View>.MultiProperty? = null
    private var stashedHeight: Int = 0

    // bubble bar properties
    private lateinit var bubbleBarAlpha: MultiPropertyFactory<View>.MultiProperty
    private lateinit var bubbleBarTranslationYAnimator: AnimatedFloat
    private lateinit var bubbleBarScale: AnimatedFloat
    private val mHandleCenterFromScreenBottom =
        resources.getDimensionPixelSize(R.dimen.bubblebar_stashed_size) / 2f

    private var animator: AnimatorSet? = null

    override var isStashed: Boolean = false
        @VisibleForTesting set

    override var isBubblesShowingOnHome: Boolean = false
        set(onHome) {
            if (field == onHome) return
            field = onHome
            if (!bubbleBarViewController.hasBubbles()) {
                // if there are no bubbles, there's nothing to show, so just return.
                return
            }
            if (onHome) {
                updateStashedAndExpandedState(stash = false, expand = false)
                // When transitioning from app to home we need to animate the bubble bar
                // here to align with hotseat center.
                animateBubbleBarYToHotseat()
            } else if (!bubbleBarViewController.isExpanded) {
                updateStashedAndExpandedState(stash = true, expand = false)
            }
            bubbleBarViewController.onBubbleBarConfigurationChanged(/* animate= */ true)
        }

    override var isBubblesShowingOnOverview: Boolean = false
        set(onOverview) {
            if (field == onOverview) return
            field = onOverview
            if (onOverview) {
                // When transitioning to overview we need to animate the bubble bar to align with
                // the taskbar bottom.
                animateBubbleBarYToTaskbar()
            } else {
                updateStashedAndExpandedState(stash = true, expand = false)
            }
            bubbleBarViewController.onBubbleBarConfigurationChanged(/* animate= */ true)
        }

    override var isSysuiLocked: Boolean = false
        set(isLocked) {
            if (field == isLocked) return
            field = isLocked
            if (!isLocked && bubbleBarViewController.hasBubbles()) {
                animateAfterUnlock()
            }
        }

    override val isTransientTaskBar: Boolean = true

    override val bubbleBarTranslationYForHotseat: Float
        get() {
            val hotseatBottomSpace = taskbarHotseatDimensionsProvider.getHotseatBottomSpace()
            val hotseatCellHeight = taskbarHotseatDimensionsProvider.getHotseatHeight()
            val bubbleBarHeight: Float = bubbleBarViewController.bubbleBarCollapsedHeight
            return -hotseatBottomSpace - (hotseatCellHeight - bubbleBarHeight) / 2
        }

    override val bubbleBarTranslationYForTaskbar: Float =
        -taskbarHotseatDimensionsProvider.getTaskbarBottomSpace().toFloat()

    /** Check if we have handle view controller */
    override val hasHandleView: Boolean
        get() = bubbleStashedHandleViewController != null

    override fun init(
        taskbarInsetsController: TaskbarInsetsController,
        bubbleBarViewController: BubbleBarViewController,
        bubbleStashedHandleViewController: BubbleStashedHandleViewController?,
        controllersAfterInitAction: ControllersAfterInitAction
    ) {
        this.taskbarInsetsController = taskbarInsetsController
        this.bubbleBarViewController = bubbleBarViewController
        this.bubbleStashedHandleViewController = bubbleStashedHandleViewController
        this.controllersAfterInitAction = controllersAfterInitAction
        bubbleBarTranslationYAnimator = bubbleBarViewController.bubbleBarTranslationY
        // bubble bar has only alpha property, getting it at index 0
        bubbleBarAlpha = bubbleBarViewController.bubbleBarAlpha.get(/* index= */ 0)
        bubbleBarScale = bubbleBarViewController.bubbleBarScale
        stashedHeight = bubbleStashedHandleViewController?.stashedHeight ?: 0
        stashHandleViewAlpha =
            bubbleStashedHandleViewController
                ?.stashedHandleAlpha
                ?.get(StashedHandleViewController.ALPHA_INDEX_STASHED)
    }

    private fun animateAfterUnlock() {
        val animatorSet = AnimatorSet()
        if (isBubblesShowingOnHome || isBubblesShowingOnOverview) {
            isStashed = false
            animatorSet.playTogether(
                bubbleBarScale.animateToValue(1f),
                bubbleBarTranslationYAnimator.animateToValue(bubbleBarTranslationY),
                bubbleBarAlpha.animateToValue(1f)
            )
        } else {
            isStashed = true
            stashHandleViewAlpha?.let { animatorSet.playTogether(it.animateToValue(1f)) }
        }
        animatorSet.updateTouchRegionOnAnimationEnd().setDuration(BAR_STASH_DURATION).start()
    }

    override fun showBubbleBarImmediate() {
        showBubbleBarImmediate(bubbleBarTranslationY)
    }

    override fun showBubbleBarImmediate(bubbleBarTranslationY: Float) {
        bubbleStashedHandleViewController?.setTranslationYForSwipe(0f)
        stashHandleViewAlpha?.value = 0f
        this.bubbleBarTranslationYAnimator.updateValue(bubbleBarTranslationY)
        bubbleBarAlpha.setValue(1f)
        bubbleBarScale.updateValue(1f)
        isStashed = false
        onIsStashedChanged()
    }

    override fun stashBubbleBarImmediate() {
        bubbleStashedHandleViewController?.setTranslationYForSwipe(0f)
        stashHandleViewAlpha?.value = 1f
        this.bubbleBarTranslationYAnimator.updateValue(getStashTranslation())
        bubbleBarAlpha.setValue(0f)
        bubbleBarScale.updateValue(STASHED_BAR_SCALE)
        isStashed = true
        onIsStashedChanged()
    }

    override fun getTouchableHeight(): Int =
        when {
            isStashed -> stashedHeight
            isBubbleBarVisible() -> bubbleBarViewController.bubbleBarCollapsedHeight.toInt()
            else -> 0
        }

    override fun isBubbleBarVisible(): Boolean = bubbleBarViewController.hasBubbles() && !isStashed

    override fun onNewBubbleAnimationInterrupted(isStashed: Boolean, bubbleBarTranslationY: Float) =
        if (isStashed) {
            stashBubbleBarImmediate()
        } else {
            showBubbleBarImmediate(bubbleBarTranslationY)
        }

    /** Check if [ev] belongs to the stash handle or the bubble bar views. */
    override fun isEventOverBubbleBarViews(ev: MotionEvent): Boolean {
        val isOverHandle = bubbleStashedHandleViewController?.isEventOverHandle(ev) ?: false
        return isOverHandle || bubbleBarViewController.isEventOverAnyItem(ev)
    }

    /** Set the bubble bar stash handle location . */
    override fun setBubbleBarLocation(bubbleBarLocation: BubbleBarLocation) {
        bubbleStashedHandleViewController?.setBubbleBarLocation(bubbleBarLocation)
    }

    override fun stashBubbleBar() {
        updateStashedAndExpandedState(stash = true, expand = false)
    }

    override fun showBubbleBar(expandBubbles: Boolean) {
        updateStashedAndExpandedState(stash = false, expandBubbles)
    }

    override fun getDiffBetweenHandleAndBarCenters(): Float {
        // the difference between the centers of the handle and the bubble bar is the difference
        // between their distance from the bottom of the screen.
        val barCenter: Float = bubbleBarViewController.bubbleBarCollapsedHeight / 2f
        return mHandleCenterFromScreenBottom - barCenter
    }

    override fun getStashedHandleTranslationForNewBubbleAnimation(): Float {
        return -mHandleCenterFromScreenBottom
    }

    override fun getStashedHandlePhysicsAnimator(): PhysicsAnimator<View>? {
        return bubbleStashedHandleViewController?.physicsAnimator
    }

    override fun updateTaskbarTouchRegion() {
        taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
    }

    override fun setHandleTranslationY(translationY: Float) {
        bubbleStashedHandleViewController?.setTranslationYForSwipe(translationY)
    }

    private fun getStashTranslation(): Float {
        return (bubbleBarViewController.bubbleBarCollapsedHeight - stashedHeight) / 2f
    }

    /**
     * Create a stash animation.
     *
     * @param isStashed whether it's a stash animation or an unstash animation
     * @param duration duration of the animation
     * @return the animation
     */
    @Suppress("SameParameterValue")
    private fun createStashAnimator(isStashed: Boolean, duration: Long): AnimatorSet {
        val animatorSet = AnimatorSet()
        val fullLengthAnimatorSet = AnimatorSet()
        // Not exactly half and may overlap. See [first|second]HalfDurationScale below.
        val firstHalfAnimatorSet = AnimatorSet()
        val secondHalfAnimatorSet = AnimatorSet()
        val firstHalfDurationScale: Float
        val secondHalfDurationScale: Float
        val stashHandleAlphaValue: Float
        if (isStashed) {
            firstHalfDurationScale = 0.75f
            secondHalfDurationScale = 0.5f
            stashHandleAlphaValue = 1f
            fullLengthAnimatorSet.play(
                bubbleBarTranslationYAnimator.animateToValue(getStashTranslation())
            )
            firstHalfAnimatorSet.playTogether(
                bubbleBarAlpha.animateToValue(0f),
                bubbleBarScale.animateToValue(STASHED_BAR_SCALE)
            )
        } else {
            firstHalfDurationScale = 0.5f
            secondHalfDurationScale = 0.75f
            stashHandleAlphaValue = 0f
            fullLengthAnimatorSet.playTogether(
                bubbleBarScale.animateToValue(1f),
                bubbleBarTranslationYAnimator.animateToValue(bubbleBarTranslationY)
            )
            secondHalfAnimatorSet.playTogether(bubbleBarAlpha.animateToValue(1f))
        }
        stashHandleViewAlpha?.let {
            secondHalfAnimatorSet.playTogether(it.animateToValue(stashHandleAlphaValue))
        }
        bubbleStashedHandleViewController?.createRevealAnimToIsStashed(isStashed)?.let {
            fullLengthAnimatorSet.play(it)
        }
        fullLengthAnimatorSet.setDuration(duration)
        firstHalfAnimatorSet.setDuration((duration * firstHalfDurationScale).toLong())
        secondHalfAnimatorSet.setDuration((duration * secondHalfDurationScale).toLong())
        secondHalfAnimatorSet.startDelay = (duration * (1 - secondHalfDurationScale)).toLong()
        animatorSet.playTogether(fullLengthAnimatorSet, firstHalfAnimatorSet, secondHalfAnimatorSet)
        animatorSet.addListener(
            object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) {
                    animator = null
                    controllersAfterInitAction.runAfterInit {
                        if (isStashed) {
                            bubbleBarViewController.isExpanded = false
                        }
                        taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
                    }
                }
            }
        )
        return animatorSet
    }

    private fun onIsStashedChanged() {
        controllersAfterInitAction.runAfterInit {
            taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
            bubbleStashedHandleViewController?.onIsStashedChanged()
        }
    }

    private fun animateBubbleBarYToHotseat() {
        translateBubbleBarYUpdateTouchRegionOnCompletion(bubbleBarTranslationYForHotseat)
    }

    private fun animateBubbleBarYToTaskbar() {
        translateBubbleBarYUpdateTouchRegionOnCompletion(bubbleBarTranslationYForTaskbar)
    }

    private fun translateBubbleBarYUpdateTouchRegionOnCompletion(toY: Float) {
        bubbleBarViewController.bubbleBarTranslationY
            .animateToValue(toY)
            .updateTouchRegionOnAnimationEnd()
            .setDuration(BAR_TRANSLATION_DURATION)
            .start()
    }

    @VisibleForTesting
    fun updateStashedAndExpandedState(stash: Boolean, expand: Boolean) {
        if (bubbleBarViewController.isHiddenForNoBubbles) {
            // If there are no bubbles the bar and handle are invisible, nothing to do here.
            return
        }
        val isStashed = stash && !isBubblesShowingOnHome && !isBubblesShowingOnOverview
        if (this.isStashed != isStashed) {
            this.isStashed = isStashed
            // notify the view controller that the stash state is about to change so that it can
            // cancel an ongoing animation if there is one.
            // note that this has to be called before updating mIsStashed with the new value,
            // otherwise interrupting an ongoing animation may update it again with the wrong state
            bubbleBarViewController.onStashStateChanging()
            animator?.cancel()
            animator =
                createStashAnimator(isStashed, BAR_STASH_DURATION).apply {
                    updateTouchRegionOnAnimationEnd()
                    start()
                }
        }
        if (bubbleBarViewController.isExpanded != expand) {
            bubbleBarViewController.isExpanded = expand
        }
    }

    private fun Animator.updateTouchRegionOnAnimationEnd(): Animator {
        this.addListener(
            object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) {
                    onIsStashedChanged()
                }
            }
        )
        return this
    }
}
+6 −4
Original line number Diff line number Diff line
@@ -40,10 +40,10 @@ import org.mockito.kotlin.clearInvocations
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever

/** Unit tests for [PersistentTaskbarStashController]. */
/** Unit tests for [PersistentBubbleStashController]. */
@SmallTest
@RunWith(AndroidJUnit4::class)
class PersistentTaskbarStashControllerTest {
class PersistentBubbleStashControllerTest {

    companion object {
        const val BUBBLE_BAR_HEIGHT = 100f
@@ -52,15 +52,17 @@ class PersistentTaskbarStashControllerTest {
    }

    @get:Rule val animatorTestRule: AnimatorTestRule = AnimatorTestRule(this)

    @get:Rule val rule: MockitoRule = MockitoJUnit.rule()

    private val context = ApplicationProvider.getApplicationContext<Context>()
    private lateinit var bubbleBarView: BubbleBarView

    @Mock lateinit var bubbleBarViewController: BubbleBarViewController

    @Mock lateinit var taskbarInsetsController: TaskbarInsetsController

    private lateinit var persistentTaskBarStashController: PersistentTaskbarStashController
    private lateinit var persistentTaskBarStashController: PersistentBubbleStashController
    private lateinit var translationY: AnimatedFloat
    private lateinit var scale: AnimatedFloat
    private lateinit var alpha: MultiValueAlpha
@@ -68,7 +70,7 @@ class PersistentTaskbarStashControllerTest {
    @Before
    fun setUp() {
        persistentTaskBarStashController =
            PersistentTaskbarStashController(DefaultDimensionsProvider())
            PersistentBubbleStashController(DefaultDimensionsProvider())
        setUpBubbleBarView()
        setUpBubbleBarController()
        persistentTaskBarStashController.init(
+10 −5
Original line number Diff line number Diff line
@@ -20,14 +20,19 @@ class ImmediateAction : BubbleStashController.ControllersAfterInitAction {
    override fun runAfterInit(action: () -> Unit) = action.invoke()
}

class DefaultDimensionsProvider : BubbleStashController.TaskbarHotseatDimensionsProvider {
    override fun getTaskbarBottomSpace(): Int = TASKBAR_BOTTOM_SPACE
class DefaultDimensionsProvider(
    private val taskBarBottomSpace: Int = TASKBAR_BOTTOM_SPACE,
    private val taskBarHeight: Int = TASKBAR_HEIGHT,
    private val hotseatBottomSpace: Int = HOTSEAT_BOTTOM_SPACE,
    private val hotseatHeight: Int = HOTSEAT_HEIGHT
) : BubbleStashController.TaskbarHotseatDimensionsProvider {
    override fun getTaskbarBottomSpace(): Int = taskBarBottomSpace

    override fun getTaskbarHeight(): Int = TASKBAR_HEIGHT
    override fun getTaskbarHeight(): Int = taskBarHeight

    override fun getHotseatBottomSpace(): Int = HOTSEAT_BOTTOM_SPACE
    override fun getHotseatBottomSpace(): Int = hotseatBottomSpace

    override fun getHotseatHeight(): Int = HOTSEAT_HEIGHT
    override fun getHotseatHeight(): Int = hotseatHeight

    companion object {
        const val TASKBAR_BOTTOM_SPACE = 0
Loading