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

Commit 32de1c94 authored by dshivangi's avatar dshivangi Committed by Shivangi Dubey
Browse files

Disabled SUBPIXEL_TEXT_FLAG for notification shade while fold unfold

Disabled the SUBPIXEL_TEXT_FLAG for all TextViews of the Notification Shade while folding and unfolding is in progress.

The SUBPIXEL_TEXT_FLAG is responsible for the default Android behaviour that tries to align the font to the nearest pixel, causing the text to jump as the nearest pixel keeps changing during folding and unfolding.

To solve this issue, we have disabled the SUBPIXEL_TEXT_FLAG for the TextViews of the Notification Shade during folding and unfolding, resulting in smoother text transitions.

Test: atest com.android.systemui.shared.animation.DisableSubpixelTextTransitionListenerTest.kt
Fixes: 227427854
Flag: SPLIT_SHADE_SUBPIXEL_OPTIMIZATION
Change-Id: I6ada51f912ee1c086674b19ea39d4cab3ad161a8
parent 9e30a4e0
Loading
Loading
Loading
Loading
+84 −0
Original line number Diff line number Diff line
/*
 * 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))
                    }
                }
            }
        }
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -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",
+11 −1
Original line number Diff line number Diff line
@@ -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();
+6 −0
Original line number Diff line number Diff line
@@ -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,
Loading