Merge "Rename layout -> blueprint and add "sections"" into udc-qpr-dev

This commit is contained in:
Aaron Liu
2023-08-03 19:09:50 +00:00
committed by Android (Google) Code Review
32 changed files with 1199 additions and 879 deletions

View File

@@ -25,13 +25,13 @@
android:orientation="horizontal"
android:gravity="center_vertical"
android:background="@drawable/keyguard_settings_popup_menu_background"
android:padding="12dp">
android:padding="@dimen/keyguard_settings_popup_menu_padding">
<ImageView
android:id="@+id/icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="8dp"
android:layout_width="@dimen/keyguard_settings_popup_menu_icon_height"
android:layout_height="@dimen/keyguard_settings_popup_menu_icon_width"
android:layout_marginEnd="@dimen/keyguard_settings_popup_menu_icon_end_margin"
android:tint="?androidprv:attr/materialColorOnSecondaryFixed"
android:importantForAccessibility="no"
tools:ignore="UseAppTint" />

View File

@@ -181,6 +181,10 @@ public class LockIconView extends FrameLayout implements Dumpable {
mLockIcon.setImageState(getLockIconState(mIconType, mAod), true);
}
public ImageView getLockIcon() {
return mLockIcon;
}
private void addLockIconImageView(Context context, AttributeSet attrs) {
mLockIcon = new ImageView(context, attrs);
mLockIcon.setId(R.id.lock_icon);

View File

@@ -417,7 +417,10 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
private void updateLockIconLocation() {
final float scaleFactor = mAuthController.getScaleFactor();
final int scaledPadding = (int) (mDefaultPaddingPx * scaleFactor);
if (!mFeatureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
if (mFeatureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
mView.getLockIcon().setPadding(scaledPadding, scaledPadding, scaledPadding,
scaledPadding);
} else {
if (mUdfpsSupported) {
mView.setCenterLocation(mAuthController.getUdfpsLocation(),
mAuthController.getUdfpsRadius(), scaledPadding);

View File

@@ -54,7 +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.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.log.dagger.MonitorLog;
import com.android.systemui.log.table.TableLogBuffer;
@@ -179,7 +179,7 @@ import javax.inject.Named;
GarbageMonitorModule.class,
KeyboardModule.class,
LetterboxModule.class,
LockscreenLayoutModule.class,
KeyguardBlueprintModule.class,
LogModule.class,
MediaProjectionModule.class,
MediaProjectionTaskSwitcherModule.class,

View File

@@ -26,27 +26,28 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.binder.KeyguardAmbientIndicationAreaViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardSettingsViewBinder
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.keyguard.ui.view.layout.KeyguardBlueprintCommandListener
import com.android.systemui.keyguard.ui.viewmodel.KeyguardAmbientIndicationViewModel
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.KeyguardBlueprintViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsMenuViewModel
import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
import com.android.systemui.shade.NotificationShadeWindowView
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import javax.inject.Inject
@@ -68,9 +69,8 @@ constructor(
private val notificationShadeWindowView: NotificationShadeWindowView,
private val featureFlags: FeatureFlags,
private val indicationController: KeyguardIndicationController,
private val keyguardLayoutManager: KeyguardLayoutManager,
private val keyguardLayoutManagerCommandListener: KeyguardLayoutManagerCommandListener,
private val keyguardQuickAffordancesCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel,
private val keyguardQuickAffordancesCombinedViewModel:
KeyguardQuickAffordancesCombinedViewModel,
private val falsingManager: FalsingManager,
private val vibratorHelper: VibratorHelper,
private val keyguardStateController: KeyguardStateController,
@@ -78,6 +78,8 @@ constructor(
private val activityStarter: ActivityStarter,
private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
private val chipbarCoordinator: ChipbarCoordinator,
private val keyguardBlueprintCommandListener: KeyguardBlueprintCommandListener,
private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel,
) : CoreStartable {
private var rootViewHandle: DisposableHandle? = null
@@ -100,8 +102,8 @@ constructor(
bindAmbientIndicationArea()
bindSettingsPopupMenu()
keyguardLayoutManager.layoutViews()
keyguardLayoutManagerCommandListener.start()
KeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel)
keyguardBlueprintCommandListener.start()
}
fun setupNotificationStackScrollLayout(legacyParent: ViewGroup) {
@@ -125,8 +127,6 @@ constructor(
leftShortcutHandle?.onConfigurationChanged()
rightShortcutHandle?.onConfigurationChanged()
ambientIndicationAreaHandle?.onConfigurationChanged()
keyguardLayoutManager.layoutViews()
}
fun bindIndicationArea() {
@@ -152,14 +152,15 @@ constructor(
private fun bindKeyguardRootView() {
rootViewHandle?.dispose()
rootViewHandle = KeyguardRootViewBinder.bind(
keyguardRootView,
keyguardRootViewModel,
featureFlags,
occludingAppDeviceEntryMessageViewModel,
chipbarCoordinator,
keyguardStateController,
)
rootViewHandle =
KeyguardRootViewBinder.bind(
keyguardRootView,
keyguardRootViewModel,
featureFlags,
occludingAppDeviceEntryMessageViewModel,
chipbarCoordinator,
keyguardStateController,
)
}
private fun bindLockIconView(legacyParent: ViewGroup) {

View File

@@ -0,0 +1,109 @@
/*
* 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.data.repository
import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint.Companion.DEFAULT
import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule
import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch
/**
* Manages blueprint changes for the lockscreen.
*
* To add a blueprint, create a class that implements LockscreenBlueprint and bind it to the map in
* the dagger module:
*
* A Blueprint determines how the layout should be constrained on a high level.
*
* A Section is a modular piece of code that implements the constraints. The blueprint uses the
* sections to define the constraints.
*
* @see KeyguardBlueprintModule
*/
@SysUISingleton
class KeyguardBlueprintRepository
@Inject
constructor(
configurationRepository: ConfigurationRepository,
blueprints: Set<@JvmSuppressWildcards KeyguardBlueprint>,
@Application private val applicationScope: CoroutineScope,
) {
private val blueprintIdMap: Map<String, KeyguardBlueprint> = blueprints.associateBy { it.id }
private val _blueprint: MutableSharedFlow<KeyguardBlueprint> = MutableSharedFlow(replay = 1)
val blueprint: Flow<KeyguardBlueprint> = _blueprint.asSharedFlow()
init {
applyBlueprint(blueprintIdMap[DEFAULT]!!)
applicationScope.launch {
configurationRepository.onAnyConfigurationChange.collect { refreshBlueprint() }
}
}
/**
* Emits the blueprint value to the collectors.
*
* @param blueprintId
* @return whether the transition has succeeded.
*/
fun applyBlueprint(blueprintId: String?): Boolean {
val blueprint = blueprintIdMap[blueprintId] ?: return false
applyBlueprint(blueprint)
return true
}
/** Emits the blueprint value to the collectors. */
fun applyBlueprint(blueprint: KeyguardBlueprint?) {
blueprint?.let { _blueprint.tryEmit(it) }
}
/** Re-emits the last emitted blueprint value if possible. */
fun refreshBlueprint() {
if (_blueprint.replayCache.isNotEmpty()) {
_blueprint.tryEmit(_blueprint.replayCache.last())
}
}
/** Prints all available blueprints to the PrintWriter. */
fun printBlueprints(pw: PrintWriter) {
blueprintIdMap.forEach { entry -> pw.println("${entry.key}") }
}
}
/** Determines the constraints for the ConstraintSet in the lockscreen root view. */
interface KeyguardBlueprint {
val id: String
fun apply(constraintSet: ConstraintSet)
}
/**
* Lower level modules that determine constraints for a particular section in the lockscreen root
* view.
*/
interface KeyguardSection {
fun apply(constraintSet: ConstraintSet)
}

View File

@@ -0,0 +1,44 @@
/*
* 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.domain.interactor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardBlueprintRepository
import javax.inject.Inject
@SysUISingleton
class KeyguardBlueprintInteractor
@Inject
constructor(private val keyguardBlueprintRepository: KeyguardBlueprintRepository) {
val blueprint = keyguardBlueprintRepository.blueprint
/**
* Transitions to a blueprint.
*
* @param blueprintId
* @return whether the transition has succeeded.
*/
fun transitionToBlueprint(blueprintId: String): Boolean {
return keyguardBlueprintRepository.applyBlueprint(blueprintId)
}
/** Re-emits the blueprint value to the collectors. */
fun refreshBlueprint() {
keyguardBlueprintRepository.refreshBlueprint()
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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.binder
import android.os.Trace
import android.util.Log
import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import kotlinx.coroutines.launch
class KeyguardBlueprintViewBinder {
companion object {
private const val TAG = "KeyguardBlueprintViewBinder"
fun bind(keyguardRootView: KeyguardRootView, viewModel: KeyguardBlueprintViewModel) {
keyguardRootView.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
launch {
viewModel.blueprint.collect { blueprint ->
Trace.beginSection("KeyguardBlueprintController#applyBlueprint")
Log.d(TAG, "applying blueprint: $blueprint")
ConstraintSet().apply {
clone(keyguardRootView)
val emptyLayout = ConstraintSet.Layout()
knownIds.forEach { getConstraint(it).layout.copyFrom(emptyLayout) }
blueprint?.apply(this)
applyTo(keyguardRootView)
}
Trace.endSection()
}
}
}
}
}
}
}

View File

@@ -44,11 +44,13 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardPreviewSmartspaceViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.keyguard.ui.view.layout.KeyguardLayoutManager
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewSmartspaceViewModel
@@ -90,12 +92,13 @@ constructor(
private val lockscreenSmartspaceController: LockscreenSmartspaceController,
private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
private val featureFlags: FeatureFlags,
private val keyguardLayoutManager: KeyguardLayoutManager,
private val falsingManager: FalsingManager,
private val vibratorHelper: VibratorHelper,
private val indicationController: KeyguardIndicationController,
private val keyguardRootViewModel: KeyguardRootViewModel,
@Assisted bundle: Bundle,
private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel,
private val keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
) {
val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN)
@@ -129,17 +132,17 @@ constructor(
if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
keyguardRootViewModel.enablePreviewMode(
initiallySelectedSlotId =
bundle.getString(
KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID,
),
bundle.getString(
KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID,
),
shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
)
} else {
bottomAreaViewModel.enablePreviewMode(
initiallySelectedSlotId =
bundle.getString(
KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID,
),
bundle.getString(
KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID,
),
shouldHighlightSelectedAffordance = shouldHighlightSelectedAffordance,
)
}
@@ -169,7 +172,8 @@ constructor(
),
)
setupShortcuts(keyguardRootView)
keyguardLayoutManager.layoutViews(keyguardRootView)
KeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel)
keyguardBlueprintInteractor.refreshBlueprint()
} else {
setUpBottomArea(rootView)
}
@@ -309,7 +313,7 @@ constructor(
false,
) as KeyguardBottomAreaView
bottomAreaView.init(
viewModel = bottomAreaViewModel,
viewModel = bottomAreaViewModel,
)
parentView.addView(
bottomAreaView,

View File

@@ -19,10 +19,10 @@ package com.android.systemui.keyguard.ui.view
import android.content.Context
import android.util.AttributeSet
import androidx.constraintlayout.widget.ConstraintLayout
import android.view.LayoutInflater
import android.view.View
import android.widget.ImageView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.res.ResourcesCompat
import com.android.keyguard.LockIconView
import com.android.systemui.R
@@ -62,8 +62,9 @@ class KeyguardRootView(
}
private fun addLeftShortcut() {
val view = LaunchableImageView(context, attrs)
.apply {
val padding = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_padding)
val view =
LaunchableImageView(context, attrs).apply {
id = R.id.start_button
scaleType = ImageView.ScaleType.FIT_CENTER
background =
@@ -79,13 +80,15 @@ class KeyguardRootView(
context.theme
)
visibility = View.INVISIBLE
setPadding(padding, padding, padding, padding)
}
addView(view)
}
private fun addRightShortcut() {
val view = LaunchableImageView(context, attrs)
.apply {
val padding = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_padding)
val view =
LaunchableImageView(context, attrs).apply {
id = R.id.end_button
scaleType = ImageView.ScaleType.FIT_CENTER
background =
@@ -101,20 +104,19 @@ class KeyguardRootView(
context.theme
)
visibility = View.INVISIBLE
setPadding(padding, padding, padding, padding)
}
addView(view)
}
private fun addSettingsPopupMenu() {
val view = LayoutInflater.from(context).inflate(
R.layout.keyguard_settings_popup_menu,
this,
false
)
.apply {
id = R.id.keyguard_settings_button
visibility = GONE
}
val view =
LayoutInflater.from(context)
.inflate(R.layout.keyguard_settings_popup_menu, this, false)
.apply {
id = R.id.keyguard_settings_button
visibility = GONE
}
addView(view)
}
}

View File

@@ -1,286 +0,0 @@
/*
* 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.ViewGroup.MarginLayoutParams
import android.view.WindowManager
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
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.LEFT
import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.RIGHT
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.core.view.setPadding
import androidx.core.view.updateLayoutParams
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.R
import com.android.systemui.animation.view.LaunchableLinearLayout
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<View>(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<View>(R.id.lock_icon_view) ?: return
val lockIcon = lockIconView.findViewById<View>(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)
}
}
override fun layoutShortcuts(rootView: KeyguardRootView) {
val leftShortcut = rootView.findViewById<View>(R.id.start_button) ?: return
val rightShortcut = rootView.findViewById<View>(R.id.end_button) ?: return
val width =
context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width)
val height =
context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height)
val horizontalOffsetMargin =
context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_horizontal_offset)
val verticalOffsetMargin =
context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_vertical_offset)
val padding =
context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_padding)
leftShortcut.apply {
updateLayoutParams<MarginLayoutParams> {
marginStart = horizontalOffsetMargin
bottomMargin = verticalOffsetMargin
}
setPadding(padding)
}
rightShortcut.apply {
updateLayoutParams<MarginLayoutParams> {
marginEnd = horizontalOffsetMargin
bottomMargin = verticalOffsetMargin
}
setPadding(padding)
}
rootView.getConstraintSet().apply {
constrainWidth(leftShortcut.id, width)
constrainHeight(leftShortcut.id, height)
connect(leftShortcut.id, LEFT, PARENT_ID, LEFT)
connect(leftShortcut.id, BOTTOM, PARENT_ID, BOTTOM)
constrainWidth(rightShortcut.id, width)
constrainHeight(rightShortcut.id, height)
connect(rightShortcut.id, RIGHT, PARENT_ID, RIGHT)
connect(rightShortcut.id, BOTTOM, PARENT_ID, BOTTOM)
applyTo(rootView)
}
}
override fun layoutAmbientIndicationArea(rootView: KeyguardRootView) {
val ambientIndicationContainer =
rootView.findViewById<View>(R.id.ambient_indication_container) ?: return
val lockIconView = rootView.findViewById<View>(R.id.lock_icon_view) ?: return
val indicationArea = rootView.findViewById<View>(R.id.keyguard_indication_area) ?: return
rootView.getConstraintSet().apply {
constrainWidth(ambientIndicationContainer.id, MATCH_PARENT)
if (keyguardUpdateMonitor.isUdfpsSupported) {
//constrain below udfps and above indication area
constrainHeight(ambientIndicationContainer.id, MATCH_CONSTRAINT)
connect(ambientIndicationContainer.id, TOP, lockIconView.id, BOTTOM)
connect(ambientIndicationContainer.id, BOTTOM, indicationArea.id, TOP)
connect(ambientIndicationContainer.id, LEFT, PARENT_ID, LEFT)
connect(ambientIndicationContainer.id, RIGHT, PARENT_ID, RIGHT)
} else {
//constrain above lock icon
constrainHeight(ambientIndicationContainer.id, WRAP_CONTENT)
connect(ambientIndicationContainer.id, BOTTOM, lockIconView.id, TOP)
connect(ambientIndicationContainer.id, LEFT, PARENT_ID, LEFT)
connect(ambientIndicationContainer.id, RIGHT, PARENT_ID, RIGHT)
}
applyTo(rootView)
}
}
override fun layoutSettingsPopupMenu(rootView: KeyguardRootView) {
val popupMenu =
rootView.findViewById<LaunchableLinearLayout>(R.id.keyguard_settings_button) ?: return
val icon = popupMenu.findViewById<ImageView>(R.id.icon) ?: return
val textView = popupMenu.findViewById<TextView>(R.id.text) ?: return
val horizontalOffsetMargin =
context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_horizontal_offset)
icon.updateLayoutParams<LinearLayout.LayoutParams> {
height =
context
.resources
.getDimensionPixelSize(R.dimen.keyguard_settings_popup_menu_icon_height)
width =
context
.resources
.getDimensionPixelSize(R.dimen.keyguard_settings_popup_menu_icon_width)
marginEnd =
context
.resources
.getDimensionPixelSize(R.dimen.keyguard_settings_popup_menu_icon_end_margin)
}
textView.updateLayoutParams<LinearLayout.LayoutParams> {
height = WRAP_CONTENT
width = WRAP_CONTENT
}
popupMenu.updateLayoutParams<MarginLayoutParams> {
bottomMargin =
context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_vertical_offset)
marginStart = horizontalOffsetMargin
marginEnd = horizontalOffsetMargin
}
popupMenu.setPadding(
context.resources.getDimensionPixelSize(R.dimen.keyguard_settings_popup_menu_padding)
)
rootView.getConstraintSet().apply {
constrainWidth(popupMenu.id, WRAP_CONTENT)
constrainHeight(popupMenu.id, WRAP_CONTENT)
constrainMinHeight(
popupMenu.id,
context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height)
)
connect(popupMenu.id, LEFT, PARENT_ID, LEFT)
connect(popupMenu.id, RIGHT, PARENT_ID, RIGHT)
connect(popupMenu.id, BOTTOM, PARENT_ID, BOTTOM)
applyTo(rootView)
}
}
private fun Int.dp(): Int {
return context.resources.getDimensionPixelSize(this)
}
private fun ConstraintLayout.getConstraintSet(): ConstraintSet =
ConstraintSet().also {
it.clone(this)
}
companion object {
const val DEFAULT = "default"
}
}

View File

@@ -16,17 +16,20 @@
package com.android.systemui.keyguard.ui.view.layout
import com.android.systemui.keyguard.data.repository.KeyguardBlueprintRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
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 <LayoutId> */
class KeyguardLayoutManagerCommandListener
/** Uses $ adb shell cmd statusbar blueprint <BlueprintId> */
class KeyguardBlueprintCommandListener
@Inject
constructor(
private val commandRegistry: CommandRegistry,
private val keyguardLayoutManager: KeyguardLayoutManager
private val keyguardBlueprintRepository: KeyguardBlueprintRepository,
private val keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
) {
private val layoutCommand = KeyguardLayoutManagerCommand()
@@ -42,22 +45,22 @@ constructor(
return
}
if (keyguardLayoutManager.transitionToLayout(arg)) {
if (keyguardBlueprintInteractor.transitionToBlueprint(arg)) {
pw.println("Transition succeeded!")
} else {
pw.println("Invalid argument! To see available layout ids, run:")
pw.println("$ adb shell cmd statusbar layout help")
pw.println("Invalid argument! To see available blueprint ids, run:")
pw.println("$ adb shell cmd statusbar blueprint help")
}
}
override fun help(pw: PrintWriter) {
pw.println("Usage: $ adb shell cmd statusbar layout <layoutId>")
pw.println("Existing Layout Ids: ")
keyguardLayoutManager.layoutIdMap.forEach { entry -> pw.println("${entry.key}") }
pw.println("Usage: $ adb shell cmd statusbar blueprint <blueprintId>")
pw.println("Existing Blueprint Ids: ")
keyguardBlueprintRepository.printBlueprints(pw)
}
}
companion object {
internal const val COMMAND = "layout"
internal const val COMMAND = "blueprint"
}
}

View File

@@ -1,97 +0,0 @@
/*
* 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<String, LockscreenLayout> = 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(rootView: KeyguardRootView = keyguardRootView) {
layout?.layoutViews(rootView)
}
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)
layoutShortcuts(rootView)
layoutAmbientIndicationArea(rootView)
layoutSettingsPopupMenu(rootView)
}
fun layoutIndicationArea(rootView: KeyguardRootView)
fun layoutLockIcon(rootView: KeyguardRootView)
fun layoutShortcuts(rootView: KeyguardRootView)
fun layoutAmbientIndicationArea(rootView: KeyguardRootView)
fun layoutSettingsPopupMenu(rootView: KeyguardRootView)
}

View File

@@ -1,305 +0,0 @@
/*
* 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
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.WindowManager
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
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.LEFT
import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.RIGHT
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.core.view.setPadding
import androidx.core.view.updateLayoutParams
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.R
import com.android.systemui.animation.view.LaunchableLinearLayout
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 ShortcutsBesideUdfpsLockscreenLayout
@Inject
constructor(
private val authController: AuthController,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val windowManager: WindowManager,
private val context: Context,
) : LockscreenLayout {
override val id: String = SHORTCUTS_BESIDE_UDFPS
override fun layoutIndicationArea(rootView: KeyguardRootView) {
val indicationArea = rootView.findViewById<View>(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<View>(R.id.lock_icon_view) ?: return
val lockIcon = lockIconView.findViewById<View>(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)
}
}
override fun layoutShortcuts(rootView: KeyguardRootView) {
val leftShortcut = rootView.findViewById<View>(R.id.start_button) ?: return
val rightShortcut = rootView.findViewById<View>(R.id.end_button) ?: return
val lockIconView = rootView.findViewById<View>(R.id.lock_icon_view) ?: return
val udfpsSupported = keyguardUpdateMonitor.isUdfpsSupported
val width =
context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width)
val height =
context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height)
val horizontalOffsetMargin =
context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_horizontal_offset)
val verticalOffsetMargin =
context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_vertical_offset)
val padding =
context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_padding)
if (!udfpsSupported) {
leftShortcut.apply {
updateLayoutParams<ViewGroup.MarginLayoutParams> {
marginStart = horizontalOffsetMargin
bottomMargin = verticalOffsetMargin
}
setPadding(padding)
}
rightShortcut.apply {
updateLayoutParams<ViewGroup.MarginLayoutParams> {
marginEnd = horizontalOffsetMargin
bottomMargin = verticalOffsetMargin
}
setPadding(padding)
}
}
rootView.getConstraintSet().apply {
if (udfpsSupported) {
constrainWidth(leftShortcut.id, width)
constrainHeight(leftShortcut.id, height)
connect(leftShortcut.id, LEFT, PARENT_ID, LEFT)
connect(leftShortcut.id, RIGHT, lockIconView.id, LEFT)
connect(leftShortcut.id, TOP, lockIconView.id, TOP)
connect(leftShortcut.id, BOTTOM, lockIconView.id, BOTTOM)
constrainWidth(rightShortcut.id, width)
constrainHeight(rightShortcut.id, height)
connect(rightShortcut.id, RIGHT, PARENT_ID, RIGHT)
connect(rightShortcut.id, LEFT, lockIconView.id, RIGHT)
connect(rightShortcut.id, TOP, lockIconView.id, TOP)
connect(rightShortcut.id, BOTTOM, lockIconView.id, BOTTOM)
} else {
constrainWidth(leftShortcut.id, width)
constrainHeight(leftShortcut.id, height)
connect(leftShortcut.id, LEFT, PARENT_ID, LEFT)
connect(leftShortcut.id, BOTTOM, PARENT_ID, BOTTOM)
constrainWidth(rightShortcut.id, width)
constrainHeight(rightShortcut.id, height)
connect(rightShortcut.id, RIGHT, PARENT_ID, RIGHT)
connect(rightShortcut.id, BOTTOM, PARENT_ID, BOTTOM)
}
applyTo(rootView)
}
}
override fun layoutAmbientIndicationArea(rootView: KeyguardRootView) {
val ambientIndicationContainer =
rootView.findViewById<View>(R.id.ambient_indication_container) ?: return
val lockIconView = rootView.findViewById<View>(R.id.lock_icon_view) ?: return
val indicationArea = rootView.findViewById<View>(R.id.keyguard_indication_area) ?: return
rootView.getConstraintSet().apply {
constrainWidth(ambientIndicationContainer.id, MATCH_PARENT)
if (keyguardUpdateMonitor.isUdfpsSupported) {
//constrain below udfps and above indication area
constrainHeight(ambientIndicationContainer.id, MATCH_CONSTRAINT)
connect(ambientIndicationContainer.id, TOP, lockIconView.id, BOTTOM)
connect(ambientIndicationContainer.id, BOTTOM, indicationArea.id, TOP)
connect(ambientIndicationContainer.id, LEFT, PARENT_ID, LEFT)
connect(ambientIndicationContainer.id, RIGHT, PARENT_ID, RIGHT)
} else {
//constrain above lock icon
constrainHeight(ambientIndicationContainer.id, WRAP_CONTENT)
connect(ambientIndicationContainer.id, BOTTOM, lockIconView.id, TOP)
connect(ambientIndicationContainer.id, LEFT, PARENT_ID, LEFT)
connect(ambientIndicationContainer.id, RIGHT, PARENT_ID, RIGHT)
}
applyTo(rootView)
}
}
override fun layoutSettingsPopupMenu(rootView: KeyguardRootView) {
val popupMenu =
rootView.findViewById<LaunchableLinearLayout>(R.id.keyguard_settings_button) ?: return
val icon = popupMenu.findViewById<ImageView>(R.id.icon) ?: return
val textView = popupMenu.findViewById<TextView>(R.id.text) ?: return
val horizontalOffsetMargin =
context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_horizontal_offset)
icon.updateLayoutParams<LinearLayout.LayoutParams> {
height =
context
.resources
.getDimensionPixelSize(R.dimen.keyguard_settings_popup_menu_icon_height)
width =
context
.resources
.getDimensionPixelSize(R.dimen.keyguard_settings_popup_menu_icon_width)
marginEnd =
context
.resources
.getDimensionPixelSize(R.dimen.keyguard_settings_popup_menu_icon_end_margin)
}
textView.updateLayoutParams<LinearLayout.LayoutParams> {
height = WRAP_CONTENT
width = WRAP_CONTENT
}
popupMenu.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin =
context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_vertical_offset)
marginStart = horizontalOffsetMargin
marginEnd = horizontalOffsetMargin
}
popupMenu.setPadding(
context.resources.getDimensionPixelSize(R.dimen.keyguard_settings_popup_menu_padding)
)
rootView.getConstraintSet().apply {
constrainWidth(popupMenu.id, WRAP_CONTENT)
constrainHeight(popupMenu.id, WRAP_CONTENT)
constrainMinHeight(
popupMenu.id,
context.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height)
)
connect(popupMenu.id, LEFT, PARENT_ID, LEFT)
connect(popupMenu.id, RIGHT, PARENT_ID, RIGHT)
connect(popupMenu.id, BOTTOM, PARENT_ID, BOTTOM)
applyTo(rootView)
}
}
private fun Int.dp(): Int {
return context.resources.getDimensionPixelSize(this)
}
private fun ConstraintLayout.getConstraintSet(): ConstraintSet =
ConstraintSet().also {
it.clone(this)
}
companion object {
const val SHORTCUTS_BESIDE_UDFPS = "shortcutsBesideUdfps"
}
}

View File

@@ -0,0 +1,59 @@
/*
* 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.blueprints
import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
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
@JvmSuppressWildcards
class DefaultKeyguardBlueprint
@Inject
constructor(
private val defaultIndicationAreaSection: DefaultIndicationAreaSection,
private val defaultLockIconSection: DefaultLockIconSection,
private val defaultShortcutsSection: DefaultShortcutsSection,
private val defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
private val defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
) : KeyguardBlueprint {
override val id: String = DEFAULT
override fun apply(constraintSet: ConstraintSet) {
defaultIndicationAreaSection.apply(constraintSet)
defaultLockIconSection.apply(constraintSet)
defaultShortcutsSection.apply(constraintSet)
defaultAmbientIndicationAreaSection.apply(constraintSet)
defaultSettingsPopupMenuSection.apply(constraintSet)
}
companion object {
const val DEFAULT = "default"
}
}

View File

@@ -15,23 +15,24 @@
*
*/
package com.android.systemui.keyguard.ui.view.layout
package com.android.systemui.keyguard.ui.view.layout.blueprints
import com.android.systemui.keyguard.data.repository.KeyguardBlueprint
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
@Module
abstract class LockscreenLayoutModule {
abstract class KeyguardBlueprintModule {
@Binds
@IntoSet
abstract fun bindDefaultLayout(
defaultLockscreenLayout: DefaultLockscreenLayout
): LockscreenLayout
abstract fun bindDefaultBlueprint(
defaultLockscreenBlueprint: DefaultKeyguardBlueprint
): KeyguardBlueprint
@Binds
@IntoSet
abstract fun bindShortcutsBesideUdfpsLockscreenLayout(
shortcutsBesideUdfpsLockscreenLayout: ShortcutsBesideUdfpsLockscreenLayout
): LockscreenLayout
abstract fun bindShortcutsBesideUdfpsLockscreenBlueprint(
shortcutsBesideUdfpsLockscreenBlueprint: ShortcutsBesideUdfpsKeyguardBlueprint
): KeyguardBlueprint
}

View File

@@ -0,0 +1,62 @@
/*
* 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.blueprints
import androidx.constraintlayout.widget.ConstraintSet
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.sections.AlignShortcutsToUdfpsSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
import javax.inject.Inject
/** Vertically aligns the shortcuts with the udfps. */
@SysUISingleton
class ShortcutsBesideUdfpsKeyguardBlueprint
@Inject
constructor(
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val defaultIndicationAreaSection: DefaultIndicationAreaSection,
private val defaultLockIconSection: DefaultLockIconSection,
private val defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
private val defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
private val alignShortcutsToUdfpsSection: AlignShortcutsToUdfpsSection,
private val defaultShortcutsSection: DefaultShortcutsSection,
) : KeyguardBlueprint {
override val id: String = SHORTCUTS_BESIDE_UDFPS
override fun apply(constraintSet: ConstraintSet) {
defaultIndicationAreaSection.apply(constraintSet)
defaultLockIconSection.apply(constraintSet)
defaultAmbientIndicationAreaSection.apply(constraintSet)
defaultSettingsPopupMenuSection.apply(constraintSet)
if (keyguardUpdateMonitor.isUdfpsSupported) {
alignShortcutsToUdfpsSection.apply(constraintSet)
} else {
defaultShortcutsSection.apply(constraintSet)
}
}
companion object {
const val SHORTCUTS_BESIDE_UDFPS = "shortcutsBesideUdfps"
}
}

View File

@@ -0,0 +1,54 @@
/*
* 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.sections
import android.content.res.Resources
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.LEFT
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.RIGHT
import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.data.repository.KeyguardSection
import javax.inject.Inject
class AlignShortcutsToUdfpsSection @Inject constructor(@Main private val resources: Resources) :
KeyguardSection {
override fun apply(constraintSet: ConstraintSet) {
val width = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width)
val height = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height)
constraintSet.apply {
constrainWidth(R.id.start_button, width)
constrainHeight(R.id.start_button, height)
connect(R.id.start_button, LEFT, PARENT_ID, LEFT)
connect(R.id.start_button, RIGHT, R.id.lock_icon_view, LEFT)
connect(R.id.start_button, TOP, R.id.lock_icon_view, TOP)
connect(R.id.start_button, BOTTOM, R.id.lock_icon_view, BOTTOM)
constrainWidth(R.id.end_button, width)
constrainHeight(R.id.end_button, height)
connect(R.id.end_button, RIGHT, PARENT_ID, RIGHT)
connect(R.id.end_button, LEFT, R.id.lock_icon_view, RIGHT)
connect(R.id.end_button, TOP, R.id.lock_icon_view, TOP)
connect(R.id.end_button, BOTTOM, R.id.lock_icon_view, BOTTOM)
}
}
}

View File

@@ -0,0 +1,62 @@
/*
* 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.sections
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.END
import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.R
import com.android.systemui.keyguard.data.repository.KeyguardSection
import javax.inject.Inject
class DefaultAmbientIndicationAreaSection
@Inject
constructor(private val keyguardUpdateMonitor: KeyguardUpdateMonitor) : KeyguardSection {
override fun apply(constraintSet: ConstraintSet) {
constraintSet.apply {
constrainWidth(R.id.ambient_indication_container, MATCH_PARENT)
if (keyguardUpdateMonitor.isUdfpsSupported) {
// constrain below udfps and above indication area
constrainHeight(R.id.ambient_indication_container, MATCH_CONSTRAINT)
connect(R.id.ambient_indication_container, TOP, R.id.lock_icon_view, BOTTOM)
connect(
R.id.ambient_indication_container,
BOTTOM,
R.id.keyguard_indication_area,
TOP
)
connect(R.id.ambient_indication_container, START, PARENT_ID, START)
connect(R.id.ambient_indication_container, END, PARENT_ID, END)
} else {
// constrain above lock icon
constrainHeight(R.id.ambient_indication_container, WRAP_CONTENT)
connect(R.id.ambient_indication_container, BOTTOM, R.id.lock_icon_view, TOP)
connect(R.id.ambient_indication_container, START, PARENT_ID, START)
connect(R.id.ambient_indication_container, END, PARENT_ID, END)
}
}
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.sections
import android.content.Context
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.R
import com.android.systemui.keyguard.data.repository.KeyguardSection
import javax.inject.Inject
class DefaultIndicationAreaSection @Inject constructor(private val context: Context) :
KeyguardSection {
private val indicationAreaViewId = R.id.keyguard_indication_area
override fun apply(constraintSet: ConstraintSet) {
constraintSet.apply {
constrainWidth(indicationAreaViewId, ViewGroup.LayoutParams.MATCH_PARENT)
constrainHeight(indicationAreaViewId, ViewGroup.LayoutParams.WRAP_CONTENT)
connect(
indicationAreaViewId,
ConstraintSet.BOTTOM,
ConstraintSet.PARENT_ID,
ConstraintSet.BOTTOM,
context.resources.getDimensionPixelSize(R.dimen.keyguard_indication_margin_bottom)
)
connect(
indicationAreaViewId,
ConstraintSet.START,
ConstraintSet.PARENT_ID,
ConstraintSet.START
)
connect(
indicationAreaViewId,
ConstraintSet.END,
ConstraintSet.PARENT_ID,
ConstraintSet.END
)
}
}
}

View File

@@ -0,0 +1,116 @@
/*
* 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.sections
import android.content.Context
import android.graphics.Point
import android.graphics.Rect
import android.util.DisplayMetrics
import android.view.WindowManager
import androidx.annotation.VisibleForTesting
import androidx.constraintlayout.widget.ConstraintSet
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.R
import com.android.systemui.biometrics.AuthController
import com.android.systemui.keyguard.data.repository.KeyguardSection
import javax.inject.Inject
class DefaultLockIconSection
@Inject
constructor(
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val authController: AuthController,
private val windowManager: WindowManager,
private val context: Context,
) : KeyguardSection {
private val lockIconViewId = R.id.lock_icon_view
override fun apply(constraintSet: ConstraintSet) {
val isUdfpsSupported = keyguardUpdateMonitor.isUdfpsSupported
val scaleFactor: Float = authController.scaleFactor
val mBottomPaddingPx =
context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
val mDefaultPaddingPx = context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding)
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,
constraintSet
)
}
} else {
centerLockIcon(
Point(
(widthPixels / 2).toInt(),
(heightPixels - ((mBottomPaddingPx + lockIconRadiusPx) * scaleFactor)).toInt()
),
lockIconRadiusPx * scaleFactor,
scaledPadding,
constraintSet,
)
}
}
@VisibleForTesting
internal fun centerLockIcon(
center: Point,
radius: Float,
drawablePadding: Int,
constraintSet: ConstraintSet
) {
val sensorRect =
Rect().apply {
set(
center.x - radius.toInt(),
center.y - radius.toInt(),
center.x + radius.toInt(),
center.y + radius.toInt(),
)
}
constraintSet.apply {
constrainWidth(lockIconViewId, sensorRect.right - sensorRect.left)
constrainHeight(lockIconViewId, sensorRect.bottom - sensorRect.top)
connect(
lockIconViewId,
ConstraintSet.TOP,
ConstraintSet.PARENT_ID,
ConstraintSet.TOP,
sensorRect.top
)
connect(
lockIconViewId,
ConstraintSet.START,
ConstraintSet.PARENT_ID,
ConstraintSet.START,
sensorRect.left
)
}
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.sections
import android.content.res.Resources
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.WRAP_CONTENT
import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.data.repository.KeyguardSection
import javax.inject.Inject
class DefaultSettingsPopupMenuSection @Inject constructor(@Main private val resources: Resources) :
KeyguardSection {
override fun apply(constraintSet: ConstraintSet) {
val horizontalOffsetMargin =
resources.getDimensionPixelSize(R.dimen.keyguard_affordance_horizontal_offset)
constraintSet.apply {
constrainWidth(R.id.keyguard_settings_button, WRAP_CONTENT)
constrainHeight(R.id.keyguard_settings_button, WRAP_CONTENT)
constrainMinHeight(
R.id.keyguard_settings_button,
resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height)
)
connect(R.id.keyguard_settings_button, START, PARENT_ID, START, horizontalOffsetMargin)
connect(R.id.keyguard_settings_button, END, PARENT_ID, END, horizontalOffsetMargin)
connect(
R.id.keyguard_settings_button,
BOTTOM,
PARENT_ID,
BOTTOM,
resources.getDimensionPixelSize(R.dimen.keyguard_affordance_vertical_offset)
)
}
}
}

View File

@@ -0,0 +1,53 @@
/*
* 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.sections
import android.content.res.Resources
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.LEFT
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.RIGHT
import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.data.repository.KeyguardSection
import javax.inject.Inject
class DefaultShortcutsSection @Inject constructor(@Main private val resources: Resources) :
KeyguardSection {
override fun apply(constraintSet: ConstraintSet) {
val width = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width)
val height = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height)
val horizontalOffsetMargin =
resources.getDimensionPixelSize(R.dimen.keyguard_affordance_horizontal_offset)
val verticalOffsetMargin =
resources.getDimensionPixelSize(R.dimen.keyguard_affordance_vertical_offset)
constraintSet.apply {
constrainWidth(R.id.start_button, width)
constrainHeight(R.id.start_button, height)
connect(R.id.start_button, LEFT, PARENT_ID, LEFT, horizontalOffsetMargin)
connect(R.id.start_button, BOTTOM, PARENT_ID, BOTTOM, verticalOffsetMargin)
constrainWidth(R.id.end_button, width)
constrainHeight(R.id.end_button, height)
connect(R.id.end_button, RIGHT, PARENT_ID, RIGHT, horizontalOffsetMargin)
connect(R.id.end_button, BOTTOM, PARENT_ID, BOTTOM, verticalOffsetMargin)
}
}
}

View File

@@ -0,0 +1,29 @@
/*
* 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.viewmodel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import javax.inject.Inject
@SysUISingleton
class KeyguardBlueprintViewModel
@Inject
constructor(keyguardBlueprintInteractor: KeyguardBlueprintInteractor) {
val blueprint = keyguardBlueprintInteractor.blueprint
}

View File

@@ -0,0 +1,104 @@
/*
* 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.data.repository
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint.Companion.DEFAULT
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
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.MockitoAnnotations
@RunWith(JUnit4::class)
@SmallTest
class KeyguardBlueprintRepositoryTest : SysuiTestCase() {
private lateinit var underTest: KeyguardBlueprintRepository
@Mock lateinit var configurationRepository: ConfigurationRepository
@Mock lateinit var defaultLockscreenBlueprint: DefaultKeyguardBlueprint
private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
private val configurationFlow = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
whenever(defaultLockscreenBlueprint.id).thenReturn(DEFAULT)
whenever(configurationRepository.onAnyConfigurationChange).thenReturn(configurationFlow)
underTest =
KeyguardBlueprintRepository(
configurationRepository,
setOf(defaultLockscreenBlueprint),
testScope.backgroundScope,
)
}
@Test
fun testApplyBlueprint_DefaultLayout() {
testScope.runTest {
val blueprint by collectLastValue(underTest.blueprint)
runCurrent()
underTest.applyBlueprint(defaultLockscreenBlueprint)
runCurrent()
assertThat(blueprint).isEqualTo(defaultLockscreenBlueprint)
}
}
@Test
fun testConfigurationChange() {
testScope.runTest {
val blueprint by collectLastValue(underTest.blueprint)
runCurrent()
configurationFlow.tryEmit(Unit)
runCurrent()
assertThat(blueprint).isEqualTo(defaultLockscreenBlueprint)
}
}
@Test
fun testRefreshBlueprint() {
testScope.runTest {
val blueprint by collectLastValue(underTest.blueprint)
runCurrent()
underTest.refreshBlueprint()
runCurrent()
assertThat(blueprint).isEqualTo(defaultLockscreenBlueprint)
}
}
@Test
fun testTransitionToLayout_validId() {
assertThat(underTest.applyBlueprint(DEFAULT)).isTrue()
}
@Test
fun testTransitionToLayout_invalidId() {
assertThat(underTest.applyBlueprint("abc")).isFalse()
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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.domain.interactor
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.KeyguardBlueprintRepository
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
class KeyguardBlueprintInteractorTest : SysuiTestCase() {
private lateinit var underTest: KeyguardBlueprintInteractor
@Mock private lateinit var keyguardBlueprintRepository: KeyguardBlueprintRepository
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
underTest = KeyguardBlueprintInteractor(keyguardBlueprintRepository)
}
@Test
fun testRefreshBlueprint() {
underTest.refreshBlueprint()
verify(keyguardBlueprintRepository).refreshBlueprint()
}
@Test
fun testTransitionToBlueprint() {
underTest.transitionToBlueprint("abc")
verify(keyguardBlueprintRepository).applyBlueprint("abc")
}
}

View File

@@ -18,10 +18,11 @@ package com.android.systemui.keyguard.ui.view.layout
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.KeyguardBlueprintRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
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
@@ -31,32 +32,33 @@ 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
class KeyguardBlueprintCommandListenerTest : SysuiTestCase() {
private lateinit var keyguardBlueprintCommandListener: KeyguardBlueprintCommandListener
@Mock private lateinit var commandRegistry: CommandRegistry
@Mock private lateinit var keyguardLayoutManager: KeyguardLayoutManager
@Mock private lateinit var keyguardBlueprintRepository: KeyguardBlueprintRepository
@Mock private lateinit var keyguardBlueprintInteractor: KeyguardBlueprintInteractor
@Mock private lateinit var pw: PrintWriter
private lateinit var command: () -> Command
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
keyguardLayoutManagerCommandListener =
KeyguardLayoutManagerCommandListener(
keyguardBlueprintCommandListener =
KeyguardBlueprintCommandListener(
commandRegistry,
keyguardLayoutManager,
keyguardBlueprintRepository,
keyguardBlueprintInteractor,
)
keyguardLayoutManagerCommandListener.start()
keyguardBlueprintCommandListener.start()
command =
withArgCaptor<() -> Command> {
verify(commandRegistry).registerCommand(eq("layout"), capture())
verify(commandRegistry).registerCommand(eq("blueprint"), capture())
}
}
@@ -64,25 +66,19 @@ class KeyguardLayoutManagerCommandListenerTest : SysuiTestCase() {
fun testHelp() {
command().execute(pw, listOf("help"))
verify(pw, atLeastOnce()).println(anyString())
verify(keyguardLayoutManager, never()).transitionToLayout(anyString())
verify(keyguardBlueprintInteractor, never()).transitionToBlueprint(anyString())
}
@Test
fun testBlank() {
command().execute(pw, listOf())
verify(pw, atLeastOnce()).println(anyString())
verify(keyguardLayoutManager, never()).transitionToLayout(anyString())
verify(keyguardBlueprintInteractor, never()).transitionToBlueprint(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)
verify(keyguardBlueprintInteractor).transitionToBlueprint("fake")
}
}

View File

@@ -1,69 +0,0 @@
/*
* 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()
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.blueprints
import androidx.constraintlayout.widget.ConstraintSet
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.sections.DefaultAmbientIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
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 DefaultKeyguardBlueprintTest : SysuiTestCase() {
private lateinit var underTest: DefaultKeyguardBlueprint
private lateinit var rootView: KeyguardRootView
@Mock private lateinit var defaultIndicationAreaSection: DefaultIndicationAreaSection
@Mock private lateinit var defaultLockIconSection: DefaultLockIconSection
@Mock private lateinit var defaultShortcutsSection: DefaultShortcutsSection
@Mock
private lateinit var defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection
@Mock private lateinit var defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
rootView = KeyguardRootView(context, null)
underTest =
DefaultKeyguardBlueprint(
defaultIndicationAreaSection,
defaultLockIconSection,
defaultShortcutsSection,
defaultAmbientIndicationAreaSection,
defaultSettingsPopupMenuSection,
)
}
@Test
fun apply() {
val cs = ConstraintSet()
underTest.apply(cs)
verify(defaultIndicationAreaSection).apply(cs)
verify(defaultLockIconSection).apply(cs)
verify(defaultShortcutsSection).apply(cs)
verify(defaultAmbientIndicationAreaSection).apply(cs)
verify(defaultSettingsPopupMenuSection).apply(cs)
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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.sections
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
@SmallTest
class DefaultIndicationAreaSectionTest : SysuiTestCase() {
private val underTest = DefaultIndicationAreaSection(context)
@Test
fun apply() {
val cs = ConstraintSet()
underTest.apply(cs)
val constraint = cs.getConstraint(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)
}
}

View File

@@ -15,10 +15,9 @@
*
*/
package com.android.systemui.keyguard.ui.view.layout
package com.android.systemui.keyguard.ui.view.layout.sections
import android.graphics.Point
import android.view.ViewGroup
import android.view.WindowManager
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
@@ -26,9 +25,6 @@ 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.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -40,45 +36,36 @@ import org.mockito.MockitoAnnotations
@RunWith(JUnit4::class)
@SmallTest
class DefaultLockscreenLayoutTest : SysuiTestCase() {
private lateinit var defaultLockscreenLayout: DefaultLockscreenLayout
private lateinit var rootView: KeyguardRootView
class DefaultLockIconSectionTest : SysuiTestCase() {
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var authController: AuthController
@Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var windowManager: WindowManager
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
private lateinit var underTest: DefaultLockIconSection
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
rootView = KeyguardRootView(context, null)
defaultLockscreenLayout =
DefaultLockscreenLayout(authController, keyguardUpdateMonitor, windowManager, context)
underTest =
DefaultLockIconSection(keyguardUpdateMonitor, authController, 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)
}
fun apply() {
val cs = ConstraintSet()
underTest.apply(cs)
val constraint = cs.getConstraint(R.id.lock_icon_view)
@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)
val cs = ConstraintSet()
underTest.centerLockIcon(Point(5, 6), 1F, 5, cs)
val constraint = cs.getConstraint(R.id.lock_icon_view)
assertThat(constraint.layout.mWidth).isEqualTo(2)
assertThat(constraint.layout.mHeight).isEqualTo(2)
@@ -87,11 +74,4 @@ class DefaultLockscreenLayoutTest : SysuiTestCase() {
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)
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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.viewmodel
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
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 KeyguardBlueprintViewModelTest : SysuiTestCase() {
@Mock private lateinit var keyguardBlueprintInteractor: KeyguardBlueprintInteractor
private lateinit var undertest: KeyguardBlueprintViewModel
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
undertest =
KeyguardBlueprintViewModel(keyguardBlueprintInteractor = keyguardBlueprintInteractor)
}
@Test
fun testBlueprintFlow() {
verify(keyguardBlueprintInteractor).blueprint
}
}