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
This commit is contained in:
Nicolo' Mazzucato
2023-08-14 12:40:41 +00:00
parent 33158ac739
commit 212dc4a193
10 changed files with 232 additions and 24 deletions

View File

@@ -62,6 +62,7 @@ class DefaultClockController(
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 @@ class DefaultClockController(
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 @@ class DefaultClockController(
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)
}

View File

@@ -177,6 +177,9 @@ interface ClockFaceEvents {
* 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 */

View File

@@ -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>

View File

@@ -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
}
}

View File

@@ -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 @@ public class KeyguardClockSwitch extends RelativeLayout {
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
*/

View File

@@ -104,6 +104,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private boolean mShownOnSecondaryDisplay = false;
private boolean mOnlyClock = false;
private boolean mIsActiveDreamLockscreenHosted = false;
private FeatureFlags mFeatureFlags;
@@ -185,8 +186,18 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
}
/**
* 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 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
}
}
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 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
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();

View File

@@ -43,16 +43,19 @@ import com.android.systemui.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;
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 @@ public class KeyguardDisplayManager {
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 @@ public class KeyguardDisplayManager {
@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 @@ public class KeyguardDisplayManager {
mDisplayTracker.addDisplayChangeCallback(mDisplayCallback, mainExecutor);
mDeviceStateHelper = deviceStateHelper;
mKeyguardStateController = keyguardStateController;
mConnectedDisplayKeyguardPresentationFactory = connectedDisplayKeyguardPresentationFactory;
mFeatureFlags = featureFlags;
}
private boolean isKeyguardShowable(Display display) {
@@ -185,8 +196,12 @@ public class KeyguardDisplayManager {
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);
}
}
/**

View File

@@ -187,6 +187,11 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV
mConfigurationController.removeCallback(mConfigurationListener);
}
/** Sets the StatusView as shown on an external display. */
public void setDisplayedOnSecondaryDisplay() {
mKeyguardClockSwitchController.setShownOnSecondaryDisplay(true);
}
/**
* Called in notificationPanelViewController to avoid leak
*/

View File

@@ -776,6 +776,10 @@ object Flags {
@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")

View File

@@ -18,6 +18,8 @@ package com.android.keyguard;
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 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;
@@ -59,8 +62,13 @@ public class KeyguardDisplayManagerTest extends SysuiTestCase {
@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 @@ public class KeyguardDisplayManagerTest extends SysuiTestCase {
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 @@ public class KeyguardDisplayManagerTest extends SysuiTestCase {
@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 @@ public class KeyguardDisplayManagerTest extends SysuiTestCase {
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));
}
}