diff --git a/AndroidManifest.xml b/AndroidManifest.xml index e846885..2aa1261 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -8,15 +8,32 @@ android:targetSdkVersion="17" /> + android:label="@string/vitamio_name" > + + + + + \ No newline at end of file diff --git a/res/layout/activity_video.xml b/res/layout/activity_video.xml index cc4bddb..aaa8425 100644 --- a/res/layout/activity_video.xml +++ b/res/layout/activity_video.xml @@ -44,36 +44,6 @@ android:textColor="@android:color/white" android:textSize="20.0sp" /> - - - - - - - \ No newline at end of file diff --git a/res/layout/mediacontroller.xml b/res/layout/mediacontroller.xml index 52c284a..277e31a 100644 --- a/res/layout/mediacontroller.xml +++ b/res/layout/mediacontroller.xml @@ -8,7 +8,8 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="0.0dip" - android:orientation="horizontal" > + android:orientation="horizontal" + android:visibility="invisible" > OK Play/Pause - Loadin… + Loading… 100% FIT SCREEN STRETCH diff --git a/src/io/vov/vitamio/activity/PlayerService.java b/src/io/vov/vitamio/activity/PlayerService.java new file mode 100644 index 0000000..3c3b25a --- /dev/null +++ b/src/io/vov/vitamio/activity/PlayerService.java @@ -0,0 +1,507 @@ +package io.vov.vitamio.activity; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Binder; +import android.os.IBinder; +import android.telephony.PhoneStateListener; +import android.telephony.TelephonyManager; +import android.view.SurfaceHolder; + +import io.vov.vitamio.MediaPlayer; +import io.vov.vitamio.MediaPlayer.OnBufferingUpdateListener; +import io.vov.vitamio.MediaPlayer.OnCompletionListener; +import io.vov.vitamio.MediaPlayer.OnErrorListener; +import io.vov.vitamio.MediaPlayer.OnHWRenderFailedListener; +import io.vov.vitamio.MediaPlayer.OnInfoListener; +import io.vov.vitamio.MediaPlayer.OnPreparedListener; +import io.vov.vitamio.MediaPlayer.OnSeekCompleteListener; +import io.vov.vitamio.MediaPlayer.OnVideoSizeChangedListener; +import me.abitno.utils.FileHelper; +import me.abitno.utils.Log; +import me.abitno.utils.VitamioInstaller; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; + +public class PlayerService extends Service implements OnBufferingUpdateListener, OnCompletionListener, OnPreparedListener, OnVideoSizeChangedListener, OnErrorListener, OnInfoListener, OnSeekCompleteListener { + private MediaPlayer mPlayer; + private VPlayerListener mListener; + private Uri mUri; + private Uri mOldUri; + private float mSeekTo = -1f; + private boolean mFromNotification; + private String[] mSubPaths; + private boolean mInitialized; + private final IBinder mBinder = new LocalBinder(); + private TelephonyManager mTelephonyManager; + private int mCurrentState; + private SurfaceHolder mSurfaceHolder; + public static final int VPLYAER_NOTIFICATION_ID = 1; + + public static final int STATE_PREPARED = -1; + public static final int STATE_PLAYING = 0; + public static final int STATE_NEED_RESUME = 1; + public static final int STATE_STOPPED = 2; + public static final int STATE_RINGING = 3; + + private int mLastAudioTrack = -1; + private String mLastSubTrack; + private int mLastSubTrackId = -1; + private long mMediaId = -1l; + + public class LocalBinder extends Binder { + public PlayerService getService() { + return PlayerService.this; + } + } + + @Override + public void onCreate() { + super.onCreate(); + mInitialized = false; + mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); + mTelephonyManager.listen(mPhoneListener, PhoneStateListener.LISTEN_CALL_STATE); + if (VitamioInstaller.isNativeLibsInited(this)) { + vplayerInit(); + } else { + stopSelf(); + } + Log.d("CREATE OK"); + } + + private void vplayerInit() { + mPlayer = new MediaPlayer(PlayerService.this.getApplicationContext(), false); + mPlayer.setOnHWRenderFailedListener(new OnHWRenderFailedListener() { + @Override + public void onFailed() { + if (mListener != null) + mListener.onHWRenderFailed(); + } + }); + mPlayer.setOnBufferingUpdateListener(PlayerService.this); + mPlayer.setOnCompletionListener(PlayerService.this); + mPlayer.setOnPreparedListener(PlayerService.this); + mPlayer.setOnVideoSizeChangedListener(PlayerService.this); + mPlayer.setOnErrorListener(PlayerService.this); + mPlayer.setOnInfoListener(PlayerService.this); + } + + public void releaseContext() { + if (mPlayer != null) + mPlayer.release(); + mPlayer = null; + } + + @Override + public IBinder onBind(Intent intent) { + Log.d("BIND OK : " + intent.getPackage()); + return mBinder; + } + + @Override + public void onDestroy() { + super.onDestroy(); + release(true); + releaseContext(); + } + + public boolean isInitialized() { + return mInitialized; + } + + public boolean initialize(Uri filePath, String displayName, boolean saveUri, float startPos, VPlayerListener listener, int parentId) { + if (mPlayer == null) + vplayerInit(); + mListener = listener; + mOldUri = mUri; + mUri = filePath; + mSeekTo = startPos; + mMediaId = -1; + mLastAudioTrack = -1; + mLastSubTrackId = -1; + mLastSubTrack = ""; + setMediaTrack(); + Log.i("%s ==> %s, %s, %s, %s", mOldUri, mUri, mInitialized, mPrepared, mVideoSizeKnown); + mFromNotification = mInitialized && mUri != null && mUri.equals(mOldUri); + mListener.onOpenStart(); + if (!mFromNotification) + openVideo(); + else + openSuccess(); + return mInitialized; + } + + private void setMediaTrack() { + } + + private void openVideo() { + if (mUri == null || mPlayer == null) + return; + + mPlayer.reset(); + mInitialized = false; + mPrepared = false; + mVideoSizeKnown = false; + + try { + mPlayer.setScreenOnWhilePlaying(true); + mPlayer.setDataSource(PlayerService.this, mUri); + if (mLastAudioTrack != -1) + mPlayer.setInitialAudioTrack(mLastAudioTrack); + if (mLastSubTrackId != -1) + mPlayer.setInitialSubTrack(mLastSubTrackId); + if (mSurfaceHolder != null && mSurfaceHolder.getSurface() != null && mSurfaceHolder.getSurface().isValid()) + mPlayer.setDisplay(mSurfaceHolder); + mPlayer.prepareAsync(); + } catch (IllegalArgumentException e) { + Log.e("openVideo", e); + } catch (IllegalStateException e) { + Log.e("openVideo", e); + } catch (IOException e) { + Log.e("openVideo", e); + } + } + + public Uri getUri() { + return mUri; + } + + public long getMediaId() { + return mMediaId; + } + + public int getLastAudioTrack() { + return mLastAudioTrack; + } + + public String getLastSubTrack() { + return mLastSubTrack; + } + + public int getLastSubTrackId() { + return mLastSubTrackId; + } + + public void setVPlayerListener(VPlayerListener listener) { + mListener = listener; + } + + public void setState(int state) { + mCurrentState = state; + } + + public boolean needResume() { + return mInitialized && (mCurrentState == STATE_NEED_RESUME || mCurrentState == STATE_PREPARED); + } + + public boolean ringingState() { + return mInitialized && mCurrentState == STATE_RINGING; + } + + public void release() { + release(true); + } + + private void release(boolean all) { + if (mPlayer != null) { + if (mListener != null) + mListener.onCloseStart(); + mPlayer.reset(); + mInitialized = false; + mPrepared = false; + mVideoSizeKnown = false; + if (mListener != null) + mListener.onCloseComplete(); + } + if (all) { + mListener = null; + mUri = null; + } + } + + public void stop() { + if (mInitialized) { + mPlayer.pause(); + } + } + + public void start() { + if (mInitialized) { + mPlayer.start(); + setState(STATE_PLAYING); + } + } + + public void setDisplay(SurfaceHolder surface) { + mSurfaceHolder = surface; + if (mPlayer != null) + mPlayer.setDisplay(surface); + } + + public void releaseSurface() { + if (mInitialized) + mPlayer.releaseDisplay(); + } + + public boolean isPlaying() { + return (mInitialized && mPlayer.isPlaying()); + } + + public int getVideoWidth() { + if (mInitialized) + return mPlayer.getVideoWidth(); + return 0; + } + + public int getVideoHeight() { + if (mInitialized) + return mPlayer.getVideoHeight(); + return 0; + } + + public float getVideoAspectRatio() { + if (mInitialized) + return mPlayer.getVideoAspectRatio(); + return 0f; + } + + public long getDuration() { + if (mInitialized) + return mPlayer.getDuration(); + return 0; + } + + public long getCurrentPosition() { + if (mInitialized) + return mPlayer.getCurrentPosition(); + return 0; + } + + public Bitmap getCurrentFrame() { + if (mInitialized) + return mPlayer.getCurrentFrame(); + return null; + } + + public float getBufferProgress() { + if (mInitialized) + return mPlayer.getBufferProgress(); + return 0f; + } + + public void seekTo(float percent) { + if (mInitialized) + mPlayer.seekTo((int) (percent * getDuration())); + } + + public String getMetaEncoding() { + if (mInitialized) + return mPlayer.getMetaEncoding(); + return null; + } + + public void setAudioTrack(int num) { + if (mInitialized) + mPlayer.setAudioTrack(num); + } + + public int getAudioTrack() { + if (mInitialized) + return mPlayer.getAudioTrack(); + return 0; + } + + public String getSubPath() { + if (mInitialized) + return mPlayer.getSubPath(); + return null; + } + + public void setSubShown(boolean shown) { + if (mInitialized) + mPlayer.setSubShown(shown); + } + + protected boolean isBuffering() { + return (mInitialized && mPlayer.isBuffering()); + } + + protected void setBuffer(int bufSize) { + if (mInitialized) + mPlayer.setBufferSize(bufSize); + } + + protected void setVolume(float left, float right) { + if (mInitialized) { + if (left <= 0f) + left = 0f; + else if (left >= 1f) + left = 1f; + if (right <= 0f) + right = 0f; + else if (right >= 1f) + right = 1f; + mPlayer.setVolume(left, right); + } + } + + protected void setVideoQuality(int quality) { + if (mInitialized) + mPlayer.setVideoQuality(quality); + } + + protected void setDeinterlace(boolean deinterlace) { + if (mInitialized) + mPlayer.setDeinterlace(deinterlace); + } + + public int getSubLocation() { + if (mInitialized) + return mPlayer.getSubLocation(); + return -1; + } + + public void setSubPath(String subPath) { + if (mInitialized) + mPlayer.setSubPath(subPath); + } + + public static interface VPlayerListener { + public void onHWRenderFailed(); + + public void onVideoSizeChanged(int width, int height); + + public void onOpenStart(); + + public void onOpenSuccess(); + + public void onOpenFailed(); + + public void onBufferStart(); + + public void onBufferComplete(); + + public void onDownloadRateChanged(int kbPerSec); + + public void onPlaybackComplete(); + + public void onCloseStart(); + + public void onCloseComplete(); + } + + private PhoneStateListener mPhoneListener = new PhoneStateListener() { + @Override + public void onCallStateChanged(int state, String incomingNumber) { + switch (state) { + case TelephonyManager.CALL_STATE_IDLE: + break; + case TelephonyManager.CALL_STATE_OFFHOOK: + case TelephonyManager.CALL_STATE_RINGING: + if (isPlaying()) { + stop(); + setState(STATE_RINGING); + } + break; + default: + break; + } + } + }; + + private boolean mVideoSizeKnown = false; + private boolean mPrepared = false; + + @Override + public void onVideoSizeChanged(MediaPlayer arg0, int arg1, int arg2) { + mVideoSizeKnown = true; + if (mListener != null) + mListener.onVideoSizeChanged(arg1, arg2); + } + + @Override + public void onPrepared(MediaPlayer arg0) { + mPrepared = true; + openSuccess(); + } + + private void openSuccess() { + mInitialized = true; + if (!mFromNotification && mSeekTo > 0 && mSeekTo < 1) + seekTo(mSeekTo); + mSeekTo = -1; + mListener.onOpenSuccess(); + if (!mFromNotification) { + if (mUri != null) + mSubPaths = getSubFiles(mUri.getPath()); + if (mSubPaths != null) + setSubPath(FileHelper.getCanonical(new File(mSubPaths[0]))); + } + } + + @Override + public void onCompletion(MediaPlayer arg0) { + if (mListener != null) { + mListener.onPlaybackComplete(); + } else { + release(true); + } + } + + @Override + public void onBufferingUpdate(MediaPlayer arg0, int arg1) { + } + + @Override + public void onSeekComplete(MediaPlayer arg0) { + } + + @Override + public boolean onInfo(MediaPlayer arg0, int arg1, int arg2) { + switch (arg1) { + case MediaPlayer.MEDIA_INFO_BUFFERING_START: + if (mListener != null) + mListener.onBufferStart(); + else + mPlayer.pause(); + break; + case MediaPlayer.MEDIA_INFO_BUFFERING_END: + if (mListener != null) + mListener.onBufferComplete(); + else + mPlayer.start(); + break; + case MediaPlayer.MEDIA_INFO_DOWNLOAD_RATE_CHANGED: + if (mListener != null) + mListener.onDownloadRateChanged(arg2); + break; + } + return true; + } + + @Override + public boolean onError(MediaPlayer arg0, int arg1, int arg2) { + mListener.onOpenFailed(); + return true; + } + + private String[] getSubFiles(String videoPath) { + ArrayList files = new ArrayList(); + for (String ext : MediaPlayer.SUB_TYPES) { + File s = new File(videoPath.substring(0, videoPath.lastIndexOf('.') > 0 ? videoPath.lastIndexOf('.') : videoPath.length()) + ext); + if (s.exists() && s.isFile() && s.canRead()) + files.add(s.getAbsolutePath()); + } + + if (files.isEmpty()) + return null; + else + return files.toArray(new String[files.size()]); + } + +} diff --git a/src/io/vov/vitamio/activity/VideoActivity.java b/src/io/vov/vitamio/activity/VideoActivity.java index 2dbfea4..81d7af8 100644 --- a/src/io/vov/vitamio/activity/VideoActivity.java +++ b/src/io/vov/vitamio/activity/VideoActivity.java @@ -1,51 +1,803 @@ package io.vov.vitamio.activity; +import android.annotation.SuppressLint; import android.app.Activity; +import android.app.ActivityManager; +import android.app.KeyguardManager; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; import android.content.res.Configuration; +import android.graphics.Bitmap; import android.media.AudioManager; import android.net.Uri; +import android.os.BatteryManager; import android.os.Bundle; - -import me.abitno.utils.IntentHelper; +import android.os.Environment; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.view.SurfaceHolder; +import android.view.View; +import android.view.WindowManager; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.widget.TextView; +import android.widget.Toast; import io.vov.vitamio.MediaPlayer; import io.vov.vitamio.R; import io.vov.vitamio.widget.MediaController; import io.vov.vitamio.widget.VideoView; +import me.abitno.utils.BitmapHelper; +import me.abitno.utils.FileHelper; +import me.abitno.utils.FileUtils; +import me.abitno.utils.IntentHelper; +import me.abitno.utils.Log; +import me.abitno.utils.Media; +import me.abitno.utils.ToastHelper; +import me.abitno.utils.UIUtils; + +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.concurrent.atomic.AtomicBoolean; + +@SuppressLint("HandlerLeak") +public class VideoActivity extends Activity implements MediaController.MediaPlayerControl, VideoView.SurfaceCallback { + + private static final int RESULT_FAILED = -7; + private static final int DEFAULT_BUF_SIZE = 0; + private static final int DEFAULT_VIDEO_QUALITY = MediaPlayer.VIDEOQUALITY_MEDIUM; + private static final boolean DEFAULT_DEINTERLACE = false; + private static final float DEFAULT_ASPECT_RATIO = 0f; + private static final float DEFAULT_STEREO_VOLUME = 1.0f; + private static final String SNAP_SHOT_PATH = "/Player"; -public class VideoActivity extends Activity { + private static final IntentFilter USER_PRESENT_FILTER = new IntentFilter(Intent.ACTION_USER_PRESENT); + private static final IntentFilter SCREEN_FILTER = new IntentFilter(Intent.ACTION_SCREEN_ON); + private static final IntentFilter HEADSET_FILTER = new IntentFilter(Intent.ACTION_HEADSET_PLUG); + private static final IntentFilter BATTERY_FILTER = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); + private boolean mCreated = false; + private boolean mNeedLock; + private String mDisplayName; + private String mBatteryLevel; + private boolean mFromStart; + private int mLoopCount; + private boolean mSaveUri; + private int mParentId; + private float mStartPos; + private View mViewRoot; private VideoView mVideoView; + private View mVideoLoadingLayout; + private TextView mVideoLoadingText; private Uri mUri; + private ScreenReceiver mScreenReceiver; + private HeadsetPlugReceiver mHeadsetPlugReceiver; + private UserPresentReceiver mUserPresentReceiver; + private BatteryReceiver mBatteryReceiver; + private boolean mReceiverRegistered = false; + private boolean mHeadsetPlaying = false; + private boolean mCloseComplete = false; + + private MediaController mMediaController; + private PlayerService vPlayer; + private ServiceConnection vPlayerServiceConnection; + private Animation mLoadingAnimation; + private View mLoadingProgressView; + + static { + SCREEN_FILTER.addAction(Intent.ACTION_SCREEN_OFF); + } @Override public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); if (!io.vov.vitamio.LibsChecker.checkVitamioLibs(this)) return; - super.onCreate(savedInstanceState); + + vPlayerServiceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + vPlayer = ((PlayerService.LocalBinder) service).getService(); + mServiceConnected = true; + if (mSurfaceCreated) + vPlayerHandler.sendEmptyMessage(OPEN_FILE); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + vPlayer = null; + mServiceConnected = false; + } + }; + setVolumeControlStream(AudioManager.STREAM_MUSIC); - setContentView(R.layout.activity_video); parseIntent(getIntent()); + loadView(R.layout.activity_video); + manageReceivers(); + + mCreated = true; } - private void parseIntent(Intent intent) { - mUri = IntentHelper.getIntentUri(intent); - mVideoView = (VideoView) findViewById(R.id.video); - mVideoView.setVideoURI(mUri); - mVideoView.setVideoTitle(intent.getStringExtra("displayName")); - mVideoView.setVideoQuality(MediaPlayer.VIDEOQUALITY_HIGH); - mVideoView.setMediaController(new MediaController(this)); + private void attachMediaController() { + if (mMediaController != null) { + mNeedLock = mMediaController.isLocked(); + mMediaController.release(); + } + mMediaController = new MediaController(this, mNeedLock); + mMediaController.setMediaPlayer(this); + mMediaController.setAnchorView(mVideoView.getRootView()); + setFileName(); + setBatteryLevel(); + } + + @Override + public void onStart() { + super.onStart(); + if (!mCreated) + return; + bindService(new Intent(this, PlayerService.class), vPlayerServiceConnection, Context.BIND_AUTO_CREATE); + } + + @Override + public void onResume() { + super.onResume(); + if (!mCreated) + return; + + if (isInitialized()) { + KeyguardManager keyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE); + if (!keyguardManager.inKeyguardRestrictedInputMode()) { + startPlayer(); + } + } else { + if (mCloseComplete) { + reOpen(); + } + } + } + + @Override + public void onPause() { + super.onPause(); + if (!mCreated) + return; + if (isInitialized()) { + if (vPlayer != null && vPlayer.isPlaying()) { + stopPlayer(); + } + } + } + + @Override + public void onStop() { + super.onStop(); + if (!mCreated) + return; + if (isInitialized()) { + vPlayer.releaseSurface(); + } + if (mServiceConnected) { + unbindService(vPlayerServiceConnection); + mServiceConnected = false; + } + + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (!mCreated) + return; + manageReceivers(); + if (isInitialized() && !vPlayer.isPlaying()) + release(); + if (mMediaController != null) + mMediaController.release(); } @Override public void onConfigurationChanged(Configuration newConfig) { - if (mVideoView != null) - mVideoView.setVideoLayout(VideoView.VIDEO_LAYOUT_SCALE, 0); + if (isInitialized()) { + setVideoLayout(); + attachMediaController(); + } + super.onConfigurationChanged(newConfig); } - private void resultFinish() { - this.finish(); + @Override + public void onBackPressed() { + super.onBackPressed(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + // http://code.google.com/p/android/issues/detail?id=19917 + outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE"); + super.onSaveInstanceState(outState); + } + + public void showMenu() { + + } + + private void loadView(int id) { + setContentView(id); + mViewRoot = findViewById(R.id.video_root); + mVideoView = (VideoView) findViewById(R.id.video); + mVideoView.initialize(this, this, false); + mVideoLoadingText = (TextView) findViewById(R.id.video_loading_text); + mVideoLoadingLayout = findViewById(R.id.video_loading); + mLoadingProgressView = mVideoLoadingLayout.findViewById(R.id.video_loading_progress); + + mLoadingAnimation = AnimationUtils.loadAnimation(VideoActivity.this, R.anim.loading_rotate); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + + private void parseIntent(Intent i) { + + Uri dat = IntentHelper.getIntentUri(i); + if (dat == null) + resultFinish(RESULT_FAILED); + + String datString = dat.toString(); + if (!datString.equals(dat.toString())) + dat = Uri.parse(datString); + + mUri = dat; + + mNeedLock = i.getBooleanExtra("lockScreen", false); + mDisplayName = i.getStringExtra("displayName"); + mFromStart = i.getBooleanExtra("fromStart", false); + mSaveUri = i.getBooleanExtra("saveUri", true); + mStartPos = i.getFloatExtra("startPosition", -1.0f); + mLoopCount = i.getIntExtra("loopCount", 1); + mParentId = i.getIntExtra("parentId", 0); + Log.i("L: %b, N: %s, S: %b, P: %f, LP: %d", mNeedLock, mDisplayName, mFromStart, mStartPos, mLoopCount); + } + + private void manageReceivers() { + if (!mReceiverRegistered) { + mScreenReceiver = new ScreenReceiver(); + registerReceiver(mScreenReceiver, SCREEN_FILTER); + mUserPresentReceiver = new UserPresentReceiver(); + registerReceiver(mUserPresentReceiver, USER_PRESENT_FILTER); + mBatteryReceiver = new BatteryReceiver(); + registerReceiver(mBatteryReceiver, BATTERY_FILTER); + mHeadsetPlugReceiver = new HeadsetPlugReceiver(); + registerReceiver(mHeadsetPlugReceiver, HEADSET_FILTER); + mReceiverRegistered = true; + } else { + try { + if (mScreenReceiver != null) + unregisterReceiver(mScreenReceiver); + if (mUserPresentReceiver != null) + unregisterReceiver(mUserPresentReceiver); + if (mHeadsetPlugReceiver != null) + unregisterReceiver(mHeadsetPlugReceiver); + if (mBatteryReceiver != null) + unregisterReceiver(mBatteryReceiver); + } catch (IllegalArgumentException e) { + } + mReceiverRegistered = false; + } + } + + private void setFileName() { + if (mUri != null) { + String name = null; + if (mUri.getScheme() == null || mUri.getScheme().equals("file")) + name = FileUtils.getName(mUri.toString()); + else + name = mUri.getLastPathSegment(); + if (name == null) + name = "null"; + if (mDisplayName == null) + mDisplayName = name; + mMediaController.setFileName(mDisplayName); + } + } + + private void applyResult(int resultCode) { + vPlayerHandler.removeMessages(BUFFER_PROGRESS); + Intent i = new Intent(); + i.putExtra("filePath", mUri.toString()); + if (isInitialized()) { + i.putExtra("position", (double) vPlayer.getCurrentPosition() / vPlayer.getDuration()); + i.putExtra("duration", vPlayer.getDuration()); + } + switch (resultCode) { + case RESULT_FAILED: + ToastHelper.showToast(this, Toast.LENGTH_LONG, R.string.video_cannot_play); + break; + case RESULT_CANCELED: + case RESULT_OK: + break; + } + setResult(resultCode, i); + } + + private void resultFinish(int resultCode) { + applyResult(resultCode); + if (UIUtils.hasICS() && resultCode != RESULT_FAILED) { + android.os.Process.killProcess(android.os.Process.myPid()); + } else { + finish(); + } + } + + private void release() { + if (vPlayer != null) { + if (UIUtils.hasICS()) { + android.os.Process.killProcess(android.os.Process.myPid()); + } else { + vPlayer.release(); + vPlayer.releaseContext(); + } + } + } + + private void reOpen(Uri path, String name, boolean fromStart) { + if (isInitialized()) { + vPlayer.release(); + vPlayer.releaseContext(); + } + Intent i = getIntent(); + i.putExtra("lockScreen", mMediaController.isLocked()); + i.putExtra("startPosition", 7.7f); + i.putExtra("fromStart", fromStart); + i.putExtra("displayName", name); + i.setData(path); + parseIntent(i); + mUri = path; + if (mViewRoot != null) + mViewRoot.invalidate(); + mOpened.set(false); + loadView(R.layout.activity_video); + } + + public void reOpen() { + reOpen(mUri, mDisplayName, false); + } + + protected void startPlayer() { + if (isInitialized() && mScreenReceiver.screenOn && !vPlayer.isBuffering()) { + Log.i("VideoActivity#startPlayer"); + if (!vPlayer.isPlaying()) { + vPlayer.start(); + } + } + } + + protected void stopPlayer() { + if (isInitialized()) { + vPlayer.stop(); + } + } + + private void setBatteryLevel() { + if (mMediaController != null) + mMediaController.setBatteryLevel(mBatteryLevel); + } + + private class BatteryReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0); + int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100); + int percent = scale > 0 ? level * 100 / scale : 0; + if (percent > 100) + percent = 100; + mBatteryLevel = String.valueOf(percent) + "%"; + setBatteryLevel(); + } + } + + private class UserPresentReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (isRootActivity()) { + startPlayer(); + } + } + } + + private boolean isRootActivity() { + ActivityManager activity = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); + return activity.getRunningTasks(1).get(0).topActivity.flattenToString().endsWith("me.abitno.vplayer.VideoActivity"); + } + + public class HeadsetPlugReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (intent != null && intent.hasExtra("state")) { + int state = intent.getIntExtra("state", -1); + if (state == 0) { + mHeadsetPlaying = isPlaying(); + stopPlayer(); + } else if (state == 1) { + if (mHeadsetPlaying) + startPlayer(); + } + } + }; + } + + private class ScreenReceiver extends BroadcastReceiver { + private boolean screenOn = true; + + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { + screenOn = false; + stopPlayer(); + } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { + screenOn = true; + } + } + } + + private void loadVPlayerPrefs() { + if (!isInitialized()) + return; + vPlayer.setBuffer(VideoActivity.DEFAULT_BUF_SIZE); + vPlayer.setVideoQuality(DEFAULT_VIDEO_QUALITY); + vPlayer.setDeinterlace(DEFAULT_DEINTERLACE); + vPlayer.setVolume(DEFAULT_STEREO_VOLUME, DEFAULT_STEREO_VOLUME); + if (mVideoView != null && isInitialized()) + setVideoLayout(); + } + + private boolean isInitialized() { + return (mCreated && vPlayer != null && vPlayer.isInitialized()); + } + + private AtomicBoolean mOpened = new AtomicBoolean(Boolean.FALSE); + private boolean mSurfaceCreated = false; + private boolean mServiceConnected = false; + private Object mOpenLock = new Object(); + private static final int OPEN_FILE = 0; + private static final int OPEN_START = 1; + private static final int OPEN_SUCCESS = 2; + private static final int OPEN_FAILED = 3; + private static final int HW_FAILED = 4; + private static final int LOAD_PREFS = 5; + private static final int BUFFER_START = 11; + private static final int BUFFER_PROGRESS = 12; + private static final int BUFFER_COMPLETE = 13; + private static final int CLOSE_START = 21; + private static final int CLOSE_COMPLETE = 22; + private Handler vPlayerHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case OPEN_FILE: + synchronized (mOpenLock) { + if (!mOpened.get() && vPlayer != null) { + mOpened.set(true); + vPlayer.setVPlayerListener(vPlayerListener); + if (vPlayer.isInitialized()) + mUri = vPlayer.getUri(); + + if (mVideoView != null) + vPlayer.setDisplay(mVideoView.getHolder()); + if (mUri != null) + vPlayer.initialize(mUri, mDisplayName, mSaveUri, getStartPosition(), vPlayerListener, mParentId); + } + } + break; + case OPEN_START: + mVideoLoadingText.setText(R.string.video_layout_loading); + setVideoLoadingLayoutVisibility(View.VISIBLE); + break; + case OPEN_SUCCESS: + loadVPlayerPrefs(); + setVideoLoadingLayoutVisibility(View.GONE); + setVideoLayout(); + vPlayer.start(); + attachMediaController(); + break; + case OPEN_FAILED: + resultFinish(RESULT_FAILED); + break; + case BUFFER_START: + setVideoLoadingLayoutVisibility(View.VISIBLE); + vPlayerHandler.sendEmptyMessageDelayed(BUFFER_PROGRESS, 1000); + break; + case BUFFER_PROGRESS: + if (vPlayer.getBufferProgress() >= 100) { + setVideoLoadingLayoutVisibility(View.GONE); + } else { + mVideoLoadingText.setText(getString(R.string.video_layout_buffering_progress, vPlayer.getBufferProgress())); + vPlayerHandler.sendEmptyMessageDelayed(BUFFER_PROGRESS, 1000); + stopPlayer(); + } + break; + case BUFFER_COMPLETE: + setVideoLoadingLayoutVisibility(View.GONE); + vPlayerHandler.removeMessages(BUFFER_PROGRESS); + break; + case CLOSE_START: + mVideoLoadingText.setText(R.string.closing_file); + setVideoLoadingLayoutVisibility(View.VISIBLE); + break; + case CLOSE_COMPLETE: + mCloseComplete = true; + break; + case HW_FAILED: + if (mVideoView != null) { + mVideoView.setVisibility(View.GONE); + mVideoView.setVisibility(View.VISIBLE); + mVideoView.initialize(VideoActivity.this, VideoActivity.this, false); + } + break; + case LOAD_PREFS: + loadVPlayerPrefs(); + break; + } + } + }; + + private void setVideoLoadingLayoutVisibility(int visibility) { + if (mVideoLoadingLayout != null && mLoadingProgressView != null) { + if (visibility == View.VISIBLE) + mLoadingProgressView.startAnimation(mLoadingAnimation); + mVideoLoadingLayout.setVisibility(visibility); + } + } + + private PlayerService.VPlayerListener vPlayerListener = new PlayerService.VPlayerListener() { + @Override + public void onHWRenderFailed() { + + } + + @Override + public void onOpenStart() { + vPlayerHandler.sendEmptyMessage(OPEN_START); + } + + @Override + public void onOpenSuccess() { + vPlayerHandler.sendEmptyMessage(OPEN_SUCCESS); + } + + @Override + public void onOpenFailed() { + vPlayerHandler.sendEmptyMessage(OPEN_FAILED); + } + + @Override + public void onBufferStart() { + vPlayerHandler.sendEmptyMessage(BUFFER_START); + stopPlayer(); + } + + @Override + public void onBufferComplete() { + Log.i("VideoActivity#onBufferComplete " + vPlayer.needResume()); + vPlayerHandler.sendEmptyMessage(BUFFER_COMPLETE); + if (vPlayer != null && !vPlayer.needResume()) + startPlayer(); + } + + @Override + public void onPlaybackComplete() { + if (mLoopCount == 0 || mLoopCount-- > 1) { + vPlayer.start(); + vPlayer.seekTo(0); + } else { + resultFinish(RESULT_OK); + } + } + + @Override + public void onCloseStart() { + vPlayerHandler.sendEmptyMessage(CLOSE_START); + } + + @Override + public void onCloseComplete() { + vPlayerHandler.sendEmptyMessage(CLOSE_COMPLETE); + } + + @Override + public void onVideoSizeChanged(int width, int height) { + if (mVideoView != null) { + setVideoLayout(); + } + } + + @Override + public void onDownloadRateChanged(int kbPerSec) { + if (!Media.isNative(mUri.toString()) && mMediaController != null) { + mMediaController.setDownloadRate(String.format("%dKB/s", kbPerSec)); + } + } + }; + + private int mVideoMode = VideoView.VIDEO_LAYOUT_SCALE; + + public static final String SESSION_LAST_POSITION_SUFIX = ".last"; + + private void setVideoLayout() { + mVideoView.setVideoLayout(mVideoMode, DEFAULT_ASPECT_RATIO, vPlayer.getVideoWidth(), vPlayer.getVideoHeight(), vPlayer.getVideoAspectRatio()); + } + + private float getStartPosition() { + return mStartPos; + } + + @Override + public int getBufferPercentage() { + if (isInitialized()) + return (int) (vPlayer.getBufferProgress() * 100); + return 0; + } + + @Override + public long getCurrentPosition() { + if (isInitialized()) + return vPlayer.getCurrentPosition(); + return (long) (getStartPosition() * vPlayer.getDuration()); + } + + @Override + public long getDuration() { + if (isInitialized()) + return vPlayer.getDuration(); + return 0; + } + + @Override + public boolean isPlaying() { + if (isInitialized()) + return vPlayer.isPlaying(); + return false; + } + + @Override + public void pause() { + if (isInitialized()) + vPlayer.stop(); + } + + @Override + public void seekTo(long arg0) { + if (isInitialized()) + vPlayer.seekTo((float) ((double) arg0 / vPlayer.getDuration())); + } + + @Override + public void start() { + if (isInitialized()) + vPlayer.start(); + } + + @Override + public void previous() { + } + + @Override + public void next() { + } + + private static final int VIDEO_MAXIMUM_HEIGHT = 2048; + private static final int VIDEO_MAXIMUM_WIDTH = 2048; + + @Override + public float scale(float scaleFactor) { + float userRatio = DEFAULT_ASPECT_RATIO; + int videoWidth = vPlayer.getVideoWidth(); + int videoHeight = vPlayer.getVideoHeight(); + float videoRatio = vPlayer.getVideoAspectRatio(); + float currentRatio = mVideoView.mVideoHeight / (float) videoHeight; + + currentRatio += (scaleFactor - 1); + if (videoWidth * currentRatio >= VIDEO_MAXIMUM_WIDTH) + currentRatio = VIDEO_MAXIMUM_WIDTH / (float) videoWidth; + + if (videoHeight * currentRatio >= VIDEO_MAXIMUM_HEIGHT) + currentRatio = VIDEO_MAXIMUM_HEIGHT / (float) videoHeight; + + if (currentRatio < 0.5f) + currentRatio = 0.5f; + + mVideoView.mVideoHeight = (int) (videoHeight * currentRatio); + mVideoView.setVideoLayout(mVideoMode, userRatio, videoWidth, videoHeight, videoRatio); + return currentRatio; + } + + @SuppressLint("SimpleDateFormat") + @Override + public void snapshot() { + if (!FileHelper.sdAvailable()) { + ToastHelper.showToast(this, R.string.file_explorer_sdcard_not_available); + } else { + Uri imgUri = null; + Bitmap bitmap = vPlayer.getCurrentFrame(); + if (bitmap != null) { + File screenshotsDirectory = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + SNAP_SHOT_PATH); + if (!screenshotsDirectory.exists()) { + screenshotsDirectory.mkdirs(); + } + + File savePath = new File(screenshotsDirectory.getPath() + "/" + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ".jpg"); + if (BitmapHelper.saveBitmapToFile(bitmap, savePath.getPath())) { + imgUri = Uri.fromFile(savePath); + } + } + if (imgUri != null) { + sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, imgUri)); + ToastHelper.showToast(this, Toast.LENGTH_LONG, getString(R.string.video_screenshot_save_in, imgUri.getPath())); + } else { + ToastHelper.showToast(this, R.string.video_screenshot_failed); + } + } + } + + @Override + public void toggleVideoMode(int mode) { + mVideoMode = mode; + setVideoLayout(); + } + + @Override + public void stop() { + onBackPressed(); + } + + @Override + public long goForward() { + return 0; + } + + @Override + public long goBack() { + return 0; + } + + @Override + public void removeLoadingView() { + mVideoLoadingLayout.setVisibility(View.GONE); + } + + @Override + public void onSurfaceCreated(SurfaceHolder holder) { + Log.i("onSurfaceCreated"); + mSurfaceCreated = true; + if (mServiceConnected) + vPlayerHandler.sendEmptyMessage(OPEN_FILE); + if (vPlayer != null) + vPlayer.setDisplay(holder); + } + + @Override + public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) { + if (vPlayer != null) { + vPlayer.setDisplay(holder); + setVideoLayout(); + } + } + + @Override + public void onSurfaceDestroyed(SurfaceHolder holder) { + Log.i("onSurfaceDestroyed"); + if (vPlayer != null && vPlayer.isInitialized()) { + if (vPlayer.isPlaying()) { + vPlayer.stop(); + vPlayer.setState(PlayerService.STATE_NEED_RESUME); + } + vPlayer.releaseSurface(); + if (vPlayer.needResume()) + vPlayer.start(); + } } } diff --git a/src/io/vov/vitamio/widget/CommonGestures.java b/src/io/vov/vitamio/utils/CommonGestures.java similarity index 96% rename from src/io/vov/vitamio/widget/CommonGestures.java rename to src/io/vov/vitamio/utils/CommonGestures.java index cdaa667..a5228e9 100644 --- a/src/io/vov/vitamio/widget/CommonGestures.java +++ b/src/io/vov/vitamio/utils/CommonGestures.java @@ -1,4 +1,4 @@ -package io.vov.vitamio.widget; +package io.vov.vitamio.utils; import android.annotation.SuppressLint; import android.app.Activity; @@ -7,8 +7,6 @@ import android.view.MotionEvent; import android.view.ScaleGestureDetector; -import io.vov.vitamio.utils.DeviceUtils; - public class CommonGestures { public static final int SCALE_STATE_BEGIN = 0; public static final int SCALE_STATE_SCALEING = 1; @@ -73,7 +71,7 @@ public void onLongPress(MotionEvent e) { } @SuppressLint("NewApi") - private class ScaleDetectorListener implements ScaleGestureDetector.OnScaleGestureListener { + private class ScaleDetectorListener implements ScaleGestureDetector.OnScaleGestureListener { @Override public boolean onScale(ScaleGestureDetector detector) { if (mListener != null && mGestureEnabled) diff --git a/src/io/vov/vitamio/widget/MediaController.java b/src/io/vov/vitamio/widget/MediaController.java index cc5ee35..2697296 100644 --- a/src/io/vov/vitamio/widget/MediaController.java +++ b/src/io/vov/vitamio/widget/MediaController.java @@ -1,351 +1,440 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * Copyright (C) 2012 YIXIA.COM - * - * 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 io.vov.vitamio.widget; -import io.vov.utils.Log; -import io.vov.utils.StringUtils; -import io.vov.vitamio.R; -import io.vov.vitamio.widget.OutlineTextView; - +import android.annotation.TargetApi; +import android.app.Activity; import android.content.Context; import android.graphics.Rect; +import android.graphics.RectF; import android.media.AudioManager; import android.os.Handler; import android.os.Message; -import android.util.AttributeSet; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.view.animation.Animation; +import android.view.animation.Animation.AnimationListener; +import android.view.animation.AnimationUtils; import android.widget.FrameLayout; import android.widget.ImageButton; +import android.widget.ImageView; import android.widget.PopupWindow; -import android.widget.ProgressBar; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; -/** - * A view containing controls for a MediaPlayer. Typically contains the buttons - * like "Play/Pause" and a progress slider. It takes care of synchronizing the - * controls with the state of the MediaPlayer. - *

- * The way to use this class is to a) instantiate it programatically or b) - * create it in your xml layout. - * - * a) The MediaController will create a default set of controls and put them in - * a window floating above your application. Specifically, the controls will - * float above the view specified with setAnchorView(). By default, the window - * will disappear if left idle for three seconds and reappear when the user - * touches the anchor view. To customize the MediaController's style, layout and - * controls you should extend MediaController and override the {#link - * {@link #makeControllerView()} method. - * - * b) The MediaController is a FrameLayout, you can put it in your layout xml - * and get it through {@link #findViewById(int)}. - * - * NOTES: In each way, if you want customize the MediaController, the SeekBar's - * id must be mediacontroller_progress, the Play/Pause's must be - * mediacontroller_pause, current time's must be mediacontroller_time_current, - * total time's must be mediacontroller_time_total, file name's must be - * mediacontroller_file_name. And your resources must have a pause_button - * drawable and a play_button drawable. - *

- * Functions like show() and hide() have no effect when MediaController is - * created in an xml layout. - */ +import io.vov.vitamio.R; +import io.vov.vitamio.utils.CommonGestures; +import io.vov.vitamio.utils.CommonGestures.TouchListener; + +import me.abitno.utils.FractionalTouchDelegate; +import me.abitno.utils.Log; +import me.abitno.utils.StringHelper; +import me.abitno.utils.StringUtils; +import me.abitno.utils.UIUtils; + +import java.lang.ref.WeakReference; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + public class MediaController extends FrameLayout { private MediaPlayerControl mPlayer; - private Context mContext; + private Activity mContext; private PopupWindow mWindow; - private int mAnimStyle; private View mAnchor; private View mRoot; - private ProgressBar mProgress; + private ImageButton mLock; + private ImageButton mScreenToggle; + private ImageButton mSnapshot; + private SeekBar mProgress; private TextView mEndTime, mCurrentTime; - private TextView mFileName; - private OutlineTextView mInfoView; - private String mTitle; private long mDuration; private boolean mShowing; + private boolean mScreenLocked = false; private boolean mDragging; private boolean mInstantSeeking = true; - private static final int sDefaultTimeout = 3000; - private static final int FADE_OUT = 1; - private static final int SHOW_PROGRESS = 2; - private boolean mFromXml = false; + private static final int DEFAULT_TIME_OUT = 3000; + private static final int DEFAULT_LONG_TIME_SHOW = 120000; + private static final int DEFAULT_SEEKBAR_VALUE = 1000; + private static final int TIME_TICK_INTERVAL = 1000; private ImageButton mPauseButton; + private View mMediaController; + private View mControlsLayout; + private View mSystemInfoLayout; + private View mControlsButtons; + private View mMenu; + private TextView mDateTime; + private TextView mDownloadRate; + private TextView mFileName; + private TextView mBatteryLevel; + + private TextView mOperationInfo; + private View mOperationVolLum; + private ImageView mVolLumNum; + private ImageView mVolLumBg; + private AudioManager mAM; + private int mMaxVolume; + private float mBrightness = 0.01f; + private int mVolume = 0; + private Handler mHandler; - public MediaController(Context context, AttributeSet attrs) { - super(context, attrs); - mRoot = this; - mFromXml = true; - initController(context); - } + private Animation mAnimSlideInTop; + private Animation mAnimSlideInBottom; + private Animation mAnimSlideOutTop; + private Animation mAnimSlideOutBottom; + + private CommonGestures mGestures; + private int mVideoMode; public MediaController(Context context) { super(context); - if (!mFromXml && initController(context)) - initFloatingWindow(); + mContext = (Activity) context; + initFloatingWindow(); + initResources(); } - private boolean initController(Context context) { - mContext = context; - mAM = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); - return true; - } - - @Override - public void onFinishInflate() { - if (mRoot != null) - initControllerView(mRoot); + public MediaController(Context context, boolean locked) { + this(context); + mScreenLocked = locked; + lock(mScreenLocked); } private void initFloatingWindow() { mWindow = new PopupWindow(mContext); - mWindow.setFocusable(false); + mWindow.setFocusable(true); mWindow.setBackgroundDrawable(null); mWindow.setOutsideTouchable(true); - mAnimStyle = android.R.style.Animation; } - /** - * Set the view that acts as the anchor for the control view. This can for - * example be a VideoView, or your Activity's main view. - * - * @param view - * The view to which to anchor the controller when it is visible. - */ - public void setAnchorView(View view) { - mAnchor = view; - if (!mFromXml) { - removeAllViews(); - mRoot = makeControllerView(); - mWindow.setContentView(mRoot); - mWindow.setWidth(LayoutParams.MATCH_PARENT); - mWindow.setHeight(LayoutParams.WRAP_CONTENT); - } - initControllerView(mRoot); - } - - /** - * Create the view that holds the widgets that control playback. Derived - * classes can override this to create their own. - * - * @return The controller view. - */ - protected View makeControllerView() { - return ((LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.mediacontroller, this); + @TargetApi(11) + public void setWindowLayoutType() { + if (UIUtils.hasICS()) { + try { + mAnchor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); + Method setWindowLayoutType = PopupWindow.class.getMethod("setWindowLayoutType", new Class[] { int.class }); + setWindowLayoutType.invoke(mWindow, WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG); + } catch (Exception e) { + Log.e("setWindowLayoutType", e); + } + } } - private void initControllerView(View v) { - mPauseButton = (ImageButton) v.findViewById(R.id.mediacontroller_play_pause); - if (mPauseButton != null) { - mPauseButton.requestFocus(); - mPauseButton.setOnClickListener(mPauseListener); - } + @TargetApi(11) + private void initResources() { + mHandler = new MHandler(this); + mAM = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + mMaxVolume = mAM.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + mGestures = new CommonGestures(mContext); + mGestures.setTouchListener(mTouchListener, true); + + mAnimSlideOutBottom = AnimationUtils.loadAnimation(mContext, R.anim.slide_out_bottom); + mAnimSlideOutTop = AnimationUtils.loadAnimation(mContext, R.anim.slide_out_top); + mAnimSlideInBottom = AnimationUtils.loadAnimation(mContext, R.anim.slide_in_bottom); + mAnimSlideInTop = AnimationUtils.loadAnimation(mContext, R.anim.slide_in_top); + mAnimSlideOutBottom.setAnimationListener(new AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + } + + @Override + public void onAnimationEnd(Animation animation) { + mMediaController.setVisibility(View.GONE); + showButtons(false); + mHandler.removeMessages(MSG_HIDE_SYSTEM_UI); + mHandler.sendEmptyMessageDelayed(MSG_HIDE_SYSTEM_UI, DEFAULT_TIME_OUT); + } - mProgress = (ProgressBar) v.findViewById(R.id.mediacontroller_seekbar); - if (mProgress != null) { - if (mProgress instanceof SeekBar) { - SeekBar seeker = (SeekBar) mProgress; - seeker.setOnSeekBarChangeListener(mSeekListener); - seeker.setThumbOffset(1); + @Override + public void onAnimationRepeat(Animation animation) { } - mProgress.setMax(1000); + }); + + removeAllViews(); + + mRoot = inflateLayout(); + mWindow.setContentView(mRoot); + mWindow.setWidth(android.view.ViewGroup.LayoutParams.MATCH_PARENT); + mWindow.setHeight(android.view.ViewGroup.LayoutParams.MATCH_PARENT); + + findViewItems(mRoot); + showSystemUi(false); + if (UIUtils.hasHoneycomb()) { + mRoot.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() { + public void onSystemUiVisibilityChange(int visibility) { + if ((visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) { + mHandler.sendEmptyMessageDelayed(MSG_HIDE_SYSTEM_UI, DEFAULT_TIME_OUT); + } + } + }); } + } + + private View inflateLayout() { + return ((LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.mediacontroller, this); + } + + private void findViewItems(View v) { + mMediaController = v.findViewById(R.id.mediacontroller); + + mSystemInfoLayout = v.findViewById(R.id.info_panel); mEndTime = (TextView) v.findViewById(R.id.mediacontroller_time_total); mCurrentTime = (TextView) v.findViewById(R.id.mediacontroller_time_current); + + mMenu = v.findViewById(R.id.video_menu); + mMenu.setOnClickListener(mMenuListener); + FractionalTouchDelegate.setupDelegate(mSystemInfoLayout, mMenu, new RectF(1.0f, 1f, 1.2f, 1.2f)); + mFileName = (TextView) v.findViewById(R.id.mediacontroller_file_name); - if (mFileName != null) - mFileName.setText(mTitle); + mDateTime = (TextView) v.findViewById(R.id.date_time); + mDownloadRate = (TextView) v.findViewById(R.id.download_rate); + mBatteryLevel = (TextView) v.findViewById(R.id.battery_level); + + mControlsLayout = v.findViewById(R.id.mediacontroller_controls); + mControlsButtons = v.findViewById(R.id.mediacontroller_controls_buttons); + + mOperationInfo = (TextView) v.findViewById(R.id.operation_info); + mOperationVolLum = v.findViewById(R.id.operation_volume_brightness); + mVolLumBg = (ImageView) v.findViewById(R.id.operation_bg); + mVolLumNum = (ImageView) v.findViewById(R.id.operation_percent); + + mLock = (ImageButton) v.findViewById(R.id.mediacontroller_lock); + mLock.setOnClickListener(mLockClickListener); + FractionalTouchDelegate.setupDelegate(mSystemInfoLayout, mLock, new RectF(1.0f, 1f, 1.2f, 1.2f)); + + mScreenToggle = (ImageButton) v.findViewById(R.id.mediacontroller_screen_size); + mScreenToggle.setOnClickListener(mScreenToggleListener); + + mSnapshot = (ImageButton) v.findViewById(R.id.mediacontroller_snapshot); + mSnapshot.setOnClickListener(mSnapshotListener); + + mPauseButton = (ImageButton) v.findViewById(R.id.mediacontroller_play_pause); + mPauseButton.setOnClickListener(mPauseListener); + + mProgress = (SeekBar) v.findViewById(R.id.mediacontroller_seekbar); + mProgress.setOnSeekBarChangeListener(mSeekListener); + mProgress.setMax(DEFAULT_SEEKBAR_VALUE); } - public void setMediaPlayer(MediaPlayerControl player) { - mPlayer = player; - updatePausePlay(); + public void setAnchorView(View view) { + mAnchor = view; + int[] location = new int[2]; + mAnchor.getLocationOnScreen(location); + Rect anchorRect = new Rect(location[0], location[1], location[0] + mAnchor.getWidth(), location[1] + mAnchor.getHeight()); + setWindowLayoutType(); + mWindow.showAtLocation(mAnchor, Gravity.NO_GRAVITY, anchorRect.left, anchorRect.bottom); } - /** - * Control the action when the seekbar dragged by user - * - * @param seekWhenDragging - * True the media will seek periodically - */ - public void setInstantSeeking(boolean seekWhenDragging) { - mInstantSeeking = seekWhenDragging; + public void release() { + if (mWindow != null) { + mWindow.dismiss(); + mWindow = null; + } } - public void show() { - show(sDefaultTimeout); + private View.OnClickListener mMenuListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + mPlayer.showMenu(); + } + }; + + private void setOperationInfo(String info, long time) { + mOperationInfo.setText(info); + mOperationInfo.setVisibility(View.VISIBLE); + mHandler.removeMessages(MSG_HIDE_OPERATION_INFO); + mHandler.sendEmptyMessageDelayed(MSG_HIDE_OPERATION_INFO, time); } - /** - * Set the content of the file_name TextView - * - * @param name - */ - public void setFileName(String name) { - mTitle = name; - if (mFileName != null) - mFileName.setText(mTitle); + private void setBrightnessScale(float scale) { + setGraphicOperationProgress(R.drawable.video_brightness_bg, scale); } - /** - * Set the View to hold some information when interact with the - * MediaController - * - * @param v - */ - public void setInfoView(OutlineTextView v) { - mInfoView = v; + private void setVolumeScale(float scale) { + setGraphicOperationProgress(R.drawable.video_volumn_bg, scale); } - private void disableUnsupportedButtons() { - try { - if (mPauseButton != null && !mPlayer.canPause()) - mPauseButton.setEnabled(false); - } catch (IncompatibleClassChangeError ex) { - } - } - - /** - *

- * Change the animation style resource for this controller. - *

- * - *

- * If the controller is showing, calling this method will take effect only - * the next time the controller is shown. - *

- * - * @param animationStyle - * animation style to use when the controller appears and - * disappears. Set to -1 for the default animation, 0 for no - * animation, or a resource identifier for an explicit animation. - * - */ - public void setAnimationStyle(int animationStyle) { - mAnimStyle = animationStyle; - } - - /** - * Show the controller on screen. It will go away automatically after - * 'timeout' milliseconds of inactivity. - * - * @param timeout - * The timeout in milliseconds. Use 0 to show the controller - * until hide() is called. - */ - public void show(int timeout) { - if (!mShowing && mAnchor != null && mAnchor.getWindowToken() != null) { - if (mPauseButton != null) - mPauseButton.requestFocus(); - disableUnsupportedButtons(); + private void setGraphicOperationProgress(int bgID, float scale) { + mVolLumBg.setImageResource(bgID); + mOperationInfo.setVisibility(View.GONE); + mOperationVolLum.setVisibility(View.VISIBLE); + ViewGroup.LayoutParams lp = mVolLumNum.getLayoutParams(); + lp.width = (int) (findViewById(R.id.operation_full).getLayoutParams().width * scale); + mVolLumNum.setLayoutParams(lp); + } + + public void setFileName(String name) { + mFileName.setText(name); + } - if (mFromXml) { - setVisibility(View.VISIBLE); - } else { - int[] location = new int[2]; + public void setDownloadRate(String rate) { + mDownloadRate.setVisibility(View.VISIBLE); + mDownloadRate.setText(rate); + } - mAnchor.getLocationOnScreen(location); - Rect anchorRect = new Rect(location[0], location[1], location[0] + mAnchor.getWidth(), location[1] + mAnchor.getHeight()); + public void setBatteryLevel(String level) { + mBatteryLevel.setVisibility(View.VISIBLE); + mBatteryLevel.setText(level); + } - mWindow.setAnimationStyle(mAnimStyle); - mWindow.showAtLocation(mAnchor, Gravity.NO_GRAVITY, anchorRect.left, anchorRect.bottom); - } - mShowing = true; - if (mShownListener != null) - mShownListener.onShown(); - } + public void setMediaPlayer(MediaPlayerControl player) { + mPlayer = player; updatePausePlay(); - mHandler.sendEmptyMessage(SHOW_PROGRESS); + } + + public void show() { + show(DEFAULT_TIME_OUT); + } + public void show(int timeout) { if (timeout != 0) { - mHandler.removeMessages(FADE_OUT); - mHandler.sendMessageDelayed(mHandler.obtainMessage(FADE_OUT), timeout); + mHandler.removeMessages(MSG_FADE_OUT); + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_FADE_OUT), timeout); } - } + if (!mShowing) { + showButtons(true); + mHandler.removeMessages(MSG_HIDE_SYSTEM_UI); + showSystemUi(true); + + mPauseButton.requestFocus(); - public boolean isShowing() { - return mShowing; + mControlsLayout.startAnimation(mAnimSlideInTop); + mSystemInfoLayout.startAnimation(mAnimSlideInBottom); + mMediaController.setVisibility(View.VISIBLE); + + updatePausePlay(); + mHandler.sendEmptyMessage(MSG_TIME_TICK); + mHandler.sendEmptyMessage(MSG_SHOW_PROGRESS); + + mShowing = true; + } } public void hide() { - if (mAnchor == null) - return; - if (mShowing) { try { - mHandler.removeMessages(SHOW_PROGRESS); - if (mFromXml) - setVisibility(View.GONE); - else - mWindow.dismiss(); + mHandler.removeMessages(MSG_TIME_TICK); + mHandler.removeMessages(MSG_SHOW_PROGRESS); + mControlsLayout.startAnimation(mAnimSlideOutTop); + mSystemInfoLayout.startAnimation(mAnimSlideOutBottom); } catch (IllegalArgumentException ex) { Log.d("MediaController already removed"); } mShowing = false; - if (mHiddenListener != null) - mHiddenListener.onHidden(); } } - public interface OnShownListener { - public void onShown(); - } + private void toggleVideoMode(boolean larger, boolean recycle) { + if (larger) { + if (mVideoMode < VideoView.VIDEO_LAYOUT_ZOOM) + mVideoMode++; + else if (recycle) + mVideoMode = VideoView.VIDEO_LAYOUT_ORIGIN; + } else { + if (mVideoMode > VideoView.VIDEO_LAYOUT_ORIGIN) + mVideoMode--; + else if (recycle) + mVideoMode = VideoView.VIDEO_LAYOUT_ZOOM; + } - private OnShownListener mShownListener; + switch (mVideoMode) { + case VideoView.VIDEO_LAYOUT_ORIGIN: + setOperationInfo(mContext.getString(R.string.video_original), 500); + mScreenToggle.setImageResource(R.drawable.mediacontroller_sreen_size_100); + break; + case VideoView.VIDEO_LAYOUT_SCALE: + setOperationInfo(mContext.getString(R.string.video_fit_screen), 500); + mScreenToggle.setImageResource(R.drawable.mediacontroller_screen_fit); + break; + case VideoView.VIDEO_LAYOUT_STRETCH: + setOperationInfo(mContext.getString(R.string.video_stretch), 500); + mScreenToggle.setImageResource(R.drawable.mediacontroller_screen_size); + break; + case VideoView.VIDEO_LAYOUT_ZOOM: + setOperationInfo(mContext.getString(R.string.video_crop), 500); + mScreenToggle.setImageResource(R.drawable.mediacontroller_sreen_size_crop); + break; + } - public void setOnShownListener(OnShownListener l) { - mShownListener = l; + mPlayer.toggleVideoMode(mVideoMode); } - public interface OnHiddenListener { - public void onHidden(); + private void lock(boolean toLock) { + if (toLock) { + mLock.setImageResource(R.drawable.mediacontroller_lock); + mMenu.setVisibility(View.GONE); + mControlsButtons.setVisibility(View.GONE); + mProgress.setEnabled(false); + if (mScreenLocked != toLock) + setOperationInfo(mContext.getString(R.string.video_screen_locked), 1000); + } else { + mLock.setImageResource(R.drawable.mediacontroller_unlock); + mMenu.setVisibility(View.VISIBLE); + mControlsButtons.setVisibility(View.VISIBLE); + mProgress.setEnabled(true); + if (mScreenLocked != toLock) + setOperationInfo(mContext.getString(R.string.video_screen_unlocked), 1000); + } + mScreenLocked = toLock; + mGestures.setTouchListener(mTouchListener, !mScreenLocked); } - private OnHiddenListener mHiddenListener; - - public void setOnHiddenListener(OnHiddenListener l) { - mHiddenListener = l; + public boolean isLocked() { + return mScreenLocked; } - private Handler mHandler = new Handler() { + private static final int MSG_FADE_OUT = 1; + private static final int MSG_SHOW_PROGRESS = 2; + private static final int MSG_HIDE_SYSTEM_UI = 3; + private static final int MSG_TIME_TICK = 4; + private static final int MSG_HIDE_OPERATION_INFO = 5; + private static final int MSG_HIDE_OPERATION_VOLLUM = 6; + + private static class MHandler extends Handler { + private WeakReference mc; + + public MHandler(MediaController mc) { + this.mc = new WeakReference(mc); + } + @Override public void handleMessage(Message msg) { - long pos; + MediaController c = mc.get(); + if (c == null) + return; + switch (msg.what) { - case FADE_OUT: - hide(); + case MSG_FADE_OUT: + c.hide(); break; - case SHOW_PROGRESS: - pos = setProgress(); - if (!mDragging && mShowing) { - msg = obtainMessage(SHOW_PROGRESS); + case MSG_SHOW_PROGRESS: + long pos = c.setProgress(); + if (!c.mDragging && c.mShowing) { + msg = obtainMessage(MSG_SHOW_PROGRESS); sendMessageDelayed(msg, 1000 - (pos % 1000)); - updatePausePlay(); + c.updatePausePlay(); } break; + case MSG_HIDE_SYSTEM_UI: + if (!c.mShowing) + c.showSystemUi(false); + break; + case MSG_TIME_TICK: + c.mDateTime.setText(StringHelper.currentTimeString()); + sendEmptyMessageDelayed(MSG_TIME_TICK, TIME_TICK_INTERVAL); + break; + case MSG_HIDE_OPERATION_INFO: + c.mOperationInfo.setVisibility(View.GONE); + break; + case MSG_HIDE_OPERATION_VOLLUM: + c.mOperationVolLum.setVisibility(View.GONE); + break; } } }; @@ -356,45 +445,146 @@ private long setProgress() { long position = mPlayer.getCurrentPosition(); long duration = mPlayer.getDuration(); - if (mProgress != null) { - if (duration > 0) { - long pos = 1000L * position / duration; - mProgress.setProgress((int) pos); - } - int percent = mPlayer.getBufferPercentage(); - mProgress.setSecondaryProgress(percent * 10); + if (duration > 0) { + long pos = 1000L * position / duration; + mProgress.setProgress((int) pos); } + int percent = mPlayer.getBufferPercentage(); + mProgress.setSecondaryProgress(percent * 10); mDuration = duration; - if (mEndTime != null) - mEndTime.setText(StringUtils.generateTime(mDuration)); - if (mCurrentTime != null) - mCurrentTime.setText(StringUtils.generateTime(position)); + mEndTime.setText(StringUtils.generateTime(mDuration)); + mCurrentTime.setText(StringUtils.generateTime(position)); return position; } @Override public boolean onTouchEvent(MotionEvent event) { - show(sDefaultTimeout); - return true; + mHandler.removeMessages(MSG_HIDE_SYSTEM_UI); + mHandler.sendEmptyMessageDelayed(MSG_HIDE_SYSTEM_UI, DEFAULT_TIME_OUT); + return mGestures.onTouchEvent(event) || super.onTouchEvent(event); + } + + private TouchListener mTouchListener = new TouchListener() { + @Override + public void onGestureBegin() { + mBrightness = mContext.getWindow().getAttributes().screenBrightness; + mVolume = mAM.getStreamVolume(AudioManager.STREAM_MUSIC); + if (mBrightness <= 0.00f) + mBrightness = 0.50f; + if (mBrightness < 0.01f) + mBrightness = 0.01f; + if (mVolume < 0) + mVolume = 0; + } + + @Override + public void onGestureEnd() { + mOperationVolLum.setVisibility(View.GONE); + } + + @Override + public void onLeftSlide(float percent) { + setBrightness(mBrightness + percent); + setBrightnessScale(mContext.getWindow().getAttributes().screenBrightness); + } + + @Override + public void onRightSlide(float percent) { + int v = (int) (percent * mMaxVolume) + mVolume; + setVolume(v); + } + + @Override + public void onSingleTap() { + if (mShowing) + hide(); + else + show(); + if (mPlayer.getBufferPercentage() >= 100) + mPlayer.removeLoadingView(); + } + + @Override + public void onDoubleTap() { + toggleVideoMode(true, true); + } + + @Override + public void onLongPress() { + doPauseResume(); + } + + @Override + public void onScale(float scaleFactor, int state) { + switch (state) { + case CommonGestures.SCALE_STATE_BEGIN: + mVideoMode = VideoView.VIDEO_LAYOUT_SCALE_ZOOM; + mScreenToggle.setImageResource(R.drawable.mediacontroller_sreen_size_100); + mPlayer.toggleVideoMode(mVideoMode); + break; + case CommonGestures.SCALE_STATE_SCALEING: + float currentRatio = mPlayer.scale(scaleFactor); + setOperationInfo((int) (currentRatio * 100) + "%", 500); + break; + case CommonGestures.SCALE_STATE_END: + break; + } + } + }; + + private void setVolume(int v) { + if (v > mMaxVolume) + v = mMaxVolume; + else if (v < 0) + v = 0; + mAM.setStreamVolume(AudioManager.STREAM_MUSIC, v, 0); + setVolumeScale((float) v / mMaxVolume); + } + + private void setBrightness(float f) { + WindowManager.LayoutParams lp = mContext.getWindow().getAttributes(); + lp.screenBrightness = f; + if (lp.screenBrightness > 1.0f) + lp.screenBrightness = 1.0f; + else if (lp.screenBrightness < 0.01f) + lp.screenBrightness = 0.01f; + mContext.getWindow().setAttributes(lp); } @Override public boolean onTrackballEvent(MotionEvent ev) { - show(sDefaultTimeout); + show(DEFAULT_TIME_OUT); return false; } @Override public boolean dispatchKeyEvent(KeyEvent event) { int keyCode = event.getKeyCode(); + + switch (keyCode) { + case KeyEvent.KEYCODE_VOLUME_MUTE: + return super.dispatchKeyEvent(event); + case KeyEvent.KEYCODE_VOLUME_UP: + case KeyEvent.KEYCODE_VOLUME_DOWN: + mVolume = mAM.getStreamVolume(AudioManager.STREAM_MUSIC); + int step = keyCode == KeyEvent.KEYCODE_VOLUME_UP ? 1 : -1; + setVolume(mVolume + step); + mHandler.removeMessages(MSG_HIDE_OPERATION_VOLLUM); + mHandler.sendEmptyMessageDelayed(MSG_HIDE_OPERATION_VOLLUM, 500); + return true; + } + + if (isLocked()) { + show(); + return true; + } + if (event.getRepeatCount() == 0 && (keyCode == KeyEvent.KEYCODE_HEADSETHOOK || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE || keyCode == KeyEvent.KEYCODE_SPACE)) { doPauseResume(); - show(sDefaultTimeout); - if (mPauseButton != null) - mPauseButton.requestFocus(); + show(DEFAULT_TIME_OUT); return true; } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP) { if (mPlayer.isPlaying()) { @@ -402,26 +592,38 @@ public boolean dispatchKeyEvent(KeyEvent event) { updatePausePlay(); } return true; - } else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) { - hide(); + } else if (keyCode == KeyEvent.KEYCODE_BACK) { + release(); + mPlayer.stop(); return true; } else { - show(sDefaultTimeout); + show(DEFAULT_TIME_OUT); } return super.dispatchKeyEvent(event); } - private View.OnClickListener mPauseListener = new View.OnClickListener() { - public void onClick(View v) { - doPauseResume(); - show(sDefaultTimeout); + @TargetApi(11) + private void showSystemUi(boolean visible) { + if (UIUtils.hasHoneycomb()) { + int flag = visible ? 0 : View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LOW_PROFILE; + mRoot.setSystemUiVisibility(flag); } - }; + } - private void updatePausePlay() { - if (mRoot == null || mPauseButton == null) - return; + private void showButtons(boolean showButtons) { + Window window = mContext.getWindow(); + WindowManager.LayoutParams layoutParams = window.getAttributes(); + float val = showButtons ? -1 : 0; + try { + Field buttonBrightness = layoutParams.getClass().getField("buttonBrightness"); + buttonBrightness.set(layoutParams, val); + } catch (Exception e) { + Log.e("dimButtons", e); + } + window.setAttributes(layoutParams); + } + private void updatePausePlay() { if (mPlayer.isPlaying()) mPauseButton.setImageResource(R.drawable.mediacontroller_pause); else @@ -436,19 +638,62 @@ private void doPauseResume() { updatePausePlay(); } + private View.OnClickListener mPauseListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mPlayer.isPlaying()) + show(DEFAULT_LONG_TIME_SHOW); + else + show(); + doPauseResume(); + } + }; + + private View.OnClickListener mLockClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + hide(); + lock(!mScreenLocked); + show(); + } + }; + + private View.OnClickListener mScreenToggleListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + show(DEFAULT_TIME_OUT); + toggleVideoMode(true, true); + } + }; + + private View.OnClickListener mSnapshotListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + show(DEFAULT_TIME_OUT); + mSnapshot.setEnabled(false); + mPlayer.snapshot(); + mSnapshot.setEnabled(true); + } + }; + private OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() { + private boolean wasStopped = false; + + @Override public void onStartTrackingTouch(SeekBar bar) { mDragging = true; show(3600000); - mHandler.removeMessages(SHOW_PROGRESS); - if (mInstantSeeking) + mHandler.removeMessages(MSG_SHOW_PROGRESS); + wasStopped = !mPlayer.isPlaying(); + if (mInstantSeeking) { mAM.setStreamMute(AudioManager.STREAM_MUSIC, true); - if (mInfoView != null) { - mInfoView.setText(""); - mInfoView.setVisibility(View.VISIBLE); + if (wasStopped) { + mPlayer.start(); + } } } + @Override public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) { if (!fromuser) return; @@ -457,57 +702,60 @@ public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) { String time = StringUtils.generateTime(newposition); if (mInstantSeeking) mPlayer.seekTo(newposition); - if (mInfoView != null) - mInfoView.setText(time); - if (mCurrentTime != null) - mCurrentTime.setText(time); + setOperationInfo(time, 1500); + mCurrentTime.setText(time); } + @Override public void onStopTrackingTouch(SeekBar bar) { - if (!mInstantSeeking) + if (!mInstantSeeking) { mPlayer.seekTo((mDuration * bar.getProgress()) / 1000); - if (mInfoView != null) { - mInfoView.setText(""); - mInfoView.setVisibility(View.GONE); + } else if (wasStopped) { + mPlayer.pause(); } - show(sDefaultTimeout); - mHandler.removeMessages(SHOW_PROGRESS); + mOperationInfo.setVisibility(View.GONE); + show(DEFAULT_TIME_OUT); + mHandler.removeMessages(MSG_SHOW_PROGRESS); mAM.setStreamMute(AudioManager.STREAM_MUSIC, false); mDragging = false; - mHandler.sendEmptyMessageDelayed(SHOW_PROGRESS, 1000); + mHandler.sendEmptyMessageDelayed(MSG_SHOW_PROGRESS, 1000); } }; - @Override - public void setEnabled(boolean enabled) { - if (mPauseButton != null) - mPauseButton.setEnabled(enabled); - if (mProgress != null) - mProgress.setEnabled(enabled); - disableUnsupportedButtons(); - super.setEnabled(enabled); - } - public interface MediaPlayerControl { void start(); void pause(); - long getDuration(); - - long getCurrentPosition(); + void stop(); void seekTo(long pos); boolean isPlaying(); + long getDuration(); + + long getCurrentPosition(); + int getBufferPercentage(); - boolean canPause(); + void previous(); + + void next(); + + long goForward(); + + long goBack(); + + void toggleVideoMode(int mode); + + void showMenu(); + + void removeLoadingView(); - boolean canSeekBackward(); + float scale(float scale); - boolean canSeekForward(); + void snapshot(); } } diff --git a/src/io/vov/vitamio/widget/VideoView.java b/src/io/vov/vitamio/widget/VideoView.java index 2016436..043efee 100644 --- a/src/io/vov/vitamio/widget/VideoView.java +++ b/src/io/vov/vitamio/widget/VideoView.java @@ -1,732 +1,107 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * Copyright (C) 2012 YIXIA.COM - * - * 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 io.vov.vitamio.widget; -import io.vov.utils.Log; -import io.vov.vitamio.MediaPlayer; -import io.vov.vitamio.MediaPlayer.OnBufferingUpdateListener; -import io.vov.vitamio.MediaPlayer.OnCompletionListener; -import io.vov.vitamio.MediaPlayer.OnErrorListener; -import io.vov.vitamio.MediaPlayer.OnInfoListener; -import io.vov.vitamio.MediaPlayer.OnPreparedListener; -import io.vov.vitamio.MediaPlayer.OnSeekCompleteListener; -import io.vov.vitamio.MediaPlayer.OnSubtitleUpdateListener; -import io.vov.vitamio.MediaPlayer.OnVideoSizeChangedListener; -import io.vov.vitamio.R; - -import java.io.IOException; -import java.util.HashMap; -import java.util.List; - +import me.abitno.utils.DeviceUtils; +import me.abitno.utils.Log; import android.app.Activity; -import android.app.AlertDialog; import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.media.AudioManager; -import android.net.Uri; -import android.text.TextUtils; import android.util.AttributeSet; -import android.util.DisplayMetrics; -import android.view.KeyEvent; -import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; -import android.view.View; import android.view.ViewGroup.LayoutParams; -/** - * Displays a video file. The VideoView class can load images from various - * sources (such as resources or content providers), takes care of computing its - * measurement from the video so that it can be used in any layout manager, and - * provides various display options such as scaling and tinting. - * - * VideoView also provide many wrapper methods for - * {@link io.vov.vitamio.MediaPlayer}, such as {@link #getVideoWidth()}, - * {@link #setSubShown(boolean)} - */ -public class VideoView extends SurfaceView implements MediaController.MediaPlayerControl { - private Uri mUri; - private String mVideoTitle; - private long mDuration; - - private static final int STATE_ERROR = -1; - private static final int STATE_IDLE = 0; - private static final int STATE_PREPARING = 1; - private static final int STATE_PREPARED = 2; - private static final int STATE_PLAYING = 3; - private static final int STATE_PAUSED = 4; - private static final int STATE_PLAYBACK_COMPLETED = 5; - private static final int STATE_SUSPEND = 6; - private static final int STATE_RESUME = 7; - private static final int STATE_SUSPEND_UNSUPPORTED = 8; - - private int mCurrentState = STATE_IDLE; - private int mTargetState = STATE_IDLE; - - private float mAspectRatio = 0; - private int mVideoLayout = VIDEO_LAYOUT_SCALE; +public class VideoView extends SurfaceView { + private Activity mActivity; + private SurfaceHolder mSurfaceHolder; + private int mSurfaceWidth, mSurfaceHeight; + private int mVideoMode = VIDEO_LAYOUT_SCALE; public static final int VIDEO_LAYOUT_ORIGIN = 0; public static final int VIDEO_LAYOUT_SCALE = 1; public static final int VIDEO_LAYOUT_STRETCH = 2; public static final int VIDEO_LAYOUT_ZOOM = 3; - - private SurfaceHolder mSurfaceHolder = null; - private MediaPlayer mMediaPlayer = null; - private int mVideoWidth; - private int mVideoHeight; - private float mVideoAspectRatio; - private int mSurfaceWidth; - private int mSurfaceHeight; - private MediaController mMediaController; - private OnCompletionListener mOnCompletionListener; - private OnPreparedListener mOnPreparedListener; - private OnErrorListener mOnErrorListener; - private OnSeekCompleteListener mOnSeekCompleteListener; - private OnSubtitleUpdateListener mOnSubtitleUpdateListener; - private OnInfoListener mOnInfoListener; - private OnBufferingUpdateListener mOnBufferingUpdateListener; - private int mCurrentBufferPercentage; - private long mSeekWhenPrepared; - private boolean mCanPause = true; - private boolean mCanSeekBack = true; - private boolean mCanSeekForward = true; - private Context mContext; - - public VideoView(Context context) { - super(context); - initVideoView(context); - } + public static final int VIDEO_LAYOUT_SCALE_ZOOM = 4; + public int mVideoHeight; public VideoView(Context context, AttributeSet attrs) { - this(context, attrs, 0); + super(context, attrs); + getHolder().addCallback(mCallback); } - public VideoView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - initVideoView(context); - } + @SuppressWarnings("deprecation") + public void initialize(Activity activity, SurfaceCallback l, boolean push) { + mActivity = activity; + mListener = l; + if (mSurfaceHolder == null) + mSurfaceHolder = getHolder(); - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int width = getDefaultSize(mVideoWidth, widthMeasureSpec); - int height = getDefaultSize(mVideoHeight, heightMeasureSpec); - setMeasuredDimension(width, height); + // These methods is set to hw decoder or sw decoder, <= 2.3 + if (push) + getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); + else + getHolder().setType(SurfaceHolder.SURFACE_TYPE_NORMAL); } - /** - * Set the display options - * - * @param layout - *
    - *
  • {@link #VIDEO_LAYOUT_ORIGIN} - *
  • {@link #VIDEO_LAYOUT_SCALE} - *
  • {@link #VIDEO_LAYOUT_STRETCH} - *
  • {@link #VIDEO_LAYOUT_ZOOM} - *
- * @param aspectRatio - * video aspect ratio, will audo detect if 0. - */ - public void setVideoLayout(int layout, float aspectRatio) { + private void setSurfaceLayout(float userRatio, int videoWidth, int videoHeight, float videoAspectRatio) { LayoutParams lp = getLayoutParams(); - DisplayMetrics disp = mContext.getResources().getDisplayMetrics(); - int windowWidth = disp.widthPixels, windowHeight = disp.heightPixels; + int windowWidth = DeviceUtils.getScreenWidth(mActivity); + int windowHeight = DeviceUtils.getScreenHeight(mActivity); float windowRatio = windowWidth / (float) windowHeight; - float videoRatio = aspectRatio <= 0.01f ? mVideoAspectRatio : aspectRatio; - mSurfaceHeight = mVideoHeight; - mSurfaceWidth = mVideoWidth; - if (VIDEO_LAYOUT_ORIGIN == layout && mSurfaceWidth < windowWidth && mSurfaceHeight < windowHeight) { + float videoRatio = userRatio <= 0.01f ? videoAspectRatio : userRatio; + mSurfaceHeight = videoHeight; + mSurfaceWidth = videoWidth; + if (VIDEO_LAYOUT_ORIGIN == mVideoMode && mSurfaceWidth < windowWidth && mSurfaceHeight < windowHeight) { lp.width = (int) (mSurfaceHeight * videoRatio); lp.height = mSurfaceHeight; - } else if (layout == VIDEO_LAYOUT_ZOOM) { + } else if (mVideoMode == VIDEO_LAYOUT_ZOOM) { lp.width = windowRatio > videoRatio ? windowWidth : (int) (videoRatio * windowHeight); lp.height = windowRatio < videoRatio ? windowHeight : (int) (windowWidth / videoRatio); + } else if (mVideoMode == VIDEO_LAYOUT_SCALE_ZOOM && mVideoHeight > 0) { + lp.width = (int) (mVideoHeight * videoRatio); + lp.height = mVideoHeight; } else { - boolean full = layout == VIDEO_LAYOUT_STRETCH; + boolean full = mVideoMode == VIDEO_LAYOUT_STRETCH; lp.width = (full || windowRatio < videoRatio) ? windowWidth : (int) (videoRatio * windowHeight); lp.height = (full || windowRatio > videoRatio) ? windowHeight : (int) (windowWidth / videoRatio); } + mVideoHeight = lp.height; setLayoutParams(lp); getHolder().setFixedSize(mSurfaceWidth, mSurfaceHeight); - Log.d("VIDEO: %dx%dx%f, Surface: %dx%d, LP: %dx%d, Window: %dx%dx%f", mVideoWidth, mVideoHeight, mVideoAspectRatio, mSurfaceWidth, mSurfaceHeight, lp.width, lp.height, windowWidth, windowHeight, windowRatio); - mVideoLayout = layout; - mAspectRatio = aspectRatio; + Log.d("VIDEO: %dx%dx%f, Surface: %dx%d, LP: %dx%d, Window: %dx%dx%f", videoWidth, videoHeight, videoAspectRatio, mSurfaceWidth, mSurfaceHeight, lp.width, lp.height, windowWidth, windowHeight, windowRatio); } - private void initVideoView(Context ctx) { - mContext = ctx; - mVideoWidth = 0; - mVideoHeight = 0; - getHolder().addCallback(mSHCallback); - setFocusable(true); - setFocusableInTouchMode(true); - requestFocus(); - mCurrentState = STATE_IDLE; - mTargetState = STATE_IDLE; - if (ctx instanceof Activity) - ((Activity) ctx).setVolumeControlStream(AudioManager.STREAM_MUSIC); + public void setVideoLayout(int layout, float userRatio, int videoWidth, int videoHeight, float videoRatio) { + mVideoMode = layout; + setSurfaceLayout(userRatio, videoWidth, videoHeight, videoRatio); } - public boolean isValid() { - return (mSurfaceHolder != null && mSurfaceHolder.getSurface().isValid()); - } - - public void setVideoPath(String path) { - setVideoURI(Uri.parse(path)); - } - - public void setVideoURI(Uri uri) { - mUri = uri; - mSeekWhenPrepared = 0; - openVideo(); - requestLayout(); - invalidate(); - } - - public void setVideoTitle(String title) { - mVideoTitle = title; - } - - public void stopPlayback() { - if (mMediaPlayer != null) { - mMediaPlayer.stop(); - mMediaPlayer.release(); - mMediaPlayer = null; - mCurrentState = STATE_IDLE; - mTargetState = STATE_IDLE; - } - } - - private void openVideo() { - if (mUri == null || mSurfaceHolder == null) - return; - - Intent i = new Intent("com.android.music.musicservicecommand"); - i.putExtra("command", "pause"); - mContext.sendBroadcast(i); - - release(false); - try { - mDuration = -1; - mCurrentBufferPercentage = 0; - mMediaPlayer = new MediaPlayer(mContext); - mMediaPlayer.setOnPreparedListener(mPreparedListener); - mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener); - mMediaPlayer.setOnCompletionListener(mCompletionListener); - mMediaPlayer.setOnErrorListener(mErrorListener); - mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener); - mMediaPlayer.setOnInfoListener(mInfoListener); - mMediaPlayer.setOnSeekCompleteListener(mSeekCompleteListener); - mMediaPlayer.setOnSubtitleUpdateListener(mSubtitleUpdateListener); - mMediaPlayer.setDataSource(mContext, mUri); - mMediaPlayer.setDisplay(mSurfaceHolder); - mMediaPlayer.setScreenOnWhilePlaying(true); - mMediaPlayer.prepareAsync(); - mCurrentState = STATE_PREPARING; - attachMediaController(); - } catch (IOException ex) { - Log.e("Unable to open content: " + mUri, ex); - mCurrentState = STATE_ERROR; - mTargetState = STATE_ERROR; - mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); - return; - } catch (IllegalArgumentException ex) { - Log.e("Unable to open content: " + mUri, ex); - mCurrentState = STATE_ERROR; - mTargetState = STATE_ERROR; - mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); - return; - } - } - - public void setMediaController(MediaController controller) { - if (mMediaController != null) - mMediaController.hide(); - mMediaController = controller; - attachMediaController(); - } - - private void attachMediaController() { - if (mMediaPlayer != null && mMediaController != null) { - mMediaController.setMediaPlayer(this); - View anchorView = this.getParent() instanceof View ? (View) this.getParent() : this; - mMediaController.setAnchorView(anchorView); - mMediaController.setEnabled(isInPlaybackState()); - - if (TextUtils.isEmpty(mVideoTitle)) { - if (mUri != null) { - List paths = mUri.getPathSegments(); - String name = paths == null || paths.isEmpty() ? "null" : paths.get(paths.size() - 1); - mMediaController.setFileName(name); - } - } else { - mMediaController.setFileName(mVideoTitle); - } - - } - } - - OnVideoSizeChangedListener mSizeChangedListener = new OnVideoSizeChangedListener() { - public void onVideoSizeChanged(MediaPlayer mp, int width, int height) { - Log.d("onVideoSizeChanged: (%dx%d)", width, height); - mVideoWidth = mp.getVideoWidth(); - mVideoHeight = mp.getVideoHeight(); - mVideoAspectRatio = mp.getVideoAspectRatio(); - if (mVideoWidth != 0 && mVideoHeight != 0) - setVideoLayout(mVideoLayout, mAspectRatio); - } - }; - - OnPreparedListener mPreparedListener = new OnPreparedListener() { - public void onPrepared(MediaPlayer mp) { - Log.d("onPrepared"); - mCurrentState = STATE_PREPARED; - mTargetState = STATE_PLAYING; - - if (mOnPreparedListener != null) - mOnPreparedListener.onPrepared(mMediaPlayer); - if (mMediaController != null) - mMediaController.setEnabled(true); - mVideoWidth = mp.getVideoWidth(); - mVideoHeight = mp.getVideoHeight(); - mVideoAspectRatio = mp.getVideoAspectRatio(); - - long seekToPosition = mSeekWhenPrepared; - - if (seekToPosition != 0) - seekTo(seekToPosition); - if (mVideoWidth != 0 && mVideoHeight != 0) { - setVideoLayout(mVideoLayout, mAspectRatio); - if (mSurfaceWidth == mVideoWidth && mSurfaceHeight == mVideoHeight) { - if (mTargetState == STATE_PLAYING) { - start(); - if (mMediaController != null) - mMediaController.show(); - } else if (!isPlaying() && (seekToPosition != 0 || getCurrentPosition() > 0)) { - if (mMediaController != null) - mMediaController.show(0); - } - } - } else if (mTargetState == STATE_PLAYING) { - start(); - } - } - }; - - private OnCompletionListener mCompletionListener = new OnCompletionListener() { - public void onCompletion(MediaPlayer mp) { - Log.d("onCompletion"); - mCurrentState = STATE_PLAYBACK_COMPLETED; - mTargetState = STATE_PLAYBACK_COMPLETED; - if (mMediaController != null) - mMediaController.hide(); - if (mOnCompletionListener != null) - mOnCompletionListener.onCompletion(mMediaPlayer); - } - }; - - private OnErrorListener mErrorListener = new OnErrorListener() { - public boolean onError(MediaPlayer mp, int framework_err, int impl_err) { - Log.d("Error: %d, %d", framework_err, impl_err); - mCurrentState = STATE_ERROR; - mTargetState = STATE_ERROR; - if (mMediaController != null) - mMediaController.hide(); - - if (mOnErrorListener != null) { - if (mOnErrorListener.onError(mMediaPlayer, framework_err, impl_err)) - return true; - } - - if (getWindowToken() != null) { - int message = framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK ? R.string.vitamio_videoview_error_text_invalid_progressive_playback : R.string.vitamio_videoview_error_text_unknown; - - new AlertDialog.Builder(mContext).setTitle(R.string.vitamio_videoview_error_title).setMessage(message).setPositiveButton(R.string.vitamio_videoview_error_button, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { - if (mOnCompletionListener != null) - mOnCompletionListener.onCompletion(mMediaPlayer); - } - }).setCancelable(false).show(); - } - return true; - } - }; - - private OnBufferingUpdateListener mBufferingUpdateListener = new OnBufferingUpdateListener() { - public void onBufferingUpdate(MediaPlayer mp, int percent) { - mCurrentBufferPercentage = percent; - if (mOnBufferingUpdateListener != null) - mOnBufferingUpdateListener.onBufferingUpdate(mp, percent); - } - }; - - private OnInfoListener mInfoListener = new OnInfoListener() { + private SurfaceHolder.Callback mCallback = new SurfaceHolder.Callback() { @Override - public boolean onInfo(MediaPlayer mp, int what, int extra) { - Log.d("onInfo: (%d, %d)", what, extra); - if (mOnInfoListener != null) { - mOnInfoListener.onInfo(mp, what, extra); - } else if (mMediaPlayer != null) { - if (what == MediaPlayer.MEDIA_INFO_BUFFERING_START) - mMediaPlayer.pause(); - else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END) - mMediaPlayer.start(); - } - - return true; + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + holder.setKeepScreenOn(true); + if (mListener != null) + mListener.onSurfaceChanged(holder, format, width, height); } - }; - - private OnSeekCompleteListener mSeekCompleteListener = new OnSeekCompleteListener() { - @Override - public void onSeekComplete(MediaPlayer mp) { - Log.d("onSeekComplete"); - if (mOnSeekCompleteListener != null) - mOnSeekCompleteListener.onSeekComplete(mp); - } - }; - private OnSubtitleUpdateListener mSubtitleUpdateListener = new OnSubtitleUpdateListener() { @Override - public void onSubtitleUpdate(byte[] pixels, int width, int height) { - Log.i("onSubtitleUpdate: bitmap subtitle, %dx%d", width, height); - if (mOnSubtitleUpdateListener != null) - mOnSubtitleUpdateListener.onSubtitleUpdate(pixels, width, height); - } - - @Override - public void onSubtitleUpdate(String text) { - Log.i("onSubtitleUpdate: %s", text); - if (mOnSubtitleUpdateListener != null) - mOnSubtitleUpdateListener.onSubtitleUpdate(text); - } - }; - - public void setOnPreparedListener(OnPreparedListener l) { - mOnPreparedListener = l; - } - - public void setOnCompletionListener(OnCompletionListener l) { - mOnCompletionListener = l; - } - - public void setOnErrorListener(OnErrorListener l) { - mOnErrorListener = l; - } - - public void setOnBufferingUpdateListener(OnBufferingUpdateListener l) { - mOnBufferingUpdateListener = l; - } - - public void setOnSeekCompleteListener(OnSeekCompleteListener l) { - mOnSeekCompleteListener = l; - } - - public void setOnSubtitleUpdateListener(OnSubtitleUpdateListener l) { - mOnSubtitleUpdateListener = l; - } - - public void setOnInfoListener(OnInfoListener l) { - mOnInfoListener = l; - } - - SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback() { - public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { - mSurfaceWidth = w; - mSurfaceHeight = h; - boolean isValidState = (mTargetState == STATE_PLAYING); - boolean hasValidSize = (mVideoWidth == w && mVideoHeight == h); - if (mMediaPlayer != null && isValidState && hasValidSize) { - if (mSeekWhenPrepared != 0) - seekTo(mSeekWhenPrepared); - start(); - if (mMediaController != null) { - if (mMediaController.isShowing()) - mMediaController.hide(); - mMediaController.show(); - } - } - } - public void surfaceCreated(SurfaceHolder holder) { mSurfaceHolder = holder; - if (mMediaPlayer != null && mCurrentState == STATE_SUSPEND && mTargetState == STATE_RESUME) { - mMediaPlayer.setDisplay(mSurfaceHolder); - resume(); - } else { - openVideo(); - } + if (mListener != null) + mListener.onSurfaceCreated(holder); } + @Override public void surfaceDestroyed(SurfaceHolder holder) { - mSurfaceHolder = null; - if (mMediaController != null) - mMediaController.hide(); - if (mCurrentState != STATE_SUSPEND) - release(true); + if (mListener != null) + mListener.onSurfaceDestroyed(holder); } }; - private void release(boolean cleartargetstate) { - if (mMediaPlayer != null) { - mMediaPlayer.reset(); - mMediaPlayer.release(); - mMediaPlayer = null; - mCurrentState = STATE_IDLE; - if (cleartargetstate) - mTargetState = STATE_IDLE; - } - } + private SurfaceCallback mListener; - @Override - public boolean onTouchEvent(MotionEvent ev) { - if (isInPlaybackState() && mMediaController != null) - toggleMediaControlsVisiblity(); - return false; - } - - @Override - public boolean onTrackballEvent(MotionEvent ev) { - if (isInPlaybackState() && mMediaController != null) - toggleMediaControlsVisiblity(); - return false; - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK && keyCode != KeyEvent.KEYCODE_VOLUME_UP && keyCode != KeyEvent.KEYCODE_VOLUME_DOWN && keyCode != KeyEvent.KEYCODE_MENU && keyCode != KeyEvent.KEYCODE_CALL && keyCode != KeyEvent.KEYCODE_ENDCALL; - if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) { - if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE || keyCode == KeyEvent.KEYCODE_SPACE) { - if (mMediaPlayer.isPlaying()) { - pause(); - mMediaController.show(); - } else { - start(); - mMediaController.hide(); - } - return true; - } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP && mMediaPlayer.isPlaying()) { - pause(); - mMediaController.show(); - } else { - toggleMediaControlsVisiblity(); - } - } - - return super.onKeyDown(keyCode, event); - } - - private void toggleMediaControlsVisiblity() { - if (mMediaController.isShowing()) { - mMediaController.hide(); - } else { - mMediaController.show(); - } - } + public interface SurfaceCallback { + public void onSurfaceCreated(SurfaceHolder holder); - public void start() { - if (isInPlaybackState()) { - mMediaPlayer.start(); - mCurrentState = STATE_PLAYING; - } - mTargetState = STATE_PLAYING; - } - - public void pause() { - if (isInPlaybackState()) { - if (mMediaPlayer.isPlaying()) { - mMediaPlayer.pause(); - mCurrentState = STATE_PAUSED; - } - } - mTargetState = STATE_PAUSED; - } - - public void suspend() { - if (isInPlaybackState()) { - release(false); - mCurrentState = STATE_SUSPEND_UNSUPPORTED; - Log.d("Unable to suspend video. Release MediaPlayer."); - } - } - - public void resume() { - if (mSurfaceHolder == null && mCurrentState == STATE_SUSPEND) { - mTargetState = STATE_RESUME; - } else if (mCurrentState == STATE_SUSPEND_UNSUPPORTED) { - openVideo(); - } - } - - public long getDuration() { - if (isInPlaybackState()) { - if (mDuration > 0) - return mDuration; - mDuration = mMediaPlayer.getDuration(); - return mDuration; - } - mDuration = -1; - return mDuration; - } - - public long getCurrentPosition() { - if (isInPlaybackState()) - return mMediaPlayer.getCurrentPosition(); - return 0; - } - - public void seekTo(long msec) { - if (isInPlaybackState()) { - mMediaPlayer.seekTo(msec); - mSeekWhenPrepared = 0; - } else { - mSeekWhenPrepared = msec; - } - } - - public boolean isPlaying() { - return isInPlaybackState() && mMediaPlayer.isPlaying(); - } - - public int getBufferPercentage() { - if (mMediaPlayer != null) - return mCurrentBufferPercentage; - return 0; - } - - public void setVolume(float leftVolume, float rightVolume) { - if (mMediaPlayer != null) - mMediaPlayer.setVolume(leftVolume, rightVolume); - } - - public int getVideoWidth() { - return mVideoWidth; - } - - public int getVideoHeight() { - return mVideoHeight; - } - - public float getVideoAspectRatio() { - return mVideoAspectRatio; - } - - public void setVideoQuality(int quality) { - if (mMediaPlayer != null) - mMediaPlayer.setVideoQuality(quality); - } - - public void setBufferSize(int bufSize) { - if (mMediaPlayer != null) - mMediaPlayer.setBufferSize(bufSize); - } - - public boolean isBuffering() { - if (mMediaPlayer != null) - return mMediaPlayer.isBuffering(); - return false; - } - - public void setMetaEncoding(String encoding) { - if (mMediaPlayer != null) - mMediaPlayer.setMetaEncoding(encoding); - } - - public String getMetaEncoding() { - if (mMediaPlayer != null) - return mMediaPlayer.getMetaEncoding(); - return null; - } - - public HashMap getAudioTrackMap(String encoding) { - if (mMediaPlayer != null) - return mMediaPlayer.getAudioTrackMap(encoding); - return null; - } - - public int getAudioTrack() { - if (mMediaPlayer != null) - return mMediaPlayer.getAudioTrack(); - return -1; - } - - public void setAudioTrack(int audioIndex) { - if (mMediaPlayer != null) - mMediaPlayer.setAudioTrack(audioIndex); - } - - public void setSubShown(boolean shown) { - if (mMediaPlayer != null) - mMediaPlayer.setSubShown(shown); - } - - public void setSubEncoding(String encoding) { - if (mMediaPlayer != null) - mMediaPlayer.setSubEncoding(encoding); - } - - public int getSubLocation() { - if (mMediaPlayer != null) - return mMediaPlayer.getSubLocation(); - return -1; - } - - public void setSubPath(String subPath) { - if (mMediaPlayer != null) - mMediaPlayer.setSubPath(subPath); - } - - public String getSubPath() { - if (mMediaPlayer != null) - return mMediaPlayer.getSubPath(); - return null; - } - - public void setSubTrack(int trackId) { - if (mMediaPlayer != null) - mMediaPlayer.setSubTrack(trackId); - } - - public int getSubTrack() { - if (mMediaPlayer != null) - return mMediaPlayer.getSubTrack(); - return -1; - } - - public HashMap getSubTrackMap(String encoding) { - if (mMediaPlayer != null) - return mMediaPlayer.getSubTrackMap(encoding); - return null; - } - - protected boolean isInPlaybackState() { - return (mMediaPlayer != null && mCurrentState != STATE_ERROR && mCurrentState != STATE_IDLE && mCurrentState != STATE_PREPARING); - } - - public boolean canPause() { - return mCanPause; - } - - public boolean canSeekBackward() { - return mCanSeekBack; - } + public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height); - public boolean canSeekForward() { - return mCanSeekForward; + public void onSurfaceDestroyed(SurfaceHolder holder); } }