mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Add an example to integrate android.animation
PiperOrigin-RevId: 687274900
This commit is contained in:
parent
2a49ffcb23
commit
f52c7a1d5c
@ -0,0 +1,188 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package androidx.media3.demo.transformer;
|
||||||
|
|
||||||
|
import android.animation.FloatEvaluator;
|
||||||
|
import android.animation.Keyframe;
|
||||||
|
import android.animation.PropertyValuesHolder;
|
||||||
|
import android.animation.TypeEvaluator;
|
||||||
|
import android.animation.ValueAnimator;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.util.Pair;
|
||||||
|
import android.view.animation.LinearInterpolator;
|
||||||
|
import androidx.media3.common.VideoFrameProcessingException;
|
||||||
|
import androidx.media3.common.util.Util;
|
||||||
|
import androidx.media3.effect.DrawableOverlay;
|
||||||
|
import androidx.media3.effect.OverlaySettings;
|
||||||
|
import androidx.media3.effect.TextureOverlay;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An animated {@link TextureOverlay} using {@link android.animation}.
|
||||||
|
*
|
||||||
|
* <p>The rotation is controlled by a simple {@link ValueAnimator}, while the position is controlled
|
||||||
|
* by key frames.
|
||||||
|
*/
|
||||||
|
public class AnimatedLogoOverlay extends DrawableOverlay {
|
||||||
|
|
||||||
|
private static final long ROTATION_PERIOD_MS = 2_000;
|
||||||
|
private static final long POSITION_PERIOD_MS = 5_000;
|
||||||
|
private static final float POSITION_X_BOUND = 0.8f;
|
||||||
|
private static final float POSITION_Y_BOUND = 0.7f;
|
||||||
|
|
||||||
|
private final Drawable logo;
|
||||||
|
private final AnimatedOverlaySettings overlaySettings;
|
||||||
|
|
||||||
|
public AnimatedLogoOverlay(Context context) {
|
||||||
|
try {
|
||||||
|
logo = context.getPackageManager().getApplicationIcon(context.getPackageName());
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
logo.setBounds(
|
||||||
|
/* left= */ 0, /* top= */ 0, logo.getIntrinsicWidth(), logo.getIntrinsicHeight());
|
||||||
|
|
||||||
|
ValueAnimator rotationAnimator = ValueAnimator.ofFloat(0, 360);
|
||||||
|
rotationAnimator.setRepeatMode(ValueAnimator.RESTART);
|
||||||
|
rotationAnimator.setRepeatCount(ValueAnimator.INFINITE);
|
||||||
|
rotationAnimator.setDuration(ROTATION_PERIOD_MS);
|
||||||
|
// Rotate the logo with a constant angular velocity.
|
||||||
|
rotationAnimator.setInterpolator(new LinearInterpolator());
|
||||||
|
|
||||||
|
Keyframe[] keyFrames = new Keyframe[5];
|
||||||
|
keyFrames[0] =
|
||||||
|
Keyframe.ofObject(/* fraction= */ 0f, Pair.create(-POSITION_X_BOUND, -POSITION_Y_BOUND));
|
||||||
|
keyFrames[2] =
|
||||||
|
Keyframe.ofObject(/* fraction= */ 0.5f, Pair.create(POSITION_X_BOUND, POSITION_Y_BOUND));
|
||||||
|
keyFrames[1] =
|
||||||
|
Keyframe.ofObject(/* fraction= */ 0.25f, Pair.create(-POSITION_X_BOUND, POSITION_Y_BOUND));
|
||||||
|
keyFrames[3] =
|
||||||
|
Keyframe.ofObject(/* fraction= */ 0.75f, Pair.create(POSITION_X_BOUND, -POSITION_Y_BOUND));
|
||||||
|
keyFrames[4] =
|
||||||
|
Keyframe.ofObject(/* fraction= */ 1f, Pair.create(-POSITION_X_BOUND, -POSITION_Y_BOUND));
|
||||||
|
PropertyValuesHolder positionValuesHolder =
|
||||||
|
PropertyValuesHolder.ofKeyframe("position", keyFrames);
|
||||||
|
|
||||||
|
ValueAnimator positionAnimator = ValueAnimator.ofPropertyValuesHolder(positionValuesHolder);
|
||||||
|
// The position can also be animated using separate animators for x and y, the purpose of
|
||||||
|
// PairEvaluator is to use one animator for both x and y.
|
||||||
|
positionAnimator.setEvaluator(new AnimatedOverlaySettings.PairEvaluator());
|
||||||
|
positionAnimator.setRepeatMode(ValueAnimator.RESTART);
|
||||||
|
positionAnimator.setRepeatCount(ValueAnimator.INFINITE);
|
||||||
|
positionAnimator.setDuration(POSITION_PERIOD_MS);
|
||||||
|
|
||||||
|
overlaySettings = new AnimatedOverlaySettings(rotationAnimator, positionAnimator);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Drawable getDrawable(long presentationTimeUs) {
|
||||||
|
return logo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OverlaySettings getOverlaySettings(long presentationTimeUs) {
|
||||||
|
overlaySettings.setCurrentPresentationTimeUs(presentationTimeUs);
|
||||||
|
return overlaySettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void release() throws VideoFrameProcessingException {
|
||||||
|
super.release();
|
||||||
|
overlaySettings.stopAnimation();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class AnimatedOverlaySettings implements OverlaySettings {
|
||||||
|
private final ValueAnimator rotationAnimator;
|
||||||
|
private final ValueAnimator positionAnimator;
|
||||||
|
private final Handler mainThreadHandler;
|
||||||
|
|
||||||
|
private boolean started;
|
||||||
|
|
||||||
|
public AnimatedOverlaySettings(ValueAnimator rotationAnimator, ValueAnimator positionAnimator) {
|
||||||
|
this.rotationAnimator = rotationAnimator;
|
||||||
|
this.positionAnimator = positionAnimator;
|
||||||
|
mainThreadHandler = new Handler(Util.getCurrentOrMainLooper());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentPresentationTimeUs(long presentationTimeUs) {
|
||||||
|
// Sets the animation time to the video presentation time, so the animation is presentation
|
||||||
|
// time based.
|
||||||
|
rotationAnimator.setCurrentPlayTime(presentationTimeUs / 1000);
|
||||||
|
positionAnimator.setCurrentPlayTime(presentationTimeUs / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getRotationDegrees() {
|
||||||
|
maybeStartAnimator();
|
||||||
|
return (float) rotationAnimator.getAnimatedValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pair<Float, Float> getBackgroundFrameAnchor() {
|
||||||
|
maybeStartAnimator();
|
||||||
|
return (Pair<Float, Float>) positionAnimator.getAnimatedValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopAnimation() {
|
||||||
|
mainThreadHandler.post(
|
||||||
|
() -> {
|
||||||
|
rotationAnimator.cancel();
|
||||||
|
positionAnimator.cancel();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void maybeStartAnimator() {
|
||||||
|
if (!started) {
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
mainThreadHandler.post(
|
||||||
|
() -> {
|
||||||
|
rotationAnimator.start();
|
||||||
|
positionAnimator.start();
|
||||||
|
latch.countDown();
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
// Block until the animators are actually started, or they'll return null values.
|
||||||
|
latch.await();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
started = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** An {@link TypeEvaluator} to animate position in the form of {@link Pair} of floats. */
|
||||||
|
private static class PairEvaluator implements TypeEvaluator<Pair<Float, Float>> {
|
||||||
|
private final FloatEvaluator floatEvaluator;
|
||||||
|
|
||||||
|
private PairEvaluator() {
|
||||||
|
floatEvaluator = new FloatEvaluator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pair<Float, Float> evaluate(
|
||||||
|
float fraction, Pair<Float, Float> startValue, Pair<Float, Float> endValue) {
|
||||||
|
return Pair.create(
|
||||||
|
floatEvaluator.evaluate(fraction, startValue.first, endValue.first),
|
||||||
|
floatEvaluator.evaluate(fraction, startValue.second, endValue.second));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -116,6 +116,7 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
public static final int TEXT_OVERLAY_INDEX = 12;
|
public static final int TEXT_OVERLAY_INDEX = 12;
|
||||||
public static final int CLOCK_OVERLAY_INDEX = 13;
|
public static final int CLOCK_OVERLAY_INDEX = 13;
|
||||||
public static final int CONFETTI_OVERLAY_INDEX = 14;
|
public static final int CONFETTI_OVERLAY_INDEX = 14;
|
||||||
|
public static final int ANIMATING_LOGO_OVERLAY = 15;
|
||||||
|
|
||||||
// Audio effect selections.
|
// Audio effect selections.
|
||||||
public static final int HIGH_PITCHED_INDEX = 0;
|
public static final int HIGH_PITCHED_INDEX = 0;
|
||||||
|
@ -654,6 +654,9 @@ public final class TransformerActivity extends AppCompatActivity {
|
|||||||
if (selectedEffects[ConfigurationActivity.CONFETTI_OVERLAY_INDEX]) {
|
if (selectedEffects[ConfigurationActivity.CONFETTI_OVERLAY_INDEX]) {
|
||||||
overlaysBuilder.add(new ConfettiOverlay());
|
overlaysBuilder.add(new ConfettiOverlay());
|
||||||
}
|
}
|
||||||
|
if (selectedEffects[ConfigurationActivity.ANIMATING_LOGO_OVERLAY]) {
|
||||||
|
overlaysBuilder.add(new AnimatedLogoOverlay(this.getApplicationContext()));
|
||||||
|
}
|
||||||
|
|
||||||
ImmutableList<TextureOverlay> overlays = overlaysBuilder.build();
|
ImmutableList<TextureOverlay> overlays = overlaysBuilder.build();
|
||||||
return overlays.isEmpty() ? null : new OverlayEffect(overlays);
|
return overlays.isEmpty() ? null : new OverlayEffect(overlays);
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
<item>Custom Text Overlay</item>
|
<item>Custom Text Overlay</item>
|
||||||
<item>Clock Overlay</item>
|
<item>Clock Overlay</item>
|
||||||
<item>Confetti Overlay</item>
|
<item>Confetti Overlay</item>
|
||||||
|
<item>Animated logo</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string-array name="audio_effects_names">
|
<string-array name="audio_effects_names">
|
||||||
<item>High pitched</item>
|
<item>High pitched</item>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user