Merge "Extract unfold classes to a separate library" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
3676e7afd6
@@ -47,6 +47,7 @@ android_library {
|
|||||||
],
|
],
|
||||||
static_libs: [
|
static_libs: [
|
||||||
"PluginCoreLib",
|
"PluginCoreLib",
|
||||||
|
"SystemUIUnfoldLib",
|
||||||
"androidx.dynamicanimation_dynamicanimation",
|
"androidx.dynamicanimation_dynamicanimation",
|
||||||
"androidx.concurrent_concurrent-futures",
|
"androidx.concurrent_concurrent-futures",
|
||||||
"dagger2",
|
"dagger2",
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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) })
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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>) {}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
package com.android.systemui.unfold.util
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import android.content.Context
|
|||||||
import android.view.IWindowManager
|
import android.view.IWindowManager
|
||||||
import com.android.systemui.keyguard.LifecycleScreenStatusProvider
|
import com.android.systemui.keyguard.LifecycleScreenStatusProvider
|
||||||
import com.android.systemui.unfold.config.UnfoldTransitionConfig
|
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.FoldStateProvider
|
||||||
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
|
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
|
||||||
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
|
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
|
||||||
@@ -34,7 +35,7 @@ import java.util.Optional
|
|||||||
import javax.inject.Named
|
import javax.inject.Named
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Module(includes = [UnfoldSharedModule::class])
|
@Module(includes = [UnfoldSharedModule::class, SystemUnfoldSharedModule::class])
|
||||||
class UnfoldTransitionModule {
|
class UnfoldTransitionModule {
|
||||||
|
|
||||||
@Provides @UnfoldTransitionATracePrefix fun tracingTagPrefix() = "systemui"
|
@Provides @UnfoldTransitionATracePrefix fun tracingTagPrefix() = "systemui"
|
||||||
@@ -60,11 +61,6 @@ class UnfoldTransitionModule {
|
|||||||
FoldStateLogger(FoldStateLoggingProvider)
|
FoldStateLogger(FoldStateLoggingProvider)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
fun provideUnfoldTransitionConfig(context: Context): UnfoldTransitionConfig =
|
|
||||||
createConfig(context)
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideNaturalRotationProgressProvider(
|
fun provideNaturalRotationProgressProvider(
|
||||||
|
|||||||
@@ -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
|
package com.android.systemui.shared.animation
|
||||||
|
|
||||||
import android.testing.AndroidTestingRunner
|
import android.testing.AndroidTestingRunner
|
||||||
@@ -7,31 +21,24 @@ import androidx.test.filters.SmallTest
|
|||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction
|
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction
|
||||||
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate
|
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate
|
||||||
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
|
import com.android.systemui.unfold.TestUnfoldTransitionProvider
|
||||||
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
|
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.mockito.ArgumentCaptor
|
|
||||||
import org.mockito.Captor
|
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.Mockito.verify
|
|
||||||
import org.mockito.Mockito.`when` as whenever
|
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
|
import org.mockito.Mockito.`when` as whenever
|
||||||
|
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWith(AndroidTestingRunner::class)
|
@RunWith(AndroidTestingRunner::class)
|
||||||
class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() {
|
class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() {
|
||||||
|
|
||||||
@Mock private lateinit var progressProvider: UnfoldTransitionProgressProvider
|
private val progressProvider = TestUnfoldTransitionProvider()
|
||||||
|
|
||||||
@Mock private lateinit var parent: ViewGroup
|
@Mock private lateinit var parent: ViewGroup
|
||||||
|
|
||||||
@Captor private lateinit var progressListenerCaptor: ArgumentCaptor<TransitionProgressListener>
|
|
||||||
|
|
||||||
private lateinit var animator: UnfoldConstantTranslateAnimator
|
private lateinit var animator: UnfoldConstantTranslateAnimator
|
||||||
private lateinit var progressListener: TransitionProgressListener
|
|
||||||
|
|
||||||
private val viewsIdToRegister =
|
private val viewsIdToRegister =
|
||||||
setOf(
|
setOf(
|
||||||
@@ -46,17 +53,14 @@ class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() {
|
|||||||
UnfoldConstantTranslateAnimator(viewsIdToRegister, progressProvider)
|
UnfoldConstantTranslateAnimator(viewsIdToRegister, progressProvider)
|
||||||
|
|
||||||
animator.init(parent, MAX_TRANSLATION)
|
animator.init(parent, MAX_TRANSLATION)
|
||||||
|
|
||||||
verify(progressProvider).addCallback(progressListenerCaptor.capture())
|
|
||||||
progressListener = progressListenerCaptor.value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun onTransition_noMatchingIds() {
|
fun onTransition_noMatchingIds() {
|
||||||
// GIVEN no views matching any ids
|
// GIVEN no views matching any ids
|
||||||
// WHEN the transition starts
|
// WHEN the transition starts
|
||||||
progressListener.onTransitionStarted()
|
progressProvider.onTransitionStarted()
|
||||||
progressListener.onTransitionProgress(.1f)
|
progressProvider.onTransitionProgress(.1f)
|
||||||
|
|
||||||
// THEN nothing... no exceptions
|
// THEN nothing... no exceptions
|
||||||
}
|
}
|
||||||
@@ -86,22 +90,22 @@ class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() {
|
|||||||
// Compare values as ints because -0f != 0f
|
// Compare values as ints because -0f != 0f
|
||||||
|
|
||||||
// WHEN the transition starts
|
// WHEN the transition starts
|
||||||
progressListener.onTransitionStarted()
|
progressProvider.onTransitionStarted()
|
||||||
progressListener.onTransitionProgress(0f)
|
progressProvider.onTransitionProgress(0f)
|
||||||
|
|
||||||
list.forEach { (view, direction) ->
|
list.forEach { (view, direction) ->
|
||||||
assertEquals((-MAX_TRANSLATION * direction).toInt(), view.translationX.toInt())
|
assertEquals((-MAX_TRANSLATION * direction).toInt(), view.translationX.toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
// WHEN the transition progresses, translation is updated
|
// WHEN the transition progresses, translation is updated
|
||||||
progressListener.onTransitionProgress(.5f)
|
progressProvider.onTransitionProgress(.5f)
|
||||||
list.forEach { (view, direction) ->
|
list.forEach { (view, direction) ->
|
||||||
assertEquals((-MAX_TRANSLATION / 2f * direction).toInt(), view.translationX.toInt())
|
assertEquals((-MAX_TRANSLATION / 2f * direction).toInt(), view.translationX.toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
// WHEN the transition ends, translation is completed
|
// WHEN the transition ends, translation is completed
|
||||||
progressListener.onTransitionProgress(1f)
|
progressProvider.onTransitionProgress(1f)
|
||||||
progressListener.onTransitionFinished()
|
progressProvider.onTransitionFinished()
|
||||||
list.forEach { (view, _) -> assertEquals(0, view.translationX.toInt()) }
|
list.forEach { (view, _) -> assertEquals(0, view.translationX.toInt()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
|
|||||||
private class UnfoldConfig : UnfoldTransitionConfig {
|
private class UnfoldConfig : UnfoldTransitionConfig {
|
||||||
override var isEnabled: Boolean = false
|
override var isEnabled: Boolean = false
|
||||||
override var isHingeAngleEnabled: Boolean = false
|
override var isHingeAngleEnabled: Boolean = false
|
||||||
|
override val halfFoldedTimeoutMillis: Int = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestTouchEventHandler : PhoneStatusBarView.TouchEventHandler {
|
private class TestTouchEventHandler : PhoneStatusBarView.TouchEventHandler {
|
||||||
|
|||||||
@@ -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_FINISH_HALF_OPEN
|
||||||
import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
|
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.FOLD_UPDATE_START_OPENING
|
||||||
import com.android.systemui.unfold.updates.FoldStateProvider
|
|
||||||
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
|
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
|
||||||
|
import com.android.systemui.unfold.util.TestFoldStateProvider
|
||||||
import com.android.systemui.util.time.FakeSystemClock
|
import com.android.systemui.util.time.FakeSystemClock
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
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
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
@RunWith(AndroidTestingRunner::class)
|
@RunWith(AndroidTestingRunner::class)
|
||||||
@SmallTest
|
@SmallTest
|
||||||
class FoldStateLoggingProviderTest : SysuiTestCase() {
|
class FoldStateLoggingProviderTest : SysuiTestCase() {
|
||||||
|
|
||||||
@Captor
|
private val testFoldStateProvider = TestFoldStateProvider()
|
||||||
private lateinit var foldUpdatesListener: ArgumentCaptor<FoldStateProvider.FoldUpdatesListener>
|
|
||||||
|
|
||||||
@Mock private lateinit var foldStateProvider: FoldStateProvider
|
|
||||||
|
|
||||||
private val fakeClock = FakeSystemClock()
|
private val fakeClock = FakeSystemClock()
|
||||||
|
|
||||||
private lateinit var foldStateLoggingProvider: FoldStateLoggingProvider
|
private lateinit var foldStateLoggingProvider: FoldStateLoggingProvider
|
||||||
@@ -65,12 +57,10 @@ class FoldStateLoggingProviderTest : SysuiTestCase() {
|
|||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
|
|
||||||
foldStateLoggingProvider =
|
foldStateLoggingProvider =
|
||||||
FoldStateLoggingProviderImpl(foldStateProvider, fakeClock).apply {
|
FoldStateLoggingProviderImpl(testFoldStateProvider, fakeClock).apply {
|
||||||
addCallback(foldStateLoggingListener)
|
addCallback(foldStateLoggingListener)
|
||||||
init()
|
init()
|
||||||
}
|
}
|
||||||
|
|
||||||
verify(foldStateProvider).addCallback(foldUpdatesListener.capture())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -183,10 +173,10 @@ class FoldStateLoggingProviderTest : SysuiTestCase() {
|
|||||||
fun uninit_removesCallback() {
|
fun uninit_removesCallback() {
|
||||||
foldStateLoggingProvider.uninit()
|
foldStateLoggingProvider.uninit()
|
||||||
|
|
||||||
verify(foldStateProvider).removeCallback(foldUpdatesListener.value)
|
assertThat(testFoldStateProvider.hasListeners).isFalse()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sendFoldUpdate(@FoldUpdate foldUpdate: Int) {
|
private fun sendFoldUpdate(@FoldUpdate foldUpdate: Int) {
|
||||||
foldUpdatesListener.value.onFoldUpdate(foldUpdate)
|
testFoldStateProvider.sendFoldUpdate(foldUpdate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,82 +16,69 @@
|
|||||||
|
|
||||||
package com.android.systemui.unfold.updates
|
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.os.Handler
|
||||||
import android.testing.AndroidTestingRunner
|
import android.testing.AndroidTestingRunner
|
||||||
import androidx.core.util.Consumer
|
import androidx.core.util.Consumer
|
||||||
import androidx.test.filters.SmallTest
|
import androidx.test.filters.SmallTest
|
||||||
import com.android.systemui.SysuiTestCase
|
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.hinge.HingeAngleProvider
|
||||||
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
|
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
|
||||||
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener
|
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.android.systemui.util.mockito.any
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.mockito.ArgumentCaptor
|
|
||||||
import org.mockito.Captor
|
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.Mockito.verify
|
|
||||||
import org.mockito.Mockito.`when` as whenever
|
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
|
import java.util.concurrent.Executor
|
||||||
|
import org.mockito.Mockito.`when` as whenever
|
||||||
|
|
||||||
@RunWith(AndroidTestingRunner::class)
|
@RunWith(AndroidTestingRunner::class)
|
||||||
@SmallTest
|
@SmallTest
|
||||||
class DeviceFoldStateProviderTest : SysuiTestCase() {
|
class DeviceFoldStateProviderTest : SysuiTestCase() {
|
||||||
|
|
||||||
@Mock private lateinit var hingeAngleProvider: HingeAngleProvider
|
@Mock
|
||||||
|
private lateinit var activityTypeProvider: ActivityManagerActivityTypeProvider
|
||||||
|
|
||||||
@Mock private lateinit var screenStatusProvider: ScreenStatusProvider
|
@Mock
|
||||||
|
private lateinit var handler: Handler
|
||||||
|
|
||||||
@Mock private lateinit var deviceStateManager: DeviceStateManager
|
private val foldProvider = TestFoldProvider()
|
||||||
|
private val screenOnStatusProvider = TestScreenOnStatusProvider()
|
||||||
@Mock private lateinit var activityManager: ActivityManager
|
private val testHingeAngleProvider = TestHingeAngleProvider()
|
||||||
|
|
||||||
@Mock private lateinit var handler: Handler
|
|
||||||
|
|
||||||
@Captor private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener>
|
|
||||||
|
|
||||||
@Captor private lateinit var screenOnListenerCaptor: ArgumentCaptor<ScreenListener>
|
|
||||||
|
|
||||||
@Captor private lateinit var hingeAngleCaptor: ArgumentCaptor<Consumer<Float>>
|
|
||||||
|
|
||||||
private lateinit var foldStateProvider: DeviceFoldStateProvider
|
private lateinit var foldStateProvider: DeviceFoldStateProvider
|
||||||
|
|
||||||
private val foldUpdates: MutableList<Int> = arrayListOf()
|
private val foldUpdates: MutableList<Int> = arrayListOf()
|
||||||
private val hingeAngleUpdates: MutableList<Float> = arrayListOf()
|
private val hingeAngleUpdates: MutableList<Float> = arrayListOf()
|
||||||
|
|
||||||
private lateinit var deviceStates: FoldableDeviceStates
|
|
||||||
|
|
||||||
private var scheduledRunnable: Runnable? = null
|
private var scheduledRunnable: Runnable? = null
|
||||||
private var scheduledRunnableDelay: Long? = null
|
private var scheduledRunnableDelay: Long? = null
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
overrideResource(
|
|
||||||
com.android.internal.R.integer.config_unfoldTransitionHalfFoldedTimeout,
|
val config = object : UnfoldTransitionConfig by ResourceUnfoldTransitionConfig() {
|
||||||
HALF_OPENED_TIMEOUT_MILLIS.toInt())
|
override val halfFoldedTimeoutMillis: Int
|
||||||
deviceStates = FoldableTestUtils.findDeviceStates(context)
|
get() = HALF_OPENED_TIMEOUT_MILLIS.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
foldStateProvider =
|
foldStateProvider =
|
||||||
DeviceFoldStateProvider(
|
DeviceFoldStateProvider(
|
||||||
context,
|
config,
|
||||||
hingeAngleProvider,
|
testHingeAngleProvider,
|
||||||
screenStatusProvider,
|
screenOnStatusProvider,
|
||||||
deviceStateManager,
|
foldProvider,
|
||||||
activityManager,
|
activityTypeProvider,
|
||||||
context.mainExecutor,
|
context.mainExecutor,
|
||||||
handler)
|
handler
|
||||||
|
)
|
||||||
|
|
||||||
foldStateProvider.addCallback(
|
foldStateProvider.addCallback(
|
||||||
object : FoldStateProvider.FoldUpdatesListener {
|
object : FoldStateProvider.FoldUpdatesListener {
|
||||||
@@ -105,10 +92,6 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
|
|||||||
})
|
})
|
||||||
foldStateProvider.start()
|
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 ->
|
whenever(handler.postDelayed(any<Runnable>(), any())).then { invocationOnMock ->
|
||||||
scheduledRunnable = invocationOnMock.getArgument<Runnable>(0)
|
scheduledRunnable = invocationOnMock.getArgument<Runnable>(0)
|
||||||
scheduledRunnableDelay = invocationOnMock.getArgument<Long>(1)
|
scheduledRunnableDelay = invocationOnMock.getArgument<Long>(1)
|
||||||
@@ -125,7 +108,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// By default, we're on launcher.
|
// By default, we're on launcher.
|
||||||
setupForegroundActivityType(ACTIVITY_TYPE_HOME)
|
setupForegroundActivityType(isHomeActivity = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -146,14 +129,14 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
|
|||||||
fun testOnFolded_stopsHingeAngleProvider() {
|
fun testOnFolded_stopsHingeAngleProvider() {
|
||||||
setFoldState(folded = true)
|
setFoldState(folded = true)
|
||||||
|
|
||||||
verify(hingeAngleProvider).stop()
|
assertThat(testHingeAngleProvider.isStarted).isFalse()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testOnUnfolded_startsHingeAngleProvider() {
|
fun testOnUnfolded_startsHingeAngleProvider() {
|
||||||
setFoldState(folded = false)
|
setFoldState(folded = false)
|
||||||
|
|
||||||
verify(hingeAngleProvider).start()
|
assertThat(testHingeAngleProvider.isStarted).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -310,7 +293,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun startClosingEvent_whileNotOnLauncher_doesNotTriggerBeforeThreshold() {
|
fun startClosingEvent_whileNotOnLauncher_doesNotTriggerBeforeThreshold() {
|
||||||
setupForegroundActivityType(ACTIVITY_TYPE_STANDARD)
|
setupForegroundActivityType(isHomeActivity = false)
|
||||||
sendHingeAngleEvent(180)
|
sendHingeAngleEvent(180)
|
||||||
|
|
||||||
sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1)
|
sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1)
|
||||||
@@ -318,9 +301,29 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
|
|||||||
assertThat(foldUpdates).isEmpty()
|
assertThat(foldUpdates).isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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
|
@Test
|
||||||
fun startClosingEvent_whileNotOnLauncher_triggersAfterThreshold() {
|
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)
|
||||||
|
|
||||||
sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES - 1)
|
sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES - 1)
|
||||||
@@ -328,9 +331,8 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
|
|||||||
assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
|
assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupForegroundActivityType(@ActivityType type: Int) {
|
private fun setupForegroundActivityType(isHomeActivity: Boolean?) {
|
||||||
val taskInfo = RunningTaskInfo().apply { topActivityType = type }
|
whenever(activityTypeProvider.isHomeActivity).thenReturn(isHomeActivity)
|
||||||
whenever(activityManager.getRunningTasks(1)).thenReturn(listOf(taskInfo))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun simulateTimeout(waitTime: Long = HALF_OPENED_TIMEOUT_MILLIS) {
|
private fun simulateTimeout(waitTime: Long = HALF_OPENED_TIMEOUT_MILLIS) {
|
||||||
@@ -348,16 +350,72 @@ class DeviceFoldStateProviderTest : SysuiTestCase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setFoldState(folded: Boolean) {
|
private fun setFoldState(folded: Boolean) {
|
||||||
val state = if (folded) deviceStates.folded else deviceStates.unfolded
|
foldProvider.notifyFolded(folded)
|
||||||
foldStateListenerCaptor.value.onStateChanged(state)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fireScreenOnEvent() {
|
private fun fireScreenOnEvent() {
|
||||||
screenOnListenerCaptor.value.onScreenTurnedOn()
|
screenOnStatusProvider.notifyScreenTurnedOn()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sendHingeAngleEvent(angle: Int) {
|
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 {
|
companion object {
|
||||||
|
|||||||
@@ -20,18 +20,18 @@ import android.view.IRotationWatcher
|
|||||||
import android.view.IWindowManager
|
import android.view.IWindowManager
|
||||||
import android.view.Surface
|
import android.view.Surface
|
||||||
import androidx.test.filters.SmallTest
|
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.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
|
||||||
import com.android.systemui.util.mockito.any
|
import com.android.systemui.util.mockito.any
|
||||||
import com.android.systemui.SysuiTestCase
|
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.mockito.ArgumentCaptor
|
import org.mockito.ArgumentCaptor
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.Mockito.verify
|
|
||||||
import org.mockito.Mockito.clearInvocations
|
import org.mockito.Mockito.clearInvocations
|
||||||
import org.mockito.Mockito.never
|
import org.mockito.Mockito.never
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
@RunWith(AndroidTestingRunner::class)
|
@RunWith(AndroidTestingRunner::class)
|
||||||
@@ -41,16 +41,13 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
|
|||||||
@Mock
|
@Mock
|
||||||
lateinit var windowManager: IWindowManager
|
lateinit var windowManager: IWindowManager
|
||||||
|
|
||||||
@Mock
|
private val sourceProvider = TestUnfoldTransitionProvider()
|
||||||
lateinit var sourceProvider: UnfoldTransitionProgressProvider
|
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
lateinit var transitionListener: TransitionProgressListener
|
lateinit var transitionListener: TransitionProgressListener
|
||||||
|
|
||||||
lateinit var progressProvider: NaturalRotationUnfoldProgressProvider
|
lateinit var progressProvider: NaturalRotationUnfoldProgressProvider
|
||||||
|
|
||||||
private val sourceProviderListenerCaptor =
|
|
||||||
ArgumentCaptor.forClass(TransitionProgressListener::class.java)
|
|
||||||
private val rotationWatcherCaptor =
|
private val rotationWatcherCaptor =
|
||||||
ArgumentCaptor.forClass(IRotationWatcher.Stub::class.java)
|
ArgumentCaptor.forClass(IRotationWatcher.Stub::class.java)
|
||||||
|
|
||||||
@@ -66,7 +63,6 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
|
|||||||
|
|
||||||
progressProvider.init()
|
progressProvider.init()
|
||||||
|
|
||||||
verify(sourceProvider).addCallback(sourceProviderListenerCaptor.capture())
|
|
||||||
verify(windowManager).watchRotation(rotationWatcherCaptor.capture(), any())
|
verify(windowManager).watchRotation(rotationWatcherCaptor.capture(), any())
|
||||||
|
|
||||||
progressProvider.addCallback(transitionListener)
|
progressProvider.addCallback(transitionListener)
|
||||||
@@ -76,7 +72,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
|
|||||||
fun testNaturalRotation0_sendTransitionStartedEvent_eventReceived() {
|
fun testNaturalRotation0_sendTransitionStartedEvent_eventReceived() {
|
||||||
onRotationChanged(Surface.ROTATION_0)
|
onRotationChanged(Surface.ROTATION_0)
|
||||||
|
|
||||||
source.onTransitionStarted()
|
sourceProvider.onTransitionStarted()
|
||||||
|
|
||||||
verify(transitionListener).onTransitionStarted()
|
verify(transitionListener).onTransitionStarted()
|
||||||
}
|
}
|
||||||
@@ -85,7 +81,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
|
|||||||
fun testNaturalRotation0_sendTransitionProgressEvent_eventReceived() {
|
fun testNaturalRotation0_sendTransitionProgressEvent_eventReceived() {
|
||||||
onRotationChanged(Surface.ROTATION_0)
|
onRotationChanged(Surface.ROTATION_0)
|
||||||
|
|
||||||
source.onTransitionProgress(0.5f)
|
sourceProvider.onTransitionProgress(0.5f)
|
||||||
|
|
||||||
verify(transitionListener).onTransitionProgress(0.5f)
|
verify(transitionListener).onTransitionProgress(0.5f)
|
||||||
}
|
}
|
||||||
@@ -94,7 +90,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
|
|||||||
fun testNotNaturalRotation90_sendTransitionStartedEvent_eventNotReceived() {
|
fun testNotNaturalRotation90_sendTransitionStartedEvent_eventNotReceived() {
|
||||||
onRotationChanged(Surface.ROTATION_90)
|
onRotationChanged(Surface.ROTATION_90)
|
||||||
|
|
||||||
source.onTransitionStarted()
|
sourceProvider.onTransitionStarted()
|
||||||
|
|
||||||
verify(transitionListener, never()).onTransitionStarted()
|
verify(transitionListener, never()).onTransitionStarted()
|
||||||
}
|
}
|
||||||
@@ -103,7 +99,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
|
|||||||
fun testNaturalRotation90_sendTransitionProgressEvent_eventNotReceived() {
|
fun testNaturalRotation90_sendTransitionProgressEvent_eventNotReceived() {
|
||||||
onRotationChanged(Surface.ROTATION_90)
|
onRotationChanged(Surface.ROTATION_90)
|
||||||
|
|
||||||
source.onTransitionProgress(0.5f)
|
sourceProvider.onTransitionProgress(0.5f)
|
||||||
|
|
||||||
verify(transitionListener, never()).onTransitionProgress(0.5f)
|
verify(transitionListener, never()).onTransitionProgress(0.5f)
|
||||||
}
|
}
|
||||||
@@ -111,7 +107,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun testRotationBecameUnnaturalDuringTransition_sendsTransitionFinishedEvent() {
|
fun testRotationBecameUnnaturalDuringTransition_sendsTransitionFinishedEvent() {
|
||||||
onRotationChanged(Surface.ROTATION_0)
|
onRotationChanged(Surface.ROTATION_0)
|
||||||
source.onTransitionStarted()
|
sourceProvider.onTransitionStarted()
|
||||||
clearInvocations(transitionListener)
|
clearInvocations(transitionListener)
|
||||||
|
|
||||||
onRotationChanged(Surface.ROTATION_90)
|
onRotationChanged(Surface.ROTATION_90)
|
||||||
@@ -122,7 +118,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun testRotationBecameNaturalDuringTransition_sendsTransitionStartedEvent() {
|
fun testRotationBecameNaturalDuringTransition_sendsTransitionStartedEvent() {
|
||||||
onRotationChanged(Surface.ROTATION_90)
|
onRotationChanged(Surface.ROTATION_90)
|
||||||
source.onTransitionStarted()
|
sourceProvider.onTransitionStarted()
|
||||||
clearInvocations(transitionListener)
|
clearInvocations(transitionListener)
|
||||||
|
|
||||||
onRotationChanged(Surface.ROTATION_0)
|
onRotationChanged(Surface.ROTATION_0)
|
||||||
@@ -133,7 +129,4 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() {
|
|||||||
private fun onRotationChanged(rotation: Int) {
|
private fun onRotationChanged(rotation: Int) {
|
||||||
rotationWatcherCaptor.value.onRotationChanged(rotation)
|
rotationWatcherCaptor.value.onRotationChanged(rotation)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val source: TransitionProgressListener
|
|
||||||
get() = sourceProviderListenerCaptor.value
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import android.database.ContentObserver
|
|||||||
import android.testing.AndroidTestingRunner
|
import android.testing.AndroidTestingRunner
|
||||||
import androidx.test.filters.SmallTest
|
import androidx.test.filters.SmallTest
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.unfold.TestUnfoldTransitionProvider
|
||||||
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
|
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
|
||||||
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
|
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
|
||||||
import com.android.systemui.util.mockito.any
|
import com.android.systemui.util.mockito.any
|
||||||
@@ -40,16 +41,12 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
|
|||||||
@Mock
|
@Mock
|
||||||
lateinit var contentResolver: ContentResolver
|
lateinit var contentResolver: ContentResolver
|
||||||
|
|
||||||
@Mock
|
|
||||||
lateinit var sourceProvider: UnfoldTransitionProgressProvider
|
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
lateinit var sinkProvider: TransitionProgressListener
|
lateinit var sinkProvider: TransitionProgressListener
|
||||||
|
|
||||||
lateinit var progressProvider: ScaleAwareTransitionProgressProvider
|
private val sourceProvider = TestUnfoldTransitionProvider()
|
||||||
|
|
||||||
private val sourceProviderListenerCaptor =
|
lateinit var progressProvider: ScaleAwareTransitionProgressProvider
|
||||||
ArgumentCaptor.forClass(TransitionProgressListener::class.java)
|
|
||||||
|
|
||||||
private val animatorDurationScaleListenerCaptor =
|
private val animatorDurationScaleListenerCaptor =
|
||||||
ArgumentCaptor.forClass(ContentObserver::class.java)
|
ArgumentCaptor.forClass(ContentObserver::class.java)
|
||||||
@@ -63,7 +60,6 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
|
|||||||
contentResolver
|
contentResolver
|
||||||
)
|
)
|
||||||
|
|
||||||
verify(sourceProvider).addCallback(sourceProviderListenerCaptor.capture())
|
|
||||||
verify(contentResolver).registerContentObserver(any(), any(),
|
verify(contentResolver).registerContentObserver(any(), any(),
|
||||||
animatorDurationScaleListenerCaptor.capture())
|
animatorDurationScaleListenerCaptor.capture())
|
||||||
|
|
||||||
@@ -74,7 +70,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
|
|||||||
fun onTransitionStarted_animationsEnabled_eventReceived() {
|
fun onTransitionStarted_animationsEnabled_eventReceived() {
|
||||||
setAnimationsEnabled(true)
|
setAnimationsEnabled(true)
|
||||||
|
|
||||||
source.onTransitionStarted()
|
sourceProvider.onTransitionStarted()
|
||||||
|
|
||||||
verify(sinkProvider).onTransitionStarted()
|
verify(sinkProvider).onTransitionStarted()
|
||||||
}
|
}
|
||||||
@@ -83,7 +79,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
|
|||||||
fun onTransitionStarted_animationsNotEnabled_eventNotReceived() {
|
fun onTransitionStarted_animationsNotEnabled_eventNotReceived() {
|
||||||
setAnimationsEnabled(false)
|
setAnimationsEnabled(false)
|
||||||
|
|
||||||
source.onTransitionStarted()
|
sourceProvider.onTransitionStarted()
|
||||||
|
|
||||||
verifyNoMoreInteractions(sinkProvider)
|
verifyNoMoreInteractions(sinkProvider)
|
||||||
}
|
}
|
||||||
@@ -92,7 +88,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
|
|||||||
fun onTransitionEnd_animationsEnabled_eventReceived() {
|
fun onTransitionEnd_animationsEnabled_eventReceived() {
|
||||||
setAnimationsEnabled(true)
|
setAnimationsEnabled(true)
|
||||||
|
|
||||||
source.onTransitionFinished()
|
sourceProvider.onTransitionFinished()
|
||||||
|
|
||||||
verify(sinkProvider).onTransitionFinished()
|
verify(sinkProvider).onTransitionFinished()
|
||||||
}
|
}
|
||||||
@@ -101,7 +97,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
|
|||||||
fun onTransitionEnd_animationsNotEnabled_eventNotReceived() {
|
fun onTransitionEnd_animationsNotEnabled_eventNotReceived() {
|
||||||
setAnimationsEnabled(false)
|
setAnimationsEnabled(false)
|
||||||
|
|
||||||
source.onTransitionFinished()
|
sourceProvider.onTransitionFinished()
|
||||||
|
|
||||||
verifyNoMoreInteractions(sinkProvider)
|
verifyNoMoreInteractions(sinkProvider)
|
||||||
}
|
}
|
||||||
@@ -110,7 +106,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
|
|||||||
fun onTransitionProgress_animationsEnabled_eventReceived() {
|
fun onTransitionProgress_animationsEnabled_eventReceived() {
|
||||||
setAnimationsEnabled(true)
|
setAnimationsEnabled(true)
|
||||||
|
|
||||||
source.onTransitionProgress(42f)
|
sourceProvider.onTransitionProgress(42f)
|
||||||
|
|
||||||
verify(sinkProvider).onTransitionProgress(42f)
|
verify(sinkProvider).onTransitionProgress(42f)
|
||||||
}
|
}
|
||||||
@@ -119,7 +115,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
|
|||||||
fun onTransitionProgress_animationsNotEnabled_eventNotReceived() {
|
fun onTransitionProgress_animationsNotEnabled_eventNotReceived() {
|
||||||
setAnimationsEnabled(false)
|
setAnimationsEnabled(false)
|
||||||
|
|
||||||
source.onTransitionProgress(42f)
|
sourceProvider.onTransitionProgress(42f)
|
||||||
|
|
||||||
verifyNoMoreInteractions(sinkProvider)
|
verifyNoMoreInteractions(sinkProvider)
|
||||||
}
|
}
|
||||||
@@ -133,7 +129,4 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
|
|||||||
ValueAnimator.setDurationScale(durationScale)
|
ValueAnimator.setDurationScale(durationScale)
|
||||||
animatorDurationScaleListenerCaptor.value.dispatchChange(/* selfChange= */false)
|
animatorDurationScaleListenerCaptor.value.dispatchChange(/* selfChange= */false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val source: TransitionProgressListener
|
|
||||||
get() = sourceProviderListenerCaptor.value
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
|
|||||||
class TestFoldStateProvider : FoldStateProvider {
|
class TestFoldStateProvider : FoldStateProvider {
|
||||||
|
|
||||||
private val listeners: MutableList<FoldUpdatesListener> = arrayListOf()
|
private val listeners: MutableList<FoldUpdatesListener> = arrayListOf()
|
||||||
|
val hasListeners: Boolean
|
||||||
|
get() = listeners.isNotEmpty()
|
||||||
|
|
||||||
override fun start() {
|
override fun start() {
|
||||||
}
|
}
|
||||||
|
|||||||
39
packages/SystemUI/unfold/Android.bp
Normal file
39
packages/SystemUI/unfold/Android.bp
Normal file
@@ -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"],
|
||||||
|
}
|
||||||
22
packages/SystemUI/unfold/AndroidManifest.xml
Normal file
22
packages/SystemUI/unfold/AndroidManifest.xml
Normal file
@@ -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>
|
||||||
3
packages/SystemUI/unfold/lint-baseline.xml
Normal file
3
packages/SystemUI/unfold/lint-baseline.xml
Normal file
@@ -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>
|
||||||
@@ -16,16 +16,16 @@
|
|||||||
|
|
||||||
package com.android.systemui.unfold
|
package com.android.systemui.unfold
|
||||||
|
|
||||||
import android.app.ActivityManager
|
|
||||||
import android.content.ContentResolver
|
import android.content.ContentResolver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.hardware.SensorManager
|
import android.hardware.SensorManager
|
||||||
import android.hardware.devicestate.DeviceStateManager
|
|
||||||
import android.os.Handler
|
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.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.updates.screen.ScreenStatusProvider
|
||||||
|
import com.android.systemui.unfold.util.CurrentActivityTypeProvider
|
||||||
import com.android.systemui.unfold.util.UnfoldTransitionATracePrefix
|
import com.android.systemui.unfold.util.UnfoldTransitionATracePrefix
|
||||||
import dagger.BindsInstance
|
import dagger.BindsInstance
|
||||||
import dagger.Component
|
import dagger.Component
|
||||||
@@ -51,12 +51,12 @@ internal interface UnfoldSharedComponent {
|
|||||||
@BindsInstance context: Context,
|
@BindsInstance context: Context,
|
||||||
@BindsInstance config: UnfoldTransitionConfig,
|
@BindsInstance config: UnfoldTransitionConfig,
|
||||||
@BindsInstance screenStatusProvider: ScreenStatusProvider,
|
@BindsInstance screenStatusProvider: ScreenStatusProvider,
|
||||||
@BindsInstance deviceStateManager: DeviceStateManager,
|
@BindsInstance foldProvider: FoldProvider,
|
||||||
@BindsInstance activityManager: ActivityManager,
|
@BindsInstance activityTypeProvider: CurrentActivityTypeProvider,
|
||||||
@BindsInstance sensorManager: SensorManager,
|
@BindsInstance sensorManager: SensorManager,
|
||||||
@BindsInstance @Main handler: Handler,
|
@BindsInstance @UnfoldMain handler: Handler,
|
||||||
@BindsInstance @Main executor: Executor,
|
@BindsInstance @UnfoldMain executor: Executor,
|
||||||
@BindsInstance @UiBackground backgroundExecutor: Executor,
|
@BindsInstance @UnfoldBackground backgroundExecutor: Executor,
|
||||||
@BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String,
|
@BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String,
|
||||||
@BindsInstance contentResolver: ContentResolver = context.contentResolver
|
@BindsInstance contentResolver: ContentResolver = context.contentResolver
|
||||||
): UnfoldSharedComponent
|
): UnfoldSharedComponent
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
package com.android.systemui.unfold
|
package com.android.systemui.unfold
|
||||||
|
|
||||||
import android.hardware.SensorManager
|
import android.hardware.SensorManager
|
||||||
import com.android.systemui.dagger.qualifiers.UiBackground
|
|
||||||
import com.android.systemui.unfold.config.UnfoldTransitionConfig
|
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.FixedTimingTransitionProgressProvider
|
||||||
import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
|
import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
|
||||||
import com.android.systemui.unfold.updates.DeviceFoldStateProvider
|
import com.android.systemui.unfold.updates.DeviceFoldStateProvider
|
||||||
@@ -70,7 +70,7 @@ class UnfoldSharedModule {
|
|||||||
fun hingeAngleProvider(
|
fun hingeAngleProvider(
|
||||||
config: UnfoldTransitionConfig,
|
config: UnfoldTransitionConfig,
|
||||||
sensorManager: SensorManager,
|
sensorManager: SensorManager,
|
||||||
@UiBackground executor: Executor
|
@UnfoldBackground executor: Executor
|
||||||
): HingeAngleProvider =
|
): HingeAngleProvider =
|
||||||
if (config.isHingeAngleEnabled) {
|
if (config.isHingeAngleEnabled) {
|
||||||
HingeSensorAngleProvider(sensorManager, executor)
|
HingeSensorAngleProvider(sensorManager, executor)
|
||||||
@@ -17,14 +17,13 @@
|
|||||||
|
|
||||||
package com.android.systemui.unfold
|
package com.android.systemui.unfold
|
||||||
|
|
||||||
import android.app.ActivityManager
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.hardware.SensorManager
|
import android.hardware.SensorManager
|
||||||
import android.hardware.devicestate.DeviceStateManager
|
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig
|
|
||||||
import com.android.systemui.unfold.config.UnfoldTransitionConfig
|
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.updates.screen.ScreenStatusProvider
|
||||||
|
import com.android.systemui.unfold.util.CurrentActivityTypeProvider
|
||||||
import java.util.concurrent.Executor
|
import java.util.concurrent.Executor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,8 +38,8 @@ fun createUnfoldTransitionProgressProvider(
|
|||||||
context: Context,
|
context: Context,
|
||||||
config: UnfoldTransitionConfig,
|
config: UnfoldTransitionConfig,
|
||||||
screenStatusProvider: ScreenStatusProvider,
|
screenStatusProvider: ScreenStatusProvider,
|
||||||
deviceStateManager: DeviceStateManager,
|
foldProvider: FoldProvider,
|
||||||
activityManager: ActivityManager,
|
activityTypeProvider: CurrentActivityTypeProvider,
|
||||||
sensorManager: SensorManager,
|
sensorManager: SensorManager,
|
||||||
mainHandler: Handler,
|
mainHandler: Handler,
|
||||||
mainExecutor: Executor,
|
mainExecutor: Executor,
|
||||||
@@ -52,8 +51,8 @@ fun createUnfoldTransitionProgressProvider(
|
|||||||
context,
|
context,
|
||||||
config,
|
config,
|
||||||
screenStatusProvider,
|
screenStatusProvider,
|
||||||
deviceStateManager,
|
foldProvider,
|
||||||
activityManager,
|
activityTypeProvider,
|
||||||
sensorManager,
|
sensorManager,
|
||||||
mainHandler,
|
mainHandler,
|
||||||
mainExecutor,
|
mainExecutor,
|
||||||
@@ -64,5 +63,3 @@ fun createUnfoldTransitionProgressProvider(
|
|||||||
?: throw IllegalStateException(
|
?: throw IllegalStateException(
|
||||||
"Trying to create " +
|
"Trying to create " +
|
||||||
"UnfoldTransitionProgressProvider when the transition is disabled")
|
"UnfoldTransitionProgressProvider when the transition is disabled")
|
||||||
|
|
||||||
fun createConfig(context: Context): UnfoldTransitionConfig = ResourceUnfoldTransitionConfig(context)
|
|
||||||
@@ -15,9 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package com.android.systemui.unfold
|
package com.android.systemui.unfold
|
||||||
|
|
||||||
import android.annotation.FloatRange
|
import androidx.annotation.FloatRange
|
||||||
import com.android.systemui.statusbar.policy.CallbackController
|
|
||||||
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
|
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
|
||||||
|
import com.android.systemui.unfold.util.CallbackController
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface that allows to receive unfold transition progress updates.
|
* Interface that allows to receive unfold transition progress updates.
|
||||||
@@ -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
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,4 +18,5 @@ package com.android.systemui.unfold.config
|
|||||||
interface UnfoldTransitionConfig {
|
interface UnfoldTransitionConfig {
|
||||||
val isEnabled: Boolean
|
val isEnabled: Boolean
|
||||||
val isHingeAngleEnabled: Boolean
|
val isHingeAngleEnabled: Boolean
|
||||||
|
val halfFoldedTimeoutMillis: Int
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -16,7 +16,6 @@
|
|||||||
package com.android.systemui.unfold.progress
|
package com.android.systemui.unfold.progress
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.MathUtils.saturate
|
|
||||||
import androidx.dynamicanimation.animation.DynamicAnimation
|
import androidx.dynamicanimation.animation.DynamicAnimation
|
||||||
import androidx.dynamicanimation.animation.FloatPropertyCompat
|
import androidx.dynamicanimation.animation.FloatPropertyCompat
|
||||||
import androidx.dynamicanimation.animation.SpringAnimation
|
import androidx.dynamicanimation.animation.SpringAnimation
|
||||||
@@ -70,6 +69,9 @@ class PhysicsBasedUnfoldTransitionProgressProvider(
|
|||||||
springAnimation.animateToFinalPosition(progress)
|
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) {
|
override fun onFoldUpdate(@FoldUpdate update: Int) {
|
||||||
when (update) {
|
when (update) {
|
||||||
FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> {
|
FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> {
|
||||||
@@ -15,46 +15,46 @@
|
|||||||
*/
|
*/
|
||||||
package com.android.systemui.unfold.updates
|
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.os.Handler
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.annotation.FloatRange
|
||||||
import androidx.annotation.VisibleForTesting
|
import androidx.annotation.VisibleForTesting
|
||||||
import androidx.core.util.Consumer
|
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.FoldUpdate
|
||||||
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
|
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_CLOSED_DEGREES
|
||||||
import com.android.systemui.unfold.updates.hinge.FULLY_OPEN_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.hinge.HingeAngleProvider
|
||||||
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
|
import com.android.systemui.unfold.updates.screen.ScreenStatusProvider
|
||||||
|
import com.android.systemui.unfold.util.CurrentActivityTypeProvider
|
||||||
import java.util.concurrent.Executor
|
import java.util.concurrent.Executor
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class DeviceFoldStateProvider
|
class DeviceFoldStateProvider
|
||||||
@Inject
|
@Inject
|
||||||
constructor(
|
constructor(
|
||||||
context: Context,
|
config: UnfoldTransitionConfig,
|
||||||
private val hingeAngleProvider: HingeAngleProvider,
|
private val hingeAngleProvider: HingeAngleProvider,
|
||||||
private val screenStatusProvider: ScreenStatusProvider,
|
private val screenStatusProvider: ScreenStatusProvider,
|
||||||
private val deviceStateManager: DeviceStateManager,
|
private val foldProvider: FoldProvider,
|
||||||
private val activityManager: ActivityManager,
|
private val activityTypeProvider: CurrentActivityTypeProvider,
|
||||||
@Main private val mainExecutor: Executor,
|
@UnfoldMain private val mainExecutor: Executor,
|
||||||
@Main private val handler: Handler
|
@UnfoldMain private val handler: Handler
|
||||||
) : FoldStateProvider {
|
) : FoldStateProvider {
|
||||||
|
|
||||||
private val outputListeners: MutableList<FoldUpdatesListener> = mutableListOf()
|
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 hingeAngleListener = HingeAngleListener()
|
||||||
private val screenListener = ScreenStatusListener()
|
private val screenListener = ScreenStatusListener()
|
||||||
private val foldStateListener = FoldStateListener(context)
|
private val foldStateListener = FoldStateListener()
|
||||||
private val timeoutRunnable = TimeoutRunnable()
|
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
|
* [FOLD_UPDATE_START_CLOSING] or [FOLD_UPDATE_START_OPENING] event, if an end state is not
|
||||||
* reached.
|
* reached.
|
||||||
*/
|
*/
|
||||||
private val halfOpenedTimeoutMillis: Int =
|
private val halfOpenedTimeoutMillis: Int = config.halfFoldedTimeoutMillis
|
||||||
context.resources.getInteger(
|
|
||||||
com.android.internal.R.integer.config_unfoldTransitionHalfFoldedTimeout)
|
|
||||||
|
|
||||||
private var isFolded = false
|
private var isFolded = false
|
||||||
private var isUnfoldHandled = true
|
private var isUnfoldHandled = true
|
||||||
|
|
||||||
override fun start() {
|
override fun start() {
|
||||||
deviceStateManager.registerCallback(mainExecutor, foldStateListener)
|
foldProvider.registerCallback(foldStateListener, mainExecutor)
|
||||||
screenStatusProvider.addCallback(screenListener)
|
screenStatusProvider.addCallback(screenListener)
|
||||||
hingeAngleProvider.addCallback(hingeAngleListener)
|
hingeAngleProvider.addCallback(hingeAngleListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun stop() {
|
override fun stop() {
|
||||||
screenStatusProvider.removeCallback(screenListener)
|
screenStatusProvider.removeCallback(screenListener)
|
||||||
deviceStateManager.unregisterCallback(foldStateListener)
|
foldProvider.unregisterCallback(foldStateListener)
|
||||||
hingeAngleProvider.removeCallback(hingeAngleListener)
|
hingeAngleProvider.removeCallback(hingeAngleListener)
|
||||||
hingeAngleProvider.stop()
|
hingeAngleProvider.stop()
|
||||||
}
|
}
|
||||||
@@ -136,29 +134,25 @@ constructor(
|
|||||||
* apps that support table-top/HALF_FOLDED mode. Only for launcher, there is no threshold.
|
* apps that support table-top/HALF_FOLDED mode. Only for launcher, there is no threshold.
|
||||||
*/
|
*/
|
||||||
private fun getClosingThreshold(): Int? {
|
private fun getClosingThreshold(): Int? {
|
||||||
val activityType =
|
val isHomeActivity = activityTypeProvider.isHomeActivity ?: return null
|
||||||
activityManager.getRunningTasks(/* maxNum= */ 1)?.getOrNull(0)?.topActivityType
|
|
||||||
?: return null
|
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "activityType=" + activityType)
|
Log.d(TAG, "isHomeActivity=$isHomeActivity")
|
||||||
}
|
}
|
||||||
|
|
||||||
return if (activityType == ACTIVITY_TYPE_HOME) {
|
return if (isHomeActivity) {
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
START_CLOSING_ON_APPS_THRESHOLD_DEGREES
|
START_CLOSING_ON_APPS_THRESHOLD_DEGREES
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class FoldStateListener(context: Context) :
|
private inner class FoldStateListener : FoldProvider.FoldCallback {
|
||||||
DeviceStateManager.FoldStateListener(
|
override fun onFoldUpdated(isFolded: Boolean) {
|
||||||
context,
|
this@DeviceFoldStateProvider.isFolded = isFolded
|
||||||
{ folded: Boolean ->
|
|
||||||
isFolded = folded
|
|
||||||
lastHingeAngle = FULLY_CLOSED_DEGREES
|
lastHingeAngle = FULLY_CLOSED_DEGREES
|
||||||
|
|
||||||
if (folded) {
|
if (isFolded) {
|
||||||
hingeAngleProvider.stop()
|
hingeAngleProvider.stop()
|
||||||
notifyFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
|
notifyFoldUpdate(FOLD_UPDATE_FINISH_CLOSED)
|
||||||
cancelTimeout()
|
cancelTimeout()
|
||||||
@@ -168,7 +162,8 @@ constructor(
|
|||||||
rescheduleAbortAnimationTimeout()
|
rescheduleAbortAnimationTimeout()
|
||||||
hingeAngleProvider.start()
|
hingeAngleProvider.start()
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun notifyFoldUpdate(@FoldUpdate update: Int) {
|
private fun notifyFoldUpdate(@FoldUpdate update: Int) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@@ -234,7 +229,9 @@ private const val TAG = "DeviceFoldProvider"
|
|||||||
private const val DEBUG = false
|
private const val DEBUG = false
|
||||||
|
|
||||||
/** Threshold after which we consider the device fully unfolded. */
|
/** 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. */
|
/** 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
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,10 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
package com.android.systemui.unfold.updates
|
package com.android.systemui.unfold.updates
|
||||||
|
|
||||||
import android.annotation.FloatRange
|
import androidx.annotation.FloatRange
|
||||||
import android.annotation.IntDef
|
import androidx.annotation.IntDef
|
||||||
import com.android.systemui.statusbar.policy.CallbackController
|
|
||||||
import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener
|
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,
|
* 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(
|
@IntDef(
|
||||||
prefix = ["FOLD_UPDATE_"],
|
|
||||||
value =
|
value =
|
||||||
[
|
[
|
||||||
FOLD_UPDATE_START_OPENING,
|
FOLD_UPDATE_START_OPENING,
|
||||||
@@ -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>) {}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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
|
package com.android.systemui.unfold.updates.hinge
|
||||||
|
|
||||||
import android.hardware.Sensor
|
import android.hardware.Sensor
|
||||||
@@ -15,8 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.android.systemui.unfold.updates.screen
|
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.updates.screen.ScreenStatusProvider.ScreenListener
|
||||||
|
import com.android.systemui.unfold.util.CallbackController
|
||||||
|
|
||||||
interface ScreenStatusProvider : CallbackController<ScreenListener> {
|
interface ScreenStatusProvider : CallbackController<ScreenListener> {
|
||||||
|
|
||||||
@@ -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
|
package com.android.systemui.unfold.util
|
||||||
|
|
||||||
import android.os.Trace
|
import android.os.Trace
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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
|
package com.android.systemui.unfold.util
|
||||||
|
|
||||||
import android.animation.ValueAnimator
|
import android.animation.ValueAnimator
|
||||||
Reference in New Issue
Block a user