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) {
+ [email protected] = 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));
+ }
}