package com.afollestad.materialcamera.internal;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Size;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import com.afollestad.materialcamera.R;
import com.afollestad.materialdialogs.MaterialDialog;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* @author Aidan Follestad (afollestad)
*/
public class VideoStreamView extends SurfaceView implements SurfaceHolder.Callback,
MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener, MediaPlayer.OnBufferingUpdateListener, MediaPlayer.OnVideoSizeChangedListener {
@Override
public void onPrepared(MediaPlayer mp) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
start(mContext);
if (!mAutoPlay) pause();
}
}, 250);
if (mCallback != null)
mCallback.onPrepared(mp);
}
@Override
public void onCompletion(MediaPlayer mp) {
if (mCallback != null)
mCallback.onCompleted();
}
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
mCallback.onError(mp, what, extra);
return true;
}
@Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
if (mCallback != null)
mCallback.onBuffer(percent);
}
@Override
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
requestLayout();
}
public interface Callback {
void onPrepared(MediaPlayer mp);
void onCompleted();
void onError(MediaPlayer mp, int what, int extra);
void onBuffer(int percent);
}
public VideoStreamView(Context context) {
super(context);
initPlayer();
}
public VideoStreamView(Context context, AttributeSet attrs) {
super(context, attrs);
initPlayer();
}
public VideoStreamView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPlayer();
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public VideoStreamView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initPlayer();
}
public void saveInstanceState(Bundle to) {
to.putParcelable("uri", mUri);
}
public void restoreInstanceState(Bundle from, Callback callback) {
if (from != null) {
mUri = from.getParcelable("uri");
mCallback = callback;
}
}
private void initPlayer() {
if (isInEditMode()) return;
else if (mPlayer != null) {
if (mPlayer.isPlaying())
mPlayer.stop();
mPlayer.reset();
mPlayer.release();
mPlayer = null;
}
mPlayer = new MediaPlayer();
mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
}
private Activity mContext;
protected Uri mUri;
private Callback mCallback;
protected MediaPlayer mPlayer;
private boolean mAutoPlay;
public void setAutoPlay(boolean autoPlay) {
mAutoPlay = autoPlay;
}
public void setURI(@NonNull Activity context, @NonNull Uri uri, @NonNull Callback callback) {
mContext = context;
mUri = uri;
mCallback = callback;
initPlayer();
mPlayer.setOnPreparedListener(this);
mPlayer.setOnCompletionListener(this);
mPlayer.setOnErrorListener(this);
mPlayer.setOnBufferingUpdateListener(this);
mPlayer.setOnVideoSizeChangedListener(this);
try {
mPlayer.setDataSource(context, uri);
mPlayer.prepareAsync();
} catch (Throwable e) {
Log.d("VideoStreamView", "Failed to setDataSource/prepareAsync: " + e.getMessage());
e.printStackTrace();
new MaterialDialog.Builder(mContext)
.title(R.string.mcam_error)
.content(e.getMessage())
.positiveText(android.R.string.ok)
.show();
}
}
public boolean start(Activity context) {
mContext = context;
if (mPlayer == null) {
initPlayer();
setURI(mContext, mUri, mCallback);
return false;
}
try {
mPlayer.setDisplay(getHolder());
mPlayer.start();
} catch (IllegalArgumentException | IllegalStateException e) {
e.printStackTrace();
}
return true;
}
public void seekTo(int msec) {
if (mPlayer == null) return;
mPlayer.seekTo(msec);
}
public int getCurrentPosition() {
if (mPlayer == null)
return -1;
final int currentPosition = mPlayer.getCurrentPosition();
int currentPositionAdjusted = currentPosition - 500;
if (currentPositionAdjusted < 0) currentPositionAdjusted = 0;
return currentPositionAdjusted;
}
public int getDuration() {
if (mPlayer == null)
return -1;
return mPlayer.getDuration();
}
public boolean isPlaying() {
return mPlayer != null && mPlayer.isPlaying();
}
public void pause() {
if (mPlayer != null)
mPlayer.pause();
}
public void stop() {
if (mPlayer != null)
mPlayer.stop();
}
public void release() {
if (mPlayer != null) {
if (mPlayer.isPlaying())
mPlayer.stop();
mPlayer.reset();
mPlayer.release();
mPlayer = null;
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
mPlayer.setDisplay(holder);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
holder.addCallback(this);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
holder.removeCallback(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mPlayer != null)
mPlayer.release();
}
// public enum Orientation {
// Portrait(Configuration.ORIENTATION_PORTRAIT),
// Landscape(Configuration.ORIENTATION_LANDSCAPE);
//
// int mValue;
//
// Orientation(int value) {
// mValue = value;
// }
//
// public static Orientation from(int value) {
// switch (value) {
// default:
// return Portrait;
// case Configuration.ORIENTATION_LANDSCAPE:
// return Landscape;
// }
// }
// }
@IntDef({ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE,
ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT, ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE})
@Retention(RetentionPolicy.SOURCE)
public @interface ActivityOrientation {
}
@ActivityOrientation
public static int getScreenOrientation(Activity context) {
int rotation = context.getWindowManager().getDefaultDisplay().getRotation();
DisplayMetrics dm = new DisplayMetrics();
context.getWindowManager().getDefaultDisplay().getMetrics(dm);
int width = dm.widthPixels;
int height = dm.heightPixels;
int orientation;
// if the device's natural orientation is portrait:
if ((rotation == Surface.ROTATION_0
|| rotation == Surface.ROTATION_180) && height > width ||
(rotation == Surface.ROTATION_90
|| rotation == Surface.ROTATION_270) && width > height) {
switch (rotation) {
case Surface.ROTATION_0:
orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
break;
case Surface.ROTATION_90:
orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
break;
case Surface.ROTATION_180:
orientation =
ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
break;
case Surface.ROTATION_270:
orientation =
ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
break;
default:
Log.e("VideoStreamView", "Unknown screen orientation. Defaulting to portrait.");
orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
break;
}
}
// if the device's natural orientation is landscape or if the device
// is square:
else {
switch (rotation) {
case Surface.ROTATION_0:
orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
break;
case Surface.ROTATION_90:
orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
break;
case Surface.ROTATION_180:
orientation =
ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
break;
case Surface.ROTATION_270:
orientation =
ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
break;
default:
Log.e("VideoStreamView", "Unknown screen orientation. Defaulting to landscape.");
orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
break;
}
}
return orientation;
}
@Size(value = 2)
private int[] getDimensions(int orientation, float videoWidth, float videoHeight) {
final float aspectRatio = videoWidth / videoHeight;
int width;
int height;
if (orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT ||
orientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT) {
width = getMeasuredWidth();
height = (int) ((float) width / aspectRatio);
if (height > getMeasuredHeight()) {
height = getMeasuredHeight();
width = (int) ((float) height * aspectRatio);
}
} else {
height = getMeasuredHeight();
width = (int) ((float) height * aspectRatio);
if (width > getMeasuredWidth()) {
width = getMeasuredWidth();
height = (int) ((float) width / aspectRatio);
}
}
return new int[]{width, height};
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
try {
if (mPlayer != null) {
final float videoWidth = (float) mPlayer.getVideoWidth();
final float videoHeight = (float) mPlayer.getVideoHeight();
if (videoWidth == 0 || videoHeight == 0) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}
int[] dimensions = getDimensions(getScreenOrientation(mContext), videoWidth, videoHeight);
setMeasuredDimension(dimensions[0], dimensions[1]);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
}