(c);
+ }
+
+ public void handleMessage(Message msg) {
+ InitActivity ctx = (InitActivity) mContext.get();
+ switch (msg.what) {
+ case 0:
+ ctx.mPD.dismiss();
+ Intent src = ctx.getIntent();
+ Intent i = new Intent();
+ i.setClassName(src.getStringExtra("package"), src.getStringExtra("className"));
+ i.setData(src.getData());
+ i.putExtras(src);
+ i.putExtra(FROM_ME, true);
+ ctx.startActivity(i);
+ ctx.finish();
+ break;
+ }
+ }
+ };
+}
diff --git a/VitamioBundle/src/io/vov/vitamio/widget/CenterLayout.java b/VitamioBundle/src/io/vov/vitamio/widget/CenterLayout.java
new file mode 100644
index 0000000..69727e8
--- /dev/null
+++ b/VitamioBundle/src/io/vov/vitamio/widget/CenterLayout.java
@@ -0,0 +1,127 @@
+/*
+ * 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 android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RemoteViews.RemoteView;
+
+@RemoteView
+public class CenterLayout extends ViewGroup {
+ private int mPaddingLeft = 0;
+ private int mPaddingRight = 0;
+ private int mPaddingTop = 0;
+ private int mPaddingBottom = 0;
+ private int mWidth, mHeight;
+
+ public CenterLayout(Context context) {
+ super(context);
+ }
+
+ public CenterLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public CenterLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int count = getChildCount();
+
+ int maxHeight = 0;
+ int maxWidth = 0;
+
+ measureChildren(widthMeasureSpec, heightMeasureSpec);
+
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ int childRight;
+ int childBottom;
+
+ CenterLayout.LayoutParams lp = (CenterLayout.LayoutParams) child.getLayoutParams();
+
+ childRight = lp.x + child.getMeasuredWidth();
+ childBottom = lp.y + child.getMeasuredHeight();
+
+ maxWidth = Math.max(maxWidth, childRight);
+ maxHeight = Math.max(maxHeight, childBottom);
+ }
+ }
+
+ maxWidth += mPaddingLeft + mPaddingRight;
+ maxHeight += mPaddingTop + mPaddingBottom;
+
+ maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
+ maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
+
+ setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec), resolveSize(maxHeight, heightMeasureSpec));
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int count = getChildCount();
+ mWidth = getMeasuredWidth();
+ mHeight = getMeasuredHeight();
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ CenterLayout.LayoutParams lp = (CenterLayout.LayoutParams) child.getLayoutParams();
+ int childLeft = mPaddingLeft + lp.x;
+ if (lp.width > 0)
+ childLeft += (int) ((mWidth - lp.width) / 2.0);
+ else
+ childLeft += (int) ((mWidth - child.getMeasuredWidth()) / 2.0);
+ int childTop = mPaddingTop + lp.y;
+ if (lp.height > 0)
+ childTop += (int) ((mHeight - lp.height) / 2.0);
+ else
+ childTop += (int) ((mHeight - child.getMeasuredHeight()) / 2.0);
+ child.layout(childLeft, childTop, childLeft + child.getMeasuredWidth(), childTop + child.getMeasuredHeight());
+ }
+ }
+ }
+
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof CenterLayout.LayoutParams;
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+ return new LayoutParams(p);
+ }
+
+ public static class LayoutParams extends ViewGroup.LayoutParams {
+ public int x;
+ public int y;
+
+ public LayoutParams(int width, int height, int x, int y) {
+ super(width, height);
+ this.x = x;
+ this.y = y;
+ }
+
+ public LayoutParams(ViewGroup.LayoutParams source) {
+ super(source);
+ }
+ }
+}
diff --git a/VitamioBundle/src/io/vov/vitamio/widget/MediaController.java b/VitamioBundle/src/io/vov/vitamio/widget/MediaController.java
new file mode 100644
index 0000000..3d2e1f9
--- /dev/null
+++ b/VitamioBundle/src/io/vov/vitamio/widget/MediaController.java
@@ -0,0 +1,511 @@
+/*
+ * 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 android.content.Context;
+import android.graphics.Rect;
+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.widget.FrameLayout;
+import android.widget.ImageButton;
+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.
+ */
+public class MediaController extends FrameLayout {
+ private MediaPlayerControl mPlayer;
+ private Context mContext;
+ private PopupWindow mWindow;
+ private int mAnimStyle;
+ private View mAnchor;
+ private View mRoot;
+ private ProgressBar mProgress;
+ private TextView mEndTime, mCurrentTime;
+ private TextView mFileName;
+ private OutlineTextView mInfoView;
+ private String mTitle;
+ private long mDuration;
+ private boolean mShowing;
+ 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 ImageButton mPauseButton;
+
+ private AudioManager mAM;
+
+ public MediaController(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mRoot = this;
+ mFromXml = true;
+ initController(context);
+ }
+
+ public MediaController(Context context) {
+ super(context);
+ if (!mFromXml && initController(context))
+ initFloatingWindow();
+ }
+
+ 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);
+ }
+
+ private void initFloatingWindow() {
+ mWindow = new PopupWindow(mContext);
+ mWindow.setFocusable(false);
+ 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);
+ }
+
+ private void initControllerView(View v) {
+ mPauseButton = (ImageButton) v.findViewById(R.id.mediacontroller_play_pause);
+ if (mPauseButton != null) {
+ mPauseButton.requestFocus();
+ mPauseButton.setOnClickListener(mPauseListener);
+ }
+
+ 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);
+ }
+ mProgress.setMax(1000);
+ }
+
+ mEndTime = (TextView) v.findViewById(R.id.mediacontroller_time_total);
+ mCurrentTime = (TextView) v.findViewById(R.id.mediacontroller_time_current);
+ mFileName = (TextView) v.findViewById(R.id.mediacontroller_file_name);
+ if (mFileName != null)
+ mFileName.setText(mTitle);
+ }
+
+ public void setMediaPlayer(MediaPlayerControl player) {
+ mPlayer = player;
+ updatePausePlay();
+ }
+
+ /**
+ * 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 show() {
+ show(sDefaultTimeout);
+ }
+
+ /**
+ * Set the content of the file_name TextView
+ *
+ * @param name
+ */
+ public void setFileName(String name) {
+ mTitle = name;
+ if (mFileName != null)
+ mFileName.setText(mTitle);
+ }
+
+ /**
+ * Set the View to hold some information when interact with the
+ * MediaController
+ *
+ * @param v
+ */
+ public void setInfoView(OutlineTextView v) {
+ mInfoView = v;
+ }
+
+ 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();
+
+ if (mFromXml) {
+ setVisibility(View.VISIBLE);
+ } else {
+ int[] location = new int[2];
+
+ mAnchor.getLocationOnScreen(location);
+ Rect anchorRect = new Rect(location[0], location[1], location[0] + mAnchor.getWidth(), location[1] + mAnchor.getHeight());
+
+ mWindow.setAnimationStyle(mAnimStyle);
+ mWindow.showAtLocation(mAnchor, Gravity.NO_GRAVITY, anchorRect.left, anchorRect.bottom);
+ }
+ mShowing = true;
+ if (mShownListener != null)
+ mShownListener.onShown();
+ }
+ updatePausePlay();
+ mHandler.sendEmptyMessage(SHOW_PROGRESS);
+
+ if (timeout != 0) {
+ mHandler.removeMessages(FADE_OUT);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(FADE_OUT), timeout);
+ }
+ }
+
+ public boolean isShowing() {
+ return mShowing;
+ }
+
+ public void hide() {
+ if (mAnchor == null)
+ return;
+
+ if (mShowing) {
+ try {
+ mHandler.removeMessages(SHOW_PROGRESS);
+ if (mFromXml)
+ setVisibility(View.GONE);
+ else
+ mWindow.dismiss();
+ } catch (IllegalArgumentException ex) {
+ Log.d("MediaController already removed");
+ }
+ mShowing = false;
+ if (mHiddenListener != null)
+ mHiddenListener.onHidden();
+ }
+ }
+
+ public interface OnShownListener {
+ public void onShown();
+ }
+
+ private OnShownListener mShownListener;
+
+ public void setOnShownListener(OnShownListener l) {
+ mShownListener = l;
+ }
+
+ public interface OnHiddenListener {
+ public void onHidden();
+ }
+
+ private OnHiddenListener mHiddenListener;
+
+ public void setOnHiddenListener(OnHiddenListener l) {
+ mHiddenListener = l;
+ }
+
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ long pos;
+ switch (msg.what) {
+ case FADE_OUT:
+ hide();
+ break;
+ case SHOW_PROGRESS:
+ pos = setProgress();
+ if (!mDragging && mShowing) {
+ msg = obtainMessage(SHOW_PROGRESS);
+ sendMessageDelayed(msg, 1000 - (pos % 1000));
+ updatePausePlay();
+ }
+ break;
+ }
+ }
+ };
+
+ private long setProgress() {
+ if (mPlayer == null || mDragging)
+ return 0;
+
+ 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);
+ }
+
+ mDuration = duration;
+
+ if (mEndTime != null)
+ mEndTime.setText(StringUtils.generateTime(mDuration));
+ if (mCurrentTime != null)
+ mCurrentTime.setText(StringUtils.generateTime(position));
+
+ return position;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ show(sDefaultTimeout);
+ return true;
+ }
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent ev) {
+ show(sDefaultTimeout);
+ return false;
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ int keyCode = event.getKeyCode();
+ 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();
+ return true;
+ } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP) {
+ if (mPlayer.isPlaying()) {
+ mPlayer.pause();
+ updatePausePlay();
+ }
+ return true;
+ } else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) {
+ hide();
+ return true;
+ } else {
+ show(sDefaultTimeout);
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ private View.OnClickListener mPauseListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ doPauseResume();
+ show(sDefaultTimeout);
+ }
+ };
+
+ private void updatePausePlay() {
+ if (mRoot == null || mPauseButton == null)
+ return;
+
+ if (mPlayer.isPlaying())
+ mPauseButton.setImageResource(R.drawable.mediacontroller_pause_button);
+ else
+ mPauseButton.setImageResource(R.drawable.mediacontroller_play_button);
+ }
+
+ private void doPauseResume() {
+ if (mPlayer.isPlaying())
+ mPlayer.pause();
+ else
+ mPlayer.start();
+ updatePausePlay();
+ }
+
+ private OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
+ public void onStartTrackingTouch(SeekBar bar) {
+ mDragging = true;
+ show(3600000);
+ mHandler.removeMessages(SHOW_PROGRESS);
+ if (mInstantSeeking)
+ mAM.setStreamMute(AudioManager.STREAM_MUSIC, true);
+ if (mInfoView != null) {
+ mInfoView.setText("");
+ mInfoView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) {
+ if (!fromuser)
+ return;
+
+ long newposition = (mDuration * progress) / 1000;
+ String time = StringUtils.generateTime(newposition);
+ if (mInstantSeeking)
+ mPlayer.seekTo(newposition);
+ if (mInfoView != null)
+ mInfoView.setText(time);
+ if (mCurrentTime != null)
+ mCurrentTime.setText(time);
+ }
+
+ public void onStopTrackingTouch(SeekBar bar) {
+ if (!mInstantSeeking)
+ mPlayer.seekTo((mDuration * bar.getProgress()) / 1000);
+ if (mInfoView != null) {
+ mInfoView.setText("");
+ mInfoView.setVisibility(View.GONE);
+ }
+ show(sDefaultTimeout);
+ mHandler.removeMessages(SHOW_PROGRESS);
+ mAM.setStreamMute(AudioManager.STREAM_MUSIC, false);
+ mDragging = false;
+ mHandler.sendEmptyMessageDelayed(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 seekTo(long pos);
+
+ boolean isPlaying();
+
+ int getBufferPercentage();
+
+ boolean canPause();
+
+ boolean canSeekBackward();
+
+ boolean canSeekForward();
+ }
+
+}
diff --git a/VitamioBundle/src/io/vov/vitamio/widget/OutlineTextView.java b/VitamioBundle/src/io/vov/vitamio/widget/OutlineTextView.java
new file mode 100644
index 0000000..ac02fbc
--- /dev/null
+++ b/VitamioBundle/src/io/vov/vitamio/widget/OutlineTextView.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2011 Cedric Fung (wolfplanet@gmail.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 android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Typeface;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+/**
+ * Display text with border, use the same XML attrs as
+ * {@link android.widget.TextView}, except that {@link OutlineTextView} will
+ * transform the shadow to border
+ */
+@SuppressLint("DrawAllocation")
+public class OutlineTextView extends TextView {
+ public OutlineTextView(Context context) {
+ super(context);
+ initPaint();
+ }
+
+ public OutlineTextView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initPaint();
+ }
+
+ public OutlineTextView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ initPaint();
+ }
+
+ private void initPaint() {
+ mTextPaint = new TextPaint();
+ mTextPaint.setAntiAlias(true);
+ mTextPaint.setTextSize(getTextSize());
+ mTextPaint.setColor(mColor);
+ mTextPaint.setStyle(Paint.Style.FILL);
+ mTextPaint.setTypeface(getTypeface());
+
+ mTextPaintOutline = new TextPaint();
+ mTextPaintOutline.setAntiAlias(true);
+ mTextPaintOutline.setTextSize(getTextSize());
+ mTextPaintOutline.setColor(mBorderColor);
+ mTextPaintOutline.setStyle(Paint.Style.STROKE);
+ mTextPaintOutline.setTypeface(getTypeface());
+ mTextPaintOutline.setStrokeWidth(mBorderSize);
+ }
+
+ public void setText(String text) {
+ super.setText(text);
+ mText = text.toString();
+ requestLayout();
+ invalidate();
+ }
+
+ public void setTextSize(float size) {
+ super.setTextSize(size);
+ requestLayout();
+ invalidate();
+ initPaint();
+ }
+
+ public void setTextColor(int color) {
+ super.setTextColor(color);
+ mColor = color;
+ invalidate();
+ initPaint();
+ }
+
+ public void setShadowLayer(float radius, float dx, float dy, int color) {
+ super.setShadowLayer(radius, dx, dy, color);
+ mBorderSize = radius;
+ mBorderColor = color;
+ requestLayout();
+ invalidate();
+ initPaint();
+ }
+
+ public void setTypeface(Typeface tf, int style) {
+ super.setTypeface(tf, style);
+ requestLayout();
+ invalidate();
+ initPaint();
+ }
+
+ public void setTypeface(Typeface tf) {
+ super.setTypeface(tf);
+ requestLayout();
+ invalidate();
+ initPaint();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ Layout layout = new StaticLayout(getText(), mTextPaintOutline, getWidth(), Layout.Alignment.ALIGN_CENTER, mSpacingMult, mSpacingAdd, mIncludePad);
+ layout.draw(canvas);
+ layout = new StaticLayout(getText(), mTextPaint, getWidth(), Layout.Alignment.ALIGN_CENTER, mSpacingMult, mSpacingAdd, mIncludePad);
+ layout.draw(canvas);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ Layout layout = new StaticLayout(getText(), mTextPaintOutline, measureWidth(widthMeasureSpec), Layout.Alignment.ALIGN_CENTER, mSpacingMult, mSpacingAdd, mIncludePad);
+ int ex = (int) (mBorderSize * 2 + 1);
+ setMeasuredDimension(measureWidth(widthMeasureSpec) + ex, measureHeight(heightMeasureSpec) * layout.getLineCount() + ex);
+ }
+
+ private int measureWidth(int measureSpec) {
+ int result = 0;
+ int specMode = MeasureSpec.getMode(measureSpec);
+ int specSize = MeasureSpec.getSize(measureSpec);
+
+ if (specMode == MeasureSpec.EXACTLY) {
+ result = specSize;
+ } else {
+ result = (int) mTextPaintOutline.measureText(mText) + getPaddingLeft() + getPaddingRight();
+ if (specMode == MeasureSpec.AT_MOST) {
+ result = Math.min(result, specSize);
+ }
+ }
+
+ return result;
+ }
+
+ private int measureHeight(int measureSpec) {
+ int result = 0;
+ int specMode = MeasureSpec.getMode(measureSpec);
+ int specSize = MeasureSpec.getSize(measureSpec);
+
+ mAscent = (int) mTextPaintOutline.ascent();
+ if (specMode == MeasureSpec.EXACTLY) {
+ result = specSize;
+ } else {
+ result = (int) (-mAscent + mTextPaintOutline.descent()) + getPaddingTop() + getPaddingBottom();
+ if (specMode == MeasureSpec.AT_MOST) {
+ result = Math.min(result, specSize);
+ }
+ }
+ return result;
+ }
+
+ private TextPaint mTextPaint;
+ private TextPaint mTextPaintOutline;
+ private String mText = "";
+ private int mAscent = 0;
+ private float mBorderSize;
+ private int mBorderColor;
+ private int mColor;
+ private float mSpacingMult = 1.0f;
+ private float mSpacingAdd = 0;
+ private boolean mIncludePad = true;
+}
\ No newline at end of file
diff --git a/VitamioBundle/src/io/vov/vitamio/widget/VideoView.java b/VitamioBundle/src/io/vov/vitamio/widget/VideoView.java
new file mode 100644
index 0000000..31d61d2
--- /dev/null
+++ b/VitamioBundle/src/io/vov/vitamio/widget/VideoView.java
@@ -0,0 +1,721 @@
+/*
+ * 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 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.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 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 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 VideoView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public VideoView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ initVideoView(context);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
+ int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
+ setMeasuredDimension(width, height);
+ }
+
+ /**
+ * 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) {
+ LayoutParams lp = getLayoutParams();
+ DisplayMetrics disp = mContext.getResources().getDisplayMetrics();
+ int windowWidth = disp.widthPixels, windowHeight = disp.heightPixels;
+ 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) {
+ lp.width = (int) (mSurfaceHeight * videoRatio);
+ lp.height = mSurfaceHeight;
+ } else if (layout == VIDEO_LAYOUT_ZOOM) {
+ lp.width = windowRatio > videoRatio ? windowWidth : (int) (videoRatio * windowHeight);
+ lp.height = windowRatio < videoRatio ? windowHeight : (int) (windowWidth / videoRatio);
+ } else {
+ boolean full = layout == VIDEO_LAYOUT_STRETCH;
+ lp.width = (full || windowRatio < videoRatio) ? windowWidth : (int) (videoRatio * windowHeight);
+ lp.height = (full || windowRatio > videoRatio) ? windowHeight : (int) (windowWidth / videoRatio);
+ }
+ 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;
+ }
+
+ 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 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 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 (mUri != null) {
+ List paths = mUri.getPathSegments();
+ String name = paths == null || paths.isEmpty() ? "null" : paths.get(paths.size() - 1);
+ mMediaController.setFileName(name);
+ }
+ }
+ }
+
+ 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() {
+ @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;
+ }
+ };
+
+ 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();
+ }
+ }
+
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ mSurfaceHolder = null;
+ if (mMediaController != null)
+ mMediaController.hide();
+ if (mCurrentState != STATE_SUSPEND)
+ release(true);
+ }
+ };
+
+ private void release(boolean cleartargetstate) {
+ if (mMediaPlayer != null) {
+ mMediaPlayer.reset();
+ mMediaPlayer.release();
+ mMediaPlayer = null;
+ mCurrentState = STATE_IDLE;
+ if (cleartargetstate)
+ mTargetState = STATE_IDLE;
+ }
+ }
+
+ @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 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 boolean canSeekForward() {
+ return mCanSeekForward;
+ }
+}
diff --git a/project.properties b/project.properties
index b7c2081..89dee5e 100644
--- a/project.properties
+++ b/project.properties
@@ -12,3 +12,4 @@
# Project target.
target=android-10
+android.library.reference.1=VitamioBundle
diff --git a/res/layout/activity_video.xml b/res/layout/activity_video.xml
index 5295250..d604eb2 100644
--- a/res/layout/activity_video.xml
+++ b/res/layout/activity_video.xml
@@ -3,7 +3,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent" >
-