Merge "Add new touch effect to player" into rvc-dev
This commit is contained in:
@@ -14,13 +14,9 @@
|
|||||||
~ See the License for the specific language governing permissions and
|
~ See the License for the specific language governing permissions and
|
||||||
~ limitations under the License
|
~ limitations under the License
|
||||||
-->
|
-->
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
<com.android.systemui.media.IlluminationDrawable
|
||||||
android:shape="rectangle">
|
xmlns:systemui="http://schemas.android.com/apk/res-auto"
|
||||||
<solid android:color="?android:attr/colorBackgroundFloating" />
|
systemui:rippleMinSize="30dp"
|
||||||
<corners
|
systemui:rippleMaxSize="135dp"
|
||||||
android:bottomLeftRadius="@dimen/qs_media_corner_radius"
|
systemui:highlight="15"
|
||||||
android:topLeftRadius="@dimen/qs_media_corner_radius"
|
systemui:cornerRadius="@dimen/qs_media_corner_radius" />
|
||||||
android:bottomRightRadius="@dimen/qs_media_corner_radius"
|
|
||||||
android:topRightRadius="@dimen/qs_media_corner_radius"
|
|
||||||
/>
|
|
||||||
</shape>
|
|
||||||
@@ -154,5 +154,12 @@
|
|||||||
<declare-styleable name="CaptionsToggleImageButton">
|
<declare-styleable name="CaptionsToggleImageButton">
|
||||||
<attr name="optedOut" format="boolean" />
|
<attr name="optedOut" format="boolean" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
|
<declare-styleable name="IlluminationDrawable">
|
||||||
|
<attr name="highlight" format="integer" />
|
||||||
|
<attr name="cornerRadius" format="dimension" />
|
||||||
|
<attr name="rippleMinSize" format="dimension" />
|
||||||
|
<attr name="rippleMaxSize" format="dimension" />
|
||||||
|
</declare-styleable>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,190 @@
|
|||||||
|
package com.android.systemui.media
|
||||||
|
|
||||||
|
import android.animation.Animator
|
||||||
|
import android.animation.AnimatorListenerAdapter
|
||||||
|
import android.animation.AnimatorSet
|
||||||
|
import android.animation.ValueAnimator
|
||||||
|
import android.content.res.ColorStateList
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.ColorFilter
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.graphics.PixelFormat
|
||||||
|
import android.graphics.RadialGradient
|
||||||
|
import android.graphics.Shader
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.util.MathUtils
|
||||||
|
import android.util.MathUtils.lerp
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
import com.android.internal.graphics.ColorUtils
|
||||||
|
import com.android.internal.graphics.ColorUtils.blendARGB
|
||||||
|
import com.android.systemui.Interpolators
|
||||||
|
import com.android.systemui.R
|
||||||
|
import org.xmlpull.v1.XmlPullParser
|
||||||
|
|
||||||
|
private const val BACKGROUND_ANIM_DURATION = 370L
|
||||||
|
private const val RIPPLE_ANIM_DURATION = 800L
|
||||||
|
private val GRADIENT_STOPS = floatArrayOf(0.2f, 1f)
|
||||||
|
|
||||||
|
private data class RippleData(
|
||||||
|
var x: Float,
|
||||||
|
var y: Float,
|
||||||
|
var alpha: Float,
|
||||||
|
var progress: Float,
|
||||||
|
var minSize: Float,
|
||||||
|
var maxSize: Float,
|
||||||
|
var highlight: Float
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drawable that can draw an animated gradient when tapped.
|
||||||
|
*/
|
||||||
|
@Keep
|
||||||
|
class IlluminationDrawable : Drawable() {
|
||||||
|
|
||||||
|
private var cornerRadius = 0f
|
||||||
|
private var highlightColor = Color.TRANSPARENT
|
||||||
|
private val rippleData = RippleData(0f, 0f, 0f, 0f, 0f, 0f, 0f)
|
||||||
|
private var tmpHsl = floatArrayOf(0f, 0f, 0f)
|
||||||
|
private var paint = Paint()
|
||||||
|
|
||||||
|
var backgroundColor = Color.TRANSPARENT
|
||||||
|
set(value) {
|
||||||
|
if (value == field) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
field = value
|
||||||
|
animateBackground()
|
||||||
|
}
|
||||||
|
|
||||||
|
private var rippleAnimation: AnimatorSet? = null
|
||||||
|
private var backgroundAnimation: ValueAnimator? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw background and gradient.
|
||||||
|
*/
|
||||||
|
override fun draw(canvas: Canvas) {
|
||||||
|
paint.shader = if (rippleData.progress > 0) {
|
||||||
|
val radius = lerp(rippleData.minSize, rippleData.maxSize, rippleData.progress)
|
||||||
|
val centerColor = blendARGB(paint.color, highlightColor, rippleData.alpha)
|
||||||
|
RadialGradient(rippleData.x, rippleData.y, radius, intArrayOf(centerColor, paint.color),
|
||||||
|
GRADIENT_STOPS, Shader.TileMode.CLAMP)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
canvas.drawRoundRect(0f, 0f, bounds.width().toFloat(), bounds.height().toFloat(),
|
||||||
|
cornerRadius, cornerRadius, paint)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getOpacity(): Int {
|
||||||
|
return PixelFormat.TRANSPARENT
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun inflate(
|
||||||
|
r: Resources,
|
||||||
|
parser: XmlPullParser,
|
||||||
|
attrs: AttributeSet,
|
||||||
|
theme: Resources.Theme?
|
||||||
|
) {
|
||||||
|
val a = obtainAttributes(r, theme, attrs, R.styleable.IlluminationDrawable)
|
||||||
|
cornerRadius = a.getDimension(R.styleable.IlluminationDrawable_cornerRadius, cornerRadius)
|
||||||
|
rippleData.minSize = a.getDimension(R.styleable.IlluminationDrawable_rippleMinSize, 0f)
|
||||||
|
rippleData.maxSize = a.getDimension(R.styleable.IlluminationDrawable_rippleMaxSize, 0f)
|
||||||
|
rippleData.highlight = a.getInteger(R.styleable.IlluminationDrawable_highlight, 0) / 100f
|
||||||
|
a.recycle()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setColorFilter(p0: ColorFilter?) {
|
||||||
|
throw UnsupportedOperationException("Color filters are not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setAlpha(value: Int) {
|
||||||
|
throw UnsupportedOperationException("Alpha is not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cross fade background.
|
||||||
|
* @see setTintList
|
||||||
|
* @see backgroundColor
|
||||||
|
*/
|
||||||
|
private fun animateBackground() {
|
||||||
|
ColorUtils.colorToHSL(backgroundColor, tmpHsl)
|
||||||
|
val L = tmpHsl[2]
|
||||||
|
tmpHsl[2] = MathUtils.constrain(if (L < 1f - rippleData.highlight) {
|
||||||
|
L + rippleData.highlight
|
||||||
|
} else {
|
||||||
|
L - rippleData.highlight
|
||||||
|
}, 0f, 1f)
|
||||||
|
|
||||||
|
val initialBackground = paint.color
|
||||||
|
val initialHighlight = highlightColor
|
||||||
|
val finalHighlight = ColorUtils.HSLToColor(tmpHsl)
|
||||||
|
|
||||||
|
backgroundAnimation?.cancel()
|
||||||
|
backgroundAnimation = ValueAnimator.ofFloat(0f, 1f).apply {
|
||||||
|
duration = BACKGROUND_ANIM_DURATION
|
||||||
|
interpolator = Interpolators.FAST_OUT_LINEAR_IN
|
||||||
|
addUpdateListener {
|
||||||
|
val progress = it.animatedValue as Float
|
||||||
|
paint.color = blendARGB(initialBackground, backgroundColor, progress)
|
||||||
|
highlightColor = blendARGB(initialHighlight, finalHighlight, progress)
|
||||||
|
invalidateSelf()
|
||||||
|
}
|
||||||
|
addListener(object : AnimatorListenerAdapter() {
|
||||||
|
override fun onAnimationEnd(animation: Animator?) {
|
||||||
|
backgroundAnimation = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setTintList(tint: ColorStateList?) {
|
||||||
|
super.setTintList(tint)
|
||||||
|
backgroundColor = tint!!.defaultColor
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws an animated ripple that expands from {@param x} and {@param y} fading away.
|
||||||
|
*
|
||||||
|
* @param x X position of gradient centroid.
|
||||||
|
* @param y Y position of gradient centroid.
|
||||||
|
*/
|
||||||
|
fun illuminate(x: Int, y: Int) {
|
||||||
|
rippleData.x = x.toFloat()
|
||||||
|
rippleData.y = y.toFloat()
|
||||||
|
rippleData.alpha = 1f
|
||||||
|
rippleData.progress = 0f
|
||||||
|
invalidateSelf()
|
||||||
|
|
||||||
|
rippleAnimation?.cancel()
|
||||||
|
rippleAnimation = AnimatorSet().apply {
|
||||||
|
playTogether(ValueAnimator.ofFloat(1f, 0f).apply {
|
||||||
|
startDelay = 133
|
||||||
|
duration = RIPPLE_ANIM_DURATION - startDelay
|
||||||
|
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
|
||||||
|
addUpdateListener {
|
||||||
|
rippleData.alpha = it.animatedValue as Float
|
||||||
|
invalidateSelf()
|
||||||
|
}
|
||||||
|
}, ValueAnimator.ofFloat(0f, 1f).apply {
|
||||||
|
duration = RIPPLE_ANIM_DURATION
|
||||||
|
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
|
||||||
|
addUpdateListener {
|
||||||
|
rippleData.progress = it.animatedValue as Float
|
||||||
|
invalidateSelf()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
addListener(object : AnimatorListenerAdapter() {
|
||||||
|
override fun onAnimationEnd(animation: Animator?) {
|
||||||
|
rippleData.progress = 0f
|
||||||
|
rippleAnimation = null
|
||||||
|
invalidateSelf()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ import android.app.PendingIntent;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
|
import android.graphics.Rect;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.Icon;
|
import android.graphics.drawable.Icon;
|
||||||
import android.media.MediaDescription;
|
import android.media.MediaDescription;
|
||||||
@@ -38,6 +39,7 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import com.android.settingslib.media.LocalMediaManager;
|
import com.android.settingslib.media.LocalMediaManager;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
|
import com.android.systemui.media.IlluminationDrawable;
|
||||||
import com.android.systemui.media.MediaControlPanel;
|
import com.android.systemui.media.MediaControlPanel;
|
||||||
import com.android.systemui.media.SeekBarObserver;
|
import com.android.systemui.media.SeekBarObserver;
|
||||||
import com.android.systemui.media.SeekBarViewModel;
|
import com.android.systemui.media.SeekBarViewModel;
|
||||||
@@ -173,7 +175,7 @@ public class QSMediaPlayer extends MediaControlPanel {
|
|||||||
LinearLayout parentActionsLayout = (LinearLayout) actionsContainer;
|
LinearLayout parentActionsLayout = (LinearLayout) actionsContainer;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (; i < parentActionsLayout.getChildCount() && i < QS_ACTION_IDS.length; i++) {
|
for (; i < parentActionsLayout.getChildCount() && i < QS_ACTION_IDS.length; i++) {
|
||||||
ImageButton thisBtn = mMediaNotifView.findViewById(QS_ACTION_IDS[i]);
|
final ImageButton thisBtn = mMediaNotifView.findViewById(QS_ACTION_IDS[i]);
|
||||||
ImageButton thatBtn = parentActionsLayout.findViewById(NOTIF_ACTION_IDS[i]);
|
ImageButton thatBtn = parentActionsLayout.findViewById(NOTIF_ACTION_IDS[i]);
|
||||||
if (thatBtn == null || thatBtn.getDrawable() == null
|
if (thatBtn == null || thatBtn.getDrawable() == null
|
||||||
|| thatBtn.getVisibility() != View.VISIBLE) {
|
|| thatBtn.getVisibility() != View.VISIBLE) {
|
||||||
@@ -187,6 +189,15 @@ public class QSMediaPlayer extends MediaControlPanel {
|
|||||||
thisBtn.setOnClickListener(v -> {
|
thisBtn.setOnClickListener(v -> {
|
||||||
Log.d(TAG, "clicking on other button");
|
Log.d(TAG, "clicking on other button");
|
||||||
thatBtn.performClick();
|
thatBtn.performClick();
|
||||||
|
if (mMediaNotifView.getBackground() instanceof IlluminationDrawable) {
|
||||||
|
Rect mediaRect = new Rect();
|
||||||
|
Rect buttonRect = new Rect();
|
||||||
|
mMediaNotifView.getGlobalVisibleRect(mediaRect);
|
||||||
|
thisBtn.getGlobalVisibleRect(buttonRect);
|
||||||
|
((IlluminationDrawable) mMediaNotifView.getBackground()).illuminate(
|
||||||
|
buttonRect.centerX() - mediaRect.left,
|
||||||
|
buttonRect.centerY() - mediaRect.top);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user