Fix keyguard on secondary displays
When projecting the display and on keyguard, keyguard
should show the current clock. This code updates the
display manager to be compatible with new clocks.
Also remove the legacy presentation and delete the
associated featureflag.
Fixes: 291289304
Test: atest KeyguardDisplayManagerTest
Test: atest CtsWindowManagerDeviceDisplay:MultiDisplayKeyguardTests
Flag: ACONFIG com.android.systemui.migrate_clocks_to_blueprint
DEVELOPMENT
Change-Id: I9ccb4d04d059b7f374ac781398a113fc17fa570a
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
index f4d34f4..8a0dd12 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
@@ -27,7 +27,7 @@
<com.android.keyguard.KeyguardStatusView
android:id="@+id/clock"
android:orientation="vertical"
- android:layout_width="410dp"
+ android:layout_width="@dimen/keyguard_presentation_width"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 4e540de..186bd7c 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -173,4 +173,6 @@
<dimen name="sfps_progress_bar_thickness">6dp</dimen>
<!-- Padding from the edge of the screen for the progress bar -->
<dimen name="sfps_progress_bar_padding_from_edge">7dp</dimen>
+
+ <dimen name="keyguard_presentation_width">410dp</dimen>
</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
index dec7d79..630610d 100644
--- a/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ConnectedDisplayKeyguardPresentation.kt
@@ -19,13 +19,22 @@
import android.app.Presentation
import android.content.Context
import android.graphics.Color
+import android.graphics.Rect
import android.os.Bundle
import android.view.Display
+import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.WindowManager
+import android.widget.FrameLayout
+import android.widget.FrameLayout.LayoutParams
import com.android.keyguard.dagger.KeyguardStatusViewComponent
+import com.android.systemui.Flags.migrateClocksToBlueprint
+import com.android.systemui.plugins.clocks.ClockController
+import com.android.systemui.plugins.clocks.ClockFaceController
import com.android.systemui.res.R
+import com.android.systemui.shared.clocks.ClockRegistry
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -37,6 +46,8 @@
@Assisted display: Display,
context: Context,
private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
+ private val clockRegistry: ClockRegistry,
+ private val clockEventController: ClockEventController,
) :
Presentation(
context,
@@ -45,18 +56,126 @@
WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
) {
+ private lateinit var rootView: FrameLayout
+ private var clock: View? = null
private lateinit var keyguardStatusViewController: KeyguardStatusViewController
- private lateinit var clock: KeyguardStatusView
+ private lateinit var faceController: ClockFaceController
+ private lateinit var clockFrame: FrameLayout
+
+ private val clockChangedListener =
+ object : ClockRegistry.ClockChangeListener {
+ override fun onCurrentClockChanged() {
+ setClock(clockRegistry.createCurrentClock())
+ }
+
+ override fun onAvailableClocksChanged() {}
+ }
+
+ private val layoutChangeListener =
+ object : View.OnLayoutChangeListener {
+ override fun onLayoutChange(
+ view: View,
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int,
+ oldLeft: Int,
+ oldTop: Int,
+ oldRight: Int,
+ oldBottom: Int
+ ) {
+ clock?.let {
+ faceController.events.onTargetRegionChanged(
+ Rect(it.left, it.top, it.width, it.height)
+ )
+ }
+ }
+ }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ if (migrateClocksToBlueprint()) {
+ onCreateV2()
+ } else {
+ onCreate()
+ }
+ }
+
+ fun onCreateV2() {
+ rootView = FrameLayout(getContext(), null)
+ rootView.setClipChildren(false)
+ setContentView(rootView)
+
+ setFullscreen()
+
+ setClock(clockRegistry.createCurrentClock())
+ }
+
+ fun onCreate() {
setContentView(
LayoutInflater.from(context)
.inflate(R.layout.keyguard_clock_presentation, /* root= */ null)
)
- val window = window ?: error("no window available.")
+ setFullscreen()
+
+ clock = requireViewById(R.id.clock)
+ keyguardStatusViewController =
+ keyguardStatusViewComponentFactory
+ .build(clock as KeyguardStatusView, display)
+ .keyguardStatusViewController
+ .apply {
+ setDisplayedOnSecondaryDisplay()
+ init()
+ }
+ }
+
+ override fun onAttachedToWindow() {
+ if (migrateClocksToBlueprint()) {
+ clockRegistry.registerClockChangeListener(clockChangedListener)
+ clockEventController.registerListeners(clock!!)
+
+ faceController.animations.enter()
+ }
+ }
+
+ override fun onDetachedFromWindow() {
+ if (migrateClocksToBlueprint()) {
+ clockEventController.unregisterListeners()
+ clockRegistry.unregisterClockChangeListener(clockChangedListener)
+ }
+
+ super.onDetachedFromWindow()
+ }
+
+ override fun onDisplayChanged() {
+ val window = window ?: error("no window available.")
+ window.getDecorView().requestLayout()
+ }
+
+ private fun setClock(clockController: ClockController) {
+ clock?.removeOnLayoutChangeListener(layoutChangeListener)
+ rootView.removeAllViews()
+
+ faceController = clockController.largeClock
+ clock = faceController.view.also { it.addOnLayoutChangeListener(layoutChangeListener) }
+ rootView.addView(
+ clock,
+ FrameLayout.LayoutParams(
+ context.resources.getDimensionPixelSize(R.dimen.keyguard_presentation_width),
+ WRAP_CONTENT,
+ Gravity.CENTER,
+ )
+ )
+
+ clockEventController.clock = clockController
+ clockEventController.setLargeClockOnSecondaryDisplay(true)
+ faceController.events.onSecondaryDisplayChanged(true)
+ }
+
+ private fun setFullscreen() {
+ val window = window ?: error("no window available.")
// Logic to make the lock screen fullscreen
window.decorView.systemUiVisibility =
(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
@@ -65,16 +184,6 @@
window.attributes.fitInsetsTypes = 0
window.isNavigationBarContrastEnforced = false
window.navigationBarColor = Color.TRANSPARENT
-
- clock = requireViewById(R.id.clock)
- keyguardStatusViewController =
- keyguardStatusViewComponentFactory
- .build(clock, display)
- .keyguardStatusViewController
- .apply {
- setDisplayedOnSecondaryDisplay()
- init()
- }
}
/** [ConnectedDisplayKeyguardPresentation] factory. */
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 8a6f101..0bb5c17 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -17,13 +17,10 @@
import android.app.Presentation;
import android.content.Context;
-import android.graphics.Color;
-import android.graphics.Rect;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.DisplayManager;
import android.media.MediaRouter;
import android.media.MediaRouter.RouteInfo;
-import android.os.Bundle;
import android.os.Trace;
import android.text.TextUtils;
import android.util.Log;
@@ -31,20 +28,14 @@
import android.view.Display;
import android.view.DisplayAddress;
import android.view.DisplayInfo;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import androidx.annotation.Nullable;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.keyguard.dagger.KeyguardStatusViewComponent;
-import com.android.systemui.res.R;
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;
@@ -66,10 +57,8 @@
private final DisplayManager mDisplayService;
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;
@@ -106,18 +95,15 @@
@Inject
public KeyguardDisplayManager(Context context,
Lazy<NavigationBarController> navigationBarControllerLazy,
- KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
DisplayTracker displayTracker,
@Main Executor mainExecutor,
@UiBackground Executor uiBgExecutor,
DeviceStateHelper deviceStateHelper,
KeyguardStateController keyguardStateController,
ConnectedDisplayKeyguardPresentation.Factory
- connectedDisplayKeyguardPresentationFactory,
- FeatureFlags featureFlags) {
+ connectedDisplayKeyguardPresentationFactory) {
mContext = context;
mNavigationBarControllerLazy = navigationBarControllerLazy;
- mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
uiBgExecutor.execute(() -> mMediaRouter = mContext.getSystemService(MediaRouter.class));
mDisplayService = mContext.getSystemService(DisplayManager.class);
mDisplayTracker = displayTracker;
@@ -125,7 +111,6 @@
mDeviceStateHelper = deviceStateHelper;
mKeyguardStateController = keyguardStateController;
mConnectedDisplayKeyguardPresentationFactory = connectedDisplayKeyguardPresentationFactory;
- mFeatureFlags = featureFlags;
}
private boolean isKeyguardShowable(Display display) {
@@ -197,11 +182,7 @@
}
Presentation createPresentation(Display display) {
- if (mFeatureFlags.isEnabled(Flags.ENABLE_CLOCK_KEYGUARD_PRESENTATION)) {
- return mConnectedDisplayKeyguardPresentationFactory.create(display);
- } else {
- return new KeyguardPresentation(mContext, display, mKeyguardStatusViewComponentFactory);
- }
+ return mConnectedDisplayKeyguardPresentationFactory.create(display);
}
/**
@@ -347,92 +328,4 @@
&& mRearDisplayPhysicalAddress.equals(display.getAddress());
}
}
-
-
- @VisibleForTesting
- static final class KeyguardPresentation extends Presentation {
- private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height
- private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s
- private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
- private KeyguardClockSwitchController mKeyguardClockSwitchController;
- private View mClock;
- private int mUsableWidth;
- private int mUsableHeight;
- private int mMarginTop;
- private int mMarginLeft;
- Runnable mMoveTextRunnable = new Runnable() {
- @Override
- public void run() {
- int x = mMarginLeft + (int) (Math.random() * (mUsableWidth - mClock.getWidth()));
- int y = mMarginTop + (int) (Math.random() * (mUsableHeight - mClock.getHeight()));
- mClock.setTranslationX(x);
- mClock.setTranslationY(y);
- mClock.postDelayed(mMoveTextRunnable, MOVE_CLOCK_TIMEOUT);
- }
- };
-
- KeyguardPresentation(Context context, Display display,
- KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory) {
- super(context, display, R.style.Theme_SystemUI_KeyguardPresentation,
- WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
- setCancelable(false);
- }
-
- @Override
- public void cancel() {
- // Do not allow anything to cancel KeyguardPresentation except KeyguardDisplayManager.
- }
-
- @Override
- public void onDetachedFromWindow() {
- mClock.removeCallbacks(mMoveTextRunnable);
- }
-
- @Override
- public void onDisplayChanged() {
- updateBounds();
- getWindow().getDecorView().requestLayout();
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- updateBounds();
-
- setContentView(LayoutInflater.from(getContext())
- .inflate(R.layout.keyguard_presentation, null));
-
- // Logic to make the lock screen fullscreen
- getWindow().getDecorView().setSystemUiVisibility(
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
- getWindow().getAttributes().setFitInsetsTypes(0 /* types */);
- getWindow().setNavigationBarContrastEnforced(false);
- getWindow().setNavigationBarColor(Color.TRANSPARENT);
-
- mClock = findViewById(R.id.clock);
-
- // Avoid screen burn in
- mClock.post(mMoveTextRunnable);
-
- mKeyguardClockSwitchController = mKeyguardStatusViewComponentFactory
- .build(findViewById(R.id.clock), getDisplay())
- .getKeyguardClockSwitchController();
-
- mKeyguardClockSwitchController.setOnlyClock(true);
- mKeyguardClockSwitchController.init();
- }
-
- private void updateBounds() {
- final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics()
- .getBounds();
- mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100;
- mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100;
- mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200;
- mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200;
- }
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
index 438f0f4..cc36cfa 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardDisplayManagerTest.java
@@ -18,8 +18,6 @@
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,9 +35,7 @@
import androidx.test.filters.SmallTest;
-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;
@@ -60,13 +56,9 @@
@Mock
private NavigationBarController mNavigationBarController;
@Mock
- private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
- @Mock
private ConnectedDisplayKeyguardPresentation.Factory
mConnectedDisplayKeyguardPresentationFactory;
@Mock
- private KeyguardDisplayManager.KeyguardPresentation mKeyguardPresentation;
- @Mock
private ConnectedDisplayKeyguardPresentation mConnectedDisplayKeyguardPresentation;
@Mock
private KeyguardDisplayManager.DeviceStateHelper mDeviceStateHelper;
@@ -77,7 +69,6 @@
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;
@@ -88,15 +79,13 @@
@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,
- mConnectedDisplayKeyguardPresentationFactory, mFakeFeatureFlags));
- doReturn(mKeyguardPresentation).when(mManager).createPresentation(any());
+ mDisplayTracker, mMainExecutor, mBackgroundExecutor, mDeviceStateHelper,
+ mKeyguardStateController, mConnectedDisplayKeyguardPresentationFactory));
doReturn(mConnectedDisplayKeyguardPresentation).when(
mConnectedDisplayKeyguardPresentationFactory).create(any());
-
+ doReturn(mConnectedDisplayKeyguardPresentation).when(mManager)
+ .createPresentation(any());
mDefaultDisplay = new Display(DisplayManagerGlobal.getInstance(), Display.DEFAULT_DISPLAY,
new DisplayInfo(), DEFAULT_DISPLAY_ADJUSTMENTS);
mSecondaryDisplay = new Display(DisplayManagerGlobal.getInstance(),
@@ -152,9 +141,8 @@
}
@Test
- public void testShow_withClockPresentationFlagEnabled_presentationCreated() {
+ public void testShow_presentationCreated() {
when(mManager.createPresentation(any())).thenCallRealMethod();
- mFakeFeatureFlags.set(ENABLE_CLOCK_KEYGUARD_PRESENTATION, true);
mDisplayTracker.setAllDisplays(new Display[]{mDefaultDisplay, mSecondaryDisplay});
mManager.show();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
deleted file mode 100644
index 5102957..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2019 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 static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.hardware.display.DisplayManager;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.util.AttributeSet;
-import android.view.Display;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.keyguard.KeyguardDisplayManager.KeyguardPresentation;
-import com.android.keyguard.dagger.KeyguardStatusViewComponent;
-import com.android.systemui.res.R;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class KeyguardPresentationTest extends SysuiTestCase {
-
- @Mock
- KeyguardClockSwitch mMockKeyguardClockSwitch;
- @Mock
- KeyguardSliceView mMockKeyguardSliceView;
- @Mock
- KeyguardStatusView mMockKeyguardStatusView;
- @Mock
- private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
- @Mock
- private KeyguardStatusViewComponent mKeyguardStatusViewComponent;
- @Mock
- private KeyguardClockSwitchController mKeyguardClockSwitchController;
-
- LayoutInflater mLayoutInflater;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- when(mMockKeyguardClockSwitch.getContext()).thenReturn(mContext);
- when(mMockKeyguardSliceView.getContext()).thenReturn(mContext);
- when(mMockKeyguardStatusView.getContext()).thenReturn(mContext);
- when(mMockKeyguardStatusView.findViewById(R.id.clock)).thenReturn(mMockKeyguardStatusView);
- when(mKeyguardStatusViewComponentFactory.build(any(KeyguardStatusView.class),
- any(Display.class)))
- .thenReturn(mKeyguardStatusViewComponent);
- when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController())
- .thenReturn(mKeyguardClockSwitchController);
-
- allowTestableLooperAsMainThread();
-
- mLayoutInflater = LayoutInflater.from(mContext);
- mLayoutInflater.setPrivateFactory(new LayoutInflater.Factory2() {
-
- @Override
- public View onCreateView(View parent, String name, Context context,
- AttributeSet attrs) {
- return onCreateView(name, context, attrs);
- }
-
- @Override
- public View onCreateView(String name, Context context, AttributeSet attrs) {
- if ("com.android.keyguard.KeyguardStatusView".equals(name)) {
- return mMockKeyguardStatusView;
- } else if ("com.android.keyguard.KeyguardClockSwitch".equals(name)) {
- return mMockKeyguardClockSwitch;
- } else if ("com.android.keyguard.KeyguardSliceView".equals(name)) {
- return mMockKeyguardStatusView;
- }
- return null;
- }
- });
- }
-
- @After
- public void tearDown() {
- disallowTestableLooperAsMainThread();
- }
-
- @Test
- public void testInflation_doesntCrash() {
- final Display display = mContext.getSystemService(DisplayManager.class).getDisplay(
- Display.DEFAULT_DISPLAY);
- KeyguardPresentation keyguardPresentation = new KeyguardPresentation(mContext, display,
- mKeyguardStatusViewComponentFactory);
- keyguardPresentation.onCreate(null /*savedInstanceState */);
- }
-}