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
|
||||
~ limitations under the License
|
||||
-->
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="?android:attr/colorBackgroundFloating" />
|
||||
<corners
|
||||
android:bottomLeftRadius="@dimen/qs_media_corner_radius"
|
||||
android:topLeftRadius="@dimen/qs_media_corner_radius"
|
||||
android:bottomRightRadius="@dimen/qs_media_corner_radius"
|
||||
android:topRightRadius="@dimen/qs_media_corner_radius"
|
||||
/>
|
||||
</shape>
|
||||
<com.android.systemui.media.IlluminationDrawable
|
||||
xmlns:systemui="http://schemas.android.com/apk/res-auto"
|
||||
systemui:rippleMinSize="30dp"
|
||||
systemui:rippleMaxSize="135dp"
|
||||
systemui:highlight="15"
|
||||
systemui:cornerRadius="@dimen/qs_media_corner_radius" />
|
||||
@@ -154,5 +154,12 @@
|
||||
<declare-styleable name="CaptionsToggleImageButton">
|
||||
<attr name="optedOut" format="boolean" />
|
||||
</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>
|
||||
|
||||
|
||||
@@ -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.pm.PackageManager;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.media.MediaDescription;
|
||||
@@ -38,6 +39,7 @@ import android.widget.TextView;
|
||||
|
||||
import com.android.settingslib.media.LocalMediaManager;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.media.IlluminationDrawable;
|
||||
import com.android.systemui.media.MediaControlPanel;
|
||||
import com.android.systemui.media.SeekBarObserver;
|
||||
import com.android.systemui.media.SeekBarViewModel;
|
||||
@@ -173,7 +175,7 @@ public class QSMediaPlayer extends MediaControlPanel {
|
||||
LinearLayout parentActionsLayout = (LinearLayout) actionsContainer;
|
||||
int i = 0;
|
||||
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]);
|
||||
if (thatBtn == null || thatBtn.getDrawable() == null
|
||||
|| thatBtn.getVisibility() != View.VISIBLE) {
|
||||
@@ -187,6 +189,15 @@ public class QSMediaPlayer extends MediaControlPanel {
|
||||
thisBtn.setOnClickListener(v -> {
|
||||
Log.d(TAG, "clicking on other button");
|
||||
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