Controls UI - Marquee + dynamic column count

Make the controls more legible across different displays.

1. Support marquee in the status field to support long strings
2. Adjust margins for different dp devices
3. Dynamically adjust the max number of columns based on fontScale and
screen width in dp

Bug: 152765942
Test: visual, use different display/font sizes

Change-Id: I72665f1164ff30e0a5746adb9fdc89f00edd35d4
This commit is contained in:
Matt Pietal
2020-04-01 15:53:54 -04:00
parent bc93009274
commit 5f478c7f39
10 changed files with 82 additions and 45 deletions

View File

@@ -40,29 +40,20 @@
<TextView
android:id="@+id/status"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Control.Status"
android:paddingTop="@dimen/control_padding_adjustment"
android:paddingStart="@dimen/control_status_padding"
android:clickable="false"
android:clickable="true"
android:focusable="false"
android:singleLine="true"
android:ellipsize="marquee"
android:marqueeRepeatLimit = "marquee_forever"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@+id/icon"
app:layout_constraintStart_toEndOf="@+id/icon" />
<TextView
android:id="@+id/status_extra"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Control.Status"
android:paddingTop="@dimen/control_padding_adjustment"
android:paddingStart="@dimen/control_status_padding"
android:clickable="false"
android:focusable="false"
app:layout_constraintBottom_toBottomOf="@+id/icon"
app:layout_constraintStart_toEndOf="@+id/status" />
<TextView
android:id="@+id/title"
android:layout_width="match_parent"

View File

@@ -67,9 +67,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginRight="@dimen/global_actions_grid_horizontal_padding"
android:layout_marginLeft="@dimen/global_actions_grid_horizontal_padding"
/>
</LinearLayout>
</com.android.systemui.globalactions.MinHeightScrollView>
</LinearLayout>
</LinearLayout>

View File

@@ -31,4 +31,7 @@
<!-- orientation of the dead zone when touches have recently occurred elsewhere on screen -->
<integer name="navigation_bar_deadzone_orientation">1</integer>
<!-- Max number of columns for quick controls area -->
<integer name="controls_max_columns">4</integer>
</resources>

View File

@@ -30,5 +30,7 @@
<dimen name="global_actions_grid_item_icon_side_margin">22dp</dimen>
<dimen name="global_actions_grid_item_icon_bottom_margin">4dp</dimen>
<!-- Home Controls -->
<dimen name="controls_list_side_margin">10dp</dimen>
</resources>

View File

@@ -26,5 +26,7 @@
navigation_extra_key_width -->
<dimen name="navigation_side_padding">40dip</dimen>
<!-- Home Controls -->
<dimen name="controls_list_side_margin">12dp</dimen>
</resources>

View File

@@ -30,5 +30,7 @@
<dimen name="global_actions_grid_item_icon_side_margin">22dp</dimen>
<dimen name="global_actions_grid_item_icon_bottom_margin">4dp</dimen>
<!-- Home Controls -->
<dimen name="controls_list_side_margin">16dp</dimen>
</resources>

View File

@@ -533,4 +533,12 @@
This config value should contain the package name of that preferred application.
-->
<string translatable="false" name="config_controlsPreferredPackage"></string>
<!-- Max number of columns for quick controls area -->
<integer name="controls_max_columns">2</integer>
<!-- If the dp width of the available space is <= this value, potentially adjust the number
of columns-->
<integer name="controls_max_columns_adjust_below_width_dp">320</integer>
<!-- If the config font scale is >= this value, potentially adjust the number of columns-->
<item name="controls_max_columns_adjust_above_font_scale" translatable="false" format="float" type="dimen">1.25</item>
</resources>

View File

@@ -51,7 +51,6 @@ class ControlViewHolder(
) {
val icon: ImageView = layout.requireViewById(R.id.icon)
val status: TextView = layout.requireViewById(R.id.status)
val statusExtra: TextView = layout.requireViewById(R.id.status_extra)
val title: TextView = layout.requireViewById(R.id.title)
val subtitle: TextView = layout.requireViewById(R.id.subtitle)
val context: Context = layout.getContext()
@@ -65,6 +64,8 @@ class ControlViewHolder(
val ld = layout.getBackground() as LayerDrawable
ld.mutate()
clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) as ClipDrawable
// needed for marquee to start
status.setSelected(true)
}
fun bindData(cws: ControlWithState) {
@@ -103,8 +104,7 @@ class ControlViewHolder(
behavior?.bind(cws)
layout.setContentDescription(
"${title.text} ${subtitle.text} ${status.text} ${statusExtra.text}")
layout.setContentDescription("${title.text} ${subtitle.text} ${status.text}")
}
fun actionResponse(@ControlAction.ResponseResult response: Int) {
@@ -113,15 +113,12 @@ class ControlViewHolder(
fun setTransientStatus(tempStatus: String) {
val previousText = status.getText()
val previousTextExtra = statusExtra.getText()
cancelUpdate = uiExecutor.executeDelayed({
status.setText(previousText)
statusExtra.setText(previousTextExtra)
}, UPDATE_DELAY_IN_MILLIS)
status.setText(tempStatus)
statusExtra.setText("")
}
fun action(action: ControlAction) {
@@ -154,7 +151,6 @@ class ControlViewHolder(
}
status.setTextColor(fg)
statusExtra.setTextColor(fg)
icon.setImageDrawable(ri.icon)

View File

@@ -21,10 +21,12 @@ import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.content.res.Configuration
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
import android.service.controls.Control
import android.service.controls.actions.ControlAction
import android.util.TypedValue
import android.util.Log
import android.view.ContextThemeWrapper
import android.view.LayoutInflater
@@ -345,10 +347,12 @@ class ControlsUiControllerImpl @Inject constructor (
val inflater = LayoutInflater.from(context)
inflater.inflate(R.layout.controls_with_favorites, parent, true)
val maxColumns = findMaxColumns()
val listView = parent.requireViewById(R.id.global_actions_controls_list) as ViewGroup
var lastRow: ViewGroup = createRow(inflater, listView)
selectedStructure.controls.forEach {
if (lastRow.getChildCount() == 2) {
if (lastRow.getChildCount() == maxColumns) {
lastRow = createRow(inflater, listView)
}
val baseLayout = inflater.inflate(
@@ -365,12 +369,40 @@ class ControlsUiControllerImpl @Inject constructor (
controlViewsById.put(key, cvh)
}
// add spacer if necessary to keep control size consistent
if ((selectedStructure.controls.size % 2) == 1) {
// add spacers if necessary to keep control size consistent
var spacersToAdd = selectedStructure.controls.size % maxColumns
while (spacersToAdd > 0) {
lastRow.addView(Space(context), LinearLayout.LayoutParams(0, 0, 1f))
spacersToAdd--
}
}
/**
* For low-dp width screens that also employ an increased font scale, adjust the
* number of columns. This helps prevent text truncation on these devices.
*/
private fun findMaxColumns(): Int {
val res = context.resources
var maxColumns = res.getInteger(R.integer.controls_max_columns)
val maxColumnsAdjustWidth =
res.getInteger(R.integer.controls_max_columns_adjust_below_width_dp)
val outValue = TypedValue()
res.getValue(R.dimen.controls_max_columns_adjust_above_font_scale, outValue, true)
val maxColumnsAdjustFontScale = outValue.getFloat()
val config = res.configuration
val isPortrait = config.orientation == Configuration.ORIENTATION_PORTRAIT
if (isPortrait &&
config.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED &&
config.screenWidthDp <= maxColumnsAdjustWidth &&
config.fontScale >= maxColumnsAdjustFontScale) {
maxColumns--
}
return maxColumns
}
private fun loadPreference(structures: List<StructureInfo>): StructureInfo {
if (structures.isEmpty()) return EMPTY_STRUCTURE

View File

@@ -47,9 +47,10 @@ class ToggleRangeBehavior : Behavior {
lateinit var control: Control
lateinit var cvh: ControlViewHolder
lateinit var rangeTemplate: RangeTemplate
lateinit var statusExtra: TextView
lateinit var status: TextView
lateinit var context: Context
var currentStatusText: CharSequence = ""
var currentRangeValue: String = ""
companion object {
private const val DEFAULT_FORMAT = "%.1f"
@@ -83,8 +84,8 @@ class ToggleRangeBehavior : Behavior {
override fun bind(cws: ControlWithState) {
this.control = cws.control!!
statusExtra = cvh.statusExtra
status.setText(control.getStatusText())
currentStatusText = control.getStatusText()
status.setText(currentStatusText)
val ld = cvh.layout.getBackground() as LayerDrawable
clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
@@ -96,7 +97,7 @@ class ToggleRangeBehavior : Behavior {
val checked = template.isChecked()
val currentRatio = rangeTemplate.getCurrentValue() /
(rangeTemplate.getMaxValue() - rangeTemplate.getMinValue())
updateRange(currentRatio, checked)
updateRange(currentRatio, checked, /* isDragging */ false)
cvh.applyRenderInfo(checked)
@@ -147,7 +148,7 @@ class ToggleRangeBehavior : Behavior {
AccessibilityNodeInfo.ACTION_ARGUMENT_PROGRESS_VALUE)
val ratioDiff = (value - rangeTemplate.getCurrentValue()) /
(rangeTemplate.getMaxValue() - rangeTemplate.getMinValue())
updateRange(ratioDiff, template.isChecked())
updateRange(ratioDiff, template.isChecked(), /* isDragging */ false)
endUpdateRange()
true
}
@@ -167,26 +168,27 @@ class ToggleRangeBehavior : Behavior {
}
fun beginUpdateRange() {
status.setVisibility(View.GONE)
statusExtra.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
status.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
.getDimensionPixelSize(R.dimen.control_status_expanded).toFloat())
}
fun updateRange(ratioDiff: Float, checked: Boolean) {
fun updateRange(ratioDiff: Float, checked: Boolean, isDragging: Boolean) {
val changeAmount = if (checked) (MAX_LEVEL * ratioDiff).toInt() else MIN_LEVEL
val newLevel = Math.max(MIN_LEVEL, Math.min(MAX_LEVEL, clipLayer.getLevel() + changeAmount))
clipLayer.setLevel(newLevel)
if (checked) {
val newValue = levelToRangeValue(clipLayer.getLevel())
val formattedNewValue = format(rangeTemplate.getFormatString().toString(),
currentRangeValue = format(rangeTemplate.getFormatString().toString(),
DEFAULT_FORMAT, newValue)
statusExtra.setText(formattedNewValue)
statusExtra.setVisibility(View.VISIBLE)
val text = if (isDragging) {
currentRangeValue
} else {
"$currentStatusText $currentRangeValue"
}
status.setText(text)
} else {
statusExtra.setText("")
statusExtra.setVisibility(View.GONE)
status.setText(currentStatusText)
}
}
@@ -210,9 +212,9 @@ class ToggleRangeBehavior : Behavior {
}
fun endUpdateRange() {
statusExtra.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
status.setTextSize(TypedValue.COMPLEX_UNIT_PX, context.getResources()
.getDimensionPixelSize(R.dimen.control_status_normal).toFloat())
status.setVisibility(View.VISIBLE)
status.setText("$currentStatusText $currentRangeValue")
cvh.action(FloatAction(rangeTemplate.getTemplateId(),
findNearestStep(levelToRangeValue(clipLayer.getLevel()))))
}
@@ -260,7 +262,8 @@ class ToggleRangeBehavior : Behavior {
isDragging = true
}
this@ToggleRangeBehavior.updateRange(-xDiff / v.getWidth(), true)
this@ToggleRangeBehavior.updateRange(-xDiff / v.getWidth(),
/* checked */ true, /* isDragging */ true)
return true
}