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

Commit af05fa7c authored by Bharat Singh's avatar Bharat Singh Committed by Android (Google) Code Review
Browse files

Merge "[SysUI][Floaty] Choose the correct resource based on primary / secondary display" into main

parents 81172d0c b3cef5de
Loading
Loading
Loading
Loading
+21 −30
Original line number Original line Diff line number Diff line
@@ -22,8 +22,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyevent.data.repository.fakeKeyEventRepository
import com.android.systemui.keyevent.data.repository.fakeKeyEventRepository
import com.android.systemui.keyevent.data.repository.keyEventRepository
import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
import com.android.systemui.keyevent.domain.interactor.keyEventInteractor
import com.android.systemui.keyevent.domain.interactor.keyEventInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.advanceTimeBy
import com.android.systemui.kosmos.advanceTimeBy
@@ -36,8 +34,8 @@ import com.android.systemui.topwindoweffects.data.repository.fakeSqueezeEffectRe
import com.android.systemui.topwindoweffects.domain.interactor.SqueezeEffectInteractor
import com.android.systemui.topwindoweffects.domain.interactor.SqueezeEffectInteractor
import com.android.systemui.topwindoweffects.ui.compose.EffectsWindowRoot
import com.android.systemui.topwindoweffects.ui.compose.EffectsWindowRoot
import com.android.systemui.topwindoweffects.ui.viewmodel.SqueezeEffectViewModel
import com.android.systemui.topwindoweffects.ui.viewmodel.SqueezeEffectViewModel
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlin.time.Duration
import kotlinx.coroutines.test.TestScope
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runCurrent
import org.junit.Before
import org.junit.Before
@@ -51,8 +49,6 @@ import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.mockito.kotlin.whenever
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds


@SmallTest
@SmallTest
@RunWith(AndroidJUnit4::class)
@RunWith(AndroidJUnit4::class)
@@ -60,22 +56,21 @@ class TopLevelWindowEffectsTest : SysuiTestCase() {


    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()


    @Mock
    @Mock private lateinit var windowManager: WindowManager
    private lateinit var windowManager: WindowManager


    @Mock
    @Mock private lateinit var viewModelFactory: SqueezeEffectViewModel.Factory
    private lateinit var viewModelFactory: SqueezeEffectViewModel.Factory


    private val Kosmos.underTest by Kosmos.Fixture {
    private val Kosmos.underTest by
        Kosmos.Fixture {
            TopLevelWindowEffects(
            TopLevelWindowEffects(
                context = mContext,
                context = mContext,
                applicationScope = testScope.backgroundScope,
                applicationScope = testScope.backgroundScope,
                bgContext = testScope.testScheduler,
                windowManager = windowManager,
                windowManager = windowManager,
                keyEventInteractor = keyEventInteractor,
                keyEventInteractor = keyEventInteractor,
                viewModelFactory = viewModelFactory,
                viewModelFactory = viewModelFactory,
            squeezeEffectInteractor = SqueezeEffectInteractor(
                squeezeEffectInteractor =
                squeezeEffectRepository = fakeSqueezeEffectRepository
                    SqueezeEffectInteractor(squeezeEffectRepository = fakeSqueezeEffectRepository),
            )
            )
            )
        }
        }


@@ -113,8 +108,7 @@ class TopLevelWindowEffectsTest : SysuiTestCase() {


            waitFor(101.milliseconds)
            waitFor(101.milliseconds)


            verify(windowManager, times(1)).addView(any<View>(),
            verify(windowManager, times(1)).addView(any<View>(), any<WindowManager.LayoutParams>())
                any<WindowManager.LayoutParams>())
        }
        }


    @Test
    @Test
@@ -161,9 +155,6 @@ class TopLevelWindowEffectsTest : SysuiTestCase() {


            runCurrent()
            runCurrent()


            verify(windowManager, times(1)).addView(
            verify(windowManager, times(1)).addView(any<View>(), any<WindowManager.LayoutParams>())
                any<View>(),
                any<WindowManager.LayoutParams>()
            )
        }
        }
}
}
+11 −10
Original line number Original line Diff line number Diff line
@@ -46,14 +46,15 @@ class SqueezeEffectRepositoryTest : SysuiTestCase() {
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val globalSettings = FakeGlobalSettings(StandardTestDispatcher())
    private val globalSettings = FakeGlobalSettings(StandardTestDispatcher())


    @Mock
    @Mock private lateinit var bgHandler: Handler
    private lateinit var bgHandler: Handler


    private val Kosmos.underTest by Kosmos.Fixture {
    private val Kosmos.underTest by
        Kosmos.Fixture {
            SqueezeEffectRepositoryImpl(
            SqueezeEffectRepositoryImpl(
                context = mContext,
                bgHandler = bgHandler,
                bgHandler = bgHandler,
                bgCoroutineContext = testScope.testScheduler,
                bgCoroutineContext = testScope.testScheduler,
            globalSettings = globalSettings
                globalSettings = globalSettings,
            )
            )
        }
        }


+45 −28
Original line number Original line Diff line number Diff line
@@ -14,7 +14,7 @@
 * limitations under the License.
 * limitations under the License.
 */
 */


package com.android.systemui.topwindoweffects;
package com.android.systemui.topwindoweffects


import android.content.Context
import android.content.Context
import android.graphics.PixelFormat
import android.graphics.PixelFormat
@@ -24,25 +24,31 @@ import android.view.WindowManager
import com.android.systemui.CoreStartable
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor
import com.android.systemui.topwindoweffects.domain.interactor.SqueezeEffectInteractor
import com.android.systemui.topwindoweffects.domain.interactor.SqueezeEffectInteractor
import com.android.systemui.topwindoweffects.ui.compose.EffectsWindowRoot
import com.android.systemui.topwindoweffects.ui.compose.EffectsWindowRoot
import com.android.systemui.topwindoweffects.ui.viewmodel.SqueezeEffectViewModel
import com.android.systemui.topwindoweffects.ui.viewmodel.SqueezeEffectViewModel
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.launch
import javax.inject.Inject


@SysUISingleton
@SysUISingleton
class TopLevelWindowEffects @Inject constructor(
class TopLevelWindowEffects
@Inject
constructor(
    @Application private val context: Context,
    @Application private val context: Context,
    @Application private val applicationScope: CoroutineScope,
    @Application private val applicationScope: CoroutineScope,
    @Background private val bgContext: CoroutineContext,
    private val windowManager: WindowManager,
    private val windowManager: WindowManager,
    private val squeezeEffectInteractor: SqueezeEffectInteractor,
    private val squeezeEffectInteractor: SqueezeEffectInteractor,
    private val keyEventInteractor: KeyEventInteractor,
    private val keyEventInteractor: KeyEventInteractor,
    private val viewModelFactory: SqueezeEffectViewModel.Factory
    private val viewModelFactory: SqueezeEffectViewModel.Factory,
) : CoreStartable {
) : CoreStartable {


    override fun start() {
    override fun start() {
@@ -56,18 +62,27 @@ class TopLevelWindowEffects @Inject constructor(
                        // threshold of 100 milliseconds
                        // threshold of 100 milliseconds
                        launchWindowEffect?.cancel()
                        launchWindowEffect?.cancel()
                        if (down) {
                        if (down) {
                            val roundedCornerId =
                                async(context = bgContext) {
                                    squeezeEffectInteractor.getRoundedCornersResourceId()
                                }
                            launchWindowEffect = launch {
                            launchWindowEffect = launch {
                                delay(100) // delay to invoke the squeeze effect
                                delay(100) // delay to invoke the squeeze effect
                                if (root == null) {
                                if (root == null) {
                                    root = EffectsWindowRoot(
                                    root =
                                        EffectsWindowRoot(
                                            context = context,
                                            context = context,
                                            viewModelFactory = viewModelFactory,
                                            viewModelFactory = viewModelFactory,
                                            topRoundedCornerResourceId =
                                                roundedCornerId.await().top,
                                            bottomRoundedCornerResourceId =
                                                roundedCornerId.await().bottom,
                                            onEffectFinished = {
                                            onEffectFinished = {
                                                if (root?.isAttachedToWindow == true) {
                                                if (root?.isAttachedToWindow == true) {
                                                    windowManager.removeView(root)
                                                    windowManager.removeView(root)
                                                    root = null
                                                    root = null
                                                }
                                                }
                                        }
                                            },
                                        )
                                        )
                                    root?.let {
                                    root?.let {
                                        windowManager.addView(it, getWindowManagerLayoutParams())
                                        windowManager.addView(it, getWindowManagerLayoutParams())
@@ -84,21 +99,23 @@ class TopLevelWindowEffects @Inject constructor(
    }
    }


    private fun getWindowManagerLayoutParams(): WindowManager.LayoutParams {
    private fun getWindowManagerLayoutParams(): WindowManager.LayoutParams {
        val lp = WindowManager.LayoutParams(
        val lp =
            WindowManager.LayoutParams(
                WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY,
                WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
                    or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
                    or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                    WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or
                    or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
                    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
            PixelFormat.TRANSPARENT
                PixelFormat.TRANSPARENT,
            )
            )


        lp.privateFlags = lp.privateFlags or
        lp.privateFlags =
                (WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
            lp.privateFlags or
                        or WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
                (WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS or
                        or WindowManager.LayoutParams.PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED
                    WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION or
                        or WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED
                    WindowManager.LayoutParams.PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED or
                        or WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)
                    WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED or
                    WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY)


        lp.layoutInDisplayCutoutMode =
        lp.layoutInDisplayCutoutMode =
            WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
            WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+19 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2025 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.topwindoweffects.data.entity

data class SqueezeEffectCornerResourceId(val top: Int, val bottom: Int)
+4 −1
Original line number Original line Diff line number Diff line
@@ -16,8 +16,11 @@


package com.android.systemui.topwindoweffects.data.repository
package com.android.systemui.topwindoweffects.data.repository


import com.android.systemui.topwindoweffects.data.entity.SqueezeEffectCornerResourceId
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.Flow


interface SqueezeEffectRepository {
interface SqueezeEffectRepository {
    val isSqueezeEffectEnabled: Flow<Boolean>
    val isSqueezeEffectEnabled: Flow<Boolean>

    suspend fun getRoundedCornersResourceId(): SqueezeEffectCornerResourceId
}
}
Loading