1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.settings.accessibility; 18 19 import android.content.Context; 20 import android.graphics.SurfaceTexture; 21 import android.media.MediaPlayer; 22 import android.view.Surface; 23 import android.view.TextureView; 24 import android.view.TextureView.SurfaceTextureListener; 25 26 import androidx.annotation.GuardedBy; 27 import androidx.annotation.RawRes; 28 29 /** 30 * Plays the video by {@link MediaPlayer} on {@link TextureView}, calls {@link #create(Context, int, 31 * TextureView)} to setup the listener for TextureView and start to play the video. Once this player 32 * is no longer used, call {@link #release()} so that MediaPlayer object can be released. 33 */ 34 public class VideoPlayer implements SurfaceTextureListener { 35 private final Context context; 36 private final Object mediaPlayerLock = new Object(); 37 // Media player object can't be used after it has been released, so it will be set to null. But 38 // VideoPlayer is asynchronized, media player object might be paused or resumed again before 39 // released media player is set to null. Therefore, lock mediaPlayer and mediaPlayerState by 40 // mediaPlayerLock keep their states consistent. 41 @GuardedBy("mediaPlayerLock") 42 private MediaPlayer mediaPlayer; 43 @GuardedBy("mediaPlayerLock") 44 private State mediaPlayerState = State.NONE; 45 @RawRes 46 private final int videoRes; 47 private Surface animationSurface; 48 49 50 /** 51 * Creates a {@link MediaPlayer} for a given resource id and starts playback when the surface 52 * for 53 * a given {@link TextureView} is ready. 54 */ create(Context context, @RawRes int videoRes, TextureView textureView)55 public static VideoPlayer create(Context context, @RawRes int videoRes, 56 TextureView textureView) { 57 return new VideoPlayer(context, videoRes, textureView); 58 } 59 VideoPlayer(Context context, @RawRes int videoRes, TextureView textureView)60 private VideoPlayer(Context context, @RawRes int videoRes, TextureView textureView) { 61 this.context = context; 62 this.videoRes = videoRes; 63 textureView.setSurfaceTextureListener(this); 64 } 65 pause()66 public void pause() { 67 synchronized (mediaPlayerLock) { 68 if (mediaPlayerState == State.STARTED) { 69 mediaPlayerState = State.PAUSED; 70 mediaPlayer.pause(); 71 } 72 } 73 } 74 resume()75 public void resume() { 76 synchronized (mediaPlayerLock) { 77 if (mediaPlayerState == State.PAUSED) { 78 mediaPlayer.start(); 79 mediaPlayerState = State.STARTED; 80 } 81 } 82 } 83 84 /** Release media player when it's no longer needed. */ release()85 public void release() { 86 synchronized (mediaPlayerLock) { 87 if (mediaPlayerState != State.NONE && mediaPlayerState != State.END) { 88 mediaPlayerState = State.END; 89 mediaPlayer.release(); 90 mediaPlayer = null; 91 } 92 } 93 if (animationSurface != null) { 94 animationSurface.release(); 95 animationSurface = null; 96 } 97 } 98 99 @Override onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height)100 public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { 101 animationSurface = new Surface(surface); 102 synchronized (mediaPlayerLock) { 103 mediaPlayer = MediaPlayer.create(context, videoRes); 104 mediaPlayerState = State.PREPARED; 105 mediaPlayer.setSurface(animationSurface); 106 mediaPlayer.setLooping(true); 107 mediaPlayer.start(); 108 mediaPlayerState = State.STARTED; 109 } 110 } 111 112 @Override onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height)113 public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { 114 } 115 116 @Override onSurfaceTextureDestroyed(SurfaceTexture surface)117 public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { 118 release(); 119 return false; 120 } 121 122 @Override onSurfaceTextureUpdated(SurfaceTexture surface)123 public void onSurfaceTextureUpdated(SurfaceTexture surface) { 124 } 125 126 /** 127 * The state of MediaPlayer object. Refer to 128 * https://developer.android.com/reference/android/media/MediaPlayer#StateDiagram. 129 */ 130 public enum State { 131 /** MediaPlayer objects has not be created. */ 132 NONE, 133 /** MediaPlayer objects is created by create() method. */ 134 PREPARED, 135 /** MediaPlayer is started. It can be paused by pause() method. */ 136 STARTED, 137 /** MediaPlayer object is paused. Calling start() to resume it. */ 138 PAUSED, 139 /** 140 * MediaPlayer object is stopped and cannot be started until calling prepare() or 141 * prepareAsync() 142 * methods. 143 */ 144 STOPPED, 145 /** MediaPlayer object is released. It cannot be used again. */ 146 END 147 } 148 } 149 150