Create connected display presentation This creates a parallel implementatino of the connected display presentation following UX directions, showing a single clock, centered in the screen. This is flagged by "ENABLE_CLOCK_KEYGUARD_PRESENTATION" flag. When the flag is off, the previous implementation is used. Note that only the default clock will appear as centered for now. Regarding custom clocks, they will be addressed in a follow up, with screenshot tests. + Extracted KeyguardPresentation to use assisted factory Test: KeyguardClockSwitchControllerTest, KeyguardClockSwitchScreenshotTest Bug: 278982856 Change-Id: I26b4800bd7c92c167a8b53e3c80d60be9224144a
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt index e539c95..e7a53e5b 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -62,6 +62,7 @@ private val burmeseLineSpacing = resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale_burmese) private val defaultLineSpacing = resources.getFloat(R.dimen.keyguard_clock_line_spacing_scale) + protected var onSecondaryDisplay: Boolean = false override val events: DefaultClockEvents override val config = ClockConfig() @@ -142,6 +143,11 @@ view.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSizePx) recomputePadding(targetRegion) } + + override fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean) { + this@DefaultClockController.onSecondaryDisplay = onSecondaryDisplay + recomputePadding(null) + } } open fun recomputePadding(targetRegion: Rect?) {} @@ -182,13 +188,19 @@ override fun recomputePadding(targetRegion: Rect?) { // We center the view within the targetRegion instead of within the parent // view by computing the difference and adding that to the padding. - val parent = view.parent - val yDiff = - if (targetRegion != null && parent is View && parent.isLaidOut()) - targetRegion.centerY() - parent.height / 2f - else 0f val lp = view.getLayoutParams() as FrameLayout.LayoutParams - lp.topMargin = (-0.5f * view.bottom + yDiff).toInt() + lp.topMargin = + if (onSecondaryDisplay) { + // On the secondary display we don't want any additional top/bottom margin. + 0 + } else { + val parent = view.parent + val yDiff = + if (targetRegion != null && parent is View && parent.isLaidOut()) + targetRegion.centerY() - parent.height / 2f + else 0f + (-0.5f * view.bottom + yDiff).toInt() + } view.setLayoutParams(lp) }
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt index d962732..527f8007 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
@@ -177,6 +177,9 @@ * targetRegion is relative to the parent view. */ fun onTargetRegionChanged(targetRegion: Rect?) + + /** Called to notify the clock about its display. */ + fun onSecondaryDisplayChanged(onSecondaryDisplay: Boolean) } /** Tick rates for clocks */
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml new file mode 100644 index 0000000..593f507f --- /dev/null +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_presentation.xml
@@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?><!-- +** +** Copyright 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. +*/ +--> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/presentation" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <com.android.keyguard.KeyguardStatusView + android:id="@+id/clock" + android:layout_width="410dp" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:orientation="vertical"> + + <include + android:id="@+id/keyguard_clock_container" + layout="@layout/keyguard_clock_switch" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + </com.android.keyguard.KeyguardStatusView> + +</FrameLayout>
diff --git a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt new file mode 100644 index 0000000..899cad89 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
@@ -0,0 +1,85 @@ +/* + * 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.keyguard + +import android.app.Presentation +import android.content.Context +import android.graphics.Color +import android.os.Bundle +import android.view.Display +import android.view.LayoutInflater +import android.view.View +import android.view.WindowManager +import com.android.keyguard.dagger.KeyguardStatusViewComponent +import com.android.systemui.R +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +/** [Presentation] shown in connected displays while on keyguard. */ +class ConnectedDisplayKeyguardPresentation +@AssistedInject +constructor( + @Assisted display: Display, + context: Context, + private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory, +) : + Presentation( + context, + display, + R.style.Theme_SystemUI_KeyguardPresentation, + WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG + ) { + + private lateinit var keyguardStatusViewController: KeyguardStatusViewController + private lateinit var clock: KeyguardStatusView + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView( + LayoutInflater.from(context) + .inflate(R.layout.keyguard_clock_presentation, /* root= */ null) + ) + val window = window ?: error("no window available.") + + // Logic to make the lock screen fullscreen + window.decorView.systemUiVisibility = + (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or + View.SYSTEM_UI_FLAG_LAYOUT_STABLE) + window.attributes.fitInsetsTypes = 0 + window.isNavigationBarContrastEnforced = false + window.navigationBarColor = Color.TRANSPARENT + + clock = findViewById(R.id.clock) + keyguardStatusViewController = + keyguardStatusViewComponentFactory.build(clock).keyguardStatusViewController.apply { + setDisplayedOnSecondaryDisplay() + init() + } + } + + /** [ConnectedDisplayKeyguardPresentation] factory. */ + @AssistedFactory + interface Factory { + /** Creates a new [Presentation] for the given [display]. */ + fun create( + display: Display, + ): ConnectedDisplayKeyguardPresentation + } +}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index b4f1956..5915b22 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -1,10 +1,5 @@ package com.android.keyguard; -import static android.view.View.ALPHA; -import static android.view.View.SCALE_X; -import static android.view.View.SCALE_Y; -import static android.view.View.TRANSLATION_Y; - import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_X_CLOCK_DESIGN; import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_Y_CLOCK_DESIGN; import static com.android.keyguard.KeyguardStatusAreaView.TRANSLATE_Y_CLOCK_SIZE; @@ -145,6 +140,13 @@ updateStatusArea(/* animate= */false); } + /** Sets whether the large clock is being shown on a connected display. */ + public void setLargeClockOnSecondaryDisplay(boolean onSecondaryDisplay) { + if (mClock != null) { + mClock.getLargeClock().getEvents().onSecondaryDisplayChanged(onSecondaryDisplay); + } + } + /** * Enable or disable split shade specific positioning */
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index a9531cf..4a7f910 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -104,6 +104,7 @@ private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; + private boolean mShownOnSecondaryDisplay = false; private boolean mOnlyClock = false; private boolean mIsActiveDreamLockscreenHosted = false; private FeatureFlags mFeatureFlags; @@ -185,8 +186,18 @@ } /** - * Mostly used for alternate displays, limit the information shown + * When set, limits the information shown in an external display. */ + public void setShownOnSecondaryDisplay(boolean shownOnSecondaryDisplay) { + mShownOnSecondaryDisplay = shownOnSecondaryDisplay; + } + + /** + * Mostly used for alternate displays, limit the information shown + * + * @deprecated use {@link KeyguardClockSwitchController#setShownOnSecondaryDisplay} + */ + @Deprecated public void setOnlyClock(boolean onlyClock) { mOnlyClock = onlyClock; } @@ -221,6 +232,15 @@ } } + private void hideSliceViewAndNotificationIconContainer() { + View ksv = mView.findViewById(R.id.keyguard_slice_view); + ksv.setVisibility(View.GONE); + + View nic = mView.findViewById( + R.id.left_aligned_notification_icon_container); + nic.setVisibility(View.GONE); + } + @Override protected void onViewAttached() { mClockRegistry.registerClockChangeListener(mClockChangedListener); @@ -233,13 +253,15 @@ mKeyguardDateWeatherViewInvisibility = mView.getResources().getInteger(R.integer.keyguard_date_weather_view_invisibility); - if (mOnlyClock) { - View ksv = mView.findViewById(R.id.keyguard_slice_view); - ksv.setVisibility(View.GONE); + if (mShownOnSecondaryDisplay) { + mView.setLargeClockOnSecondaryDisplay(true); + displayClock(LARGE, /* animate= */ false); + hideSliceViewAndNotificationIconContainer(); + return; + } - View nic = mView.findViewById( - R.id.left_aligned_notification_icon_container); - nic.setVisibility(View.GONE); + if (mOnlyClock) { + hideSliceViewAndNotificationIconContainer(); return; } updateAodIcons();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java index 9f21a31..1c5a575 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -43,16 +43,19 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.statusbar.policy.KeyguardStateController; +import dagger.Lazy; + import java.util.concurrent.Executor; import javax.inject.Inject; -import dagger.Lazy; @SysUISingleton public class KeyguardDisplayManager { @@ -64,6 +67,9 @@ private final DisplayTracker mDisplayTracker; private final Lazy<NavigationBarController> mNavigationBarControllerLazy; private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; + private final ConnectedDisplayKeyguardPresentation.Factory + mConnectedDisplayKeyguardPresentationFactory; + private final FeatureFlags mFeatureFlags; private final Context mContext; private boolean mShowing; @@ -105,7 +111,10 @@ @Main Executor mainExecutor, @UiBackground Executor uiBgExecutor, DeviceStateHelper deviceStateHelper, - KeyguardStateController keyguardStateController) { + KeyguardStateController keyguardStateController, + ConnectedDisplayKeyguardPresentation.Factory + connectedDisplayKeyguardPresentationFactory, + FeatureFlags featureFlags) { mContext = context; mNavigationBarControllerLazy = navigationBarControllerLazy; mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory; @@ -115,6 +124,8 @@ mDisplayTracker.addDisplayChangeCallback(mDisplayCallback, mainExecutor); mDeviceStateHelper = deviceStateHelper; mKeyguardStateController = keyguardStateController; + mConnectedDisplayKeyguardPresentationFactory = connectedDisplayKeyguardPresentationFactory; + mFeatureFlags = featureFlags; } private boolean isKeyguardShowable(Display display) { @@ -185,8 +196,12 @@ return false; } - KeyguardPresentation createPresentation(Display display) { - return new KeyguardPresentation(mContext, display, mKeyguardStatusViewComponentFactory); + Presentation createPresentation(Display display) { + if (mFeatureFlags.isEnabled(Flags.ENABLE_CLOCK_KEYGUARD_PRESENTATION)) { + return mConnectedDisplayKeyguardPresentationFactory.create(display); + } else { + return new KeyguardPresentation(mContext, display, mKeyguardStatusViewComponentFactory); + } } /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 757022d..c314586 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -187,6 +187,11 @@ mConfigurationController.removeCallback(mConfigurationListener); } + /** Sets the StatusView as shown on an external display. */ + public void setDisplayedOnSecondaryDisplay() { + mKeyguardClockSwitchController.setShownOnSecondaryDisplay(true); + } + /** * Called in notificationPanelViewController to avoid leak */
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 5b56223..dfe373e 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -776,6 +776,10 @@ @JvmField val ONE_WAY_HAPTICS_API_MIGRATION = unreleasedFlag("oneway_haptics_api_migration") + /** TODO(b/296223317): Enables the new keyguard presentation containing a clock. */ + @JvmField + val ENABLE_CLOCK_KEYGUARD_PRESENTATION = unreleasedFlag("enable_clock_keyguard_presentation") + /** Enable the Compose implementation of the PeopleSpaceActivity. */ @JvmField val COMPOSE_PEOPLE_SPACE = unreleasedFlag("compose_people_space")
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java index b349696..438f0f4 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
@@ -18,6 +18,8 @@ import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; +import static com.android.systemui.flags.Flags.ENABLE_CLOCK_KEYGUARD_PRESENTATION; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; @@ -37,6 +39,7 @@ import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.systemui.SysuiTestCase; +import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.settings.FakeDisplayTracker; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -59,8 +62,13 @@ @Mock private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; @Mock + private ConnectedDisplayKeyguardPresentation.Factory + mConnectedDisplayKeyguardPresentationFactory; + @Mock private KeyguardDisplayManager.KeyguardPresentation mKeyguardPresentation; @Mock + private ConnectedDisplayKeyguardPresentation mConnectedDisplayKeyguardPresentation; + @Mock private KeyguardDisplayManager.DeviceStateHelper mDeviceStateHelper; @Mock private KeyguardStateController mKeyguardStateController; @@ -69,7 +77,7 @@ private Executor mBackgroundExecutor = Runnable::run; private KeyguardDisplayManager mManager; private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext); - + private FakeFeatureFlags mFakeFeatureFlags = new FakeFeatureFlags(); // The default and secondary displays are both in the default group private Display mDefaultDisplay; private Display mSecondaryDisplay; @@ -80,10 +88,14 @@ @Before public void setUp() { MockitoAnnotations.initMocks(this); + mFakeFeatureFlags.set(ENABLE_CLOCK_KEYGUARD_PRESENTATION, false); mManager = spy(new KeyguardDisplayManager(mContext, () -> mNavigationBarController, mKeyguardStatusViewComponentFactory, mDisplayTracker, mMainExecutor, - mBackgroundExecutor, mDeviceStateHelper, mKeyguardStateController)); + mBackgroundExecutor, mDeviceStateHelper, mKeyguardStateController, + mConnectedDisplayKeyguardPresentationFactory, mFakeFeatureFlags)); doReturn(mKeyguardPresentation).when(mManager).createPresentation(any()); + doReturn(mConnectedDisplayKeyguardPresentation).when( + mConnectedDisplayKeyguardPresentationFactory).create(any()); mDefaultDisplay = new Display(DisplayManagerGlobal.getInstance(), Display.DEFAULT_DISPLAY, new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS); @@ -138,4 +150,15 @@ when(mKeyguardStateController.isOccluded()).thenReturn(true); verify(mManager, never()).createPresentation(eq(mSecondaryDisplay)); } + + @Test + public void testShow_withClockPresentationFlagEnabled_presentationCreated() { + when(mManager.createPresentation(any())).thenCallRealMethod(); + mFakeFeatureFlags.set(ENABLE_CLOCK_KEYGUARD_PRESENTATION, true); + mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mSecondaryDisplay}); + + mManager.show(); + + verify(mConnectedDisplayKeyguardPresentationFactory).create(eq(mSecondaryDisplay)); + } }