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

Commit 798934e5 authored by Chris Göllner's avatar Chris Göllner
Browse files

Extract privacy dot corner logic into a shared enum

Test: PrivacyDotViewControllerTest.kt
Flag: EXEMPT no behavior change
Bug: 362720432
Change-Id: I4310542375da305b99347490c94ae234ad67c183
parent 55a51601
Loading
Loading
Loading
Loading
+12 −8
Original line number Original line Diff line number Diff line
@@ -29,6 +29,10 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestCase
import com.android.systemui.res.R
import com.android.systemui.res.R
import com.android.systemui.statusbar.FakeStatusBarStateController
import com.android.systemui.statusbar.FakeStatusBarStateController
import com.android.systemui.statusbar.events.PrivacyDotCorner.BottomLeft
import com.android.systemui.statusbar.events.PrivacyDotCorner.BottomRight
import com.android.systemui.statusbar.events.PrivacyDotCorner.TopLeft
import com.android.systemui.statusbar.events.PrivacyDotCorner.TopRight
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -215,7 +219,7 @@ class PrivacyDotViewControllerTest : SysuiTestCase() {


        val controller = createAndInitializeController()
        val controller = createAndInitializeController()


        assertThat(controller.currentViewState.cornerIndex).isEqualTo(TOP_RIGHT)
        assertThat(controller.currentViewState.corner).isEqualTo(TopRight)
        assertThat(controller.currentViewState.designatedCorner).isEqualTo(topRightView)
        assertThat(controller.currentViewState.designatedCorner).isEqualTo(topRightView)
    }
    }


@@ -225,7 +229,7 @@ class PrivacyDotViewControllerTest : SysuiTestCase() {


        val controller = createAndInitializeController()
        val controller = createAndInitializeController()


        assertThat(controller.currentViewState.cornerIndex).isEqualTo(BOTTOM_RIGHT)
        assertThat(controller.currentViewState.corner).isEqualTo(BottomRight)
        assertThat(controller.currentViewState.designatedCorner).isEqualTo(bottomRightView)
        assertThat(controller.currentViewState.designatedCorner).isEqualTo(bottomRightView)
    }
    }


@@ -235,7 +239,7 @@ class PrivacyDotViewControllerTest : SysuiTestCase() {


        val controller = createAndInitializeController()
        val controller = createAndInitializeController()


        assertThat(controller.currentViewState.cornerIndex).isEqualTo(TOP_LEFT)
        assertThat(controller.currentViewState.corner).isEqualTo(TopLeft)
        assertThat(controller.currentViewState.designatedCorner).isEqualTo(topLeftView)
        assertThat(controller.currentViewState.designatedCorner).isEqualTo(topLeftView)
    }
    }


@@ -245,7 +249,7 @@ class PrivacyDotViewControllerTest : SysuiTestCase() {


        val controller = createAndInitializeController()
        val controller = createAndInitializeController()


        assertThat(controller.currentViewState.cornerIndex).isEqualTo(BOTTOM_LEFT)
        assertThat(controller.currentViewState.corner).isEqualTo(BottomLeft)
        assertThat(controller.currentViewState.designatedCorner).isEqualTo(bottomLeftView)
        assertThat(controller.currentViewState.designatedCorner).isEqualTo(bottomLeftView)
    }
    }


@@ -256,7 +260,7 @@ class PrivacyDotViewControllerTest : SysuiTestCase() {
        enableRtl()
        enableRtl()
        val controller = createAndInitializeController()
        val controller = createAndInitializeController()


        assertThat(controller.currentViewState.cornerIndex).isEqualTo(TOP_LEFT)
        assertThat(controller.currentViewState.corner).isEqualTo(TopLeft)
        assertThat(controller.currentViewState.designatedCorner).isEqualTo(topLeftView)
        assertThat(controller.currentViewState.designatedCorner).isEqualTo(topLeftView)
    }
    }


@@ -267,7 +271,7 @@ class PrivacyDotViewControllerTest : SysuiTestCase() {
        enableRtl()
        enableRtl()
        val controller = createAndInitializeController()
        val controller = createAndInitializeController()


        assertThat(controller.currentViewState.cornerIndex).isEqualTo(TOP_RIGHT)
        assertThat(controller.currentViewState.corner).isEqualTo(TopRight)
        assertThat(controller.currentViewState.designatedCorner).isEqualTo(topRightView)
        assertThat(controller.currentViewState.designatedCorner).isEqualTo(topRightView)
    }
    }


@@ -278,7 +282,7 @@ class PrivacyDotViewControllerTest : SysuiTestCase() {
        enableRtl()
        enableRtl()
        val controller = createAndInitializeController()
        val controller = createAndInitializeController()


        assertThat(controller.currentViewState.cornerIndex).isEqualTo(BOTTOM_LEFT)
        assertThat(controller.currentViewState.corner).isEqualTo(BottomLeft)
        assertThat(controller.currentViewState.designatedCorner).isEqualTo(bottomLeftView)
        assertThat(controller.currentViewState.designatedCorner).isEqualTo(bottomLeftView)
    }
    }


@@ -289,7 +293,7 @@ class PrivacyDotViewControllerTest : SysuiTestCase() {
        enableRtl()
        enableRtl()
        val controller = createAndInitializeController()
        val controller = createAndInitializeController()


        assertThat(controller.currentViewState.cornerIndex).isEqualTo(BOTTOM_RIGHT)
        assertThat(controller.currentViewState.corner).isEqualTo(BottomRight)
        assertThat(controller.currentViewState.designatedCorner).isEqualTo(bottomRightView)
        assertThat(controller.currentViewState.designatedCorner).isEqualTo(bottomRightView)
    }
    }


+61 −0
Original line number Original line 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.systemui.statusbar.events

import android.view.Gravity
import android.view.Surface

/** Represents a corner on the display for the privacy dot. */
enum class PrivacyDotCorner(
    val index: Int,
    val gravity: Int,
    val innerGravity: Int,
    val title: String,
) {
    TopLeft(
        index = 0,
        gravity = Gravity.TOP or Gravity.LEFT,
        innerGravity = Gravity.CENTER_VERTICAL or Gravity.RIGHT,
        title = "TopLeft",
    ),
    TopRight(
        index = 1,
        gravity = Gravity.TOP or Gravity.RIGHT,
        innerGravity = Gravity.CENTER_VERTICAL or Gravity.LEFT,
        title = "TopRight",
    ),
    BottomRight(
        index = 2,
        gravity = Gravity.BOTTOM or Gravity.RIGHT,
        innerGravity = Gravity.CENTER_VERTICAL or Gravity.RIGHT,
        title = "BottomRight",
    ),
    BottomLeft(
        index = 3,
        gravity = Gravity.BOTTOM or Gravity.LEFT,
        innerGravity = Gravity.CENTER_VERTICAL or Gravity.LEFT,
        title = "BottomLeft",
    ),
}

fun PrivacyDotCorner.rotatedCorner(@Surface.Rotation rotation: Int): PrivacyDotCorner {
    var modded = index - rotation
    if (modded < 0) {
        modded += 4
    }
    return PrivacyDotCorner.entries[modded]
}
+24 −73
Original line number Original line Diff line number Diff line
@@ -20,7 +20,6 @@ import android.annotation.UiThread
import android.graphics.Point
import android.graphics.Point
import android.graphics.Rect
import android.graphics.Rect
import android.util.Log
import android.util.Log
import android.view.Gravity
import android.view.View
import android.view.View
import android.widget.FrameLayout
import android.widget.FrameLayout
import androidx.core.animation.Animator
import androidx.core.animation.Animator
@@ -38,6 +37,10 @@ import com.android.systemui.statusbar.StatusBarState.SHADE
import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
import com.android.systemui.statusbar.events.PrivacyDotCorner.BottomLeft
import com.android.systemui.statusbar.events.PrivacyDotCorner.BottomRight
import com.android.systemui.statusbar.events.PrivacyDotCorner.TopLeft
import com.android.systemui.statusbar.events.PrivacyDotCorner.TopRight
import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener
import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -209,8 +212,8 @@ constructor(
        // If we rotated, hide all dotes until the next state resolves
        // If we rotated, hide all dotes until the next state resolves
        setCornerVisibilities(View.INVISIBLE)
        setCornerVisibilities(View.INVISIBLE)


        val newCorner = selectDesignatedCorner(rot, isRtl)
        val newCornerView = selectDesignatedCorner(rot, isRtl)
        val index = newCorner.cornerIndex()
        val corner = newCornerView.corner()
        val paddingTop = contentInsetsProvider.getStatusBarPaddingTop(rot)
        val paddingTop = contentInsetsProvider.getStatusBarPaddingTop(rot)


        synchronized(lock) {
        synchronized(lock) {
@@ -218,8 +221,8 @@ constructor(
                nextViewState.copy(
                nextViewState.copy(
                    rotation = rot,
                    rotation = rot,
                    paddingTop = paddingTop,
                    paddingTop = paddingTop,
                    designatedCorner = newCorner,
                    designatedCorner = newCornerView,
                    cornerIndex = index,
                    corner = corner,
                )
                )
        }
        }
    }
    }
@@ -271,24 +274,15 @@ constructor(
        views.forEach { corner ->
        views.forEach { corner ->
            corner.setPadding(0, paddingTop, 0, 0)
            corner.setPadding(0, paddingTop, 0, 0)


            val rotatedCorner = rotatedCorner(cornerForView(corner), rotation)
            val rotatedCorner = cornerForView(corner).rotatedCorner(rotation)
            (corner.layoutParams as FrameLayout.LayoutParams).apply {
            (corner.layoutParams as FrameLayout.LayoutParams).apply {
                gravity = rotatedCorner.toGravity()
                gravity = rotatedCorner.gravity
            }
            }


            // Set the dot's view gravity to hug the status bar
            // Set the dot's view gravity to hug the status bar
            (corner.requireViewById<View>(R.id.privacy_dot).layoutParams
            (corner.requireViewById<View>(R.id.privacy_dot).layoutParams
                    as FrameLayout.LayoutParams)
                    as FrameLayout.LayoutParams)
                .gravity = rotatedCorner.innerGravity()
                .gravity = rotatedCorner.innerGravity
        }
    }

    @UiThread
    private fun updateCornerSizes(l: Int, r: Int, rotation: Int) {
        views.forEach { corner ->
            val rotatedCorner = rotatedCorner(cornerForView(corner), rotation)
            val w = widthForCorner(rotatedCorner, l, r)
            (corner.layoutParams as FrameLayout.LayoutParams).width = w
        }
        }
    }
    }


@@ -406,25 +400,16 @@ constructor(
        }
        }
    }
    }


    private fun cornerForView(v: View): Int {
    private fun cornerForView(v: View): PrivacyDotCorner {
        return when (v) {
        return when (v) {
            tl -> TOP_LEFT
            tl -> TopLeft
            tr -> TOP_RIGHT
            tr -> TopRight
            bl -> BOTTOM_LEFT
            bl -> BottomLeft
            br -> BOTTOM_RIGHT
            br -> BottomRight
            else -> throw IllegalArgumentException("not a corner view")
            else -> throw IllegalArgumentException("not a corner view")
        }
        }
    }
    }


    private fun rotatedCorner(corner: Int, rotation: Int): Int {
        var modded = corner - rotation
        if (modded < 0) {
            modded += 4
        }

        return modded
    }

    @Rotation
    @Rotation
    private fun activeRotationForCorner(corner: View, rtl: Boolean): Int {
    private fun activeRotationForCorner(corner: View, rtl: Boolean): Int {
        // Each corner will only be visible in a single rotation, based on rtl
        // Each corner will only be visible in a single rotation, based on rtl
@@ -436,16 +421,6 @@ constructor(
        }
        }
    }
    }


    private fun widthForCorner(corner: Int, left: Int, right: Int): Int {
        return when (corner) {
            TOP_LEFT,
            BOTTOM_LEFT -> left
            TOP_RIGHT,
            BOTTOM_RIGHT -> right
            else -> throw IllegalArgumentException("Unknown corner")
        }
    }

    override fun initialize(topLeft: View, topRight: View, bottomLeft: View, bottomRight: View) {
    override fun initialize(topLeft: View, topRight: View, bottomLeft: View, bottomRight: View) {
        if (
        if (
            this::tl.isInitialized &&
            this::tl.isInitialized &&
@@ -465,9 +440,9 @@ constructor(


        val rtl = configurationController.isLayoutRtl
        val rtl = configurationController.isLayoutRtl
        val currentRotation = RotationUtils.getExactRotation(tl.context)
        val currentRotation = RotationUtils.getExactRotation(tl.context)
        val dc = selectDesignatedCorner(currentRotation, rtl)
        val designatedCornerView = selectDesignatedCorner(currentRotation, rtl)


        val index = dc.cornerIndex()
        val corner = designatedCornerView.corner()


        mainExecutor.execute { animationScheduler.addCallback(systemStatusAnimationCallback) }
        mainExecutor.execute { animationScheduler.addCallback(systemStatusAnimationCallback) }


@@ -481,8 +456,8 @@ constructor(
            nextViewState =
            nextViewState =
                nextViewState.copy(
                nextViewState.copy(
                    viewInitialized = true,
                    viewInitialized = true,
                    designatedCorner = dc,
                    designatedCorner = designatedCornerView,
                    cornerIndex = index,
                    corner = corner,
                    seascapeRect = left,
                    seascapeRect = left,
                    portraitRect = top,
                    portraitRect = top,
                    landscapeRect = right,
                    landscapeRect = right,
@@ -511,7 +486,7 @@ constructor(
        dlog("scheduleUpdate: ")
        dlog("scheduleUpdate: ")


        cancelRunnable?.run()
        cancelRunnable?.run()
        cancelRunnable = uiExecutor?.executeDelayed({ processNextViewState() }, 100)
        cancelRunnable = uiExecutor.executeDelayed({ processNextViewState() }, 100)
    }
    }


    @UiThread
    @UiThread
@@ -600,11 +575,11 @@ constructor(
            }
            }
        }
        }


    private fun View?.cornerIndex(): Int {
    private fun View?.corner(): PrivacyDotCorner? {
        if (this != null) {
        if (this != null) {
            return cornerForView(this)
            return cornerForView(this)
        }
        }
        return -1
        return null
    }
    }


    // Returns [left, top, right, bottom] aka [seascape, none, landscape, upside-down]
    // Returns [left, top, right, bottom] aka [seascape, none, landscape, upside-down]
@@ -653,35 +628,11 @@ private fun vlog(s: String) {
    }
    }
}
}


const val TOP_LEFT = 0
const val TOP_RIGHT = 1
const val BOTTOM_RIGHT = 2
const val BOTTOM_LEFT = 3
private const val DURATION = 160L
private const val DURATION = 160L
private const val TAG = "PrivacyDotViewController"
private const val TAG = "PrivacyDotViewController"
private const val DEBUG = false
private const val DEBUG = false
private const val DEBUG_VERBOSE = false
private const val DEBUG_VERBOSE = false


private fun Int.toGravity(): Int {
    return when (this) {
        TOP_LEFT -> Gravity.TOP or Gravity.LEFT
        TOP_RIGHT -> Gravity.TOP or Gravity.RIGHT
        BOTTOM_LEFT -> Gravity.BOTTOM or Gravity.LEFT
        BOTTOM_RIGHT -> Gravity.BOTTOM or Gravity.RIGHT
        else -> throw IllegalArgumentException("Not a corner")
    }
}

private fun Int.innerGravity(): Int {
    return when (this) {
        TOP_LEFT -> Gravity.CENTER_VERTICAL or Gravity.RIGHT
        TOP_RIGHT -> Gravity.CENTER_VERTICAL or Gravity.LEFT
        BOTTOM_LEFT -> Gravity.CENTER_VERTICAL or Gravity.RIGHT
        BOTTOM_RIGHT -> Gravity.CENTER_VERTICAL or Gravity.LEFT
        else -> throw IllegalArgumentException("Not a corner")
    }
}

data class ViewState(
data class ViewState(
    val viewInitialized: Boolean = false,
    val viewInitialized: Boolean = false,
    val systemPrivacyEventIsActive: Boolean = false,
    val systemPrivacyEventIsActive: Boolean = false,
@@ -694,7 +645,7 @@ data class ViewState(
    val layoutRtl: Boolean = false,
    val layoutRtl: Boolean = false,
    val rotation: Int = 0,
    val rotation: Int = 0,
    val paddingTop: Int = 0,
    val paddingTop: Int = 0,
    val cornerIndex: Int = -1,
    val corner: PrivacyDotCorner? = null,
    val designatedCorner: View? = null,
    val designatedCorner: View? = null,
    val contentDescription: String? = null,
    val contentDescription: String? = null,
) {
) {