Merge "Correct RTL layout of media players" into rvc-dev

This commit is contained in:
TreeHugger Robot
2020-06-23 03:58:54 +00:00
committed by Android (Google) Code Review
6 changed files with 111 additions and 16 deletions

View File

@@ -26,6 +26,7 @@
android:gravity="center_horizontal|fill_vertical" android:gravity="center_horizontal|fill_vertical"
android:background="@drawable/qs_media_background"> android:background="@drawable/qs_media_background">
<!-- As per Material Design on Biderectionality, this is forced to LTR in code -->
<FrameLayout <FrameLayout
android:id="@+id/notification_media_progress_time" android:id="@+id/notification_media_progress_time"
android:layout_width="0dp" android:layout_width="0dp"
@@ -36,7 +37,7 @@
android:id="@+id/media_elapsed_time" android:id="@+id/media_elapsed_time"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentLeft="true" android:layout_alignParentStart="true"
android:fontFamily="@*android:string/config_bodyFontFamily" android:fontFamily="@*android:string/config_bodyFontFamily"
android:textColor="@color/media_primary_text" android:textColor="@color/media_primary_text"
android:gravity="start" android:gravity="start"
@@ -46,13 +47,36 @@
android:id="@+id/media_total_time" android:id="@+id/media_total_time"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentRight="true" android:layout_alignParentEnd="true"
android:fontFamily="@*android:string/config_bodyFontFamily" android:fontFamily="@*android:string/config_bodyFontFamily"
android:textColor="@color/media_primary_text" android:textColor="@color/media_primary_text"
android:gravity="end" android:gravity="end"
android:textSize="14sp" /> android:textSize="14sp" />
</FrameLayout> </FrameLayout>
<!-- Actions must be ordered left-to-right even in RTL layout. However, they appear in a chain
with the album art and the title, and must as a group appear at the end of that chain. This is
accomplished by having the guidebox (an invisible view that is positioned around all 5 actions)
in the chain with the album art and the title. The actions are in a LTR chain bounded by that
guidebox, and the ambiguity of how wide the guidebox should be is resolved by using a barrier
which forces it's starting edge to be as far to the end as possible while fitting the actions.
-->
<androidx.constraintlayout.widget.Barrier
android:id="@+id/media_action_barrier"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:barrierDirection="start"
/>
<View
android:id="@+id/media_action_guidebox"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginTop="16dp"
android:visibility="invisible"
/>
<ImageButton <ImageButton
android:id="@+id/action0" android:id="@+id/action0"
style="@style/MediaPlayer.Button" style="@style/MediaPlayer.Button"
@@ -98,16 +122,16 @@
android:background="@drawable/qs_media_light_source" android:background="@drawable/qs_media_light_source"
android:orientation="horizontal" android:orientation="horizontal"
android:forceHasOverlappingRendering="false" android:forceHasOverlappingRendering="false"
android:paddingLeft="12dp" android:paddingStart="12dp"
android:paddingTop="6dp" android:paddingTop="6dp"
android:paddingRight="12dp" android:paddingEnd="12dp"
android:paddingBottom="6dp"> android:paddingBottom="6dp">
<ImageView <ImageView
android:id="@+id/media_seamless_image" android:id="@+id/media_seamless_image"
android:layout_width="@dimen/qs_seamless_icon_size" android:layout_width="@dimen/qs_seamless_icon_size"
android:layout_height="@dimen/qs_seamless_icon_size" android:layout_height="@dimen/qs_seamless_icon_size"
android:layout_marginRight="8dp" android:layout_marginEnd="8dp"
android:tint="@color/media_primary_text" android:tint="@color/media_primary_text"
android:src="@*android:drawable/ic_media_seamless" /> android:src="@*android:drawable/ic_media_seamless" />
@@ -119,6 +143,7 @@
android:singleLine="true" android:singleLine="true"
android:text="@*android:string/ext_media_seamless_action" android:text="@*android:string/ext_media_seamless_action"
android:textColor="@color/media_primary_text" android:textColor="@color/media_primary_text"
android:textDirection="locale"
android:textSize="14sp" /> android:textSize="14sp" />
</LinearLayout> </LinearLayout>
@@ -140,6 +165,7 @@
/> />
<!-- Seek Bar --> <!-- Seek Bar -->
<!-- As per Material Design on Biderectionality, this is forced to LTR in code -->
<SeekBar <SeekBar
android:id="@+id/media_progress_bar" android:id="@+id/media_progress_bar"
style="@android:style/Widget.ProgressBar.Horizontal" style="@android:style/Widget.ProgressBar.Horizontal"
@@ -162,6 +188,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:singleLine="true" android:singleLine="true"
android:fontFamily="@*android:string/config_headlineFontFamily" android:fontFamily="@*android:string/config_headlineFontFamily"
android:textDirection="locale"
android:textSize="14sp" /> android:textSize="14sp" />
<!-- Song name --> <!-- Song name -->
@@ -172,6 +199,7 @@
android:fontFamily="@*android:string/config_headlineFontFamilyMedium" android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
android:singleLine="true" android:singleLine="true"
android:textColor="@color/media_primary_text" android:textColor="@color/media_primary_text"
android:textDirection="locale"
android:textSize="16sp" /> android:textSize="16sp" />
<!-- Artist name --> <!-- Artist name -->
@@ -182,6 +210,7 @@
android:fontFamily="@*android:string/config_headlineFontFamily" android:fontFamily="@*android:string/config_headlineFontFamily"
android:singleLine="true" android:singleLine="true"
android:textColor="@color/media_secondary_text" android:textColor="@color/media_secondary_text"
android:textDirection="locale"
android:textSize="14sp" /> android:textSize="14sp" />
<com.android.internal.widget.CachingIconView <com.android.internal.widget.CachingIconView

View File

@@ -85,7 +85,7 @@
app:layout_constraintTop_toBottomOf="@id/app_name" app:layout_constraintTop_toBottomOf="@id/app_name"
app:layout_constraintBottom_toTopOf="@id/header_artist" app:layout_constraintBottom_toTopOf="@id/header_artist"
app:layout_constraintStart_toEndOf="@id/album_art" app:layout_constraintStart_toEndOf="@id/album_art"
app:layout_constraintEnd_toStartOf="@id/action0" app:layout_constraintEnd_toStartOf="@id/media_action_barrier"
app:layout_constraintHorizontal_bias="0"/> app:layout_constraintHorizontal_bias="0"/>
<!-- Artist name --> <!-- Artist name -->
@@ -97,7 +97,7 @@
android:layout_marginBottom="24dp" android:layout_marginBottom="24dp"
app:layout_constraintTop_toBottomOf="@id/header_title" app:layout_constraintTop_toBottomOf="@id/header_title"
app:layout_constraintStart_toStartOf="@id/header_title" app:layout_constraintStart_toStartOf="@id/header_title"
app:layout_constraintEnd_toStartOf="@id/action0" app:layout_constraintEnd_toStartOf="@id/media_action_barrier"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0"/> app:layout_constraintHorizontal_bias="0"/>
@@ -127,16 +127,38 @@
android:visibility="gone" android:visibility="gone"
/> />
<Constraint
android:id="@+id/media_action_barrier"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintTop_toTopOf="parent"
app:barrierDirection="start"
app:constraint_referenced_ids="media_action_guidebox,action0,action1,action2,action3,action4"
/>
<Constraint
android:id="@+id/media_action_guidebox"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginTop="18dp"
android:visibility="invisible"
app:layout_constraintTop_toBottomOf="@id/app_name"
app:layout_constraintStart_toEndOf="@id/header_title"
app:layout_constraintEnd_toEndOf="parent"
/>
<Constraint <Constraint
android:id="@+id/action0" android:id="@+id/action0"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
android:layout_marginStart="4dp" android:layout_marginStart="4dp"
android:layout_marginTop="16dp" android:layout_marginEnd="4dp"
android:layout_marginTop="18dp"
android:visibility="gone" android:visibility="gone"
app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintTop_toBottomOf="@id/app_name" app:layout_constraintTop_toBottomOf="@id/app_name"
app:layout_constraintLeft_toRightOf="@id/header_title" app:layout_constraintLeft_toLeftOf="@id/media_action_guidebox"
app:layout_constraintRight_toLeftOf="@id/action1" app:layout_constraintRight_toLeftOf="@id/action1"
> >
</Constraint> </Constraint>
@@ -188,9 +210,10 @@
android:layout_marginEnd="4dp" android:layout_marginEnd="4dp"
android:visibility="gone" android:visibility="gone"
android:layout_marginTop="18dp" android:layout_marginTop="18dp"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintTop_toBottomOf="@id/app_name" app:layout_constraintTop_toBottomOf="@id/app_name"
app:layout_constraintLeft_toRightOf="@id/action3" app:layout_constraintLeft_toRightOf="@id/action3"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="@id/media_action_guidebox"
> >
</Constraint> </Constraint>
</ConstraintSet> </ConstraintSet>

View File

@@ -182,6 +182,7 @@
android:layout_marginStart="4dp" android:layout_marginStart="4dp"
android:layout_marginEnd="4dp" android:layout_marginEnd="4dp"
android:layout_marginBottom="@dimen/qs_media_panel_outer_padding" android:layout_marginBottom="@dimen/qs_media_panel_outer_padding"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toRightOf="@id/action3" app:layout_constraintLeft_toRightOf="@id/action3"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/action0" app:layout_constraintTop_toTopOf="@id/action0"

View File

@@ -17,9 +17,11 @@
package com.android.systemui.media package com.android.systemui.media
import android.content.Context import android.content.Context
import android.content.res.Configuration
import android.graphics.PointF import android.graphics.PointF
import androidx.constraintlayout.widget.ConstraintSet import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.animation.MeasurementOutput import com.android.systemui.util.animation.MeasurementOutput
import com.android.systemui.util.animation.TransitionLayout import com.android.systemui.util.animation.TransitionLayout
import com.android.systemui.util.animation.TransitionLayoutController import com.android.systemui.util.animation.TransitionLayoutController
@@ -32,6 +34,7 @@ import javax.inject.Inject
*/ */
class MediaViewController @Inject constructor( class MediaViewController @Inject constructor(
context: Context, context: Context,
private val configurationController: ConfigurationController,
private val mediaHostStatesManager: MediaHostStatesManager private val mediaHostStatesManager: MediaHostStatesManager
) { ) {
@@ -56,12 +59,14 @@ class MediaViewController @Inject constructor(
* The ending location of the view where it ends when all animations and transitions have * The ending location of the view where it ends when all animations and transitions have
* finished * finished
*/ */
@MediaLocation
private var currentEndLocation: Int = -1 private var currentEndLocation: Int = -1
/** /**
* The ending location of the view where it ends when all animations and transitions have * The ending location of the view where it ends when all animations and transitions have
* finished * finished
*/ */
@MediaLocation
private var currentStartLocation: Int = -1 private var currentStartLocation: Int = -1
/** /**
@@ -90,11 +95,32 @@ class MediaViewController @Inject constructor(
*/ */
var currentHeight: Int = 0 var currentHeight: Int = 0
/**
* A callback for RTL config changes
*/
private val configurationListener = object : ConfigurationController.ConfigurationListener {
override fun onConfigChanged(newConfig: Configuration?) {
// Because the TransitionLayout is not always attached (and calculates/caches layout
// results regardless of attach state), we have to force the layoutDirection of the view
// to the correct value for the user's current locale to ensure correct recalculation
// when/after calling refreshState()
newConfig?.apply {
if (transitionLayout?.rawLayoutDirection != layoutDirection) {
transitionLayout?.layoutDirection = layoutDirection
refreshState()
}
}
}
}
/** /**
* A callback for media state changes * A callback for media state changes
*/ */
val stateCallback = object : MediaHostStatesManager.Callback { val stateCallback = object : MediaHostStatesManager.Callback {
override fun onHostStateChanged(location: Int, mediaHostState: MediaHostState) { override fun onHostStateChanged(
@MediaLocation location: Int,
mediaHostState: MediaHostState
) {
if (location == currentEndLocation || location == currentStartLocation) { if (location == currentEndLocation || location == currentStartLocation) {
setCurrentState(currentStartLocation, setCurrentState(currentStartLocation,
currentEndLocation, currentEndLocation,
@@ -125,6 +151,7 @@ class MediaViewController @Inject constructor(
currentHeight = height currentHeight = height
sizeChangedListener.invoke() sizeChangedListener.invoke()
} }
configurationController.addCallback(configurationListener)
} }
/** /**
@@ -132,6 +159,7 @@ class MediaViewController @Inject constructor(
*/ */
fun onDestroy() { fun onDestroy() {
mediaHostStatesManager.removeController(this) mediaHostStatesManager.removeController(this)
configurationController.removeCallback(configurationListener)
} }
private fun ensureAllMeasurements() { private fun ensureAllMeasurements() {
@@ -346,7 +374,7 @@ class MediaViewController @Inject constructor(
// Let's clear all of our measurements and recreate them! // Let's clear all of our measurements and recreate them!
viewStates.clear() viewStates.clear()
setCurrentState(currentStartLocation, currentEndLocation, currentTransitionProgress, setCurrentState(currentStartLocation, currentEndLocation, currentTransitionProgress,
applyImmediately = false) applyImmediately = true)
} }
firstRefresh = false firstRefresh = false
} }

View File

@@ -48,6 +48,7 @@ class PlayerViewHolder private constructor(itemView: View) {
// Seek bar // Seek bar
val seekBar = itemView.requireViewById<SeekBar>(R.id.media_progress_bar) val seekBar = itemView.requireViewById<SeekBar>(R.id.media_progress_bar)
val progressTimes = itemView.requireViewById<ViewGroup>(R.id.notification_media_progress_time)
val elapsedTimeView = itemView.requireViewById<TextView>(R.id.media_elapsed_time) val elapsedTimeView = itemView.requireViewById<TextView>(R.id.media_elapsed_time)
val totalTimeView = itemView.requireViewById<TextView>(R.id.media_total_time) val totalTimeView = itemView.requireViewById<TextView>(R.id.media_total_time)
@@ -93,8 +94,16 @@ class PlayerViewHolder private constructor(itemView: View) {
* @param parent Parent of inflated view. * @param parent Parent of inflated view.
*/ */
@JvmStatic fun create(inflater: LayoutInflater, parent: ViewGroup): PlayerViewHolder { @JvmStatic fun create(inflater: LayoutInflater, parent: ViewGroup): PlayerViewHolder {
val v = inflater.inflate(R.layout.media_view, parent, false) val mediaView = inflater.inflate(R.layout.media_view, parent, false)
return PlayerViewHolder(v) // Because this media view (a TransitionLayout) is used to measure and layout the views
// in various states before being attached to its parent, we can't depend on the default
// LAYOUT_DIRECTION_INHERIT to correctly resolve the ltr direction.
mediaView.layoutDirection = View.LAYOUT_DIRECTION_LOCALE
return PlayerViewHolder(mediaView).apply {
// Media playback is in the direction of tape, not time, so it stays LTR
seekBar.layoutDirection = View.LAYOUT_DIRECTION_LTR
progressTimes.layoutDirection = View.LAYOUT_DIRECTION_LTR
}
} }
} }
} }

View File

@@ -70,7 +70,10 @@ class UniqueObjectHostView(
} }
override fun addView(child: View?, index: Int, params: ViewGroup.LayoutParams?) { override fun addView(child: View?, index: Int, params: ViewGroup.LayoutParams?) {
if (child?.measuredWidth == 0 || measuredWidth == 0 || child?.requiresRemeasuring == true) { if (child == null) {
throw IllegalArgumentException("child must be non-null")
}
if (child.measuredWidth == 0 || measuredWidth == 0 || child.requiresRemeasuring == true) {
super.addView(child, index, params) super.addView(child, index, params)
return return
} }
@@ -78,11 +81,13 @@ class UniqueObjectHostView(
// right size when being attached to this view // right size when being attached to this view
invalidate() invalidate()
addViewInLayout(child, index, params, true /* preventRequestLayout */) addViewInLayout(child, index, params, true /* preventRequestLayout */)
// RTL properties are normally resolved in onMeasure(), which we are intentionally skipping
child.resolveRtlPropertiesIfNeeded()
val left = paddingLeft val left = paddingLeft
val top = paddingTop val top = paddingTop
val paddingHorizontal = paddingStart + paddingEnd val paddingHorizontal = paddingStart + paddingEnd
val paddingVertical = paddingTop + paddingBottom val paddingVertical = paddingTop + paddingBottom
child!!.layout(left, child.layout(left,
top, top,
left + measuredWidth - paddingHorizontal, left + measuredWidth - paddingHorizontal,
top + measuredHeight - paddingVertical) top + measuredHeight - paddingVertical)