Merge "Fix MediaCarousel in RTL" into rvc-dev am: b01bd6a560
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/11988335 Change-Id: I2df164d6b9c7308a245cdb95837a92c1bc8f2b86
This commit is contained in:
@@ -2,6 +2,7 @@ package com.android.systemui.media
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.res.Configuration
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS
|
import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@@ -96,7 +97,6 @@ class MediaCarouselController @Inject constructor(
|
|||||||
* The measured height of the carousel
|
* The measured height of the carousel
|
||||||
*/
|
*/
|
||||||
private var carouselMeasureHeight: Int = 0
|
private var carouselMeasureHeight: Int = 0
|
||||||
private var playerWidthPlusPadding: Int = 0
|
|
||||||
private var desiredHostState: MediaHostState? = null
|
private var desiredHostState: MediaHostState? = null
|
||||||
private val mediaCarousel: MediaScrollView
|
private val mediaCarousel: MediaScrollView
|
||||||
private val mediaCarouselScrollHandler: MediaCarouselScrollHandler
|
private val mediaCarouselScrollHandler: MediaCarouselScrollHandler
|
||||||
@@ -108,6 +108,15 @@ class MediaCarouselController @Inject constructor(
|
|||||||
private val pageIndicator: PageIndicator
|
private val pageIndicator: PageIndicator
|
||||||
private val visualStabilityCallback: VisualStabilityManager.Callback
|
private val visualStabilityCallback: VisualStabilityManager.Callback
|
||||||
private var needsReordering: Boolean = false
|
private var needsReordering: Boolean = false
|
||||||
|
private var isRtl: Boolean = false
|
||||||
|
set(value) {
|
||||||
|
if (value != field) {
|
||||||
|
field = value
|
||||||
|
mediaFrame.layoutDirection =
|
||||||
|
if (value) View.LAYOUT_DIRECTION_RTL else View.LAYOUT_DIRECTION_LTR
|
||||||
|
mediaCarouselScrollHandler.scrollToStart()
|
||||||
|
}
|
||||||
|
}
|
||||||
private var currentlyExpanded = true
|
private var currentlyExpanded = true
|
||||||
set(value) {
|
set(value) {
|
||||||
if (field != value) {
|
if (field != value) {
|
||||||
@@ -126,6 +135,11 @@ class MediaCarouselController @Inject constructor(
|
|||||||
override fun onOverlayChanged() {
|
override fun onOverlayChanged() {
|
||||||
inflateSettingsButton()
|
inflateSettingsButton()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onConfigChanged(newConfig: Configuration?) {
|
||||||
|
if (newConfig == null) return
|
||||||
|
isRtl = newConfig.layoutDirection == View.LAYOUT_DIRECTION_RTL
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -135,6 +149,7 @@ class MediaCarouselController @Inject constructor(
|
|||||||
mediaCarouselScrollHandler = MediaCarouselScrollHandler(mediaCarousel, pageIndicator,
|
mediaCarouselScrollHandler = MediaCarouselScrollHandler(mediaCarousel, pageIndicator,
|
||||||
executor, mediaDataManager::onSwipeToDismiss, this::updatePageIndicatorLocation,
|
executor, mediaDataManager::onSwipeToDismiss, this::updatePageIndicatorLocation,
|
||||||
falsingManager)
|
falsingManager)
|
||||||
|
isRtl = context.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL
|
||||||
inflateSettingsButton()
|
inflateSettingsButton()
|
||||||
mediaContent = mediaCarousel.requireViewById(R.id.media_carousel)
|
mediaContent = mediaCarousel.requireViewById(R.id.media_carousel)
|
||||||
configurationController.addCallback(configListener)
|
configurationController.addCallback(configListener)
|
||||||
@@ -144,7 +159,7 @@ class MediaCarouselController @Inject constructor(
|
|||||||
reorderAllPlayers()
|
reorderAllPlayers()
|
||||||
}
|
}
|
||||||
// Let's reset our scroll position
|
// Let's reset our scroll position
|
||||||
mediaCarousel.scrollX = 0
|
mediaCarouselScrollHandler.scrollToStart()
|
||||||
}
|
}
|
||||||
visualStabilityManager.addReorderingAllowedCallback(visualStabilityCallback,
|
visualStabilityManager.addReorderingAllowedCallback(visualStabilityCallback,
|
||||||
true /* persistent */)
|
true /* persistent */)
|
||||||
@@ -196,8 +211,13 @@ class MediaCarouselController @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun inflateMediaCarousel(): ViewGroup {
|
private fun inflateMediaCarousel(): ViewGroup {
|
||||||
return LayoutInflater.from(context).inflate(R.layout.media_carousel,
|
val mediaCarousel = LayoutInflater.from(context).inflate(R.layout.media_carousel,
|
||||||
UniqueObjectHostView(context), false) as ViewGroup
|
UniqueObjectHostView(context), false) as ViewGroup
|
||||||
|
// Because this is inflated when not attached to the true view hierarchy, it resolves some
|
||||||
|
// potential issues to force that the layout direction is defined by the locale
|
||||||
|
// (rather than inherited from the parent, which would resolve to LTR when unattached).
|
||||||
|
mediaCarousel.layoutDirection = View.LAYOUT_DIRECTION_LOCALE
|
||||||
|
return mediaCarousel
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reorderAllPlayers() {
|
private fun reorderAllPlayers() {
|
||||||
@@ -313,8 +333,12 @@ class MediaCarouselController @Inject constructor(
|
|||||||
|
|
||||||
private fun updatePageIndicatorLocation() {
|
private fun updatePageIndicatorLocation() {
|
||||||
// Update the location of the page indicator, carousel clipping
|
// Update the location of the page indicator, carousel clipping
|
||||||
pageIndicator.translationX = (currentCarouselWidth - pageIndicator.width) / 2.0f +
|
val translationX = if (isRtl) {
|
||||||
mediaCarouselScrollHandler.contentTranslation
|
(pageIndicator.width - currentCarouselWidth) / 2.0f
|
||||||
|
} else {
|
||||||
|
(currentCarouselWidth - pageIndicator.width) / 2.0f
|
||||||
|
}
|
||||||
|
pageIndicator.translationX = translationX + mediaCarouselScrollHandler.contentTranslation
|
||||||
val layoutParams = pageIndicator.layoutParams as ViewGroup.MarginLayoutParams
|
val layoutParams = pageIndicator.layoutParams as ViewGroup.MarginLayoutParams
|
||||||
pageIndicator.translationY = (currentCarouselHeight - pageIndicator.height -
|
pageIndicator.translationY = (currentCarouselHeight - pageIndicator.height -
|
||||||
layoutParams.bottomMargin).toFloat()
|
layoutParams.bottomMargin).toFloat()
|
||||||
@@ -334,7 +358,8 @@ class MediaCarouselController @Inject constructor(
|
|||||||
if (width != currentCarouselWidth || height != currentCarouselHeight) {
|
if (width != currentCarouselWidth || height != currentCarouselHeight) {
|
||||||
currentCarouselWidth = width
|
currentCarouselWidth = width
|
||||||
currentCarouselHeight = height
|
currentCarouselHeight = height
|
||||||
mediaCarouselScrollHandler.setCarouselBounds(currentCarouselWidth, currentCarouselHeight)
|
mediaCarouselScrollHandler.setCarouselBounds(
|
||||||
|
currentCarouselWidth, currentCarouselHeight)
|
||||||
updatePageIndicatorLocation()
|
updatePageIndicatorLocation()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -348,7 +373,7 @@ class MediaCarouselController @Inject constructor(
|
|||||||
if (currentlyShowingOnlyActive != endShowsActive ||
|
if (currentlyShowingOnlyActive != endShowsActive ||
|
||||||
((currentTransitionProgress != 1.0f && currentTransitionProgress != 0.0f) &&
|
((currentTransitionProgress != 1.0f && currentTransitionProgress != 0.0f) &&
|
||||||
startShowsActive != endShowsActive)) {
|
startShowsActive != endShowsActive)) {
|
||||||
/// Whenever we're transitioning from between differing states or the endstate differs
|
// Whenever we're transitioning from between differing states or the endstate differs
|
||||||
// we reset the translation
|
// we reset the translation
|
||||||
currentlyShowingOnlyActive = endShowsActive
|
currentlyShowingOnlyActive = endShowsActive
|
||||||
mediaCarouselScrollHandler.resetTranslation(animate = true)
|
mediaCarouselScrollHandler.resetTranslation(animate = true)
|
||||||
@@ -416,14 +441,15 @@ class MediaCarouselController @Inject constructor(
|
|||||||
height != carouselMeasureWidth && height != 0) {
|
height != carouselMeasureWidth && height != 0) {
|
||||||
carouselMeasureWidth = width
|
carouselMeasureWidth = width
|
||||||
carouselMeasureHeight = height
|
carouselMeasureHeight = height
|
||||||
playerWidthPlusPadding = carouselMeasureWidth + context.resources.getDimensionPixelSize(
|
val playerWidthPlusPadding = carouselMeasureWidth +
|
||||||
R.dimen.qs_media_padding)
|
context.resources.getDimensionPixelSize(R.dimen.qs_media_padding)
|
||||||
mediaCarouselScrollHandler.playerWidthPlusPadding = playerWidthPlusPadding
|
|
||||||
// Let's remeasure the carousel
|
// Let's remeasure the carousel
|
||||||
val widthSpec = desiredHostState?.measurementInput?.widthMeasureSpec ?: 0
|
val widthSpec = desiredHostState?.measurementInput?.widthMeasureSpec ?: 0
|
||||||
val heightSpec = desiredHostState?.measurementInput?.heightMeasureSpec ?: 0
|
val heightSpec = desiredHostState?.measurementInput?.heightMeasureSpec ?: 0
|
||||||
mediaCarousel.measure(widthSpec, heightSpec)
|
mediaCarousel.measure(widthSpec, heightSpec)
|
||||||
mediaCarousel.layout(0, 0, width, mediaCarousel.measuredHeight)
|
mediaCarousel.layout(0, 0, width, mediaCarousel.measuredHeight)
|
||||||
|
// Update the padding after layout; view widths are used in RTL to calculate scrollX
|
||||||
|
mediaCarouselScrollHandler.playerWidthPlusPadding = playerWidthPlusPadding
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,10 @@ class MediaCarouselScrollHandler(
|
|||||||
private var translationChangedListener: () -> Unit,
|
private var translationChangedListener: () -> Unit,
|
||||||
private val falsingManager: FalsingManager
|
private val falsingManager: FalsingManager
|
||||||
) {
|
) {
|
||||||
|
/**
|
||||||
|
* Is the view in RTL
|
||||||
|
*/
|
||||||
|
val isRtl: Boolean get() = scrollView.isLayoutRtl
|
||||||
/**
|
/**
|
||||||
* Do we need falsing protection?
|
* Do we need falsing protection?
|
||||||
*/
|
*/
|
||||||
@@ -121,14 +125,14 @@ class MediaCarouselScrollHandler(
|
|||||||
field = value
|
field = value
|
||||||
// The player width has changed, let's update the scroll position to make sure
|
// The player width has changed, let's update the scroll position to make sure
|
||||||
// it's still at the same place
|
// it's still at the same place
|
||||||
var newScroll = activeMediaIndex * playerWidthPlusPadding
|
var newRelativeScroll = activeMediaIndex * playerWidthPlusPadding
|
||||||
if (scrollIntoCurrentMedia > playerWidthPlusPadding) {
|
if (scrollIntoCurrentMedia > playerWidthPlusPadding) {
|
||||||
newScroll += playerWidthPlusPadding -
|
newRelativeScroll += playerWidthPlusPadding -
|
||||||
(scrollIntoCurrentMedia - playerWidthPlusPadding)
|
(scrollIntoCurrentMedia - playerWidthPlusPadding)
|
||||||
} else {
|
} else {
|
||||||
newScroll += scrollIntoCurrentMedia
|
newRelativeScroll += scrollIntoCurrentMedia
|
||||||
}
|
}
|
||||||
scrollView.scrollX = newScroll
|
scrollView.relativeScrollX = newRelativeScroll
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -184,8 +188,9 @@ class MediaCarouselScrollHandler(
|
|||||||
if (playerWidthPlusPadding == 0) {
|
if (playerWidthPlusPadding == 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
onMediaScrollingChanged(scrollX / playerWidthPlusPadding,
|
val relativeScrollX = scrollView.relativeScrollX
|
||||||
scrollX % playerWidthPlusPadding)
|
onMediaScrollingChanged(relativeScrollX / playerWidthPlusPadding,
|
||||||
|
relativeScrollX % playerWidthPlusPadding)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,11 +227,19 @@ class MediaCarouselScrollHandler(
|
|||||||
Math.abs(contentTranslation))
|
Math.abs(contentTranslation))
|
||||||
val settingsTranslation = (1.0f - settingsOffset) * -settingsButton.width *
|
val settingsTranslation = (1.0f - settingsOffset) * -settingsButton.width *
|
||||||
SETTINGS_BUTTON_TRANSLATION_FRACTION
|
SETTINGS_BUTTON_TRANSLATION_FRACTION
|
||||||
val newTranslationX: Float
|
val newTranslationX = if (isRtl) {
|
||||||
if (contentTranslation > 0) {
|
// In RTL, the 0-placement is on the right side of the view, not the left...
|
||||||
newTranslationX = settingsTranslation
|
if (contentTranslation > 0) {
|
||||||
|
-(scrollView.width - settingsTranslation - settingsButton.width)
|
||||||
|
} else {
|
||||||
|
-settingsTranslation
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
newTranslationX = scrollView.width - settingsTranslation - settingsButton.width
|
if (contentTranslation > 0) {
|
||||||
|
settingsTranslation
|
||||||
|
} else {
|
||||||
|
scrollView.width - settingsTranslation - settingsButton.width
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val rotation = (1.0f - settingsOffset) * 50
|
val rotation = (1.0f - settingsOffset) * 50
|
||||||
settingsButton.rotation = rotation * -Math.signum(contentTranslation)
|
settingsButton.rotation = rotation * -Math.signum(contentTranslation)
|
||||||
@@ -259,26 +272,26 @@ class MediaCarouselScrollHandler(
|
|||||||
}
|
}
|
||||||
if (isUp || motionEvent.action == MotionEvent.ACTION_CANCEL) {
|
if (isUp || motionEvent.action == MotionEvent.ACTION_CANCEL) {
|
||||||
// It's an up and the fling didn't take it above
|
// It's an up and the fling didn't take it above
|
||||||
val pos = scrollView.scrollX % playerWidthPlusPadding
|
val relativePos = scrollView.relativeScrollX % playerWidthPlusPadding
|
||||||
val scollXAmount: Int
|
val scrollXAmount: Int
|
||||||
if (pos > playerWidthPlusPadding / 2) {
|
if (relativePos > playerWidthPlusPadding / 2) {
|
||||||
scollXAmount = playerWidthPlusPadding - pos
|
scrollXAmount = playerWidthPlusPadding - relativePos
|
||||||
} else {
|
} else {
|
||||||
scollXAmount = -1 * pos
|
scrollXAmount = -1 * relativePos
|
||||||
}
|
}
|
||||||
if (scollXAmount != 0) {
|
if (scrollXAmount != 0) {
|
||||||
// Delay the scrolling since scrollView calls springback which cancels
|
// Delay the scrolling since scrollView calls springback which cancels
|
||||||
// the animation again..
|
// the animation again..
|
||||||
mainExecutor.execute {
|
mainExecutor.execute {
|
||||||
scrollView.smoothScrollBy(scollXAmount, 0)
|
scrollView.smoothScrollBy(if (isRtl) -scrollXAmount else scrollXAmount, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val currentTranslation = scrollView.getContentTranslation()
|
val currentTranslation = scrollView.getContentTranslation()
|
||||||
if (currentTranslation != 0.0f) {
|
if (currentTranslation != 0.0f) {
|
||||||
// We started a Swipe but didn't end up with a fling. Let's either go to the
|
// We started a Swipe but didn't end up with a fling. Let's either go to the
|
||||||
// dismissed position or go back.
|
// dismissed position or go back.
|
||||||
val springBack = Math.abs(currentTranslation) < getMaxTranslation() / 2
|
val springBack = Math.abs(currentTranslation) < getMaxTranslation() / 2 ||
|
||||||
|| isFalseTouch()
|
isFalseTouch()
|
||||||
val newTranslation: Float
|
val newTranslation: Float
|
||||||
if (springBack) {
|
if (springBack) {
|
||||||
newTranslation = 0.0f
|
newTranslation = 0.0f
|
||||||
@@ -313,9 +326,11 @@ class MediaCarouselScrollHandler(
|
|||||||
return gestureDetector.onTouchEvent(motionEvent)
|
return gestureDetector.onTouchEvent(motionEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onScroll(down: MotionEvent,
|
fun onScroll(
|
||||||
lastMotion: MotionEvent,
|
down: MotionEvent,
|
||||||
distanceX: Float): Boolean {
|
lastMotion: MotionEvent,
|
||||||
|
distanceX: Float
|
||||||
|
): Boolean {
|
||||||
val totalX = lastMotion.x - down.x
|
val totalX = lastMotion.x - down.x
|
||||||
val currentTranslation = scrollView.getContentTranslation()
|
val currentTranslation = scrollView.getContentTranslation()
|
||||||
if (currentTranslation != 0.0f ||
|
if (currentTranslation != 0.0f ||
|
||||||
@@ -339,8 +354,8 @@ class MediaCarouselScrollHandler(
|
|||||||
} // Otherwise we don't have do do anything, and will remove the unrubberbanded
|
} // Otherwise we don't have do do anything, and will remove the unrubberbanded
|
||||||
// translation
|
// translation
|
||||||
}
|
}
|
||||||
if (Math.signum(newTranslation) != Math.signum(currentTranslation)
|
if (Math.signum(newTranslation) != Math.signum(currentTranslation) &&
|
||||||
&& currentTranslation != 0.0f) {
|
currentTranslation != 0.0f) {
|
||||||
// We crossed the 0.0 threshold of the translation. Let's see if we're allowed
|
// We crossed the 0.0 threshold of the translation. Let's see if we're allowed
|
||||||
// to scroll into the new direction
|
// to scroll into the new direction
|
||||||
if (scrollView.canScrollHorizontally(-newTranslation.toInt())) {
|
if (scrollView.canScrollHorizontally(-newTranslation.toInt())) {
|
||||||
@@ -394,9 +409,10 @@ class MediaCarouselScrollHandler(
|
|||||||
scrollView.animationTargetX = newTranslation
|
scrollView.animationTargetX = newTranslation
|
||||||
} else {
|
} else {
|
||||||
// We're flinging the player! Let's go either to the previous or to the next player
|
// We're flinging the player! Let's go either to the previous or to the next player
|
||||||
val pos = scrollView.scrollX
|
val pos = scrollView.relativeScrollX
|
||||||
val currentIndex = if (playerWidthPlusPadding > 0) pos / playerWidthPlusPadding else 0
|
val currentIndex = if (playerWidthPlusPadding > 0) pos / playerWidthPlusPadding else 0
|
||||||
var destIndex = if (vX <= 0) currentIndex + 1 else currentIndex
|
val flungTowardEnd = if (isRtl) vX > 0 else vX < 0
|
||||||
|
var destIndex = if (flungTowardEnd) currentIndex + 1 else currentIndex
|
||||||
destIndex = Math.max(0, destIndex)
|
destIndex = Math.max(0, destIndex)
|
||||||
destIndex = Math.min(mediaContent.getChildCount() - 1, destIndex)
|
destIndex = Math.min(mediaContent.getChildCount() - 1, destIndex)
|
||||||
val view = mediaContent.getChildAt(destIndex)
|
val view = mediaContent.getChildAt(destIndex)
|
||||||
@@ -438,8 +454,14 @@ class MediaCarouselScrollHandler(
|
|||||||
activeMediaIndex = newIndex
|
activeMediaIndex = newIndex
|
||||||
updatePlayerVisibilities()
|
updatePlayerVisibilities()
|
||||||
}
|
}
|
||||||
val location = activeMediaIndex.toFloat() + if (playerWidthPlusPadding > 0)
|
val relativeLocation = activeMediaIndex.toFloat() + if (playerWidthPlusPadding > 0)
|
||||||
scrollInAmount.toFloat() / playerWidthPlusPadding else 0f
|
scrollInAmount.toFloat() / playerWidthPlusPadding else 0f
|
||||||
|
// Fix the location, because PageIndicator does not handle RTL internally
|
||||||
|
val location = if (isRtl) {
|
||||||
|
mediaContent.childCount - relativeLocation - 1
|
||||||
|
} else {
|
||||||
|
relativeLocation
|
||||||
|
}
|
||||||
pageIndicator.setLocation(location)
|
pageIndicator.setLocation(location)
|
||||||
updateClipToOutline()
|
updateClipToOutline()
|
||||||
}
|
}
|
||||||
@@ -480,13 +502,20 @@ class MediaCarouselScrollHandler(
|
|||||||
* where it was and update our scroll position.
|
* where it was and update our scroll position.
|
||||||
*/
|
*/
|
||||||
fun onPrePlayerRemoved(removed: MediaControlPanel) {
|
fun onPrePlayerRemoved(removed: MediaControlPanel) {
|
||||||
val beforeActive = mediaContent.indexOfChild(removed.view?.player) <= activeMediaIndex
|
val removedIndex = mediaContent.indexOfChild(removed.view?.player)
|
||||||
|
// If the removed index is less than the activeMediaIndex, then we need to decrement it.
|
||||||
|
// RTL has no effect on this, because indices are always relative (start-to-end).
|
||||||
|
// Update the index 'manually' since we won't always get a call to onMediaScrollingChanged
|
||||||
|
val beforeActive = removedIndex <= activeMediaIndex
|
||||||
if (beforeActive) {
|
if (beforeActive) {
|
||||||
// also update the index here since the scroll below might not always lead
|
|
||||||
// to a scrolling changed
|
|
||||||
activeMediaIndex = Math.max(0, activeMediaIndex - 1)
|
activeMediaIndex = Math.max(0, activeMediaIndex - 1)
|
||||||
scrollView.scrollX = Math.max(scrollView.scrollX -
|
}
|
||||||
playerWidthPlusPadding, 0)
|
// If the removed media item is "left of" the active one (in an absolute sense), we need to
|
||||||
|
// scroll the view to keep that player in view. This is because scroll position is always
|
||||||
|
// calculated from left to right.
|
||||||
|
val leftOfActive = if (isRtl) !beforeActive else beforeActive
|
||||||
|
if (leftOfActive) {
|
||||||
|
scrollView.scrollX = Math.max(scrollView.scrollX - playerWidthPlusPadding, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,6 +530,13 @@ class MediaCarouselScrollHandler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the MediaScrollView to the start.
|
||||||
|
*/
|
||||||
|
fun scrollToStart() {
|
||||||
|
scrollView.relativeScrollX = 0
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val CONTENT_TRANSLATION = object : FloatPropertyCompat<MediaCarouselScrollHandler>(
|
private val CONTENT_TRANSLATION = object : FloatPropertyCompat<MediaCarouselScrollHandler>(
|
||||||
"contentTranslation") {
|
"contentTranslation") {
|
||||||
|
|||||||
@@ -15,7 +15,10 @@ import com.android.systemui.util.animation.physicsAnimator
|
|||||||
* when only measuring children but not the parent, when trying to apply a new scroll position
|
* when only measuring children but not the parent, when trying to apply a new scroll position
|
||||||
*/
|
*/
|
||||||
class MediaScrollView @JvmOverloads constructor(
|
class MediaScrollView @JvmOverloads constructor(
|
||||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0)
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0
|
||||||
|
)
|
||||||
: HorizontalScrollView(context, attrs, defStyleAttr) {
|
: HorizontalScrollView(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
lateinit var contentContainer: ViewGroup
|
lateinit var contentContainer: ViewGroup
|
||||||
@@ -37,6 +40,26 @@ class MediaScrollView @JvmOverloads constructor(
|
|||||||
contentContainer.translationX
|
contentContainer.translationX
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert between the absolute (left-to-right) and relative (start-to-end) scrollX of the media
|
||||||
|
* carousel. The player indices are always relative (start-to-end) and the scrollView.scrollX
|
||||||
|
* is always absolute. This function is its own inverse.
|
||||||
|
*/
|
||||||
|
private fun transformScrollX(scrollX: Int): Int = if (isLayoutRtl) {
|
||||||
|
contentContainer.width - width - scrollX
|
||||||
|
} else {
|
||||||
|
scrollX
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the layoutDirection-relative (start-to-end) scroll X position of the carousel.
|
||||||
|
*/
|
||||||
|
var relativeScrollX: Int
|
||||||
|
get() = transformScrollX(scrollX)
|
||||||
|
set(value) {
|
||||||
|
scrollX = transformScrollX(value)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allow all scrolls to go through, use base implementation
|
* Allow all scrolls to go through, use base implementation
|
||||||
*/
|
*/
|
||||||
@@ -55,15 +78,15 @@ class MediaScrollView @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
|
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
|
||||||
var intercept = false;
|
var intercept = false
|
||||||
touchListener?.let {
|
touchListener?.let {
|
||||||
intercept = it.onInterceptTouchEvent(ev)
|
intercept = it.onInterceptTouchEvent(ev)
|
||||||
}
|
}
|
||||||
return super.onInterceptTouchEvent(ev) || intercept;
|
return super.onInterceptTouchEvent(ev) || intercept
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTouchEvent(ev: MotionEvent?): Boolean {
|
override fun onTouchEvent(ev: MotionEvent?): Boolean {
|
||||||
var touch = false;
|
var touch = false
|
||||||
touchListener?.let {
|
touchListener?.let {
|
||||||
touch = it.onTouchEvent(ev)
|
touch = it.onTouchEvent(ev)
|
||||||
}
|
}
|
||||||
@@ -75,9 +98,17 @@ class MediaScrollView @JvmOverloads constructor(
|
|||||||
contentContainer = getChildAt(0) as ViewGroup
|
contentContainer = getChildAt(0) as ViewGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun overScrollBy(deltaX: Int, deltaY: Int, scrollX: Int, scrollY: Int,
|
override fun overScrollBy(
|
||||||
scrollRangeX: Int, scrollRangeY: Int, maxOverScrollX: Int,
|
deltaX: Int,
|
||||||
maxOverScrollY: Int, isTouchEvent: Boolean): Boolean {
|
deltaY: Int,
|
||||||
|
scrollX: Int,
|
||||||
|
scrollY: Int,
|
||||||
|
scrollRangeX: Int,
|
||||||
|
scrollRangeY: Int,
|
||||||
|
maxOverScrollX: Int,
|
||||||
|
maxOverScrollY: Int,
|
||||||
|
isTouchEvent: Boolean
|
||||||
|
): Boolean {
|
||||||
if (getContentTranslation() != 0.0f) {
|
if (getContentTranslation() != 0.0f) {
|
||||||
// When we're dismissing we ignore all the scrolling
|
// When we're dismissing we ignore all the scrolling
|
||||||
return false
|
return false
|
||||||
|
|||||||
Reference in New Issue
Block a user