Merge "Fix flicker of QS footer actions when restarting SystemUI (1/2)" into udc-dev

This commit is contained in:
Jordan Demeulenaere
2023-04-24 14:52:01 +00:00
committed by Android (Google) Code Review
5 changed files with 20 additions and 10 deletions

View File

@@ -120,6 +120,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
private final QSLogger mLogger; private final QSLogger mLogger;
private final FooterActionsController mFooterActionsController; private final FooterActionsController mFooterActionsController;
private final FooterActionsViewModel.Factory mFooterActionsViewModelFactory; private final FooterActionsViewModel.Factory mFooterActionsViewModelFactory;
private final FooterActionsViewBinder mFooterActionsViewBinder;
private final ListeningAndVisibilityLifecycleOwner mListeningAndVisibilityLifecycleOwner; private final ListeningAndVisibilityLifecycleOwner mListeningAndVisibilityLifecycleOwner;
private boolean mShowCollapsedOnKeyguard; private boolean mShowCollapsedOnKeyguard;
private boolean mLastKeyguardAndExpanded; private boolean mLastKeyguardAndExpanded;
@@ -177,6 +178,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
DumpManager dumpManager, QSLogger qsLogger, DumpManager dumpManager, QSLogger qsLogger,
FooterActionsController footerActionsController, FooterActionsController footerActionsController,
FooterActionsViewModel.Factory footerActionsViewModelFactory, FooterActionsViewModel.Factory footerActionsViewModelFactory,
FooterActionsViewBinder footerActionsViewBinder,
LargeScreenShadeInterpolator largeScreenShadeInterpolator, LargeScreenShadeInterpolator largeScreenShadeInterpolator,
FeatureFlags featureFlags) { FeatureFlags featureFlags) {
mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler; mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
@@ -193,6 +195,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
mDumpManager = dumpManager; mDumpManager = dumpManager;
mFooterActionsController = footerActionsController; mFooterActionsController = footerActionsController;
mFooterActionsViewModelFactory = footerActionsViewModelFactory; mFooterActionsViewModelFactory = footerActionsViewModelFactory;
mFooterActionsViewBinder = footerActionsViewBinder;
mListeningAndVisibilityLifecycleOwner = new ListeningAndVisibilityLifecycleOwner(); mListeningAndVisibilityLifecycleOwner = new ListeningAndVisibilityLifecycleOwner();
} }
@@ -285,7 +288,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
if (!ComposeFacade.INSTANCE.isComposeAvailable()) { if (!ComposeFacade.INSTANCE.isComposeAvailable()) {
Log.d(TAG, "Binding the View implementation of the QS footer actions"); Log.d(TAG, "Binding the View implementation of the QS footer actions");
FooterActionsViewBinder.bind(footerActionsView, mQSFooterActionsViewModel, mFooterActionsViewBinder.bind(footerActionsView, mQSFooterActionsViewModel,
mListeningAndVisibilityLifecycleOwner); mListeningAndVisibilityLifecycleOwner);
return; return;
} }

View File

@@ -33,27 +33,27 @@ import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.animation.Expandable import com.android.systemui.animation.Expandable
import com.android.systemui.common.ui.binder.IconViewBinder import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.people.ui.view.PeopleViewBinder.bind import com.android.systemui.people.ui.view.PeopleViewBinder.bind
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsButtonViewModel import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsButtonViewModel
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsForegroundServicesButtonViewModel import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsForegroundServicesButtonViewModel
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsSecurityButtonViewModel import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsSecurityButtonViewModel
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
import javax.inject.Inject
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
/** A ViewBinder for [FooterActionsViewBinder]. */ /** A ViewBinder for [FooterActionsViewBinder]. */
object FooterActionsViewBinder { @SysUISingleton
class FooterActionsViewBinder @Inject constructor() {
/** Create a view that can later be [bound][bind] to a [FooterActionsViewModel]. */ /** Create a view that can later be [bound][bind] to a [FooterActionsViewModel]. */
@JvmStatic
fun create(context: Context): LinearLayout { fun create(context: Context): LinearLayout {
return LayoutInflater.from(context).inflate(R.layout.footer_actions, /* root= */ null) return LayoutInflater.from(context).inflate(R.layout.footer_actions, /* root= */ null)
as LinearLayout as LinearLayout
} }
/** Bind [view] to [viewModel]. */ /** Bind [view] to [viewModel]. */
@JvmStatic
fun bind( fun bind(
view: LinearLayout, view: LinearLayout,
viewModel: FooterActionsViewModel, viewModel: FooterActionsViewModel,
@@ -98,6 +98,11 @@ object FooterActionsViewBinder {
var previousForegroundServices: FooterActionsForegroundServicesButtonViewModel? = null var previousForegroundServices: FooterActionsForegroundServicesButtonViewModel? = null
var previousUserSwitcher: FooterActionsButtonViewModel? = null var previousUserSwitcher: FooterActionsButtonViewModel? = null
// Set the initial visibility on the View directly so that we don't briefly show it for a
// few frames before [viewModel.isVisible] is collected.
view.isInvisible = !viewModel.isVisible.value
// Listen for ViewModel updates when the View is attached.
view.repeatWhenAttached { view.repeatWhenAttached {
val attachedScope = this.lifecycleScope val attachedScope = this.lifecycleScope

View File

@@ -64,7 +64,7 @@ class FooterActionsViewModel(
* the UI should still participate to the layout it is included in (i.e. in the View world it * the UI should still participate to the layout it is included in (i.e. in the View world it
* should be INVISIBLE, not GONE). * should be INVISIBLE, not GONE).
*/ */
private val _isVisible = MutableStateFlow(true) private val _isVisible = MutableStateFlow(false)
val isVisible: StateFlow<Boolean> = _isVisible.asStateFlow() val isVisible: StateFlow<Boolean> = _isVisible.asStateFlow()
/** The alpha the UI rendering this ViewModel should have. */ /** The alpha the UI rendering this ViewModel should have. */

View File

@@ -106,6 +106,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
@Mock private QSSquishinessController mSquishinessController; @Mock private QSSquishinessController mSquishinessController;
@Mock private FooterActionsViewModel mFooterActionsViewModel; @Mock private FooterActionsViewModel mFooterActionsViewModel;
@Mock private FooterActionsViewModel.Factory mFooterActionsViewModelFactory; @Mock private FooterActionsViewModel.Factory mFooterActionsViewModelFactory;
@Mock private FooterActionsViewBinder mFooterActionsViewBinder;
@Mock private LargeScreenShadeInterpolator mLargeScreenShadeInterpolator; @Mock private LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
@Mock private FeatureFlags mFeatureFlags; @Mock private FeatureFlags mFeatureFlags;
private View mQsFragmentView; private View mQsFragmentView;
@@ -558,6 +559,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
mock(QSLogger.class), mock(QSLogger.class),
mock(FooterActionsController.class), mock(FooterActionsController.class),
mFooterActionsViewModelFactory, mFooterActionsViewModelFactory,
mFooterActionsViewBinder,
mLargeScreenShadeInterpolator, mLargeScreenShadeInterpolator,
mFeatureFlags); mFeatureFlags);
} }
@@ -584,7 +586,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
when(mQsFragmentView.findViewById(R.id.header)).thenReturn(mHeader); when(mQsFragmentView.findViewById(R.id.header)).thenReturn(mHeader);
when(mQsFragmentView.findViewById(android.R.id.edit)).thenReturn(new View(mContext)); when(mQsFragmentView.findViewById(android.R.id.edit)).thenReturn(new View(mContext));
when(mQsFragmentView.findViewById(R.id.qs_footer_actions)).thenAnswer( when(mQsFragmentView.findViewById(R.id.qs_footer_actions)).thenAnswer(
invocation -> FooterActionsViewBinder.create(mContext)); invocation -> new FooterActionsViewBinder().create(mContext));
} }
private void setUpInflater() { private void setUpInflater() {

View File

@@ -376,13 +376,13 @@ class FooterActionsViewModelTest : SysuiTestCase() {
@Test @Test
fun isVisible() { fun isVisible() {
val underTest = utils.footerActionsViewModel() val underTest = utils.footerActionsViewModel()
assertThat(underTest.isVisible.value).isTrue()
underTest.onVisibilityChangeRequested(visible = false)
assertThat(underTest.isVisible.value).isFalse() assertThat(underTest.isVisible.value).isFalse()
underTest.onVisibilityChangeRequested(visible = true) underTest.onVisibilityChangeRequested(visible = true)
assertThat(underTest.isVisible.value).isTrue() assertThat(underTest.isVisible.value).isTrue()
underTest.onVisibilityChangeRequested(visible = false)
assertThat(underTest.isVisible.value).isFalse()
} }
@Test @Test