From 02ec5b8bc029b4a71123a0ac01729b495ba96f2c Mon Sep 17 00:00:00 2001 From: Aaron Liu Date: Wed, 14 Jun 2023 11:00:48 -0700 Subject: [PATCH 1/2] Add layout manager Adds a layout manager that can make transitions. Fixes: 283134494 Test: test LS with indication area and lock icon flags on. Test: test with LS on different configurations, open, folded, landscape. Change-Id: I31a47234b5c4b65c52074ca93ae4fe701649ecdb --- .../com/android/keyguard/LockIconView.java | 2 - .../keyguard/LockIconViewController.java | 19 ++- .../systemui/dagger/SystemUIModule.java | 2 + .../keyguard/KeyguardViewConfigurator.kt | 3 + .../keyguard/ui/view/KeyguardRootView.kt | 31 +--- .../ui/view/layout/DefaultLockscreenLayout.kt | 152 ++++++++++++++++++ .../ui/view/layout/KeyguardLayoutManager.kt | 91 +++++++++++ .../ui/view/layout/LockscreenLayoutModule.kt | 31 ++++ .../LockIconViewControllerBaseTest.java | 2 + .../layout/DefaultLockscreenLayoutTest.kt | 94 +++++++++++ .../view/layout/KeyguardLayoutManagerTest.kt | 69 ++++++++ 11 files changed, 459 insertions(+), 37 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayout.kt create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManager.kt create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/LockscreenLayoutModule.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayoutTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerTest.kt diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java index d1fffaa926ea6..76b073ef73afe 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java @@ -32,7 +32,6 @@ import android.widget.ImageView; import androidx.annotation.IntDef; import androidx.annotation.NonNull; -import androidx.annotation.VisibleForTesting; import com.android.internal.graphics.ColorUtils; import com.android.settingslib.Utils; @@ -126,7 +125,6 @@ public class LockIconView extends FrameLayout implements Dumpable { /** * Set the location of the lock icon. */ - @VisibleForTesting public void setCenterLocation(@NonNull Point center, float radius, int drawablePadding) { mLockIconCenter = center; mRadius = radius; diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index 239a0cc01c457..e255f5c2ded69 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -60,6 +60,7 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.shared.model.TransitionStep; @@ -387,15 +388,17 @@ public class LockIconViewController extends ViewController impleme private void updateLockIconLocation() { final float scaleFactor = mAuthController.getScaleFactor(); final int scaledPadding = (int) (mDefaultPaddingPx * scaleFactor); - if (mUdfpsSupported) { - mView.setCenterLocation(mAuthController.getUdfpsLocation(), - mAuthController.getUdfpsRadius(), scaledPadding); - } else { - mView.setCenterLocation( - new Point((int) mWidthPixels / 2, - (int) (mHeightPixels - - ((mBottomPaddingPx + sLockIconRadiusPx) * scaleFactor))), + if (!mFeatureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) { + if (mUdfpsSupported) { + mView.setCenterLocation(mAuthController.getUdfpsLocation(), + mAuthController.getUdfpsRadius(), scaledPadding); + } else { + mView.setCenterLocation( + new Point((int) mWidthPixels / 2, + (int) (mHeightPixels + - ((mBottomPaddingPx + sLockIconRadiusPx) * scaleFactor))), sLockIconRadiusPx * scaleFactor, scaledPadding); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 8f3c3d6e1dd50..ead24ae71cc61 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -54,6 +54,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.FlagsModule; import com.android.systemui.keyboard.KeyboardModule; +import com.android.systemui.keyguard.ui.view.layout.LockscreenLayoutModule; import com.android.systemui.log.dagger.LogModule; import com.android.systemui.log.dagger.MonitorLog; import com.android.systemui.log.table.TableLogBuffer; @@ -178,6 +179,7 @@ import javax.inject.Named; GarbageMonitorModule.class, KeyboardModule.class, LetterboxModule.class, + LockscreenLayoutModule.class, LogModule.class, MediaProjectionModule.class, MotionToolModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt index df83aafecb4b8..5fdabecc9eb31 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt @@ -26,6 +26,7 @@ import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder import com.android.systemui.keyguard.ui.view.KeyguardRootView +import com.android.systemui.keyguard.ui.view.layout.KeyguardLayoutManager import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel import com.android.systemui.shade.NotificationShadeWindowView import com.android.systemui.statusbar.KeyguardIndicationController @@ -42,6 +43,7 @@ constructor( private val notificationShadeWindowView: NotificationShadeWindowView, private val featureFlags: FeatureFlags, private val indicationController: KeyguardIndicationController, + private val keyguardLayoutManager: KeyguardLayoutManager, ) : CoreStartable { private var indicationAreaHandle: DisposableHandle? = null @@ -51,6 +53,7 @@ constructor( notificationShadeWindowView.requireViewById(R.id.notification_panel) as ViewGroup bindIndicationArea(notificationPanel) bindLockIconView(notificationPanel) + keyguardLayoutManager.layoutViews() } fun bindIndicationArea(legacyParent: ViewGroup) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt index a62f383ed7042..0077f2d68c7e9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt @@ -19,10 +19,7 @@ package com.android.systemui.keyguard.ui.view import android.content.Context import android.util.AttributeSet -import android.view.Gravity -import android.view.ViewGroup.LayoutParams.MATCH_PARENT -import android.view.ViewGroup.LayoutParams.WRAP_CONTENT -import android.widget.FrameLayout +import androidx.constraintlayout.widget.ConstraintLayout import com.android.keyguard.LockIconView import com.android.systemui.R @@ -31,7 +28,7 @@ class KeyguardRootView( context: Context, private val attrs: AttributeSet?, ) : - FrameLayout( + ConstraintLayout( context, attrs, ) { @@ -43,31 +40,11 @@ class KeyguardRootView( private fun addIndicationTextArea() { val view = KeyguardIndicationArea(context, attrs) - addView( - view, - FrameLayout.LayoutParams( - MATCH_PARENT, - WRAP_CONTENT, - ) - .apply { - gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL - bottomMargin = R.dimen.keyguard_indication_margin_bottom.dp() - } - ) + addView(view) } private fun addLockIconView() { val view = LockIconView(context, attrs).apply { id = R.id.lock_icon_view } - addView( - view, - LayoutParams( - WRAP_CONTENT, - WRAP_CONTENT, - ) - ) - } - - private fun Int.dp(): Int { - return context.resources.getDimensionPixelSize(this) + addView(view) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayout.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayout.kt new file mode 100644 index 0000000000000..baaeb60f364ea --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayout.kt @@ -0,0 +1,152 @@ +/* + * 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.systemui.keyguard.ui.view.layout + +import android.content.Context +import android.graphics.Point +import android.graphics.Rect +import android.util.DisplayMetrics +import android.view.View +import android.view.ViewGroup.LayoutParams.MATCH_PARENT +import android.view.ViewGroup.LayoutParams.WRAP_CONTENT +import android.view.WindowManager +import androidx.annotation.VisibleForTesting +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.constraintlayout.widget.ConstraintSet +import androidx.constraintlayout.widget.ConstraintSet.BOTTOM +import androidx.constraintlayout.widget.ConstraintSet.END +import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID +import androidx.constraintlayout.widget.ConstraintSet.START +import androidx.constraintlayout.widget.ConstraintSet.TOP +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.R +import com.android.systemui.biometrics.AuthController +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.ui.view.KeyguardRootView +import javax.inject.Inject + +/** + * Positions elements of the lockscreen to the default position. + * + * This will be the most common use case for phones in portrait mode. + */ +@SysUISingleton +class DefaultLockscreenLayout +@Inject +constructor( + private val authController: AuthController, + private val keyguardUpdateMonitor: KeyguardUpdateMonitor, + private val windowManager: WindowManager, + private val context: Context, +) : LockscreenLayout { + override val id: String = DEFAULT + + override fun layoutIndicationArea(rootView: KeyguardRootView) { + val indicationArea = rootView.findViewById(R.id.keyguard_indication_area) ?: return + + rootView.getConstraintSet().apply { + constrainWidth(indicationArea.id, MATCH_PARENT) + constrainHeight(indicationArea.id, WRAP_CONTENT) + connect( + indicationArea.id, + BOTTOM, + PARENT_ID, + BOTTOM, + R.dimen.keyguard_indication_margin_bottom.dp() + ) + connect(indicationArea.id, START, PARENT_ID, START) + connect(indicationArea.id, END, PARENT_ID, END) + applyTo(rootView) + } + } + + override fun layoutLockIcon(rootView: KeyguardRootView) { + val isUdfpsSupported = keyguardUpdateMonitor.isUdfpsSupported + val scaleFactor: Float = authController.scaleFactor + val mBottomPaddingPx = R.dimen.lock_icon_margin_bottom.dp() + val mDefaultPaddingPx = R.dimen.lock_icon_padding.dp() + val scaledPadding: Int = (mDefaultPaddingPx * scaleFactor).toInt() + val bounds = windowManager.currentWindowMetrics.bounds + val widthPixels = bounds.right.toFloat() + val heightPixels = bounds.bottom.toFloat() + val defaultDensity = + DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() / + DisplayMetrics.DENSITY_DEFAULT.toFloat() + val lockIconRadiusPx = (defaultDensity * 36).toInt() + + if (isUdfpsSupported) { + authController.udfpsLocation?.let { udfpsLocation -> + centerLockIcon(udfpsLocation, authController.udfpsRadius, scaledPadding, rootView) + } + } else { + centerLockIcon( + Point( + (widthPixels / 2).toInt(), + (heightPixels - ((mBottomPaddingPx + lockIconRadiusPx) * scaleFactor)).toInt() + ), + lockIconRadiusPx * scaleFactor, + scaledPadding, + rootView + ) + } + } + + @VisibleForTesting + internal fun centerLockIcon( + center: Point, + radius: Float, + drawablePadding: Int, + rootView: KeyguardRootView, + ) { + val lockIconView = rootView.findViewById(R.id.lock_icon_view) ?: return + val lockIcon = lockIconView.findViewById(R.id.lock_icon) ?: return + lockIcon.setPadding(drawablePadding, drawablePadding, drawablePadding, drawablePadding) + + val sensorRect = + Rect().apply { + set( + center.x - radius.toInt(), + center.y - radius.toInt(), + center.x + radius.toInt(), + center.y + radius.toInt(), + ) + } + + rootView.getConstraintSet().apply { + constrainWidth(lockIconView.id, sensorRect.right - sensorRect.left) + constrainHeight(lockIconView.id, sensorRect.bottom - sensorRect.top) + connect(lockIconView.id, TOP, PARENT_ID, TOP, sensorRect.top) + connect(lockIconView.id, START, PARENT_ID, START, sensorRect.left) + applyTo(rootView) + } + } + + private fun Int.dp(): Int { + return context.resources.getDimensionPixelSize(this) + } + + private fun ConstraintLayout.getConstraintSet(): ConstraintSet { + val cs = ConstraintSet() + cs.clone(this) + return cs + } + + companion object { + const val DEFAULT = "default" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManager.kt new file mode 100644 index 0000000000000..9bc630265a3f5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManager.kt @@ -0,0 +1,91 @@ +/* + * 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.systemui.keyguard.ui.view.layout + +import android.content.res.Configuration +import androidx.constraintlayout.widget.ConstraintSet +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.ui.view.KeyguardRootView +import com.android.systemui.keyguard.ui.view.layout.DefaultLockscreenLayout.Companion.DEFAULT +import com.android.systemui.statusbar.policy.ConfigurationController +import javax.inject.Inject + +/** + * Manages layout changes for the lockscreen. + * + * To add a layout, add an entry to the map with a unique id and call #transitionToLayout(string). + */ +@SysUISingleton +class KeyguardLayoutManager +@Inject +constructor( + configurationController: ConfigurationController, + layouts: Set<@JvmSuppressWildcards LockscreenLayout>, + private val keyguardRootView: KeyguardRootView, +) { + internal val layoutIdMap: Map = layouts.associateBy { it.id } + private var layout: LockscreenLayout? = layoutIdMap[DEFAULT] + + init { + configurationController.addCallback( + object : ConfigurationController.ConfigurationListener { + override fun onConfigChanged(newConfig: Configuration?) { + layoutViews() + } + } + ) + } + + /** + * Transitions to a layout. + * + * @param layoutId + * @return whether the transition has succeeded. + */ + fun transitionToLayout(layoutId: String): Boolean { + layout = layoutIdMap[layoutId] ?: return false + layoutViews() + return true + } + + fun layoutViews() { + layout?.layoutViews(keyguardRootView) + } + + companion object { + const val TAG = "KeyguardLayoutManager" + } +} + +interface LockscreenLayout { + val id: String + + fun layoutViews(rootView: KeyguardRootView) { + // Clear constraints. + ConstraintSet() + .apply { + clone(rootView) + knownIds.forEach { getConstraint(it).layout.copyFrom(ConstraintSet.Layout()) } + } + .applyTo(rootView) + layoutIndicationArea(rootView) + layoutLockIcon(rootView) + } + fun layoutIndicationArea(rootView: KeyguardRootView) + fun layoutLockIcon(rootView: KeyguardRootView) +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/LockscreenLayoutModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/LockscreenLayoutModule.kt new file mode 100644 index 0000000000000..00f93e33f3706 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/LockscreenLayoutModule.kt @@ -0,0 +1,31 @@ +/* + * 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.systemui.keyguard.ui.view.layout + +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoSet + +@Module +abstract class LockscreenLayoutModule { + @Binds + @IntoSet + abstract fun bindDefaultLayout( + defaultLockscreenLayout: DefaultLockscreenLayout + ): LockscreenLayout +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java index c88c4d65f4128..e7e1cc94b7c8f 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java @@ -19,6 +19,7 @@ package com.android.keyguard; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1; import static com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR; +import static com.android.systemui.flags.Flags.MIGRATE_LOCK_ICON; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; @@ -143,6 +144,7 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { mFeatureFlags = new FakeFeatureFlags(); mFeatureFlags.set(FACE_AUTH_REFACTOR, false); + mFeatureFlags.set(MIGRATE_LOCK_ICON, false); mUnderTest = new LockIconViewController( mLockIconView, mStatusBarStateController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayoutTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayoutTest.kt new file mode 100644 index 0000000000000..2e97208e44deb --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayoutTest.kt @@ -0,0 +1,94 @@ +/* + * 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.systemui.keyguard.ui.view.layout + +import android.graphics.Point +import android.view.ViewGroup +import android.view.WindowManager +import androidx.constraintlayout.widget.ConstraintSet +import androidx.test.filters.SmallTest +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.AuthController +import com.android.systemui.keyguard.ui.view.KeyguardRootView +import com.android.systemui.monet.utils.ArgbSubject.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Answers +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@RunWith(JUnit4::class) +@SmallTest +class DefaultLockscreenLayoutTest : SysuiTestCase() { + private lateinit var defaultLockscreenLayout: DefaultLockscreenLayout + private lateinit var rootView: KeyguardRootView + @Mock private lateinit var authController: AuthController + @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor + @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var windowManager: WindowManager + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + rootView = KeyguardRootView(context, null) + defaultLockscreenLayout = + DefaultLockscreenLayout(authController, keyguardUpdateMonitor, windowManager, context) + } + + @Test + fun testLayoutViews_KeyguardIndicationArea() { + defaultLockscreenLayout.layoutViews(rootView) + val constraint = getViewConstraint(R.id.keyguard_indication_area) + assertThat(constraint.layout.bottomToBottom).isEqualTo(ConstraintSet.PARENT_ID) + assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID) + assertThat(constraint.layout.endToEnd).isEqualTo(ConstraintSet.PARENT_ID) + assertThat(constraint.layout.mWidth).isEqualTo(ViewGroup.LayoutParams.MATCH_PARENT) + assertThat(constraint.layout.mHeight).isEqualTo(ViewGroup.LayoutParams.WRAP_CONTENT) + } + + @Test + fun testLayoutViews_lockIconView() { + defaultLockscreenLayout.layoutViews(rootView) + val constraint = getViewConstraint(R.id.lock_icon_view) + assertThat(constraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID) + assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID) + } + + @Test + fun testCenterLockIcon() { + defaultLockscreenLayout.centerLockIcon(Point(5, 6), 1F, 5, rootView) + val constraint = getViewConstraint(R.id.lock_icon_view) + + assertThat(constraint.layout.mWidth).isEqualTo(2) + assertThat(constraint.layout.mHeight).isEqualTo(2) + assertThat(constraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID) + assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID) + assertThat(constraint.layout.topMargin).isEqualTo(5) + assertThat(constraint.layout.startMargin).isEqualTo(4) + } + + /** Get the ConstraintLayout constraint of the view. */ + private fun getViewConstraint(viewId: Int): ConstraintSet.Constraint { + val constraintSet = ConstraintSet() + constraintSet.clone(rootView) + return constraintSet.getConstraint(viewId) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerTest.kt new file mode 100644 index 0000000000000..95b2030de923c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerTest.kt @@ -0,0 +1,69 @@ +/* + * 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.systemui.keyguard.ui.view.layout + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.keyguard.ui.view.KeyguardRootView +import com.android.systemui.keyguard.ui.view.layout.DefaultLockscreenLayout.Companion.DEFAULT +import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@RunWith(JUnit4::class) +@SmallTest +class KeyguardLayoutManagerTest : SysuiTestCase() { + private lateinit var keyguardLayoutManager: KeyguardLayoutManager + @Mock lateinit var configurationController: ConfigurationController + @Mock lateinit var defaultLockscreenLayout: DefaultLockscreenLayout + @Mock lateinit var keyguardRootView: KeyguardRootView + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + whenever(defaultLockscreenLayout.id).thenReturn(DEFAULT) + keyguardLayoutManager = + KeyguardLayoutManager( + configurationController, + setOf(defaultLockscreenLayout), + keyguardRootView + ) + } + + @Test + fun testDefaultLayout() { + keyguardLayoutManager.transitionToLayout(DEFAULT) + verify(defaultLockscreenLayout).layoutViews(keyguardRootView) + } + + @Test + fun testTransitionToLayout_validId() { + assertThat(keyguardLayoutManager.transitionToLayout(DEFAULT)).isTrue() + } + @Test + fun testTransitionToLayout_invalidId() { + assertThat(keyguardLayoutManager.transitionToLayout("abc")).isFalse() + } +} From 252fb2b6c66e3d00e2f9094e6313f06f8033a758 Mon Sep 17 00:00:00 2001 From: Aaron Liu Date: Sat, 24 Jun 2023 11:27:09 -0700 Subject: [PATCH 2/2] Add a shell command interface for layouts. Uses a map that will change layouts based on the shell command. Bug: 288242927 Test: run relevant shell commands Change-Id: I9b6121a7389c2003b9a6275fa9e3098d8b8a5b69 --- .../keyguard/KeyguardViewConfigurator.kt | 3 + .../KeyguardLayoutManagerCommandListener.kt | 63 +++++++++++++ ...eyguardLayoutManagerCommandListenerTest.kt | 88 +++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListener.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListenerTest.kt diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt index 5fdabecc9eb31..f59ad90d86ffa 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt @@ -27,6 +27,7 @@ import com.android.systemui.flags.Flags import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder import com.android.systemui.keyguard.ui.view.KeyguardRootView import com.android.systemui.keyguard.ui.view.layout.KeyguardLayoutManager +import com.android.systemui.keyguard.ui.view.layout.KeyguardLayoutManagerCommandListener import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel import com.android.systemui.shade.NotificationShadeWindowView import com.android.systemui.statusbar.KeyguardIndicationController @@ -44,6 +45,7 @@ constructor( private val featureFlags: FeatureFlags, private val indicationController: KeyguardIndicationController, private val keyguardLayoutManager: KeyguardLayoutManager, + private val keyguardLayoutManagerCommandListener: KeyguardLayoutManagerCommandListener, ) : CoreStartable { private var indicationAreaHandle: DisposableHandle? = null @@ -54,6 +56,7 @@ constructor( bindIndicationArea(notificationPanel) bindLockIconView(notificationPanel) keyguardLayoutManager.layoutViews() + keyguardLayoutManagerCommandListener.start() } fun bindIndicationArea(legacyParent: ViewGroup) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListener.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListener.kt new file mode 100644 index 0000000000000..b351ea8923f63 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListener.kt @@ -0,0 +1,63 @@ +/* + * 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.systemui.keyguard.ui.view.layout + +import com.android.systemui.statusbar.commandline.Command +import com.android.systemui.statusbar.commandline.CommandRegistry +import java.io.PrintWriter +import javax.inject.Inject + +/** Uses $ adb shell cmd statusbar layout */ +class KeyguardLayoutManagerCommandListener +@Inject +constructor( + private val commandRegistry: CommandRegistry, + private val keyguardLayoutManager: KeyguardLayoutManager +) { + private val layoutCommand = KeyguardLayoutManagerCommand() + + fun start() { + commandRegistry.registerCommand(COMMAND) { layoutCommand } + } + + internal inner class KeyguardLayoutManagerCommand : Command { + override fun execute(pw: PrintWriter, args: List) { + val arg = args.getOrNull(0) + if (arg == null || arg.lowercase() == "help") { + help(pw) + return + } + + if (keyguardLayoutManager.transitionToLayout(arg)) { + pw.println("Transition succeeded!") + } else { + pw.println("Invalid argument! To see available layout ids, run:") + pw.println("$ adb shell cmd statusbar layout help") + } + } + + override fun help(pw: PrintWriter) { + pw.println("Usage: $ adb shell cmd statusbar layout ") + pw.println("Existing Layout Ids: ") + keyguardLayoutManager.layoutIdMap.forEach { entry -> pw.println("${entry.key}") } + } + } + + companion object { + internal const val COMMAND = "layout" + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListenerTest.kt new file mode 100644 index 0000000000000..145b2fdb000f4 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListenerTest.kt @@ -0,0 +1,88 @@ +/* + * 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.systemui.keyguard.ui.view.layout + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.commandline.Command +import com.android.systemui.statusbar.commandline.CommandRegistry +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.mockito.withArgCaptor +import java.io.PrintWriter +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.ArgumentMatchers.anyString +import org.mockito.Mock +import org.mockito.Mockito.atLeastOnce +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@RunWith(JUnit4::class) +@SmallTest +class KeyguardLayoutManagerCommandListenerTest : SysuiTestCase() { + private lateinit var keyguardLayoutManagerCommandListener: KeyguardLayoutManagerCommandListener + @Mock private lateinit var commandRegistry: CommandRegistry + @Mock private lateinit var keyguardLayoutManager: KeyguardLayoutManager + @Mock private lateinit var pw: PrintWriter + private lateinit var command: () -> Command + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + keyguardLayoutManagerCommandListener = + KeyguardLayoutManagerCommandListener( + commandRegistry, + keyguardLayoutManager, + ) + keyguardLayoutManagerCommandListener.start() + command = + withArgCaptor<() -> Command> { + verify(commandRegistry).registerCommand(eq("layout"), capture()) + } + } + + @Test + fun testHelp() { + command().execute(pw, listOf("help")) + verify(pw, atLeastOnce()).println(anyString()) + verify(keyguardLayoutManager, never()).transitionToLayout(anyString()) + } + + @Test + fun testBlank() { + command().execute(pw, listOf()) + verify(pw, atLeastOnce()).println(anyString()) + verify(keyguardLayoutManager, never()).transitionToLayout(anyString()) + } + + @Test + fun testValidArg() { + bindFakeIdMapToLayoutManager() + command().execute(pw, listOf("fake")) + verify(keyguardLayoutManager).transitionToLayout("fake") + } + + private fun bindFakeIdMapToLayoutManager() { + val map = mapOf("fake" to mock(LockscreenLayout::class.java)) + whenever(keyguardLayoutManager.layoutIdMap).thenReturn(map) + } +}