diff options
| -rw-r--r-- | packages/SystemUI/shared/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListener.kt | 84 | ||||
| -rw-r--r-- | packages/SystemUI/shared/src/com/android/systemui/utils/TraceUtils.kt (renamed from packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt) | 0 | ||||
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/flags/Flags.kt | 5 | ||||
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java | 12 | ||||
| -rw-r--r-- | packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt | 6 | ||||
| -rw-r--r-- | packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt | 6 | ||||
| -rw-r--r-- | packages/SystemUI/tests/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListenerTest.kt | 130 |
7 files changed, 242 insertions, 1 deletions
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListener.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListener.kt new file mode 100644 index 000000000000..4c6d99a412b7 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListener.kt @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.shared.animation + +import android.graphics.Paint +import android.view.ViewGroup +import android.widget.TextView +import androidx.core.view.forEach +import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener +import com.android.systemui.util.traceSection +import java.lang.ref.WeakReference + +/** + * A listener which disables subpixel flag for all TextView children of a given parent ViewGroup + * during fold/unfold transitions. + */ +class DisableSubpixelTextTransitionListener(private val rootView: ViewGroup?) : + TransitionProgressListener { + private val childrenTextViews: MutableList<WeakReference<TextView>> = mutableListOf() + private var isTransitionInProgress: Boolean = false + + override fun onTransitionStarted() { + isTransitionInProgress = true + traceSection("subpixelFlagSetForTextView") { + traceSection("subpixelFlagTraverseHierarchy") { + getAllChildTextView(rootView, childrenTextViews) + } + traceSection("subpixelFlagDisableForTextView") { + childrenTextViews.forEach { child -> + val childTextView = child.get() ?: return@forEach + childTextView.paintFlags = childTextView.paintFlags or Paint.SUBPIXEL_TEXT_FLAG + } + } + } + } + + override fun onTransitionFinished() { + if (!isTransitionInProgress) return + isTransitionInProgress = false + traceSection("subpixelFlagEnableForTextView") { + childrenTextViews.forEach { child -> + val childTextView = child.get() ?: return@forEach + childTextView.paintFlags = + childTextView.paintFlags and Paint.SUBPIXEL_TEXT_FLAG.inv() + } + childrenTextViews.clear() + } + } + + /** + * Populates a list of all TextView children of a given parent ViewGroup + * + * @param parent the root ViewGroup for which to retrieve TextView children + * @param childrenTextViews the list to store the retrieved TextView children + */ + private fun getAllChildTextView( + parent: ViewGroup?, + childrenTextViews: MutableList<WeakReference<TextView>> + ) { + parent?.forEach { child -> + when (child) { + is ViewGroup -> getAllChildTextView(child, childrenTextViews) + is TextView -> { + if ((child.paintFlags and Paint.SUBPIXEL_TEXT_FLAG) <= 0) { + childrenTextViews.add(WeakReference(child)) + } + } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt b/packages/SystemUI/shared/src/com/android/systemui/utils/TraceUtils.kt index 64234c205617..64234c205617 100644 --- a/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/utils/TraceUtils.kt diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index cd2bf13e37a2..af6567dad8a3 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -689,6 +689,11 @@ object Flags { val ADVANCED_VPN_ENABLED = releasedFlag(2800, name = "AdvancedVpn__enable_feature", namespace = "vpn") + // TODO(b/277201412): Tracking Bug + @JvmField + val SPLIT_SHADE_SUBPIXEL_OPTIMIZATION = + unreleasedFlag(2805, "split_shade_subpixel_optimization", teamfood = true) + // TODO(b/278761837): Tracking Bug @JvmField val USE_NEW_ACTIVITY_STARTER = unreleasedFlag(2801, name = "use_new_activity_starter", diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 7cc257ba0a97..cb3fa15655b2 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -54,6 +54,7 @@ import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransition import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor; import com.android.systemui.multishade.domain.interactor.MultiShadeMotionEventInteractor; import com.android.systemui.multishade.ui.view.MultiShadeView; +import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationInsetsController; @@ -68,9 +69,11 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; import com.android.systemui.statusbar.window.StatusBarWindowStateController; +import com.android.systemui.unfold.UnfoldTransitionProgressProvider; import com.android.systemui.util.time.SystemClock; import java.io.PrintWriter; +import java.util.Optional; import java.util.function.Consumer; import javax.inject.Inject; @@ -114,7 +117,7 @@ public class NotificationShadeWindowViewController { private boolean mIsTrackingBarGesture = false; private boolean mIsOcclusionTransitionRunning = false; - + private DisableSubpixelTextTransitionListener mDisableSubpixelTextTransitionListener; private final Consumer<TransitionStep> mLockscreenToDreamingTransition = (TransitionStep step) -> { mIsOcclusionTransitionRunning = @@ -139,6 +142,7 @@ public class NotificationShadeWindowViewController { LockIconViewController lockIconViewController, CentralSurfaces centralSurfaces, NotificationShadeWindowController controller, + Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider, KeyguardUnlockAnimationController keyguardUnlockAnimationController, NotificationInsetsController notificationInsetsController, AmbientState ambientState, @@ -174,6 +178,7 @@ public class NotificationShadeWindowViewController { // This view is not part of the newly inflated expanded status bar. mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container); + mDisableSubpixelTextTransitionListener = new DisableSubpixelTextTransitionListener(mView); KeyguardBouncerViewBinder.bind( mView.findViewById(R.id.keyguard_bouncer_container), keyguardBouncerViewModel, @@ -184,6 +189,11 @@ public class NotificationShadeWindowViewController { mLockscreenToDreamingTransition); mClock = clock; + if (featureFlags.isEnabled(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION)) { + unfoldTransitionProgressProvider.ifPresent( + progressProvider -> progressProvider.addCallback( + mDisableSubpixelTextTransitionListener)); + } if (ComposeFacade.INSTANCE.isComposeAvailable() && featureFlags.isEnabled(Flags.DUAL_SHADE)) { mMultiShadeMotionEventInteractor = multiShadeMotionEventInteractorProvider.get(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index bc8ab1faf9eb..16277de850d6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -53,6 +53,7 @@ import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.PhoneStatusBarViewController import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.window.StatusBarWindowStateController +import com.android.systemui.unfold.UnfoldTransitionProgressProvider import com.android.systemui.util.mockito.any import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat @@ -71,6 +72,7 @@ import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations +import java.util.Optional @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @@ -101,6 +103,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { @Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory @Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent @Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController + @Mock + private lateinit var unfoldTransitionProgressProvider: Optional<UnfoldTransitionProgressProvider> @Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor @Mock lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel @@ -129,6 +133,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { featureFlags.set(Flags.TRACKPAD_GESTURE_COMMON, true) featureFlags.set(Flags.TRACKPAD_GESTURE_FEATURES, false) featureFlags.set(Flags.DUAL_SHADE, false) + featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true) val inputProxy = MultiShadeInputProxy() testScope = TestScope() @@ -158,6 +163,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { lockIconViewController, centralSurfaces, notificationShadeWindowController, + unfoldTransitionProgressProvider, keyguardUnlockAnimationController, notificationInsetsController, ambientState, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt index 56385b2ec5da..16af208fd531 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt @@ -53,6 +53,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.window.StatusBarWindowStateController +import com.android.systemui.unfold.UnfoldTransitionProgressProvider import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever @@ -71,6 +72,7 @@ import org.mockito.Mock import org.mockito.Mockito.spy import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +import java.util.Optional @OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidTestingRunner::class) @@ -103,6 +105,8 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { @Mock private lateinit var keyguardBouncerComponent: KeyguardBouncerComponent @Mock private lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController + @Mock + private lateinit var unfoldTransitionProgressProvider: Optional<UnfoldTransitionProgressProvider> @Mock private lateinit var notificationInsetsController: NotificationInsetsController @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor @Mock @@ -141,6 +145,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { featureFlags.set(Flags.TRACKPAD_GESTURE_COMMON, true) featureFlags.set(Flags.TRACKPAD_GESTURE_FEATURES, false) featureFlags.set(Flags.DUAL_SHADE, false) + featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true) val inputProxy = MultiShadeInputProxy() testScope = TestScope() val multiShadeInteractor = @@ -169,6 +174,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { lockIconViewController, centralSurfaces, notificationShadeWindowController, + unfoldTransitionProgressProvider, keyguardUnlockAnimationController, notificationInsetsController, ambientState, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListenerTest.kt new file mode 100644 index 000000000000..fc230e362735 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/DisableSubpixelTextTransitionListenerTest.kt @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.animation + +import android.graphics.Paint +import android.testing.AndroidTestingRunner +import android.widget.FrameLayout +import android.widget.TextView +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class DisableSubpixelTextTransitionListenerTest : SysuiTestCase() { + + private lateinit var disableSubpixelTextTransitionListener: + DisableSubpixelTextTransitionListener + private var rootViewWithNoTextView = FrameLayout(context) + private var rootView = FrameLayout(context) + private var childView = FrameLayout(context) + private var childViewWithNoTextView = FrameLayout(context) + private var childTextView = TextView(context) + private var childOfChildTextView = TextView(context) + + @Before + fun setup() { + + childView.addView(childOfChildTextView) + rootView.addView(childTextView) + rootView.addView(childView) + } + + @Test + fun onTransitionStarted_addsSubpixelFlagToChildTextView() { + disableSubpixelTextTransitionListener = DisableSubpixelTextTransitionListener(rootView) + + disableSubpixelTextTransitionListener.onTransitionStarted() + + assertThat(childTextView.paintFlags and Paint.SUBPIXEL_TEXT_FLAG).isGreaterThan(0) + } + + @Test + fun onTransitionStarted_addsSupbixelFlagToChildOfChildTextView() { + disableSubpixelTextTransitionListener = DisableSubpixelTextTransitionListener(rootView) + + disableSubpixelTextTransitionListener.onTransitionStarted() + + assertThat(childOfChildTextView.paintFlags and Paint.SUBPIXEL_TEXT_FLAG).isGreaterThan(0) + } + + @Test + fun onTransitionFinished_removeSupbixelFlagFromChildTextView() { + disableSubpixelTextTransitionListener = DisableSubpixelTextTransitionListener(rootView) + + disableSubpixelTextTransitionListener.onTransitionStarted() + disableSubpixelTextTransitionListener.onTransitionFinished() + + assertThat(childTextView.paintFlags and Paint.SUBPIXEL_TEXT_FLAG).isEqualTo(0) + } + + @Test + fun onTransitionFinished_removeSupbixelFlagFromChildOfChildTextView() { + disableSubpixelTextTransitionListener = DisableSubpixelTextTransitionListener(rootView) + + disableSubpixelTextTransitionListener.onTransitionStarted() + disableSubpixelTextTransitionListener.onTransitionFinished() + + assertThat(childOfChildTextView.paintFlags and Paint.SUBPIXEL_TEXT_FLAG).isEqualTo(0) + } + + @Test + fun whenRootViewIsNull_runWithoutExceptions() { + disableSubpixelTextTransitionListener = DisableSubpixelTextTransitionListener(null) + + disableSubpixelTextTransitionListener.onTransitionStarted() + disableSubpixelTextTransitionListener.onTransitionFinished() + } + + @Test + fun whenFlagAlreadyPresent_flagNotRemovedOnTransitionFinished() { + childTextView.paintFlags = childTextView.paintFlags or Paint.SUBPIXEL_TEXT_FLAG + disableSubpixelTextTransitionListener = DisableSubpixelTextTransitionListener(rootView) + + disableSubpixelTextTransitionListener.onTransitionStarted() + disableSubpixelTextTransitionListener.onTransitionFinished() + + assertThat(childTextView.paintFlags and Paint.SUBPIXEL_TEXT_FLAG).isGreaterThan(0) + } + + @Test + fun whenFlagNotPresent_flagRemovedOnTransitionFinished() { + childTextView.paintFlags = childTextView.paintFlags or Paint.SUBPIXEL_TEXT_FLAG + disableSubpixelTextTransitionListener = DisableSubpixelTextTransitionListener(rootView) + + disableSubpixelTextTransitionListener.onTransitionStarted() + disableSubpixelTextTransitionListener.onTransitionFinished() + + assertThat(childOfChildTextView.paintFlags and Paint.SUBPIXEL_TEXT_FLAG).isEqualTo(0) + } + + @Test + fun whenRootViewHasNoChildTextView_flagNotAddedToRelatedTextviews() { + rootViewWithNoTextView.addView(childViewWithNoTextView) + rootView.addView(rootViewWithNoTextView) + disableSubpixelTextTransitionListener = + DisableSubpixelTextTransitionListener(rootViewWithNoTextView) + + disableSubpixelTextTransitionListener.onTransitionStarted() + + assertThat(childTextView.paintFlags and Paint.SUBPIXEL_TEXT_FLAG).isEqualTo(0) + } +} |