diff options
44 files changed, 887 insertions, 284 deletions
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp index 3cf5bc1bf13a..9f790c66302d 100644 --- a/packages/SystemUI/shared/Android.bp +++ b/packages/SystemUI/shared/Android.bp @@ -47,6 +47,7 @@ android_library { ], static_libs: [ "PluginCoreLib", + "SystemUIUnfoldLib", "androidx.dynamicanimation_dynamicanimation", "androidx.concurrent_concurrent-futures", "dagger2", diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt deleted file mode 100644 index d1b06394b818..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2021 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.unfold.config - -import android.content.Context -import android.os.SystemProperties - -internal class ResourceUnfoldTransitionConfig(private val context: Context) : - UnfoldTransitionConfig { - - override val isEnabled: Boolean - get() = readIsEnabledResource() && isPropertyEnabled - - override val isHingeAngleEnabled: Boolean - get() = readIsHingeAngleEnabled() - - private val isPropertyEnabled: Boolean - get() = - SystemProperties.getInt( - UNFOLD_TRANSITION_MODE_PROPERTY_NAME, UNFOLD_TRANSITION_PROPERTY_ENABLED) == - UNFOLD_TRANSITION_PROPERTY_ENABLED - - private fun readIsEnabledResource(): Boolean = - context.resources.getBoolean(com.android.internal.R.bool.config_unfoldTransitionEnabled) - - private fun readIsHingeAngleEnabled(): Boolean = - context.resources.getBoolean(com.android.internal.R.bool.config_unfoldTransitionHingeAngle) -} - -/** - * Temporary persistent property to control unfold transition mode. - * - * See [com.android.unfold.config.AnimationMode]. - */ -private const val UNFOLD_TRANSITION_MODE_PROPERTY_NAME = "persist.unfold.transition_enabled" -private const val UNFOLD_TRANSITION_PROPERTY_ENABLED = 1 diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt new file mode 100644 index 000000000000..7f2933e44b32 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 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.unfold.system + +import android.app.ActivityManager +import android.app.WindowConfiguration +import com.android.systemui.unfold.util.CurrentActivityTypeProvider +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ActivityManagerActivityTypeProvider @Inject constructor( + private val activityManager: ActivityManager +) : CurrentActivityTypeProvider { + + override val isHomeActivity: Boolean? + get() { + val activityType = activityManager.getRunningTasks(/* maxNum= */ 1) + ?.getOrNull(0)?.topActivityType ?: return null + + return activityType == WindowConfiguration.ACTIVITY_TYPE_HOME + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateManagerFoldProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateManagerFoldProvider.kt new file mode 100644 index 000000000000..3b8d318a3a79 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateManagerFoldProvider.kt @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2022 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.unfold.system + +import android.content.Context +import android.hardware.devicestate.DeviceStateManager +import com.android.systemui.unfold.updates.FoldProvider +import com.android.systemui.unfold.updates.FoldProvider.FoldCallback +import java.util.concurrent.Executor +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class DeviceStateManagerFoldProvider @Inject constructor( + private val deviceStateManager: DeviceStateManager, + private val context: Context +) : FoldProvider { + + private val callbacks: MutableMap<FoldCallback, + DeviceStateManager.DeviceStateCallback> = hashMapOf() + + override fun registerCallback(callback: FoldCallback, executor: Executor) { + val listener = FoldStateListener(context, callback) + deviceStateManager.registerCallback(executor, listener) + callbacks[callback] = listener + } + + override fun unregisterCallback(callback: FoldCallback) { + val listener = callbacks.remove(callback) + listener?.let { + deviceStateManager.unregisterCallback(it) + } + } + + private inner class FoldStateListener( + context: Context, + listener: FoldCallback + ) : DeviceStateManager.FoldStateListener(context, { listener.onFoldUpdated(it) }) +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt new file mode 100644 index 000000000000..24ae42ae4db2 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2022 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.unfold.system + +import android.os.Handler +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.dagger.qualifiers.UiBackground +import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig +import com.android.systemui.unfold.config.UnfoldTransitionConfig +import com.android.systemui.unfold.dagger.UnfoldBackground +import com.android.systemui.unfold.dagger.UnfoldMain +import com.android.systemui.unfold.updates.FoldProvider +import com.android.systemui.unfold.util.CurrentActivityTypeProvider +import dagger.Binds +import dagger.Module +import java.util.concurrent.Executor + +/** + * Dagger module with system-only dependencies for the unfold animation. + * The code that is used to calculate unfold transition progress + * depends on some hidden APIs that are not available in normal + * apps. In order to re-use this code and use alternative implementations + * of these classes in other apps and hidden APIs here. + */ +@Module +abstract class SystemUnfoldSharedModule { + + @Binds + abstract fun activityTypeProvider(executor: ActivityManagerActivityTypeProvider): + CurrentActivityTypeProvider + + @Binds + abstract fun config(config: ResourceUnfoldTransitionConfig): UnfoldTransitionConfig + + @Binds + abstract fun foldState(provider: DeviceStateManagerFoldProvider): FoldProvider + + @Binds + @UnfoldMain + abstract fun mainExecutor(@Main executor: Executor): Executor + + @Binds + @UnfoldMain + abstract fun mainHandler(@Main handler: Handler): Handler + + @Binds + @UnfoldBackground + abstract fun backgroundExecutor(@UiBackground executor: Executor): Executor +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt deleted file mode 100644 index b351585de364..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.android.systemui.unfold.updates.hinge - -import androidx.core.util.Consumer - -internal object EmptyHingeAngleProvider : HingeAngleProvider { - override fun start() {} - - override fun stop() {} - - override fun removeCallback(listener: Consumer<Float>) {} - - override fun addCallback(listener: Consumer<Float>) {} -} diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt deleted file mode 100644 index 48a5b12c759a..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.android.systemui.unfold.updates.hinge - -import androidx.core.util.Consumer -import com.android.systemui.statusbar.policy.CallbackController - -/** - * Emits device hinge angle values (angle between two integral parts of the device). - * - * The hinge angle could be from 0 to 360 degrees inclusive. For foldable devices usually 0 - * corresponds to fully closed (folded) state and 180 degrees corresponds to fully open (flat) - * state. - */ -interface HingeAngleProvider : CallbackController<Consumer<Float>> { - fun start() - fun stop() -} - -const val FULLY_OPEN_DEGREES = 180f -const val FULLY_CLOSED_DEGREES = 0f diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt index 53c528ff24a8..ec938b219933 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt @@ -1,3 +1,17 @@ +/* + * Copyright (C) 2022 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.unfold.util import android.content.Context diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt index d2d2361d613d..eea6ac0e72e9 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt @@ -20,6 +20,7 @@ import android.content.Context import android.view.IWindowManager import com.android.systemui.keyguard.LifecycleScreenStatusProvider import com.android.systemui.unfold.config.UnfoldTransitionConfig +import com.android.systemui.unfold.system.SystemUnfoldSharedModule import com.android.systemui.unfold.updates.FoldStateProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider @@ -34,7 +35,7 @@ import java.util.Optional import javax.inject.Named import javax.inject.Singleton -@Module(includes = [UnfoldSharedModule::class]) +@Module(includes = [UnfoldSharedModule::class, SystemUnfoldSharedModule::class]) class UnfoldTransitionModule { @Provides @UnfoldTransitionATracePrefix fun tracingTagPrefix() = "systemui" @@ -62,11 +63,6 @@ class UnfoldTransitionModule { @Provides @Singleton - fun provideUnfoldTransitionConfig(context: Context): UnfoldTransitionConfig = - createConfig(context) - - @Provides - @Singleton fun provideNaturalRotationProgressProvider( context: Context, windowManager: IWindowManager, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt index 32314159f865..a4a89a4c67f2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt @@ -1,3 +1,17 @@ +/* + * Copyright (C) 2022 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.shared.animation import android.testing.AndroidTestingRunner @@ -7,31 +21,24 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate -import com.android.systemui.unfold.UnfoldTransitionProgressProvider -import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener +import com.android.systemui.unfold.TestUnfoldTransitionProvider import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.Captor import org.mockito.Mock -import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations +import org.mockito.Mockito.`when` as whenever @SmallTest @RunWith(AndroidTestingRunner::class) class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() { - @Mock private lateinit var progressProvider: UnfoldTransitionProgressProvider + private val progressProvider = TestUnfoldTransitionProvider() @Mock private lateinit var parent: ViewGroup - @Captor private lateinit var progressListenerCaptor: ArgumentCaptor<TransitionProgressListener> - private lateinit var animator: UnfoldConstantTranslateAnimator - private lateinit var progressListener: TransitionProgressListener private val viewsIdToRegister = setOf( @@ -46,17 +53,14 @@ class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() { UnfoldConstantTranslateAnimator(viewsIdToRegister, progressProvider) animator.init(parent, MAX_TRANSLATION) - - verify(progressProvider).addCallback(progressListenerCaptor.capture()) - progressListener = progressListenerCaptor.value } @Test fun onTransition_noMatchingIds() { // GIVEN no views matching any ids // WHEN the transition starts - progressListener.onTransitionStarted() - progressListener.onTransitionProgress(.1f) + progressProvider.onTransitionStarted() + progressProvider.onTransitionProgress(.1f) // THEN nothing... no exceptions } @@ -86,22 +90,22 @@ class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() { // Compare values as ints because -0f != 0f // WHEN the transition starts - progressListener.onTransitionStarted() - progressListener.onTransitionProgress(0f) + progressProvider.onTransitionStarted() + progressProvider.onTransitionProgress(0f) list.forEach { (view, direction) -> assertEquals((-MAX_TRANSLATION * direction).toInt(), view.translationX.toInt()) } // WHEN the transition progresses, translation is updated - progressListener.onTransitionProgress(.5f) + progressProvider.onTransitionProgress(.5f) list.forEach { (view, direction) -> assertEquals((-MAX_TRANSLATION / 2f * direction).toInt(), view.translationX.toInt()) } // WHEN the transition ends, translation is completed - progressListener.onTransitionProgress(1f) - progressListener.onTransitionFinished() + progressProvider.onTransitionProgress(1f) + progressProvider.onTransitionFinished() list.forEach { (view, _) -> assertEquals(0, view.translationX.toInt()) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt index 9ab88dc2d764..ba29e953c73d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt @@ -136,6 +136,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { private class UnfoldConfig : UnfoldTransitionConfig { override var isEnabled: Boolean = false override var isHingeAngleEnabled: Boolean = false + override val halfFoldedTimeoutMillis: Int = 0 } private class TestTouchEventHandler : PhoneStatusBarView.TouchEventHandler { diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt index 8076b4eda883..39e4e6446d02 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt @@ -25,28 +25,20 @@ import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING -import com.android.systemui.unfold.updates.FoldStateProvider import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate +import com.android.systemui.unfold.util.TestFoldStateProvider import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.Captor -import org.mockito.Mock -import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @SmallTest class FoldStateLoggingProviderTest : SysuiTestCase() { - @Captor - private lateinit var foldUpdatesListener: ArgumentCaptor<FoldStateProvider.FoldUpdatesListener> - - @Mock private lateinit var foldStateProvider: FoldStateProvider - + private val testFoldStateProvider = TestFoldStateProvider() private val fakeClock = FakeSystemClock() private lateinit var foldStateLoggingProvider: FoldStateLoggingProvider @@ -65,12 +57,10 @@ class FoldStateLoggingProviderTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) foldStateLoggingProvider = - FoldStateLoggingProviderImpl(foldStateProvider, fakeClock).apply { + FoldStateLoggingProviderImpl(testFoldStateProvider, fakeClock).apply { addCallback(foldStateLoggingListener) init() } - - verify(foldStateProvider).addCallback(foldUpdatesListener.capture()) } @Test @@ -183,10 +173,10 @@ class FoldStateLoggingProviderTest : SysuiTestCase() { fun uninit_removesCallback() { foldStateLoggingProvider.uninit() - verify(foldStateProvider).removeCallback(foldUpdatesListener.value) + assertThat(testFoldStateProvider.hasListeners).isFalse() } private fun sendFoldUpdate(@FoldUpdate foldUpdate: Int) { - foldUpdatesListener.value.onFoldUpdate(foldUpdate) + testFoldStateProvider.sendFoldUpdate(foldUpdate) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt new file mode 100644 index 000000000000..ab450e2506f9 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2022 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.unfold.config + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + + +/** + * A test that checks that we load correct resources in + * ResourceUnfoldTransitionConfig as we use strings there instead of R constants. + * Internal Android resource constants are not available in public APIs, + * so we can't use them there directly. + */ +@RunWith(AndroidTestingRunner::class) +@SmallTest +class ResourceUnfoldTransitionConfigTest : SysuiTestCase() { + + private val config = ResourceUnfoldTransitionConfig() + + @Test + fun testIsEnabled() { + assertThat(config.isEnabled).isEqualTo(mContext.resources + .getBoolean(com.android.internal.R.bool.config_unfoldTransitionEnabled)) + } + + @Test + fun testHingeAngleEnabled() { + assertThat(config.isHingeAngleEnabled).isEqualTo(mContext.resources + .getBoolean(com.android.internal.R.bool.config_unfoldTransitionHingeAngle)) + } + + @Test + fun testHalfFoldedTimeout() { + assertThat(config.halfFoldedTimeoutMillis).isEqualTo(mContext.resources + .getInteger(com.android.internal.R.integer.config_unfoldTransitionHalfFoldedTimeout)) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt index 1f1f88b07ea1..87fca1f23f1a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt @@ -16,82 +16,69 @@ package com.android.systemui.unfold.updates -import android.app.ActivityManager -import android.app.ActivityManager.RunningTaskInfo -import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME -import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD -import android.app.WindowConfiguration.ActivityType -import android.hardware.devicestate.DeviceStateManager -import android.hardware.devicestate.DeviceStateManager.FoldStateListener import android.os.Handler import android.testing.AndroidTestingRunner import androidx.core.util.Consumer import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig +import com.android.systemui.unfold.config.UnfoldTransitionConfig +import com.android.systemui.unfold.system.ActivityManagerActivityTypeProvider +import com.android.systemui.unfold.updates.FoldProvider.FoldCallback import com.android.systemui.unfold.updates.hinge.HingeAngleProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener -import com.android.systemui.unfold.util.FoldableDeviceStates -import com.android.systemui.unfold.util.FoldableTestUtils import com.android.systemui.util.mockito.any import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.Captor import org.mockito.Mock -import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations +import java.util.concurrent.Executor +import org.mockito.Mockito.`when` as whenever @RunWith(AndroidTestingRunner::class) @SmallTest class DeviceFoldStateProviderTest : SysuiTestCase() { - @Mock private lateinit var hingeAngleProvider: HingeAngleProvider - - @Mock private lateinit var screenStatusProvider: ScreenStatusProvider - - @Mock private lateinit var deviceStateManager: DeviceStateManager - - @Mock private lateinit var activityManager: ActivityManager - - @Mock private lateinit var handler: Handler - - @Captor private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener> + @Mock + private lateinit var activityTypeProvider: ActivityManagerActivityTypeProvider - @Captor private lateinit var screenOnListenerCaptor: ArgumentCaptor<ScreenListener> + @Mock + private lateinit var handler: Handler - @Captor private lateinit var hingeAngleCaptor: ArgumentCaptor<Consumer<Float>> + private val foldProvider = TestFoldProvider() + private val screenOnStatusProvider = TestScreenOnStatusProvider() + private val testHingeAngleProvider = TestHingeAngleProvider() private lateinit var foldStateProvider: DeviceFoldStateProvider private val foldUpdates: MutableList<Int> = arrayListOf() private val hingeAngleUpdates: MutableList<Float> = arrayListOf() - private lateinit var deviceStates: FoldableDeviceStates - private var scheduledRunnable: Runnable? = null private var scheduledRunnableDelay: Long? = null @Before fun setUp() { MockitoAnnotations.initMocks(this) - overrideResource( - com.android.internal.R.integer.config_unfoldTransitionHalfFoldedTimeout, - HALF_OPENED_TIMEOUT_MILLIS.toInt()) - deviceStates = FoldableTestUtils.findDeviceStates(context) + + val config = object : UnfoldTransitionConfig by ResourceUnfoldTransitionConfig() { + override val halfFoldedTimeoutMillis: Int + get() = HALF_OPENED_TIMEOUT_MILLIS.toInt() + } foldStateProvider = DeviceFoldStateProvider( - context, - hingeAngleProvider, - screenStatusProvider, - deviceStateManager, - activityManager, + config, + testHingeAngleProvider, + screenOnStatusProvider, + foldProvider, + activityTypeProvider, context.mainExecutor, - handler) + handler + ) foldStateProvider.addCallback( object : FoldStateProvider.FoldUpdatesListener { @@ -105,10 +92,6 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { }) foldStateProvider.start() - verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture()) - verify(screenStatusProvider).addCallback(screenOnListenerCaptor.capture()) - verify(hingeAngleProvider).addCallback(hingeAngleCaptor.capture()) - whenever(handler.postDelayed(any<Runnable>(), any())).then { invocationOnMock -> scheduledRunnable = invocationOnMock.getArgument<Runnable>(0) scheduledRunnableDelay = invocationOnMock.getArgument<Long>(1) @@ -125,7 +108,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { } // By default, we're on launcher. - setupForegroundActivityType(ACTIVITY_TYPE_HOME) + setupForegroundActivityType(isHomeActivity = true) } @Test @@ -146,14 +129,14 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { fun testOnFolded_stopsHingeAngleProvider() { setFoldState(folded = true) - verify(hingeAngleProvider).stop() + assertThat(testHingeAngleProvider.isStarted).isFalse() } @Test fun testOnUnfolded_startsHingeAngleProvider() { setFoldState(folded = false) - verify(hingeAngleProvider).start() + assertThat(testHingeAngleProvider.isStarted).isTrue() } @Test @@ -310,7 +293,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_whileNotOnLauncher_doesNotTriggerBeforeThreshold() { - setupForegroundActivityType(ACTIVITY_TYPE_STANDARD) + setupForegroundActivityType(isHomeActivity = false) sendHingeAngleEvent(180) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) @@ -319,8 +302,28 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { } @Test + fun startClosingEvent_whileActivityTypeNotAvailable_triggerBeforeThreshold() { + setupForegroundActivityType(isHomeActivity = null) + sendHingeAngleEvent(180) + + sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) + + assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) + } + + @Test + fun startClosingEvent_whileOnLauncher_doesTriggerBeforeThreshold() { + setupForegroundActivityType(isHomeActivity = true) + sendHingeAngleEvent(180) + + sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) + + assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) + } + + @Test fun startClosingEvent_whileNotOnLauncher_triggersAfterThreshold() { - setupForegroundActivityType(ACTIVITY_TYPE_STANDARD) + setupForegroundActivityType(isHomeActivity = false) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES - 1) @@ -328,9 +331,8 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) } - private fun setupForegroundActivityType(@ActivityType type: Int) { - val taskInfo = RunningTaskInfo().apply { topActivityType = type } - whenever(activityManager.getRunningTasks(1)).thenReturn(listOf(taskInfo)) + private fun setupForegroundActivityType(isHomeActivity: Boolean?) { + whenever(activityTypeProvider.isHomeActivity).thenReturn(isHomeActivity) } private fun simulateTimeout(waitTime: Long = HALF_OPENED_TIMEOUT_MILLIS) { @@ -348,16 +350,72 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { } private fun setFoldState(folded: Boolean) { - val state = if (folded) deviceStates.folded else deviceStates.unfolded - foldStateListenerCaptor.value.onStateChanged(state) + foldProvider.notifyFolded(folded) } private fun fireScreenOnEvent() { - screenOnListenerCaptor.value.onScreenTurnedOn() + screenOnStatusProvider.notifyScreenTurnedOn() } private fun sendHingeAngleEvent(angle: Int) { - hingeAngleCaptor.value.accept(angle.toFloat()) + testHingeAngleProvider.notifyAngle(angle.toFloat()) + } + + private class TestFoldProvider : FoldProvider { + private val callbacks = arrayListOf<FoldCallback>() + + override fun registerCallback(callback: FoldCallback, executor: Executor) { + callbacks += callback + } + + override fun unregisterCallback(callback: FoldCallback) { + callbacks -= callback + } + + fun notifyFolded(isFolded: Boolean) { + callbacks.forEach { it.onFoldUpdated(isFolded) } + } + } + + private class TestScreenOnStatusProvider : ScreenStatusProvider { + private val callbacks = arrayListOf<ScreenListener>() + + override fun addCallback(listener: ScreenListener) { + callbacks += listener + } + + override fun removeCallback(listener: ScreenListener) { + callbacks -= listener + } + + fun notifyScreenTurnedOn() { + callbacks.forEach { it.onScreenTurnedOn() } + } + } + + private class TestHingeAngleProvider : HingeAngleProvider { + private val callbacks = arrayListOf<Consumer<Float>>() + var isStarted: Boolean = false + + override fun start() { + isStarted = true; + } + + override fun stop() { + isStarted = false; + } + + override fun addCallback(listener: Consumer<Float>) { + callbacks += listener + } + + override fun removeCallback(listener: Consumer<Float>) { + callbacks -= listener + } + + fun notifyAngle(angle: Float) { + callbacks.forEach { it.accept(angle) } + } } companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt index a3f17aa5ba55..b2cedbf8d606 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt @@ -20,18 +20,18 @@ import android.view.IRotationWatcher import android.view.IWindowManager import android.view.Surface import androidx.test.filters.SmallTest -import com.android.systemui.unfold.UnfoldTransitionProgressProvider +import com.android.systemui.SysuiTestCase +import com.android.systemui.unfold.TestUnfoldTransitionProvider import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener import com.android.systemui.util.mockito.any -import com.android.systemui.SysuiTestCase import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Mock -import org.mockito.Mockito.verify import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.never +import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @@ -41,16 +41,13 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() { @Mock lateinit var windowManager: IWindowManager - @Mock - lateinit var sourceProvider: UnfoldTransitionProgressProvider + private val sourceProvider = TestUnfoldTransitionProvider() @Mock lateinit var transitionListener: TransitionProgressListener lateinit var progressProvider: NaturalRotationUnfoldProgressProvider - private val sourceProviderListenerCaptor = - ArgumentCaptor.forClass(TransitionProgressListener::class.java) private val rotationWatcherCaptor = ArgumentCaptor.forClass(IRotationWatcher.Stub::class.java) @@ -66,7 +63,6 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() { progressProvider.init() - verify(sourceProvider).addCallback(sourceProviderListenerCaptor.capture()) verify(windowManager).watchRotation(rotationWatcherCaptor.capture(), any()) progressProvider.addCallback(transitionListener) @@ -76,7 +72,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() { fun testNaturalRotation0_sendTransitionStartedEvent_eventReceived() { onRotationChanged(Surface.ROTATION_0) - source.onTransitionStarted() + sourceProvider.onTransitionStarted() verify(transitionListener).onTransitionStarted() } @@ -85,7 +81,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() { fun testNaturalRotation0_sendTransitionProgressEvent_eventReceived() { onRotationChanged(Surface.ROTATION_0) - source.onTransitionProgress(0.5f) + sourceProvider.onTransitionProgress(0.5f) verify(transitionListener).onTransitionProgress(0.5f) } @@ -94,7 +90,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() { fun testNotNaturalRotation90_sendTransitionStartedEvent_eventNotReceived() { onRotationChanged(Surface.ROTATION_90) - source.onTransitionStarted() + sourceProvider.onTransitionStarted() verify(transitionListener, never()).onTransitionStarted() } @@ -103,7 +99,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() { fun testNaturalRotation90_sendTransitionProgressEvent_eventNotReceived() { onRotationChanged(Surface.ROTATION_90) - source.onTransitionProgress(0.5f) + sourceProvider.onTransitionProgress(0.5f) verify(transitionListener, never()).onTransitionProgress(0.5f) } @@ -111,7 +107,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() { @Test fun testRotationBecameUnnaturalDuringTransition_sendsTransitionFinishedEvent() { onRotationChanged(Surface.ROTATION_0) - source.onTransitionStarted() + sourceProvider.onTransitionStarted() clearInvocations(transitionListener) onRotationChanged(Surface.ROTATION_90) @@ -122,7 +118,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() { @Test fun testRotationBecameNaturalDuringTransition_sendsTransitionStartedEvent() { onRotationChanged(Surface.ROTATION_90) - source.onTransitionStarted() + sourceProvider.onTransitionStarted() clearInvocations(transitionListener) onRotationChanged(Surface.ROTATION_0) @@ -133,7 +129,4 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() { private fun onRotationChanged(rotation: Int) { rotationWatcherCaptor.value.onRotationChanged(rotation) } - - private val source: TransitionProgressListener - get() = sourceProviderListenerCaptor.value } diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt index db7a85166807..fc2a78a5ee7f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt @@ -21,6 +21,7 @@ import android.database.ContentObserver import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.unfold.TestUnfoldTransitionProvider import com.android.systemui.unfold.UnfoldTransitionProgressProvider import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener import com.android.systemui.util.mockito.any @@ -41,15 +42,11 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() { lateinit var contentResolver: ContentResolver @Mock - lateinit var sourceProvider: UnfoldTransitionProgressProvider - - @Mock lateinit var sinkProvider: TransitionProgressListener - lateinit var progressProvider: ScaleAwareTransitionProgressProvider + private val sourceProvider = TestUnfoldTransitionProvider() - private val sourceProviderListenerCaptor = - ArgumentCaptor.forClass(TransitionProgressListener::class.java) + lateinit var progressProvider: ScaleAwareTransitionProgressProvider private val animatorDurationScaleListenerCaptor = ArgumentCaptor.forClass(ContentObserver::class.java) @@ -63,7 +60,6 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() { contentResolver ) - verify(sourceProvider).addCallback(sourceProviderListenerCaptor.capture()) verify(contentResolver).registerContentObserver(any(), any(), animatorDurationScaleListenerCaptor.capture()) @@ -74,7 +70,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() { fun onTransitionStarted_animationsEnabled_eventReceived() { setAnimationsEnabled(true) - source.onTransitionStarted() + sourceProvider.onTransitionStarted() verify(sinkProvider).onTransitionStarted() } @@ -83,7 +79,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() { fun onTransitionStarted_animationsNotEnabled_eventNotReceived() { setAnimationsEnabled(false) - source.onTransitionStarted() + sourceProvider.onTransitionStarted() verifyNoMoreInteractions(sinkProvider) } @@ -92,7 +88,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() { fun onTransitionEnd_animationsEnabled_eventReceived() { setAnimationsEnabled(true) - source.onTransitionFinished() + sourceProvider.onTransitionFinished() verify(sinkProvider).onTransitionFinished() } @@ -101,7 +97,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() { fun onTransitionEnd_animationsNotEnabled_eventNotReceived() { setAnimationsEnabled(false) - source.onTransitionFinished() + sourceProvider.onTransitionFinished() verifyNoMoreInteractions(sinkProvider) } @@ -110,7 +106,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() { fun onTransitionProgress_animationsEnabled_eventReceived() { setAnimationsEnabled(true) - source.onTransitionProgress(42f) + sourceProvider.onTransitionProgress(42f) verify(sinkProvider).onTransitionProgress(42f) } @@ -119,7 +115,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() { fun onTransitionProgress_animationsNotEnabled_eventNotReceived() { setAnimationsEnabled(false) - source.onTransitionProgress(42f) + sourceProvider.onTransitionProgress(42f) verifyNoMoreInteractions(sinkProvider) } @@ -133,7 +129,4 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() { ValueAnimator.setDurationScale(durationScale) animatorDurationScaleListenerCaptor.value.dispatchChange(/* selfChange= */false) } - - private val source: TransitionProgressListener - get() = sourceProviderListenerCaptor.value } diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt index 8f851ec60981..a064e8c81076 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt @@ -24,6 +24,8 @@ import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener class TestFoldStateProvider : FoldStateProvider { private val listeners: MutableList<FoldUpdatesListener> = arrayListOf() + val hasListeners: Boolean + get() = listeners.isNotEmpty() override fun start() { } diff --git a/packages/SystemUI/unfold/Android.bp b/packages/SystemUI/unfold/Android.bp new file mode 100644 index 000000000000..108295b90e58 --- /dev/null +++ b/packages/SystemUI/unfold/Android.bp @@ -0,0 +1,39 @@ +// Copyright (C) 2022 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"], +} + +android_library { + name: "SystemUIUnfoldLib", + srcs: [ + "src/**/*.java", + "src/**/*.kt", + "src/**/*.aidl", + ], + static_libs: [ + "androidx.dynamicanimation_dynamicanimation", + "dagger2", + "jsr330", + ], + java_version: "1.8", + min_sdk_version: "current", + plugins: ["dagger2-compiler"], +} diff --git a/packages/SystemUI/unfold/AndroidManifest.xml b/packages/SystemUI/unfold/AndroidManifest.xml new file mode 100644 index 000000000000..ee8afe1aff5b --- /dev/null +++ b/packages/SystemUI/unfold/AndroidManifest.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2017 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.systemui.unfold"> + + +</manifest> diff --git a/packages/SystemUI/unfold/lint-baseline.xml b/packages/SystemUI/unfold/lint-baseline.xml new file mode 100644 index 000000000000..449ed2e60853 --- /dev/null +++ b/packages/SystemUI/unfold/lint-baseline.xml @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="UTF-8"?> +<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" name="" variant="all" version="7.1.0-dev"> +</issues> diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedComponent.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt index 9e5aeb84b624..a5ec0a454412 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedComponent.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt @@ -16,16 +16,16 @@ package com.android.systemui.unfold -import android.app.ActivityManager import android.content.ContentResolver import android.content.Context import android.hardware.SensorManager -import android.hardware.devicestate.DeviceStateManager import android.os.Handler -import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.dagger.qualifiers.UiBackground import com.android.systemui.unfold.config.UnfoldTransitionConfig +import com.android.systemui.unfold.dagger.UnfoldBackground +import com.android.systemui.unfold.dagger.UnfoldMain +import com.android.systemui.unfold.updates.FoldProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider +import com.android.systemui.unfold.util.CurrentActivityTypeProvider import com.android.systemui.unfold.util.UnfoldTransitionATracePrefix import dagger.BindsInstance import dagger.Component @@ -51,12 +51,12 @@ internal interface UnfoldSharedComponent { @BindsInstance context: Context, @BindsInstance config: UnfoldTransitionConfig, @BindsInstance screenStatusProvider: ScreenStatusProvider, - @BindsInstance deviceStateManager: DeviceStateManager, - @BindsInstance activityManager: ActivityManager, + @BindsInstance foldProvider: FoldProvider, + @BindsInstance activityTypeProvider: CurrentActivityTypeProvider, @BindsInstance sensorManager: SensorManager, - @BindsInstance @Main handler: Handler, - @BindsInstance @Main executor: Executor, - @BindsInstance @UiBackground backgroundExecutor: Executor, + @BindsInstance @UnfoldMain handler: Handler, + @BindsInstance @UnfoldMain executor: Executor, + @BindsInstance @UnfoldBackground backgroundExecutor: Executor, @BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String, @BindsInstance contentResolver: ContentResolver = context.contentResolver ): UnfoldSharedComponent diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedModule.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt index c612995241ef..8f4ee4dc9838 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedModule.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt @@ -17,8 +17,8 @@ package com.android.systemui.unfold import android.hardware.SensorManager -import com.android.systemui.dagger.qualifiers.UiBackground import com.android.systemui.unfold.config.UnfoldTransitionConfig +import com.android.systemui.unfold.dagger.UnfoldBackground import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider import com.android.systemui.unfold.updates.DeviceFoldStateProvider @@ -70,7 +70,7 @@ class UnfoldSharedModule { fun hingeAngleProvider( config: UnfoldTransitionConfig, sensorManager: SensorManager, - @UiBackground executor: Executor + @UnfoldBackground executor: Executor ): HingeAngleProvider = if (config.isHingeAngleEnabled) { HingeSensorAngleProvider(sensorManager, executor) diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt index cc56007c431a..402dd8474bc4 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt @@ -17,14 +17,13 @@ package com.android.systemui.unfold -import android.app.ActivityManager import android.content.Context import android.hardware.SensorManager -import android.hardware.devicestate.DeviceStateManager import android.os.Handler -import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig import com.android.systemui.unfold.config.UnfoldTransitionConfig +import com.android.systemui.unfold.updates.FoldProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider +import com.android.systemui.unfold.util.CurrentActivityTypeProvider import java.util.concurrent.Executor /** @@ -39,8 +38,8 @@ fun createUnfoldTransitionProgressProvider( context: Context, config: UnfoldTransitionConfig, screenStatusProvider: ScreenStatusProvider, - deviceStateManager: DeviceStateManager, - activityManager: ActivityManager, + foldProvider: FoldProvider, + activityTypeProvider: CurrentActivityTypeProvider, sensorManager: SensorManager, mainHandler: Handler, mainExecutor: Executor, @@ -52,8 +51,8 @@ fun createUnfoldTransitionProgressProvider( context, config, screenStatusProvider, - deviceStateManager, - activityManager, + foldProvider, + activityTypeProvider, sensorManager, mainHandler, mainExecutor, @@ -64,5 +63,3 @@ fun createUnfoldTransitionProgressProvider( ?: throw IllegalStateException( "Trying to create " + "UnfoldTransitionProgressProvider when the transition is disabled") - -fun createConfig(context: Context): UnfoldTransitionConfig = ResourceUnfoldTransitionConfig(context) diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt index 409dc95ab131..d54481c72bfd 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt @@ -15,9 +15,9 @@ */ package com.android.systemui.unfold -import android.annotation.FloatRange -import com.android.systemui.statusbar.policy.CallbackController +import androidx.annotation.FloatRange import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener +import com.android.systemui.unfold.util.CallbackController /** * Interface that allows to receive unfold transition progress updates. diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt new file mode 100644 index 000000000000..2044f05664d0 --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2022 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.unfold.compat + +import android.content.Context +import android.content.res.Configuration +import com.android.systemui.unfold.updates.FoldProvider +import com.android.systemui.unfold.updates.FoldProvider.FoldCallback +import java.util.concurrent.Executor + +/** + * Fold provider that notifies about fold state based on the screen size + * It could be used when no activity context is available + * TODO(b/232369816): use Jetpack WM library when non-activity contexts supported b/169740873 + */ +class ScreenSizeFoldProvider(private val context: Context) : FoldProvider { + + private var callbacks: MutableList<FoldCallback> = arrayListOf() + private var lastWidth: Int = 0 + + override fun registerCallback(callback: FoldCallback, executor: Executor) { + callbacks += callback + onConfigurationChange(context.resources.configuration) + } + + override fun unregisterCallback(callback: FoldCallback) { + callbacks -= callback + } + + fun onConfigurationChange(newConfig: Configuration) { + if (lastWidth == newConfig.smallestScreenWidthDp) { + return + } + + if (newConfig.smallestScreenWidthDp > INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP) { + callbacks.forEach { it.onFoldUpdated(false) } + } else { + callbacks.forEach { it.onFoldUpdated(true) } + } + lastWidth = newConfig.smallestScreenWidthDp + } +} + +private const val INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP = 600 diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/SizeScreenStatusProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/SizeScreenStatusProvider.kt new file mode 100644 index 000000000000..c405f3110297 --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/SizeScreenStatusProvider.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2022 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.unfold.compat + +import com.android.systemui.unfold.updates.FoldProvider +import com.android.systemui.unfold.updates.screen.ScreenStatusProvider +import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener +import java.util.concurrent.Executor + +class SizeScreenStatusProvider( + private val foldProvider: FoldProvider, + private val executor: Executor +) : ScreenStatusProvider { + + private val listeners: MutableList<ScreenListener> = arrayListOf() + private val callback = object : FoldProvider.FoldCallback { + override fun onFoldUpdated(isFolded: Boolean) { + if (!isFolded) { + listeners.forEach { it.onScreenTurnedOn() } + } + } + } + + fun start() { + foldProvider.registerCallback( + callback, + executor + ) + } + + fun stop() { + foldProvider.unregisterCallback(callback) + } + + override fun addCallback(listener: ScreenListener) { + listeners.add(listener) + } + + override fun removeCallback(listener: ScreenListener) { + listeners.remove(listener) + } +} diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt new file mode 100644 index 000000000000..c51372975a67 --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 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.unfold.config + +import android.content.res.Resources +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ResourceUnfoldTransitionConfig @Inject constructor() : UnfoldTransitionConfig { + + override val isEnabled: Boolean by lazy { + val id = Resources.getSystem() + .getIdentifier("config_unfoldTransitionEnabled", "bool", "android") + Resources.getSystem().getBoolean(id) + } + + override val isHingeAngleEnabled: Boolean by lazy { + val id = Resources.getSystem() + .getIdentifier("config_unfoldTransitionHingeAngle", "bool", "android") + Resources.getSystem().getBoolean(id) + } + + override val halfFoldedTimeoutMillis: Int by lazy { + val id = Resources.getSystem() + .getIdentifier("config_unfoldTransitionHalfFoldedTimeout", "integer", "android") + Resources.getSystem().getInteger(id) + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt index 5b187b3486c6..765e862aa00d 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt @@ -18,4 +18,5 @@ package com.android.systemui.unfold.config interface UnfoldTransitionConfig { val isEnabled: Boolean val isHingeAngleEnabled: Boolean + val halfFoldedTimeoutMillis: Int } diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBackground.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBackground.kt new file mode 100644 index 000000000000..60747954dac3 --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBackground.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 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.unfold.dagger + +import javax.inject.Qualifier + +/** + * Alternative to [UiBackground] qualifier annotation in unfold module. + * It is needed as we can't depend on SystemUI code in this module. + */ +@Qualifier +@Retention(AnnotationRetention.RUNTIME) +annotation class UnfoldBackground diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldMain.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldMain.kt new file mode 100644 index 000000000000..5553690fb562 --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldMain.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 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.unfold.dagger + +import javax.inject.Qualifier + +/** + * Alternative to [Main] qualifier annotation in unfold module. + * It is needed as we can't depend on SystemUI code in this module. + */ +@Qualifier +@Retention(AnnotationRetention.RUNTIME) +annotation class UnfoldMain diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt index 4c85b055aeae..4c85b055aeae 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt index 04d920cb15d5..2ab28c65f32f 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt @@ -16,7 +16,6 @@ package com.android.systemui.unfold.progress import android.util.Log -import android.util.MathUtils.saturate import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.FloatPropertyCompat import androidx.dynamicanimation.animation.SpringAnimation @@ -70,6 +69,9 @@ class PhysicsBasedUnfoldTransitionProgressProvider( springAnimation.animateToFinalPosition(progress) } + private fun saturate(amount: Float, low: Float = 0f, high: Float = 1f): Float = + if (amount < low) low else if (amount > high) high else amount + override fun onFoldUpdate(@FoldUpdate update: Int) { when (update) { FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> { diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt index 14581ccd5c0a..e8038fd7dfa6 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt @@ -15,46 +15,46 @@ */ package com.android.systemui.unfold.updates -import android.annotation.FloatRange -import android.app.ActivityManager -import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME -import android.content.Context -import android.hardware.devicestate.DeviceStateManager import android.os.Handler import android.util.Log +import androidx.annotation.FloatRange import androidx.annotation.VisibleForTesting import androidx.core.util.Consumer -import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.unfold.config.UnfoldTransitionConfig +import com.android.systemui.unfold.dagger.UnfoldMain import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener import com.android.systemui.unfold.updates.hinge.FULLY_CLOSED_DEGREES import com.android.systemui.unfold.updates.hinge.FULLY_OPEN_DEGREES import com.android.systemui.unfold.updates.hinge.HingeAngleProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider +import com.android.systemui.unfold.util.CurrentActivityTypeProvider import java.util.concurrent.Executor import javax.inject.Inject class DeviceFoldStateProvider @Inject constructor( - context: Context, + config: UnfoldTransitionConfig, private val hingeAngleProvider: HingeAngleProvider, private val screenStatusProvider: ScreenStatusProvider, - private val deviceStateManager: DeviceStateManager, - private val activityManager: ActivityManager, - @Main private val mainExecutor: Executor, - @Main private val handler: Handler + private val foldProvider: FoldProvider, + private val activityTypeProvider: CurrentActivityTypeProvider, + @UnfoldMain private val mainExecutor: Executor, + @UnfoldMain private val handler: Handler ) : FoldStateProvider { private val outputListeners: MutableList<FoldUpdatesListener> = mutableListOf() - @FoldUpdate private var lastFoldUpdate: Int? = null + @FoldUpdate + private var lastFoldUpdate: Int? = null - @FloatRange(from = 0.0, to = 180.0) private var lastHingeAngle: Float = 0f + @FloatRange(from = 0.0, to = 180.0) + private var lastHingeAngle: Float = 0f private val hingeAngleListener = HingeAngleListener() private val screenListener = ScreenStatusListener() - private val foldStateListener = FoldStateListener(context) + private val foldStateListener = FoldStateListener() private val timeoutRunnable = TimeoutRunnable() /** @@ -62,22 +62,20 @@ constructor( * [FOLD_UPDATE_START_CLOSING] or [FOLD_UPDATE_START_OPENING] event, if an end state is not * reached. */ - private val halfOpenedTimeoutMillis: Int = - context.resources.getInteger( - com.android.internal.R.integer.config_unfoldTransitionHalfFoldedTimeout) + private val halfOpenedTimeoutMillis: Int = config.halfFoldedTimeoutMillis private var isFolded = false private var isUnfoldHandled = true override fun start() { - deviceStateManager.registerCallback(mainExecutor, foldStateListener) + foldProvider.registerCallback(foldStateListener, mainExecutor) screenStatusProvider.addCallback(screenListener) hingeAngleProvider.addCallback(hingeAngleListener) } override fun stop() { screenStatusProvider.removeCallback(screenListener) - deviceStateManager.unregisterCallback(foldStateListener) + foldProvider.unregisterCallback(foldStateListener) hingeAngleProvider.removeCallback(hingeAngleListener) hingeAngleProvider.stop() } @@ -92,13 +90,13 @@ constructor( override val isFinishedOpening: Boolean get() = !isFolded && - (lastFoldUpdate == FOLD_UPDATE_FINISH_FULL_OPEN || - lastFoldUpdate == FOLD_UPDATE_FINISH_HALF_OPEN) + (lastFoldUpdate == FOLD_UPDATE_FINISH_FULL_OPEN || + lastFoldUpdate == FOLD_UPDATE_FINISH_HALF_OPEN) private val isTransitionInProgress: Boolean get() = lastFoldUpdate == FOLD_UPDATE_START_OPENING || - lastFoldUpdate == FOLD_UPDATE_START_CLOSING + lastFoldUpdate == FOLD_UPDATE_START_CLOSING private fun onHingeAngle(angle: Float) { if (DEBUG) { @@ -136,39 +134,36 @@ constructor( * apps that support table-top/HALF_FOLDED mode. Only for launcher, there is no threshold. */ private fun getClosingThreshold(): Int? { - val activityType = - activityManager.getRunningTasks(/* maxNum= */ 1)?.getOrNull(0)?.topActivityType - ?: return null + val isHomeActivity = activityTypeProvider.isHomeActivity ?: return null if (DEBUG) { - Log.d(TAG, "activityType=" + activityType) + Log.d(TAG, "isHomeActivity=$isHomeActivity") } - return if (activityType == ACTIVITY_TYPE_HOME) { + return if (isHomeActivity) { null } else { START_CLOSING_ON_APPS_THRESHOLD_DEGREES } } - private inner class FoldStateListener(context: Context) : - DeviceStateManager.FoldStateListener( - context, - { folded: Boolean -> - isFolded = folded - lastHingeAngle = FULLY_CLOSED_DEGREES - - if (folded) { - hingeAngleProvider.stop() - notifyFoldUpdate(FOLD_UPDATE_FINISH_CLOSED) - cancelTimeout() - isUnfoldHandled = false - } else { - notifyFoldUpdate(FOLD_UPDATE_START_OPENING) - rescheduleAbortAnimationTimeout() - hingeAngleProvider.start() - } - }) + private inner class FoldStateListener : FoldProvider.FoldCallback { + override fun onFoldUpdated(isFolded: Boolean) { + this@DeviceFoldStateProvider.isFolded = isFolded + lastHingeAngle = FULLY_CLOSED_DEGREES + + if (isFolded) { + hingeAngleProvider.stop() + notifyFoldUpdate(FOLD_UPDATE_FINISH_CLOSED) + cancelTimeout() + isUnfoldHandled = false + } else { + notifyFoldUpdate(FOLD_UPDATE_START_OPENING) + rescheduleAbortAnimationTimeout() + hingeAngleProvider.start() + } + } + } private fun notifyFoldUpdate(@FoldUpdate update: Int) { if (DEBUG) { @@ -234,7 +229,9 @@ private const val TAG = "DeviceFoldProvider" private const val DEBUG = false /** Threshold after which we consider the device fully unfolded. */ -@VisibleForTesting const val FULLY_OPEN_THRESHOLD_DEGREES = 15f +@VisibleForTesting +const val FULLY_OPEN_THRESHOLD_DEGREES = 15f /** Fold animation on top of apps only when the angle exceeds this threshold. */ -@VisibleForTesting const val START_CLOSING_ON_APPS_THRESHOLD_DEGREES = 60 +@VisibleForTesting +const val START_CLOSING_ON_APPS_THRESHOLD_DEGREES = 60 diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldProvider.kt new file mode 100644 index 000000000000..6e87beeb295f --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldProvider.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 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.unfold.updates + +import java.util.concurrent.Executor + +interface FoldProvider { + fun registerCallback(callback: FoldCallback, executor: Executor) + fun unregisterCallback(callback: FoldCallback) + + interface FoldCallback { + fun onFoldUpdated(isFolded: Boolean) + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt index 14a3a70fc6b0..c7a8bf336777 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt @@ -15,10 +15,10 @@ */ package com.android.systemui.unfold.updates -import android.annotation.FloatRange -import android.annotation.IntDef -import com.android.systemui.statusbar.policy.CallbackController +import androidx.annotation.FloatRange +import androidx.annotation.IntDef import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener +import com.android.systemui.unfold.util.CallbackController /** * Allows to subscribe to main events related to fold/unfold process such as hinge angle update, @@ -36,7 +36,6 @@ interface FoldStateProvider : CallbackController<FoldUpdatesListener> { } @IntDef( - prefix = ["FOLD_UPDATE_"], value = [ FOLD_UPDATE_START_OPENING, diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt new file mode 100644 index 000000000000..e985506bd989 --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2022 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.unfold.updates.hinge + +import androidx.core.util.Consumer + +internal object EmptyHingeAngleProvider : HingeAngleProvider { + override fun start() {} + + override fun stop() {} + + override fun removeCallback(listener: Consumer<Float>) {} + + override fun addCallback(listener: Consumer<Float>) {} +} diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt new file mode 100644 index 000000000000..e464c3f81546 --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022 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.unfold.updates.hinge + +import androidx.core.util.Consumer +import com.android.systemui.unfold.util.CallbackController + +/** + * Emits device hinge angle values (angle between two integral parts of the device). + * + * The hinge angle could be from 0 to 360 degrees inclusive. For foldable devices usually 0 + * corresponds to fully closed (folded) state and 180 degrees corresponds to fully open (flat) + * state. + */ +interface HingeAngleProvider : CallbackController<Consumer<Float>> { + fun start() + fun stop() +} + +const val FULLY_OPEN_DEGREES = 180f +const val FULLY_CLOSED_DEGREES = 0f diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt index c93412b53817..3fc5d610dc2d 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt @@ -1,3 +1,17 @@ +/* + * Copyright (C) 2022 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.unfold.updates.hinge import android.hardware.Sensor diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt index 668c69442cac..d95e050474de 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt @@ -15,8 +15,8 @@ */ package com.android.systemui.unfold.updates.screen -import com.android.systemui.statusbar.policy.CallbackController import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener +import com.android.systemui.unfold.util.CallbackController interface ScreenStatusProvider : CallbackController<ScreenListener> { diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt index 1574c8d37ab1..d8bc01804f14 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt @@ -1,3 +1,17 @@ +/* + * Copyright (C) 2022 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.unfold.util import android.os.Trace diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CallbackController.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CallbackController.kt new file mode 100644 index 000000000000..46ad534722cd --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CallbackController.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2022 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.unfold.util + +interface CallbackController<T> { + fun addCallback(listener: T) + fun removeCallback(listener: T) +} diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CurrentActivityTypeProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CurrentActivityTypeProvider.kt new file mode 100644 index 000000000000..d0e6cdc9a3c6 --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CurrentActivityTypeProvider.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2022 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.unfold.util + +interface CurrentActivityTypeProvider { + val isHomeActivity: Boolean? +} + +class EmptyCurrentActivityTypeProvider(override val isHomeActivity: Boolean? = null) : + CurrentActivityTypeProvider diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt index dfe87921dd42..5c92b3499835 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt @@ -1,3 +1,17 @@ +/* + * Copyright (C) 2022 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.unfold.util import android.animation.ValueAnimator diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt index 8491f832b740..8491f832b740 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt |