Implementing keyboard indicator dialog
Dialog is not using xml resource as currently number of steps is flexible and comes from API call. That means everything in the view is built dynamically. Flag: KEYBOARD_BACKLIGHT_INDICATOR Fixes: 268650355 Test: manual + will add unit tests for the whole path in future CLs Change-Id: I35179c73dd1bba9962e3881d74209760d1d7dac5
This commit is contained in:
committed by
Christian Göllner
parent
9b7e8c6191
commit
bb0710089a
12
packages/SystemUI/res/drawable/ic_keyboard_backlight.xml
Normal file
12
packages/SystemUI/res/drawable/ic_keyboard_backlight.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<vector android:height="11dp" android:viewportHeight="12"
|
||||
android:viewportWidth="22" android:width="20.166666dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<group>
|
||||
<clip-path android:pathData="M0,0.5h22v11h-22z"/>
|
||||
<path android:fillColor="#231F20" android:pathData="M6.397,9.908H0V11.5H6.397V9.908Z"/>
|
||||
<path android:fillColor="#231F20" android:pathData="M14.199,9.908H7.801V11.5H14.199V9.908Z"/>
|
||||
<path android:fillColor="#231F20" android:pathData="M11.858,0.5H10.142V6.434H11.858V0.5Z"/>
|
||||
<path android:fillColor="#231F20" android:pathData="M8.348,7.129L3.885,2.975L3.823,2.932L2.668,4.003L2.621,4.046L7.084,8.2L7.146,8.243L8.301,7.172L8.348,7.129Z"/>
|
||||
<path android:fillColor="#231F20" android:pathData="M18.224,2.975L18.177,2.932L13.653,7.129L14.807,8.2L14.854,8.243L19.379,4.046L18.224,2.975Z"/>
|
||||
<path android:fillColor="#231F20" android:pathData="M22,9.908H15.603V11.5H22V9.908Z"/>
|
||||
</group>
|
||||
</vector>
|
||||
@@ -207,6 +207,11 @@
|
||||
<color name="control_thumbnail_shadow_color">@*android:color/black</color>
|
||||
<color name="controls_task_view_bg">#CC191C1D</color>
|
||||
|
||||
<!-- Keyboard backlight indicator-->
|
||||
<color name="backlight_indicator_step_filled">#F6E388</color>
|
||||
<color name="backlight_indicator_step_empty">#494740</color>
|
||||
<color name="backlight_indicator_background">#32302A</color>
|
||||
|
||||
<!-- Docked misalignment message -->
|
||||
<color name="misalignment_text_color">#F28B82</color>
|
||||
|
||||
|
||||
@@ -1653,6 +1653,19 @@
|
||||
<dimen name="media_output_broadcast_info_summary_height">20dp</dimen>
|
||||
<dimen name="media_output_broadcast_info_edit">18dp</dimen>
|
||||
|
||||
<!-- Keyboard backlight indicator-->
|
||||
<dimen name="backlight_indicator_root_corner_radius">48dp</dimen>
|
||||
<dimen name="backlight_indicator_root_vertical_padding">8dp</dimen>
|
||||
<dimen name="backlight_indicator_root_horizontal_padding">4dp</dimen>
|
||||
<dimen name="backlight_indicator_icon_width">22dp</dimen>
|
||||
<dimen name="backlight_indicator_icon_height">11dp</dimen>
|
||||
<dimen name="backlight_indicator_icon_left_margin">2dp</dimen>
|
||||
<dimen name="backlight_indicator_step_width">52dp</dimen>
|
||||
<dimen name="backlight_indicator_step_height">40dp</dimen>
|
||||
<dimen name="backlight_indicator_step_horizontal_margin">4dp</dimen>
|
||||
<dimen name="backlight_indicator_step_small_radius">4dp</dimen>
|
||||
<dimen name="backlight_indicator_step_large_radius">48dp</dimen>
|
||||
|
||||
<!-- Broadcast dialog -->
|
||||
<dimen name="broadcast_dialog_title_img_margin_top">18dp</dimen>
|
||||
<dimen name="broadcast_dialog_title_text_size">24sp</dimen>
|
||||
|
||||
@@ -46,8 +46,15 @@ constructor(
|
||||
viewModel.dialogContent.collect { dialogViewModel ->
|
||||
if (dialogViewModel != null) {
|
||||
if (dialog == null) {
|
||||
dialog = KeyboardBacklightDialog(context, dialogViewModel)
|
||||
// pass viewModel and show
|
||||
dialog =
|
||||
KeyboardBacklightDialog(
|
||||
context,
|
||||
initialCurrentLevel = dialogViewModel.currentValue,
|
||||
initialMaxLevel = dialogViewModel.maxValue
|
||||
)
|
||||
dialog?.show()
|
||||
} else {
|
||||
dialog?.updateState(dialogViewModel.currentValue, dialogViewModel.maxValue)
|
||||
}
|
||||
} else {
|
||||
dialog?.dismiss()
|
||||
|
||||
@@ -17,16 +17,260 @@
|
||||
|
||||
package com.android.systemui.keyboard.backlight.ui.view
|
||||
|
||||
import android.annotation.ColorInt
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.ShapeDrawable
|
||||
import android.graphics.drawable.shapes.RoundRectShape
|
||||
import android.os.Bundle
|
||||
import com.android.systemui.keyboard.backlight.ui.viewmodel.BacklightDialogContentViewModel
|
||||
import android.view.Gravity
|
||||
import android.view.Window
|
||||
import android.view.WindowManager
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.LinearLayout.LayoutParams
|
||||
import android.widget.LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.util.children
|
||||
|
||||
class KeyboardBacklightDialog(context: Context, val viewModel: BacklightDialogContentViewModel) :
|
||||
Dialog(context) {
|
||||
class KeyboardBacklightDialog(
|
||||
context: Context,
|
||||
initialCurrentLevel: Int,
|
||||
initialMaxLevel: Int,
|
||||
) : Dialog(context) {
|
||||
|
||||
private data class RootProperties(
|
||||
val cornerRadius: Float,
|
||||
val verticalPadding: Int,
|
||||
val horizontalPadding: Int,
|
||||
)
|
||||
|
||||
private data class BacklightIconProperties(
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
val leftMargin: Int,
|
||||
)
|
||||
|
||||
private data class StepViewProperties(
|
||||
val width: Int,
|
||||
val height: Int,
|
||||
val horizontalMargin: Int,
|
||||
val smallRadius: Float,
|
||||
val largeRadius: Float,
|
||||
)
|
||||
|
||||
private var currentLevel: Int = 0
|
||||
private var maxLevel: Int = 0
|
||||
|
||||
private lateinit var rootView: LinearLayout
|
||||
|
||||
private var dialogBottomMargin = 208
|
||||
private lateinit var rootProperties: RootProperties
|
||||
private lateinit var iconProperties: BacklightIconProperties
|
||||
private lateinit var stepProperties: StepViewProperties
|
||||
@ColorInt var filledRectangleColor: Int = 0
|
||||
@ColorInt var emptyRectangleColor: Int = 0
|
||||
@ColorInt var backgroundColor: Int = 0
|
||||
|
||||
init {
|
||||
currentLevel = initialCurrentLevel
|
||||
maxLevel = initialMaxLevel
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setUpWindowProperties(this)
|
||||
setWindowTitle()
|
||||
updateResources()
|
||||
rootView = buildRootView()
|
||||
setContentView(rootView)
|
||||
super.onCreate(savedInstanceState)
|
||||
// TODO(b/268650355) Implement the dialog
|
||||
updateState(currentLevel, maxLevel, forceRefresh = true)
|
||||
}
|
||||
|
||||
private fun updateResources() {
|
||||
context.resources.apply {
|
||||
filledRectangleColor = getColor(R.color.backlight_indicator_step_filled)
|
||||
emptyRectangleColor = getColor(R.color.backlight_indicator_step_empty)
|
||||
backgroundColor = getColor(R.color.backlight_indicator_background)
|
||||
rootProperties =
|
||||
RootProperties(
|
||||
cornerRadius =
|
||||
getDimensionPixelSize(R.dimen.backlight_indicator_root_corner_radius)
|
||||
.toFloat(),
|
||||
verticalPadding =
|
||||
getDimensionPixelSize(R.dimen.backlight_indicator_root_vertical_padding),
|
||||
horizontalPadding =
|
||||
getDimensionPixelSize(R.dimen.backlight_indicator_root_horizontal_padding)
|
||||
)
|
||||
iconProperties =
|
||||
BacklightIconProperties(
|
||||
width = getDimensionPixelSize(R.dimen.backlight_indicator_icon_width),
|
||||
height = getDimensionPixelSize(R.dimen.backlight_indicator_icon_height),
|
||||
leftMargin =
|
||||
getDimensionPixelSize(R.dimen.backlight_indicator_icon_left_margin),
|
||||
)
|
||||
stepProperties =
|
||||
StepViewProperties(
|
||||
width = getDimensionPixelSize(R.dimen.backlight_indicator_step_width),
|
||||
height = getDimensionPixelSize(R.dimen.backlight_indicator_step_height),
|
||||
horizontalMargin =
|
||||
getDimensionPixelSize(R.dimen.backlight_indicator_step_horizontal_margin),
|
||||
smallRadius =
|
||||
getDimensionPixelSize(R.dimen.backlight_indicator_step_small_radius)
|
||||
.toFloat(),
|
||||
largeRadius =
|
||||
getDimensionPixelSize(R.dimen.backlight_indicator_step_large_radius)
|
||||
.toFloat(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateState(current: Int, max: Int, forceRefresh: Boolean = false) {
|
||||
if (maxLevel != max || forceRefresh) {
|
||||
maxLevel = max
|
||||
rootView.removeAllViews()
|
||||
buildStepViews().forEach { rootView.addView(it) }
|
||||
}
|
||||
currentLevel = current
|
||||
updateLevel()
|
||||
}
|
||||
|
||||
private fun updateLevel() {
|
||||
rootView.children.forEachIndexed(
|
||||
action = { index, v ->
|
||||
val drawable = v.background as ShapeDrawable
|
||||
if (index <= currentLevel) {
|
||||
updateColor(drawable, filledRectangleColor)
|
||||
} else {
|
||||
updateColor(drawable, emptyRectangleColor)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun updateColor(drawable: ShapeDrawable, @ColorInt color: Int) {
|
||||
if (drawable.paint.color != color) {
|
||||
drawable.paint.color = color
|
||||
drawable.invalidateSelf()
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildRootView(): LinearLayout {
|
||||
val linearLayout =
|
||||
LinearLayout(context).apply {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
layoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT)
|
||||
setPadding(
|
||||
/* left= */ rootProperties.horizontalPadding,
|
||||
/* top= */ rootProperties.verticalPadding,
|
||||
/* right= */ rootProperties.horizontalPadding,
|
||||
/* bottom= */ rootProperties.verticalPadding
|
||||
)
|
||||
}
|
||||
val drawable =
|
||||
ShapeDrawable(
|
||||
RoundRectShape(
|
||||
/* outerRadii= */ FloatArray(8) { rootProperties.cornerRadius },
|
||||
/* inset= */ null,
|
||||
/* innerRadii= */ null
|
||||
)
|
||||
)
|
||||
drawable.paint.color = backgroundColor
|
||||
linearLayout.background = drawable
|
||||
return linearLayout
|
||||
}
|
||||
|
||||
private fun buildStepViews(): List<FrameLayout> {
|
||||
val stepViews = (0..maxLevel).map { i -> createStepViewAt(i) }
|
||||
stepViews[0].addView(createBacklightIconView())
|
||||
return stepViews
|
||||
}
|
||||
|
||||
private fun createStepViewAt(i: Int): FrameLayout {
|
||||
return FrameLayout(context).apply {
|
||||
layoutParams =
|
||||
FrameLayout.LayoutParams(stepProperties.width, stepProperties.height).apply {
|
||||
setMargins(
|
||||
/* left= */ stepProperties.horizontalMargin,
|
||||
/* top= */ 0,
|
||||
/* right= */ stepProperties.horizontalMargin,
|
||||
/* bottom= */ 0
|
||||
)
|
||||
}
|
||||
val drawable =
|
||||
ShapeDrawable(
|
||||
RoundRectShape(
|
||||
/* outerRadii= */ radiiForIndex(i, maxLevel),
|
||||
/* inset= */ null,
|
||||
/* innerRadii= */ null
|
||||
)
|
||||
)
|
||||
drawable.paint.color = emptyRectangleColor
|
||||
background = drawable
|
||||
}
|
||||
}
|
||||
|
||||
private fun createBacklightIconView(): ImageView {
|
||||
return ImageView(context).apply {
|
||||
setImageResource(R.drawable.ic_keyboard_backlight)
|
||||
layoutParams =
|
||||
FrameLayout.LayoutParams(iconProperties.width, iconProperties.height).apply {
|
||||
gravity = Gravity.CENTER
|
||||
leftMargin = iconProperties.leftMargin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setWindowTitle() {
|
||||
val attrs = window.attributes
|
||||
// TODO(b/271796169): check if title needs to be a translatable resource.
|
||||
attrs.title = "KeyboardBacklightDialog"
|
||||
attrs?.y = dialogBottomMargin
|
||||
window.attributes = attrs
|
||||
}
|
||||
|
||||
private fun setUpWindowProperties(dialog: Dialog) {
|
||||
val window = dialog.window
|
||||
window.requestFeature(Window.FEATURE_NO_TITLE) // otherwise fails while creating actionBar
|
||||
window.setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
|
||||
window.addFlags(
|
||||
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM or
|
||||
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
|
||||
)
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
|
||||
window.setBackgroundDrawableResource(android.R.color.transparent)
|
||||
window.setGravity(Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL)
|
||||
setCanceledOnTouchOutside(true)
|
||||
}
|
||||
|
||||
private fun radiiForIndex(i: Int, last: Int): FloatArray {
|
||||
val smallRadius = stepProperties.smallRadius
|
||||
val largeRadius = stepProperties.largeRadius
|
||||
return when (i) {
|
||||
0 -> // left radii bigger
|
||||
floatArrayOf(
|
||||
largeRadius,
|
||||
largeRadius,
|
||||
smallRadius,
|
||||
smallRadius,
|
||||
smallRadius,
|
||||
smallRadius,
|
||||
largeRadius,
|
||||
largeRadius
|
||||
)
|
||||
last -> // right radii bigger
|
||||
floatArrayOf(
|
||||
smallRadius,
|
||||
smallRadius,
|
||||
largeRadius,
|
||||
largeRadius,
|
||||
largeRadius,
|
||||
largeRadius,
|
||||
smallRadius,
|
||||
smallRadius
|
||||
)
|
||||
else -> FloatArray(8) { smallRadius } // all radii equal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user