package com.example.umengshakedemo; import android.annotation.SuppressLint; import android.app.Activity; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.graphics.BitmapFactory; import android.graphics.PixelFormat; import android.hardware.SensorEvent; import android.media.AudioManager; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.View.OnClickListener; import android.view.Window; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.Toast; import com.umeng.scrshot.adapter.UMBaseAdapter; import com.umeng.socialize.bean.SHARE_MEDIA; import com.umeng.socialize.bean.SocializeConfig; import com.umeng.socialize.bean.SocializeEntity; import com.umeng.socialize.bean.StatusCode; import com.umeng.socialize.controller.RequestType; import com.umeng.socialize.controller.UMServiceFactory; import com.umeng.socialize.controller.UMSocialService; import com.umeng.socialize.controller.UMSsoHandler; import com.umeng.socialize.controller.listener.SocializeListeners.SnsPostListener; import com.umeng.socialize.sensor.UMSensor.OnSensorListener; import com.umeng.socialize.sensor.UMSensor.WhitchButton; import com.umeng.socialize.sensor.controller.UMShakeService; import com.umeng.socialize.sensor.controller.impl.UMShakeServiceFactory; import com.umeng.socialize.sso.QZoneSsoHandler; import com.umeng.socialize.sso.SinaSsoHandler; import com.umeng.socialize.sso.TencentWBSsoHandler; import io.vov.vitamio.LibsChecker; import io.vov.vitamio.MediaPlayer; import io.vov.vitamio.MediaPlayer.OnBufferingUpdateListener; import io.vov.vitamio.MediaPlayer.OnCompletionListener; import io.vov.vitamio.MediaPlayer.OnPreparedListener; import io.vov.vitamio.MediaPlayer.OnVideoSizeChangedListener; import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Timer; import java.util.TimerTask; /** * @ClassName: MainActivity * @Description: 该应用演示了使用Vitamio库来播放在线视频, 并且集成友盟社会化组件当中的摇一摇截屏分享功能.由于默认是播放 * 流媒体,为了播放流畅,大家在测试时可连接WIFI,也避免消耗过多的流量. 说明 : * 该demo并不是要演示如何做一个播放器,只是为了演示视频播放器如何集成摇一摇功能, * 为了使整个demo更简单、清晰,忽略了很多细节处理,因此该demo有很多不足之处. * @author Honghui He */ public class MainActivity extends Activity implements OnBufferingUpdateListener, OnCompletionListener, OnPreparedListener, OnVideoSizeChangedListener, SurfaceHolder.Callback { private static final String TAG = "MainActivity"; private int mVideoWidth = 0; private int mVideoHeight = 0; /** * Vitamio的MediaPlayer */ private MediaPlayer mMediaPlayer = null; /** * 显示视频的surfaceview */ private SurfaceView mSurfaceView = null; private SurfaceHolder mSurfaceHolder = null; private ImageButton mPlayButton = null; /** * */ private LinearLayout mSeekbarLayout = null; /** * 进度条 */ private SeekBar mVideoSeekBar = null; /** * 视频总时间 */ private long mVideoDuration = -1; private static long mPosition = 0; /** * 视频的路径或者url */ private String mVideoPath = ""; private boolean mIsVideoSizeKnown = false; private boolean mIsVideoReadyToBePlayed = false; /** * 定时器, 用于更新进度条 */ private Timer mTimer = null; /** * 更新进度条的消息 */ private final int UPDATE_SEEKBAR_MSG = 123; /** * 每个一秒更新 */ private final int UPDATE_INTERVAL = 1000; /** * */ private final int HIDE_SEEKBAR_MSG = 456; private final int HIDE_MSG_DELAY = 3000; /** * 控制器描述符 */ private final String UMENG_DESCRIPTION = "com.umeng.share"; /** * 友盟分享SDK控制器 */ private UMSocialService mSocialController = UMServiceFactory .getUMSocialService(UMENG_DESCRIPTION, RequestType.SOCIAL); private SocializeConfig mSocializeConfig = SocializeConfig.getSocializeConfig() ; /** * 摇一摇控制器, 如果您在使用摇一摇功能时已经声明了UMSocialService对象, * 则摇一摇的描述符应与UMSocialService对象的一致,即UMENG_DESCRIPTION. */ private UMShakeService mShakeController = UMShakeServiceFactory .getShakeService(UMENG_DESCRIPTION); /** * (非 Javadoc) * * @Title: onCreate * @Description: onCreate * @param icicle * @see android.app.Activity#onCreate(android.os.Bundle) */ @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); if (!LibsChecker.checkVitamioLibs(this)) { Log.d(TAG, "#### lib载入失败."); return; } this.getWindow().requestFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); // 初始化SurfaceView initSurfaceView(); // 配置SSO, 并且要覆写onActivityResult方法进行回调,否则无法授权成功 configSocialSso(); } /** * @Title: initSurfaceView * @Description: 初始化SurfaceView * @throws */ private void initSurfaceView() { mSurfaceView = (SurfaceView) findViewById(R.id.surface); mSurfaceView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { showSeekbar(); } }); mSurfaceHolder = mSurfaceView.getHolder(); mSurfaceHolder.addCallback(this); mSurfaceHolder.setFormat(PixelFormat.RGBA_8888); mPlayButton = (ImageButton) findViewById(R.id.play_btn); mPlayButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (mMediaPlayer != null) { if (mMediaPlayer.isPlaying()) { pause(); } else { play(); } } } }); mSeekbarLayout = (LinearLayout) findViewById(R.id.seekbar_layout); // 初始化进度条 initSeekBar(); } /** * @Title: configSocialSso * @Description: 配置SSO授权, 并且要覆写onActivityResult方法进行回调,否则无法授权成功 * @throws */ private void configSocialSso() { // 添加微信支持 mSocializeConfig.supportWXPlatform(MainActivity.this, "wx9f162ffbf5731350", "http://www.umeng.com/social"); mSocializeConfig.supportWXCirclePlatform(MainActivity.this, "wx9f162ffbf5731350", "http://www.umeng.com/social"); // 添加QQ平台, 并且设置SSO授权 mSocializeConfig.supportQQPlatform(MainActivity.this, "http://www.umeng.com/social"); // 添加QQ空间的sso授权 mSocializeConfig.setSsoHandler( new QZoneSsoHandler(MainActivity.this)); // 添加腾讯微博的sso授权 mSocializeConfig.setSsoHandler(new TencentWBSsoHandler()); // 添加新浪微博的sso授权 mSocializeConfig.setSsoHandler(new SinaSsoHandler()); mSocializeConfig.closeToast(); } /** * @Title: initSeekBar * @Description: 初始化进度条 * @throws */ private void initSeekBar() { mVideoSeekBar = (SeekBar) findViewById(R.id.video_seekbar); mVideoSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { /** * @Description: 用户拖动进度条 * @param seekBar */ @Override public void onStopTrackingTouch(SeekBar seekBar) { int progress = seekBar.getProgress(); int duration = (int) mVideoDuration; int position = progress * duration / 100; if (mMediaPlayer != null) { mMediaPlayer.seekTo(position); } updateUI(position); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } }); showSeekbar(); } /** * 通过定时器和Handler来更新进度条 */ private class VideoTimerTask extends TimerTask { @Override public void run() { if (mMediaPlayer != null && mMediaPlayer.isPlaying()) { Message msg = Message.obtain(mHandler); msg.what = UPDATE_SEEKBAR_MSG; msg.obj = mMediaPlayer.getCurrentPosition(); mHandler.sendMessage(msg); } } }; /** * 处理各种消息 */ private final Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { if (msg.what == UPDATE_SEEKBAR_MSG) { long millseconds = (Long) msg.obj; // 更新UI updateUI((int) millseconds); } else if (msg.what == HIDE_SEEKBAR_MSG) { mSeekbarLayout.setVisibility(View.GONE); } }; }; /** * @Title: updateUI * @Description: 更新播放进度条和时间 * @param millseconds * @throws */ private void updateUI(int millseconds) { float total = (float) mVideoDuration; int progress = (int) ((millseconds / total) * 100); mVideoSeekBar.setProgress(progress); } /** * (非 Javadoc) * * @Title: onResume * @Description: 注册摇一摇功能, 注册操作最好放在onResume方法 * @see android.app.Activity#onResume() */ @Override protected void onResume() { super.onResume(); // 注册摇一摇截屏分享 registerShake(); if (mTimer == null) { mTimer = new Timer(); mTimer.schedule(new VideoTimerTask(), 0, UPDATE_INTERVAL); } } /** * (非 Javadoc) * * @Title: onStop * @Description: 注销摇一摇功能 * @see android.app.Activity#onStop() */ @Override protected void onStop() { super.onStop(); if (mTimer != null) { mTimer.cancel(); mTimer.purge(); mTimer = null; } mHandler.removeMessages(UPDATE_SEEKBAR_MSG); // 注销摇一摇传感器 mShakeController.unregisterShakeListener(MainActivity.this); releaseMediaPlayer(); cleanUp(); } /** * (非 Javadoc) * * @Title: onActivityResult * @Description: SSO授权必须覆写该方法进行回调 * @param requestCode * @param resultCode * @param data * @see android.app.Activity#onActivityResult(int, int, * android.content.Intent) */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // 通过requestCode获取ssoHandler UMSsoHandler ssoHandler = mSocialController.getConfig().getSsoHandler( requestCode); if (ssoHandler != null) { // 回调给umeng sdk ssoHandler.authorizeCallBack(requestCode, resultCode, data); } super.onActivityResult(requestCode, resultCode, data); } /** * @Title: registerShake * @Description: 注册摇一摇截屏分享 * @throws */ private void registerShake() { // 最多支持5个平台, 如果多于5个,则取前5个 List<SHARE_MEDIA> platforms = new ArrayList<SHARE_MEDIA>(); platforms.add(SHARE_MEDIA.SINA); platforms.add(SHARE_MEDIA.QZONE); platforms.add(SHARE_MEDIA.WEIXIN); platforms.add(SHARE_MEDIA.WEIXIN_CIRCLE); platforms.add(SHARE_MEDIA.SMS); // 设置摇一摇分享的文字内容 mShakeController.setShareContent("精彩瞬间,摇摇分享 -- 来自友盟社会化组件." + new Date().toString()); // 注册摇一摇截屏分享, 自定义的VitamioAdapter, mShakeController.registerShakeListender(MainActivity.this, new VitamioAdapter(), platforms, new VitamioListener()); } /** * @ClassName: VitamioAdapter * @Description: 自定义的截屏适配器,返回当前视频的图像,不包含界面上的其他view截图. * @author Honghui He */ private class VitamioAdapter extends UMBaseAdapter { /** * (非 Javadoc) * * @Title: getBitmap * @Description: 覆写该方法, 并且返回视频图像 * @return * @see com.umeng.scrshot.adapter.UMBaseAdapter#getBitmap() */ @Override public Bitmap getBitmap() { if (mMediaPlayer != null) { Bitmap bmp = mMediaPlayer.getCurrentFrame(); if (bmp == null) { return null; } // 使用Vitamio获取高清视频截图,分享到社交平台时会出现条纹. // 如果您的截图分享没有任何问题, 则不需要做这一步. ( 添加这一步会造成摇一摇动画不流畅 ) Bitmap scrshot = compressBitmap(bmp); return scrshot; } return null; } } /** * 使用Vitamio获取截图,分享到社交平台时会出现条纹,目前不知道是什么原因. 如果您的截图分享没有任何问题, 则不需要做这一步. * * @param bmp * @return */ @SuppressLint("NewApi") private Bitmap compressBitmap(Bitmap bmp) { // 将Vitamio获取截图压缩到outStream ByteArrayOutputStream outStream = new ByteArrayOutputStream(); // 考虑到用户的网络速度,建议将图片压缩至70kb以下,保证图片上传的成功率. bmp.compress(CompressFormat.JPEG, 30, outStream); byte[] data = outStream.toByteArray(); if (data != null && data.length > 0) { Log.d(TAG, "### 图片大小 : " + data.length / 1024 + " KB"); } // 再从outStream解析一张图片 Bitmap scrshot = BitmapFactory.decodeByteArray(data, 0, data.length); // Bitmap scrshot = BitmapFactory.decodeResource(getResources(), // R.drawable.icon); return scrshot; } /** * @ClassName: VitamioListener * @Description: 摇一摇监听器,包含用户摇一摇完成、分享开始、分享完成、按钮点击的监听 * @author Honghui He */ private class VitamioListener implements OnSensorListener { @Override public void onComplete(SHARE_MEDIA arg0, int code, SocializeEntity arg2) { if (code == StatusCode.ST_CODE_SUCCESSED) { Toast.makeText(MainActivity.this, "分享成功", Toast.LENGTH_SHORT) .show(); } else { Toast.makeText(MainActivity.this, " 抱歉,您的网络不给力,请重试...", Toast.LENGTH_SHORT) .show(); } } @Override public void onStart() { Toast.makeText(MainActivity.this, "开始分享", Toast.LENGTH_SHORT) .show(); } @Override public void onActionComplete(SensorEvent arg0) { // Toast.makeText(MainActivity.this, "用户摇一摇", Toast.LENGTH_SHORT) // .show(); // 暂停视频 pause(); } @Override public void onButtonClick(WhitchButton button) { // if (button == WhitchButton.BUTTON_SHARE) { // Toast.makeText(MainActivity.this, "用户点击分享按钮", // Toast.LENGTH_SHORT).show(); // } else { // Toast.makeText(MainActivity.this, "用户点击取消按钮", // Toast.LENGTH_SHORT).show(); // } // 重新开始 play(); } } /** * @Title: playVideo * @Description: 播放视频, 初始化视频播放器 * @throws */ private void playVideo() { cleanUp(); try { // 视频的url地址, 也可以是本地的视频路径 mVideoPath = "http://blog.umeng.com/images/video.mp4"; // Create a new media player and set the listeners mMediaPlayer = new MediaPlayer(this); mMediaPlayer.setDataSource(mVideoPath); mMediaPlayer.setDisplay(mSurfaceHolder); mMediaPlayer.prepare(); mMediaPlayer.setOnBufferingUpdateListener(this); mMediaPlayer.setOnCompletionListener(this); mMediaPlayer.setOnPreparedListener(this); mMediaPlayer.setOnVideoSizeChangedListener(this); mMediaPlayer.getMetadata(); mMediaPlayer.seekTo(mPosition); setVolumeControlStream(AudioManager.STREAM_MUSIC); } catch (Exception e) { Log.e(TAG, "error: " + e.getMessage(), e); } } /** * @Title: play * @Description: 播放视频 * @throws */ private void play() { Log.d(TAG, "### Click play button"); if (mMediaPlayer != null) { mMediaPlayer.start(); } mPlayButton.setBackgroundResource(R.drawable.pause); } /** * */ /** * @Title: pause * @Description: 暂停视频 * @throws */ private void pause() { Log.d(TAG, "### Click pause button"); if (mMediaPlayer != null) { mMediaPlayer.pause(); } mPlayButton.setBackgroundResource(R.drawable.play); } /** * @Title: startVideoPlayback * @Description: 播放视频 * @throws */ private void startVideoPlayback() { Log.v(TAG, "startVideoPlayback"); mSurfaceHolder.setFixedSize(mVideoWidth, mVideoHeight); mSurfaceHolder.setKeepScreenOn(true); play(); if (mTimer == null) { mTimer = new Timer(); // 设置定时器 mTimer.schedule(new VideoTimerTask(), 0, UPDATE_INTERVAL); } mVideoSeekBar.setVisibility(View.VISIBLE); mVideoDuration = mMediaPlayer.getDuration(); } /** * @Title: releaseMediaPlayer * @Description: 释放mMediaPlayer对象 * @throws */ private void releaseMediaPlayer() { if (mMediaPlayer != null) { mPosition = mMediaPlayer.getCurrentPosition(); mMediaPlayer.release(); mMediaPlayer = null; } } /** * @Title: doCleanUp * @Description: 清空状态 * @throws */ private void cleanUp() { mVideoWidth = 0; mVideoHeight = 0; mIsVideoReadyToBePlayed = false; mIsVideoSizeKnown = false; } /** * (非 Javadoc) * * @Title: onBufferingUpdate * @Description: * @param arg0 * @param percent * @see io.vov.vitamio.MediaPlayer.OnBufferingUpdateListener#onBufferingUpdate(io.vov.vitamio.MediaPlayer, * int) */ public void onBufferingUpdate(MediaPlayer arg0, int percent) { // Log.d(TAG, "onBufferingUpdate percent:" + percent); } public void onCompletion(MediaPlayer arg0) { Log.d(TAG, "视频播放结束"); pause(); } public void onVideoSizeChanged(MediaPlayer mp, int width, int height) { Log.v(TAG, "onVideoSizeChanged called"); if (width == 0 || height == 0) { Log.e(TAG, "invalid video width(" + width + ") or height(" + height + ")"); return; } mIsVideoSizeKnown = true; mVideoWidth = width; mVideoHeight = height; if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) { startVideoPlayback(); } } public void onPrepared(MediaPlayer mediaplayer) { Log.d(TAG, "onPrepared called"); mIsVideoReadyToBePlayed = true; if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) { startVideoPlayback(); } } public void surfaceChanged(SurfaceHolder surfaceholder, int i, int j, int k) { Log.d(TAG, "### surfaceChanged called"); } public void surfaceDestroyed(SurfaceHolder surfaceholder) { Log.d(TAG, "### surfaceDestroyed called"); } /** * (非 Javadoc) * * @Title: surfaceCreated * @Description: SurfaceView创建以后开始播放视频 * @param holder * @see android.view.SurfaceHolder.Callback#surfaceCreated(android.view.SurfaceHolder) */ public void surfaceCreated(SurfaceHolder holder) { Log.d(TAG, "#### surfaceCreated called"); playVideo(); } @Override protected void onPause() { super.onPause(); // releaseMediaPlayer(); // cleanUp(); } @Override protected void onDestroy() { super.onDestroy(); releaseMediaPlayer(); cleanUp(); } /** * @Title: hideSeekbar * @Description: 用户点击视频时,显示进度条, 三秒后隐藏 * @throws */ private void showSeekbar() { mSeekbarLayout.setVisibility(View.VISIBLE); mHandler.removeMessages(HIDE_SEEKBAR_MSG); // 用户点击视频, 3秒后隐藏进度条 Message msg = mHandler.obtainMessage(HIDE_SEEKBAR_MSG); msg.what = HIDE_SEEKBAR_MSG; mHandler.sendMessageDelayed(msg, HIDE_MSG_DELAY); } }