Merge "Disabled SUBPIXEL_TEXT_FLAG for notification shade while fold unfold" into udc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
efbf3c9e1d
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -700,6 +700,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 = releasedFlag(2801, name = "use_new_activity_starter")
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user