├── .gitignore ├── README.md ├── TopsTVPlayer.iml ├── app ├── app.iml ├── build.gradle ├── libs │ ├── core.jar │ └── universal-image-loader-1.9.3.jar ├── lint.xml └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── yuantops │ │ └── tvplayer │ │ ├── adapter │ │ └── ListviewAdapter.java │ │ ├── player │ │ ├── VideoPlayer.java │ │ ├── VideoPlayer_native.java │ │ └── VideoPlayer_vitamio.java │ │ ├── ui │ │ ├── LiveListFragment.java │ │ ├── MainActivity.java │ │ ├── VODListFragment.java │ │ ├── VideoPlayActivity.java │ │ └── WebAPIServerActivity.java │ │ └── util │ │ ├── StringUtils.java │ │ ├── UIRobot.java │ │ └── VolleySingleton.java │ └── res │ ├── drawable-hdpi │ └── ic_launcher.png │ ├── drawable-mdpi │ ├── ic_launcher.png │ ├── ic_seekbar_thumb.png │ ├── pause.png │ ├── play.png │ ├── ratingbar_full.xml │ ├── star_selected.png │ ├── star_unselect.png │ └── vlc.jpg │ ├── drawable-xhdpi │ └── ic_launcher.png │ ├── drawable-xxhdpi │ └── ic_launcher.png │ ├── layout │ ├── activity_main.xml │ ├── activity_player.xml │ ├── activity_videoplay.xml │ ├── activity_web_server.xml │ ├── fragment_live.xml │ ├── fragment_vod.xml │ └── view_video.xml │ ├── menu │ └── main.xml │ ├── values-sw600dp │ └── dimens.xml │ ├── values-sw720dp-land │ └── dimens.xml │ ├── values-v11 │ └── styles.xml │ ├── values-v14 │ └── styles.xml │ └── values │ ├── dimens.xml │ ├── ratingbar_style.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── project.properties └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | 15 | # Gradle files 16 | .gradle/ 17 | build/ 18 | 19 | # Local configuration file (sdk path, etc) 20 | local.properties 21 | 22 | # Proguard folder generated by Eclipse 23 | proguard/ 24 | 25 | # Log Files 26 | *.log 27 | 28 | # idea settings 29 | .idea/ 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TopsTVPlayer 2 | ============ 3 | 4 | Android video player which plays real-time live video stream and on demand video stream, supporting HTTP/RTSP protocol. 5 | 6 | ##Notes: 7 | 8 | - (2015.09.13) Update: Basic features have finished developing in this version. 9 | 10 | - This is the **Android client part** of a complete online video serving system. This player fetches JSON-format data from the web server, parsing it and then playing the vod/real-time video stream from the retrieved stream url. It does NOT support playing local videos. 11 | 12 | - (2016.04.20) Update: web server for this project can be found at [TopsAPIServer](https://github.com/yuantops/TopsAPIServer). **Server part** of this online video system includes: a media server (Helix), a web api server (Tomcat) and a database server (MySQL). Deployments of all those servers are requisites to make this app work. 13 | 14 | - Deployment guides (Chinese version) of the server can be found at [this link](http://blog.yuantops.com/tech/write-your-own-vod-system). 15 | 16 | ##Projects/Libraries used by this project: 17 | - [ActionbarSherlock](http://actionbarsherlock.com/) 18 | - [Vitamio SDK](https://www.vitamio.org/en/) 19 | - [Android Volley](https://github.com/mcxiaoke/android-volley) 20 | -------------------------------------------------------------------------------- /TopsTVPlayer.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 19 5 | buildToolsVersion "22.0.1" 6 | 7 | defaultConfig { 8 | applicationId "com.yuantops.tvplayer" 9 | minSdkVersion 14 10 | targetSdkVersion 19 11 | } 12 | 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.3' 23 | compile 'com.actionbarsherlock:actionbarsherlock:4.4.0@aar' 24 | compile 'com.android.support:support-v4:19.0.+' 25 | compile 'me.neavo:vitamio:4.2.2' 26 | compile 'com.google.zxing:core:2.0' 27 | compile 'com.mcxiaoke.volley:library:1.0.18' 28 | 29 | testCompile 'junit:junit:4.12' 30 | } 31 | -------------------------------------------------------------------------------- /app/libs/core.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuantops/TopsTVPlayer/ccbe8d28f43cf5aa0724a3bd7c5c8e53edb1e427/app/libs/core.jar -------------------------------------------------------------------------------- /app/libs/universal-image-loader-1.9.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuantops/TopsTVPlayer/ccbe8d28f43cf5aa0724a3bd7c5c8e53edb1e427/app/libs/universal-image-loader-1.9.3.jar -------------------------------------------------------------------------------- /app/lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 23 | 29 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/yuantops/tvplayer/adapter/ListviewAdapter.java: -------------------------------------------------------------------------------- 1 | package com.yuantops.tvplayer.adapter; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.BaseAdapter; 8 | import android.widget.TextView; 9 | import com.yuantops.tvplayer.R; 10 | import org.json.JSONArray; 11 | import org.json.JSONException; 12 | import org.json.JSONObject; 13 | 14 | /** 15 | * Created by yuan on 9/5/15. 16 | */ 17 | public class ListviewAdapter extends BaseAdapter { 18 | private static final String TAG = ListviewAdapter.class.getSimpleName(); 19 | 20 | private Context mCtx; 21 | private JSONArray mJsonArr; 22 | 23 | public ListviewAdapter(Context context, JSONArray jsonArray) { 24 | mCtx = context; 25 | mJsonArr = jsonArray; 26 | } 27 | 28 | public int getCount() { 29 | return mJsonArr.length(); 30 | } 31 | 32 | @Override 33 | public Object getItem(int position) { 34 | JSONObject jsonObject = null; 35 | try { 36 | jsonObject = (JSONObject) mJsonArr.get(position); 37 | } catch (JSONException e) { 38 | e.printStackTrace(); 39 | } 40 | return jsonObject; 41 | } 42 | 43 | @Override 44 | public long getItemId(int position) { 45 | return position; 46 | } 47 | 48 | @Override 49 | public View getView(int position, View convertView, ViewGroup parent) { 50 | ViewHolder vh = null; 51 | if (convertView == null) { 52 | LayoutInflater layoutInflater = (LayoutInflater) mCtx.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 53 | convertView = layoutInflater.inflate(R.layout.view_video, null); 54 | 55 | vh = new ViewHolder(); 56 | vh.txtVwName = (TextView) convertView.findViewById(R.id.video_name_view); 57 | vh.txtVwGenre = (TextView) convertView.findViewById(R.id.video_genre); 58 | vh.txtVwDate = (TextView) convertView.findViewById(R.id.video_date); 59 | 60 | convertView.setTag(vh); 61 | } else { 62 | vh = (ViewHolder) convertView.getTag(); 63 | } 64 | 65 | JSONObject jsonItem = (JSONObject) getItem(position); 66 | if (jsonItem != null) { 67 | try { 68 | vh.txtVwName.setText((String) jsonItem.get("videoNameCn")); 69 | vh.txtVwGenre.setText((String) jsonItem.get("genre")); 70 | vh.txtVwDate.setText((String) jsonItem.get("releaseDate")); 71 | } catch (JSONException e) { 72 | e.printStackTrace(); 73 | } 74 | } 75 | return convertView; 76 | } 77 | 78 | class ViewHolder { 79 | TextView txtVwName; 80 | TextView txtVwGenre; 81 | TextView txtVwDate; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/com/yuantops/tvplayer/player/VideoPlayer.java: -------------------------------------------------------------------------------- 1 | package com.yuantops.tvplayer.player; 2 | 3 | /** 4 | * Interface for VideoPlayer component, defining universal methods like play, pause and stop 5 | * @author yuan (Email: yuan.tops@gmail.com) * 6 | * @date Mar 9, 2015 7 | */ 8 | public interface VideoPlayer { 9 | void play(); 10 | void pause(); 11 | void stop(); 12 | boolean isPlaying(); 13 | int getDuration(); 14 | void seekTo(int progress); 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/yuantops/tvplayer/player/VideoPlayer_native.java: -------------------------------------------------------------------------------- 1 | package com.yuantops.tvplayer.player; 2 | 3 | import java.util.Timer; 4 | import java.util.TimerTask; 5 | 6 | import com.yuantops.tvplayer.util.StringUtils; 7 | 8 | import android.content.Context; 9 | import android.media.AudioManager; 10 | import android.media.MediaPlayer; 11 | import android.media.MediaPlayer.OnBufferingUpdateListener; 12 | import android.media.MediaPlayer.OnPreparedListener; 13 | import android.os.Handler; 14 | import android.os.Message; 15 | import android.util.Log; 16 | import android.view.SurfaceHolder; 17 | import android.view.SurfaceView; 18 | import android.widget.SeekBar; 19 | import android.widget.TextView; 20 | 21 | /** 22 | * Videoplayer based on native MediaPlayer provided by official Android library; 23 | * Used for playing on-demand video stream in HTTP protocol; 24 | * Do not support real-time live video stream in RTSP protocol 25 | * 基于Andorid官方提供的原生MediaPlayer插件; 26 | * 用以播放HTTP格式的点播流; 27 | * 不支持RTSP格式的实时视频直播流 28 | * @author yuan (Email: yuan.tops@gmail.com) 29 | * @date Mar 9, 2015 30 | */ 31 | public class VideoPlayer_native implements VideoPlayer{ 32 | private static final String TAG = VideoPlayer_native.class.getSimpleName(); 33 | private static final int CURR_TIME_VALUE = 8; 34 | 35 | private Context mContext; 36 | private SurfaceHolder surHolder; 37 | private SeekBar seekBar; 38 | private MediaPlayer mePlayer; 39 | private TextView vpCurrentTime, vpTotalTime;//currentTime, total video time length 40 | private int vpWidth, vpHeight; 41 | 42 | private String vUrl; 43 | private int vpBeginTime;//seekbar position when initializing 44 | private boolean flag = true; 45 | private Timer timer = new Timer(); 46 | 47 | private TimerTask mTimerTask = new TimerTask() { 48 | @Override 49 | public void run() { 50 | if (mePlayer == null) { 51 | return; 52 | } else 53 | try { 54 | if (mePlayer.isPlaying()) { 55 | int position = (int) mePlayer.getCurrentPosition(); 56 | int max = seekBar.getMax(); 57 | if (position != 0) { 58 | Message msg = mHandler.obtainMessage( 59 | CURR_TIME_VALUE, position, max); 60 | mHandler.sendMessage(msg); 61 | try { 62 | Thread.sleep(500); 63 | } catch (InterruptedException e) { 64 | e.printStackTrace(); 65 | } 66 | } 67 | } 68 | } catch (Exception e) { 69 | e.printStackTrace(); 70 | } 71 | } 72 | }; 73 | 74 | Handler mHandler = new Handler() { 75 | public void handleMessage(android.os.Message msg) { 76 | switch (msg.what) { 77 | case CURR_TIME_VALUE: 78 | int position = msg.arg1; 79 | int max = msg.arg2; 80 | int duration = (int) mePlayer.getDuration(); 81 | vpCurrentTime.setText(StringUtils.millisToString(position)); 82 | vpTotalTime.setText(StringUtils.millisToString(duration)); 83 | if (duration > 0) { 84 | int pos = max * position / duration; 85 | seekBar.setProgress(pos); 86 | } 87 | if (vpBeginTime != 0 && flag) { 88 | mePlayer.seekTo(vpBeginTime); 89 | seekBar.setProgress(max * vpBeginTime / duration); 90 | flag = false; 91 | } 92 | break; 93 | case 1: 94 | break; 95 | } 96 | }; 97 | }; 98 | 99 | private SurfaceHolder.Callback surCallbackListener = new SurfaceHolder.Callback() { 100 | @Override 101 | public void surfaceDestroyed(SurfaceHolder arg0) { 102 | } 103 | @Override 104 | public void surfaceCreated(SurfaceHolder arg0) { 105 | mePlayer = new MediaPlayer(); 106 | mePlayer.setDisplay(surHolder); 107 | mePlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); 108 | mePlayer.setOnBufferingUpdateListener(onBufferingUpdateListener); 109 | mePlayer.setOnPreparedListener(onPreparedListener); 110 | 111 | try { 112 | mePlayer.reset(); 113 | mePlayer.setDataSource(vUrl); 114 | mePlayer.prepareAsync(); 115 | } catch (Exception e) { 116 | e.printStackTrace(); 117 | } 118 | } 119 | @Override 120 | public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { 121 | mePlayer.setDisplay(surHolder); 122 | } 123 | }; 124 | 125 | private OnBufferingUpdateListener onBufferingUpdateListener = new OnBufferingUpdateListener() { 126 | @Override 127 | public void onBufferingUpdate(MediaPlayer mp, int percent) { 128 | seekBar.setSecondaryProgress(percent); 129 | } 130 | }; 131 | 132 | private OnPreparedListener onPreparedListener = new OnPreparedListener() { 133 | @Override 134 | public void onPrepared(MediaPlayer mp) { 135 | vpHeight = mp.getVideoHeight(); 136 | vpWidth = mp.getVideoWidth(); 137 | if (vpHeight != 0 && vpWidth != 0) { 138 | mp.start();// 播放视频 139 | } 140 | Log.v("mediaPlayer", "onPrepared"); 141 | } 142 | }; 143 | 144 | /** 145 | * @param surView 146 | * @param seekBar 147 | * @param vUrl 148 | * @param vpCurrentTime 149 | * @param vpTotalTime 150 | * @param vpBeginTime 151 | * @param context 152 | */ 153 | public VideoPlayer_native(SurfaceView surView, SeekBar seekBar, String vUrl, 154 | TextView vpCurrentTime, TextView vpTotalTime, 155 | int vpBeginTime, Context context) { 156 | 157 | Log.v(TAG, "initialize"); 158 | this.mContext = context; 159 | this.seekBar = seekBar; 160 | this.vUrl = vUrl; 161 | this.vpCurrentTime = vpCurrentTime; 162 | this.vpTotalTime = vpTotalTime; 163 | this.vpBeginTime = vpBeginTime; 164 | 165 | surHolder = surView.getHolder(); 166 | surHolder.addCallback(surCallbackListener); 167 | surHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 168 | 169 | timer.schedule(mTimerTask, 0, 1000); 170 | } 171 | 172 | @Override 173 | public void play() { 174 | mePlayer.start(); 175 | } 176 | 177 | @Override 178 | public void pause() { 179 | mePlayer.pause(); 180 | } 181 | 182 | @Override 183 | public void stop() { 184 | if (mePlayer != null) { 185 | mePlayer.stop(); 186 | mePlayer.release(); 187 | mTimerTask.cancel(); 188 | mePlayer = null; 189 | } 190 | } 191 | 192 | @Override 193 | public boolean isPlaying() { 194 | boolean flag; 195 | if (mePlayer == null) { 196 | flag = false; 197 | } else { 198 | flag = mePlayer.isPlaying(); 199 | } 200 | return flag; 201 | } 202 | 203 | public int getCurrentPosition() { 204 | if (mePlayer == null) { 205 | return 0; 206 | } else { 207 | return (int) mePlayer.getCurrentPosition(); 208 | } 209 | } 210 | 211 | public int getDuration() { 212 | return mePlayer.getDuration(); 213 | } 214 | 215 | public void seekTo(int progress) { 216 | mePlayer.seekTo(progress); 217 | //Log.v(TAG, "seekTo() " + progress); 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /app/src/main/java/com/yuantops/tvplayer/player/VideoPlayer_vitamio.java: -------------------------------------------------------------------------------- 1 | package com.yuantops.tvplayer.player; 2 | 3 | import io.vov.vitamio.MediaPlayer; 4 | import io.vov.vitamio.MediaPlayer.OnBufferingUpdateListener; 5 | import io.vov.vitamio.MediaPlayer.OnCompletionListener; 6 | import io.vov.vitamio.MediaPlayer.OnPreparedListener; 7 | import io.vov.vitamio.MediaPlayer.OnVideoSizeChangedListener; 8 | import android.content.Context; 9 | import android.graphics.PixelFormat; 10 | import android.util.Log; 11 | import android.view.SurfaceHolder; 12 | import android.view.SurfaceView; 13 | 14 | /** 15 | * Videoplayer based on thrid-party library Vitamio; 16 | * Used for playing real-time live video stream in RTSP protocol; 17 | * Also support on-demand video stream in HTTP protocol 18 | * 基于第三方的MediaPlayer插件 Vitamio; 19 | * 用以播放RTSP格式的实时视频直播流; 20 | * 同時支持HTTP格式的点播流 21 | * @author yuan (Email: yuan.tops@gmail.com) 22 | * @date Mar 9, 2015 23 | */ 24 | public class VideoPlayer_vitamio implements VideoPlayer, 25 | OnBufferingUpdateListener, OnCompletionListener, OnPreparedListener, 26 | OnVideoSizeChangedListener, SurfaceHolder.Callback { 27 | private static final String TAG = VideoPlayer.class.getSimpleName(); 28 | private int mVideoWidth; 29 | private int mVideoHeight; 30 | private Context mContext; // VideoPlayerVitamio所处的上下文 31 | private SurfaceHolder surfaceHolder; // 32 | private MediaPlayer mediaPlayer; // 组件:MediaPlayer 33 | private String videoUrl; // 视频流URL 34 | private boolean mIsVideoSizeKnown = false; 35 | private boolean mIsVideoReadyToBePlayed = false; 36 | 37 | public VideoPlayer_vitamio(SurfaceView surfaceView, String videoUrl, 38 | Context context) { 39 | Log.v(TAG, "initialize"); 40 | this.surfaceHolder = surfaceView.getHolder(); 41 | this.videoUrl = videoUrl; 42 | this.mContext = context; 43 | 44 | this.surfaceHolder.addCallback(this); 45 | this.surfaceHolder.setFormat(PixelFormat.RGBX_8888); 46 | } 47 | 48 | @Override 49 | public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { 50 | Log.v(TAG, "surfaceChanged"); 51 | } 52 | 53 | @Override 54 | public void surfaceCreated(SurfaceHolder arg0) { 55 | 56 | doCleanUp(); 57 | 58 | mediaPlayer = new MediaPlayer(mContext); 59 | try { 60 | Log.v(TAG + " >>videoUrl", videoUrl); 61 | mediaPlayer.setDataSource(videoUrl); 62 | mediaPlayer.setDisplay(surfaceHolder); 63 | mediaPlayer.prepareAsync(); 64 | mediaPlayer.setOnBufferingUpdateListener(this); 65 | mediaPlayer.setOnCompletionListener(this); 66 | mediaPlayer.setOnPreparedListener(this); 67 | mediaPlayer.setOnVideoSizeChangedListener(this); 68 | // this.mContext.getSystemService(arg0)setVolumeControlStream(AudioManager.STREAM_MUSIC); 69 | } catch (Exception e) { 70 | //Log.e(TAG, "error: " + e.getMessage(), e); 71 | e.printStackTrace(); 72 | } 73 | } 74 | 75 | @Override 76 | public void surfaceDestroyed(SurfaceHolder arg0) { 77 | Log.v(TAG, "surfaceDestroyed called"); 78 | 79 | } 80 | 81 | @Override 82 | public void onVideoSizeChanged(MediaPlayer mp, int width, int height) { 83 | Log.v(TAG, "onVideoSizeChanged called"); 84 | if (width == 0 || height == 0) { 85 | Log.e(TAG, "invalid video width(" + width + ") or height(" + height 86 | + ")"); 87 | return; 88 | } 89 | mIsVideoSizeKnown = true; 90 | mVideoWidth = width; 91 | mVideoHeight = height; 92 | if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) { 93 | startVideoPlayback(); 94 | } 95 | } 96 | 97 | @Override 98 | public void onPrepared(MediaPlayer mp) { 99 | Log.d(TAG, "onPrepared called"); 100 | mIsVideoReadyToBePlayed = true; 101 | if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) { 102 | startVideoPlayback(); 103 | } 104 | } 105 | 106 | @Override 107 | public void onCompletion(MediaPlayer mp) { 108 | Log.d(TAG, "onCompletion called"); 109 | } 110 | 111 | @Override 112 | public void onBufferingUpdate(MediaPlayer mp, int percent) { 113 | Log.d(TAG, "onBufferingUpdate called"); 114 | } 115 | 116 | private void doCleanUp() { 117 | mVideoWidth = 0; 118 | mVideoHeight = 0; 119 | mIsVideoReadyToBePlayed = false; 120 | mIsVideoSizeKnown = false; 121 | } 122 | 123 | private void startVideoPlayback() { 124 | Log.v(TAG, "startVideoPlayback"); 125 | surfaceHolder.setFixedSize(mVideoWidth, mVideoHeight); 126 | mediaPlayer.start(); 127 | } 128 | 129 | public void play() { 130 | mediaPlayer.start(); 131 | } 132 | 133 | public void pause() { 134 | mediaPlayer.pause(); 135 | } 136 | 137 | public void stop() { 138 | if (mediaPlayer != null) { 139 | // mediaPlayer.stop(); 140 | mediaPlayer.release(); 141 | mediaPlayer = null; 142 | } 143 | } 144 | 145 | public boolean isPlaying() { 146 | if (mediaPlayer == null) { 147 | return false; 148 | } else { 149 | return mediaPlayer.isPlaying(); 150 | } 151 | } 152 | 153 | public int getCurrentPosition() { 154 | Log.v(TAG, "set returned postion 50"); 155 | return 50; 156 | } 157 | 158 | public int getDuration() { 159 | return 0; 160 | } 161 | 162 | public void seekTo(int progress) { 163 | return; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /app/src/main/java/com/yuantops/tvplayer/ui/LiveListFragment.java: -------------------------------------------------------------------------------- 1 | package com.yuantops.tvplayer.ui; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.util.Log; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.AdapterView; 10 | import android.widget.ListView; 11 | import com.actionbarsherlock.app.SherlockFragment; 12 | import com.android.volley.Request; 13 | import com.android.volley.Response; 14 | import com.android.volley.VolleyError; 15 | import com.android.volley.toolbox.JsonArrayRequest; 16 | import com.yuantops.tvplayer.R; 17 | import com.yuantops.tvplayer.adapter.ListviewAdapter; 18 | import com.yuantops.tvplayer.util.VolleySingleton; 19 | import org.json.JSONArray; 20 | import org.json.JSONException; 21 | import org.json.JSONObject; 22 | 23 | /** 24 | * Created by yuan on 9/4/15. 25 | */ 26 | public class LiveListFragment extends SherlockFragment { 27 | private static final String TAG = LiveListFragment.class.getSimpleName(); 28 | private static JSONArray liveList = new JSONArray(); 29 | private static final String LIVE_API_SUFFIX = "/topstv/debug"; 30 | //private static final String LIVE_API_SUFFIX = "/videos"; 31 | private static String LiveApiUrl; 32 | private static ListviewAdapter movielistAdapter; 33 | 34 | private ListView listViewLive; 35 | private AdapterView.OnItemClickListener clickListener; 36 | 37 | @Override 38 | public void onCreate(Bundle savedInstanceState) { 39 | super.onCreate(savedInstanceState); 40 | 41 | if (LiveApiUrl == null) { 42 | LiveApiUrl = MainActivity.url + LIVE_API_SUFFIX; 43 | } 44 | Log.v(TAG + " >>intent from MainActivity", MainActivity.url); 45 | } 46 | 47 | @Override 48 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 49 | Bundle savedInstanceState) { 50 | super.onCreateView(inflater, container, savedInstanceState); 51 | View v = inflater.inflate(R.layout.fragment_vod, container, false); 52 | listViewLive = (ListView) v.findViewById(R.id.listview_vod); 53 | 54 | if (liveList == null || liveList.length() == 0 && LiveApiUrl != null) { 55 | JsonArrayRequest request = new JsonArrayRequest(Request.Method.GET, LiveApiUrl, new Response.Listener() { 56 | @Override 57 | public void onResponse(JSONArray jsonArray) { 58 | for (int i = 0; i < jsonArray.length(); i++) { 59 | try { 60 | liveList.put((JSONObject)jsonArray.get(i)); 61 | } catch (JSONException e) { 62 | e.printStackTrace(); 63 | } 64 | } 65 | movielistAdapter.notifyDataSetChanged(); 66 | } 67 | }, new Response.ErrorListener() { 68 | @Override 69 | public void onErrorResponse(VolleyError volleyError) { 70 | Log.e(TAG + " >>>response error", " "); 71 | volleyError.printStackTrace(); 72 | } 73 | }); 74 | VolleySingleton.getInstance(getActivity().getApplicationContext()).addToRequestQueue(request); 75 | } 76 | 77 | movielistAdapter = new ListviewAdapter(getActivity(), liveList); 78 | listViewLive.setAdapter(movielistAdapter); 79 | 80 | clickListener = new AdapterView.OnItemClickListener() { 81 | @Override 82 | public void onItemClick(AdapterView parent, View view, int position, long id) { 83 | String videoUrl; 84 | try { 85 | videoUrl = ((JSONObject) liveList.get(position)).getString("broadcastUrl"); 86 | } catch (JSONException e) { 87 | videoUrl = null; 88 | e.printStackTrace(); 89 | } 90 | Intent intent = new Intent(getActivity(), VideoPlayActivity.class); 91 | Bundle intentArgs = new Bundle(); 92 | intentArgs.putString("broadcastUrl", videoUrl); 93 | intentArgs.putString("type", "LIVE"); 94 | intent.putExtras(intentArgs); 95 | getActivity().startActivity(intent); 96 | } 97 | }; 98 | listViewLive.setOnItemClickListener(clickListener); 99 | 100 | return v; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /app/src/main/java/com/yuantops/tvplayer/ui/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.yuantops.tvplayer.ui; 2 | 3 | import java.util.HashMap; 4 | 5 | import com.actionbarsherlock.app.SherlockFragmentActivity; 6 | import com.yuantops.tvplayer.R; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.os.Bundle; 10 | import android.support.v4.app.Fragment; 11 | import android.support.v4.app.FragmentActivity; 12 | import android.support.v4.app.FragmentTransaction; 13 | import android.util.Log; 14 | import android.view.View; 15 | import android.widget.TabHost; 16 | 17 | public class MainActivity extends SherlockFragmentActivity { 18 | private static final String TAG = MainActivity.class.getSimpleName(); 19 | public static String url = null; 20 | 21 | private TabHost mTabHost; 22 | private TabManager mTabManager; 23 | 24 | private String[] tabNames = { "点播", "直播" }; 25 | private String[] tabTags = { "movie", "broadcast" }; 26 | @SuppressWarnings("rawtypes") 27 | private Class[] fragmentClasses = { VODListFragment.class, 28 | LiveListFragment.class }; 29 | 30 | public void onCreate(Bundle savedInstanceState) { 31 | Log.v(TAG, "onCreate()"); 32 | super.onCreate(savedInstanceState); 33 | setContentView(R.layout.activity_main); 34 | setTheme(R.style.Theme_Sherlock_Light); 35 | 36 | Intent intent = getIntent(); 37 | Bundle args = intent.getExtras(); 38 | 39 | if (url == null) { 40 | url = args.getString("url"); 41 | } 42 | 43 | mTabHost = (TabHost) findViewById(android.R.id.tabhost); 44 | mTabHost.setup(); 45 | mTabManager = new TabManager(this, mTabHost, R.id.realtabcontent_1); 46 | for (int i = 0; i < tabNames.length; i++) { 47 | mTabManager.addTab( 48 | mTabHost.newTabSpec(tabTags[i]).setIndicator(tabNames[i]), 49 | fragmentClasses[i], args); 50 | } 51 | if (savedInstanceState != null) { 52 | mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab")); 53 | } 54 | } 55 | 56 | @Override 57 | protected void onSaveInstanceState(Bundle outState) { 58 | super.onSaveInstanceState(outState); 59 | outState.putString("tab", mTabHost.getCurrentTabTag()); 60 | } 61 | 62 | /** 63 | * This is a helper class that implements a generic mechanism for 64 | * associating fragments with the tabs in a tab host. It relies on a trick. 65 | * Normally a tab host has a simple API for supplying a View or Intent that 66 | * each tab will show. This is not sufficient for switching between 67 | * fragments. So instead we make the content part of the tab host 0dp high 68 | * (it is not shown) and the TabManager supplies its own dummy view to show 69 | * as the tab content. It listens to changes in tabs, and takes care of 70 | * switch to the correct fragment shown in a separate content area whenever 71 | * the selected tab changes. 72 | */ 73 | public static class TabManager implements TabHost.OnTabChangeListener { 74 | private final FragmentActivity mActivity; 75 | private final TabHost mTabHost; 76 | private final int mContainerId; 77 | private final HashMap mTabs = new HashMap(); 78 | TabInfo mLastTab; 79 | 80 | static final class TabInfo { 81 | private final String tag; 82 | private final Class clss; 83 | private final Bundle args; 84 | private Fragment fragment; 85 | 86 | TabInfo(String _tag, Class _class, Bundle _args) { 87 | tag = _tag; 88 | clss = _class; 89 | args = _args; 90 | } 91 | } 92 | 93 | public static class DummyTabFactory implements 94 | TabHost.TabContentFactory { 95 | private final Context mContext; 96 | 97 | public DummyTabFactory(Context context) { 98 | mContext = context; 99 | } 100 | 101 | @Override 102 | public View createTabContent(String tag) { 103 | View v = new View(mContext); 104 | v.setMinimumWidth(0); 105 | v.setMinimumHeight(0); 106 | return v; 107 | } 108 | } 109 | 110 | public TabManager(FragmentActivity activity, TabHost tabHost, 111 | int containerId) { 112 | mActivity = activity; 113 | mTabHost = tabHost; 114 | mContainerId = containerId; 115 | mTabHost.setOnTabChangedListener(this); 116 | } 117 | 118 | public void addTab(TabHost.TabSpec tabSpec, Class clss, Bundle args) { 119 | tabSpec.setContent(new DummyTabFactory(mActivity)); 120 | String tag = tabSpec.getTag(); 121 | 122 | TabInfo info = new TabInfo(tag, clss, args); 123 | 124 | // Check to see if we already have a fragment for this tab, probably 125 | // from a previously saved state. If so, deactivate it, because our 126 | // initial state is that a tab isn't shown. 127 | info.fragment = mActivity.getSupportFragmentManager() 128 | .findFragmentByTag(tag); 129 | if (info.fragment != null && !info.fragment.isDetached()) { 130 | FragmentTransaction ft = mActivity.getSupportFragmentManager() 131 | .beginTransaction(); 132 | ft.detach(info.fragment); 133 | ft.commit(); 134 | } 135 | 136 | mTabs.put(tag, info); 137 | mTabHost.addTab(tabSpec); 138 | } 139 | 140 | @Override 141 | public void onTabChanged(String tabId) { 142 | TabInfo newTab = mTabs.get(tabId); 143 | if (mLastTab != newTab) { 144 | FragmentTransaction ft = mActivity.getSupportFragmentManager() 145 | .beginTransaction(); 146 | if (mLastTab != null) { 147 | if (mLastTab.fragment != null) { 148 | ft.detach(mLastTab.fragment); 149 | } 150 | } 151 | if (newTab != null) { 152 | if (newTab.fragment == null) { 153 | newTab.fragment = Fragment.instantiate(mActivity, 154 | newTab.clss.getName(), newTab.args); 155 | ft.add(mContainerId, newTab.fragment, newTab.tag); 156 | } else { 157 | ft.attach(newTab.fragment); 158 | } 159 | } 160 | 161 | mLastTab = newTab; 162 | ft.commit(); 163 | mActivity.getSupportFragmentManager() 164 | .executePendingTransactions(); 165 | } 166 | } 167 | } 168 | 169 | long waitTime = 2000; 170 | long touchTime = 0; 171 | } 172 | -------------------------------------------------------------------------------- /app/src/main/java/com/yuantops/tvplayer/ui/VODListFragment.java: -------------------------------------------------------------------------------- 1 | package com.yuantops.tvplayer.ui; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.util.Log; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.AdapterView; 10 | import android.widget.ListView; 11 | import com.actionbarsherlock.app.SherlockFragment; 12 | import com.android.volley.Request; 13 | import com.android.volley.Response; 14 | import com.android.volley.VolleyError; 15 | import com.android.volley.toolbox.JsonArrayRequest; 16 | import com.yuantops.tvplayer.R; 17 | import com.yuantops.tvplayer.adapter.ListviewAdapter; 18 | import com.yuantops.tvplayer.util.VolleySingleton; 19 | import org.json.JSONArray; 20 | import org.json.JSONException; 21 | import org.json.JSONObject; 22 | 23 | /** 24 | * Created by yuan on 9/4/15. 25 | */ 26 | public class VODListFragment extends SherlockFragment { 27 | private static final String TAG = VODListFragment.class.getSimpleName(); 28 | private static JSONArray movieList = new JSONArray(); 29 | private static final String VOD_API_SUFFIX = "/topstv/debug"; 30 | //private static final String VOD_API_SUFFIX = "/videos"; 31 | private static String VODApiUrl; 32 | private static ListviewAdapter movielistAdapter; 33 | 34 | private ListView listViewVOD; 35 | private AdapterView.OnItemClickListener clickListener; 36 | 37 | @Override 38 | public void onCreate(Bundle savedInstanceState) { 39 | super.onCreate(savedInstanceState); 40 | 41 | if (VODApiUrl == null) { 42 | VODApiUrl = MainActivity.url + VOD_API_SUFFIX; 43 | } 44 | Log.v(TAG + " >>intent from MainActivity", MainActivity.url); 45 | } 46 | 47 | @Override 48 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 49 | Bundle savedInstanceState) { 50 | super.onCreateView(inflater, container, savedInstanceState); 51 | View v = inflater.inflate(R.layout.fragment_vod, container, false); 52 | listViewVOD = (ListView) v.findViewById(R.id.listview_vod); 53 | 54 | if (movieList == null || movieList.length() == 0 && VODApiUrl != null) { 55 | JsonArrayRequest request = new JsonArrayRequest(Request.Method.GET, VODApiUrl, new Response.Listener() { 56 | @Override 57 | public void onResponse(JSONArray jsonArray) { 58 | for (int i = 0; i < jsonArray.length(); i++) { 59 | try { 60 | movieList.put((JSONObject)jsonArray.get(i)); 61 | } catch (JSONException e) { 62 | e.printStackTrace(); 63 | } 64 | } 65 | movielistAdapter.notifyDataSetChanged(); 66 | } 67 | }, new Response.ErrorListener() { 68 | @Override 69 | public void onErrorResponse(VolleyError volleyError) { 70 | Log.e(TAG + " >>>response error", " "); 71 | volleyError.printStackTrace(); 72 | } 73 | }); 74 | VolleySingleton.getInstance(getActivity().getApplicationContext()).addToRequestQueue(request); 75 | } 76 | 77 | movielistAdapter = new ListviewAdapter(getActivity(), movieList); 78 | listViewVOD.setAdapter(movielistAdapter); 79 | 80 | clickListener = new AdapterView.OnItemClickListener() { 81 | @Override 82 | public void onItemClick(AdapterView parent, View view, int position, long id) { 83 | String videoUrl; 84 | try { 85 | videoUrl = ((JSONObject) movieList.get(position)).getString("standardDefiUrl"); 86 | } catch (JSONException e) { 87 | videoUrl = null; 88 | e.printStackTrace(); 89 | } 90 | Intent intent = new Intent(getActivity(), VideoPlayActivity.class); 91 | Bundle intentArgs = new Bundle(); 92 | intentArgs.putString("standardDefiUrl", videoUrl); 93 | intentArgs.putString("type", "VOD"); 94 | intent.putExtras(intentArgs); 95 | getActivity().startActivity(intent); 96 | } 97 | }; 98 | listViewVOD.setOnItemClickListener(clickListener); 99 | 100 | return v; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /app/src/main/java/com/yuantops/tvplayer/ui/VideoPlayActivity.java: -------------------------------------------------------------------------------- 1 | package com.yuantops.tvplayer.ui; 2 | 3 | import io.vov.vitamio.LibsChecker; 4 | 5 | import com.yuantops.tvplayer.R; 6 | import com.yuantops.tvplayer.player.VideoPlayer; 7 | import com.yuantops.tvplayer.player.VideoPlayer_native; 8 | import com.yuantops.tvplayer.player.VideoPlayer_vitamio; 9 | 10 | import android.app.Activity; 11 | import android.content.ComponentName; 12 | import android.content.Intent; 13 | import android.content.ServiceConnection; 14 | import android.content.pm.ActivityInfo; 15 | import android.graphics.Color; 16 | import android.os.Bundle; 17 | import android.os.IBinder; 18 | import android.util.DisplayMetrics; 19 | import android.util.Log; 20 | import android.view.SurfaceView; 21 | import android.view.View; 22 | import android.view.View.OnClickListener; 23 | import android.view.View.OnFocusChangeListener; 24 | import android.view.Window; 25 | import android.view.WindowManager; 26 | import android.widget.ImageButton; 27 | import android.widget.ImageView; 28 | import android.widget.SeekBar; 29 | import android.widget.SeekBar.OnSeekBarChangeListener; 30 | import android.widget.TextView; 31 | 32 | /** 33 | * Activity for playing on-demand/live video streams 34 | * 播放点播/直播流的Activity 35 | * @author yuan (Email: yuan.tops@gmail.com) * 36 | * @date Mar 12, 2015 37 | */ 38 | public class VideoPlayActivity extends Activity{ 39 | private static final String TAG = VideoPlayActivity.class.getSimpleName(); 40 | private static final byte STANDARD = 1, HIGH = 2, SUPER = 3; //Device resolution for deciding video type 41 | 42 | private int viHeight, viWidth; //Video Player size: height, width 43 | private int initTime, curTime, totTime; //Video started at, current at, will end at 44 | 45 | private SurfaceView surView; //View for displaying video content; 显示视频内容的组件 46 | private SeekBar seekBar; //SeekBar 47 | private TextView curTimeView, totTimeView; //current moment, total time TextView under SeekBar 48 | private ImageButton playImgBtn; //play/pause button 49 | private VideoPlayer viPlayer; // 50 | 51 | private ServiceConnection conn = new ServiceConnection() { 52 | @Override 53 | public void onServiceConnected(ComponentName arg0, IBinder arg1) { 54 | Log.v(TAG, "Service connected"); 55 | } 56 | @Override 57 | public void onServiceDisconnected(ComponentName arg0) { 58 | Log.v(TAG, "Service disconnected"); 59 | } 60 | }; 61 | 62 | @Override 63 | public void onCreate(Bundle savedInstanceState) { 64 | super.onCreate(savedInstanceState); 65 | Log.v(TAG, "onCreate()..."); 66 | 67 | requestWindowFeature(Window.FEATURE_NO_TITLE); // 不显示标题 68 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, // 全屏 69 | WindowManager.LayoutParams.FLAG_FULLSCREEN); 70 | this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);// 设置横屏播放 71 | this.setContentView(R.layout.activity_videoplay); 72 | 73 | initViewComponents(); 74 | addViewListeners(); 75 | 76 | //Retrieve video from intent 77 | Intent rcvIntent = getIntent(); 78 | Bundle intentArgs = rcvIntent.getExtras(); 79 | String type = intentArgs.getString("type"); 80 | Log.d(TAG + ">>>type", type); 81 | 82 | String viUrl; 83 | if (type.equals("LIVE")) { 84 | viUrl = intentArgs.getString("broadcastUrl"); 85 | 86 | if (!LibsChecker.checkVitamioLibs(this)) 87 | return; 88 | 89 | viPlayer = new VideoPlayer_vitamio(surView, viUrl, this); 90 | } else { 91 | viUrl = intentArgs.getString("standardDefiUrl"); 92 | viPlayer = new VideoPlayer_native(surView, seekBar, viUrl, curTimeView, totTimeView, 0, this); 93 | } 94 | } 95 | 96 | /** 97 | * Get references of view components 98 | */ 99 | private void initViewComponents() { 100 | surView = (SurfaceView) this.findViewById(R.id.main_surface_view); 101 | seekBar = (SeekBar) this.findViewById(R.id.sb_video_player); 102 | curTimeView = (TextView) this.findViewById(R.id.tv_video_player_time); 103 | totTimeView = (TextView) this.findViewById(R.id.tv_video_player_length); 104 | playImgBtn = (ImageButton) this.findViewById(R.id.ib_play_pause); 105 | 106 | //Set focus, background image for play button 107 | playImgBtn.requestFocus(); 108 | playImgBtn.setImageResource(R.drawable.pause); 109 | 110 | //Get screen size and thus deciding device resolution 111 | DisplayMetrics dm = new DisplayMetrics(); 112 | dm = getResources().getDisplayMetrics(); 113 | viHeight = dm.heightPixels; 114 | viWidth = dm.widthPixels; 115 | } 116 | 117 | /** 118 | * Add listeners to views 119 | */ 120 | private void addViewListeners() { 121 | seekBar.setOnSeekBarChangeListener(seekBarListener); 122 | playImgBtn.setOnClickListener(clickListener); 123 | playImgBtn.setOnFocusChangeListener(viewFocusChangeListener); 124 | } 125 | 126 | /** 127 | * When moving seeking bar, update progress. 128 | */ 129 | private OnSeekBarChangeListener seekBarListener = new OnSeekBarChangeListener() { 130 | int progress; 131 | @Override 132 | public void onProgressChanged(SeekBar seekBar, int progress0, boolean fromUser) { 133 | progress = (int) (progress0 * viPlayer.getDuration() 134 | / seekBar.getMax()); 135 | } 136 | @Override 137 | public void onStartTrackingTouch(SeekBar arg0) { 138 | } 139 | @Override 140 | public void onStopTrackingTouch(SeekBar arg0) { 141 | viPlayer.seekTo(progress); 142 | //Log.v(TAG, "onStopTrackingTouch() called"); 143 | } 144 | }; 145 | 146 | /** 147 | * when clicking pause/play button, flip button's background image 148 | */ 149 | private OnClickListener clickListener = new OnClickListener() { 150 | @Override 151 | public void onClick(View v) { 152 | switch (v.getId()) { 153 | case R.id.ib_play_pause: 154 | if (!viPlayer.isPlaying()) { 155 | viPlayer.play(); 156 | playImgBtn.setImageResource(R.drawable.pause); 157 | } else { 158 | viPlayer.pause(); 159 | playImgBtn.setImageResource(R.drawable.play); 160 | } 161 | break; 162 | default: 163 | break; 164 | } 165 | } 166 | }; 167 | 168 | /** 169 | * when focus of view changes; 170 | */ 171 | private OnFocusChangeListener viewFocusChangeListener = new OnFocusChangeListener() { 172 | @Override 173 | public void onFocusChange(View v, boolean hasFocus) { 174 | if (hasFocus) { 175 | v.setBackgroundColor(Color.GRAY); 176 | Log.v(TAG + " >focus changed", "has focus"); 177 | } else { 178 | v.setBackgroundColor(Color.TRANSPARENT); 179 | Log.v(TAG + " >focus changed", "lose focus"); 180 | } 181 | } 182 | }; 183 | 184 | } 185 | -------------------------------------------------------------------------------- /app/src/main/java/com/yuantops/tvplayer/ui/WebAPIServerActivity.java: -------------------------------------------------------------------------------- 1 | package com.yuantops.tvplayer.ui; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.util.Log; 7 | import android.view.View; 8 | import android.view.View.OnClickListener; 9 | import android.widget.Button; 10 | import android.widget.EditText; 11 | import com.yuantops.tvplayer.R; 12 | import com.yuantops.tvplayer.util.StringUtils; 13 | import com.yuantops.tvplayer.util.UIRobot; 14 | 15 | /** 16 | * Created by yuan on 9/3/15. 17 | */ 18 | public class WebAPIServerActivity extends Activity implements OnClickListener { 19 | private static final String TAG = WebAPIServerActivity.class.getName(); 20 | 21 | private EditText webServerIPEdtx; 22 | private Button enterBtn; 23 | 24 | @Override 25 | protected void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.activity_web_server); 28 | webServerIPEdtx = (EditText) findViewById(R.id.web_api_server_ip); 29 | enterBtn = (Button) findViewById(R.id.web_server_enter); 30 | enterBtn.setOnClickListener(this); 31 | } 32 | 33 | public void onClick(View v) { 34 | String edtxContent = webServerIPEdtx.getText().toString(); 35 | if (StringUtils.isEmpty(edtxContent)) { 36 | return; 37 | } 38 | Log.d(TAG, "Valid url, jump to MainPage"); 39 | UIRobot.gotoMainPage(this, edtxContent); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/yuantops/tvplayer/util/StringUtils.java: -------------------------------------------------------------------------------- 1 | package com.yuantops.tvplayer.util; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.ByteArrayInputStream; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.InputStreamReader; 8 | import java.text.DecimalFormat; 9 | import java.text.NumberFormat; 10 | import java.text.ParseException; 11 | import java.text.SimpleDateFormat; 12 | import java.util.Calendar; 13 | import java.util.Date; 14 | import java.util.Locale; 15 | import java.util.regex.Pattern; 16 | 17 | /** 18 | * 字符串操作工具包 19 | * 20 | * @author liux (http://my.oschina.net/liux) 21 | * @version 1.0 22 | * @created 2012-3-21 23 | */ 24 | public class StringUtils { 25 | private final static Pattern emailer = Pattern 26 | .compile("\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*"); 27 | // private final static SimpleDateFormat dateFormater = new 28 | // SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 29 | // private final static SimpleDateFormat dateFormater2 = new 30 | // SimpleDateFormat("yyyy-MM-dd"); 31 | 32 | private static final Pattern IPAddresser = Pattern.compile( 33 | "^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + 34 | "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + 35 | "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + 36 | "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$"); 37 | 38 | private final static ThreadLocal dateFormater = new ThreadLocal() { 39 | @Override 40 | protected SimpleDateFormat initialValue() { 41 | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 42 | } 43 | }; 44 | 45 | private final static ThreadLocal dateFormater2 = new ThreadLocal() { 46 | @Override 47 | protected SimpleDateFormat initialValue() { 48 | return new SimpleDateFormat("yyyy-MM-dd"); 49 | } 50 | }; 51 | 52 | /** 53 | * 将字符串转位日期类型 54 | * 55 | * @param sdate 56 | * @return 57 | */ 58 | public static Date toDate(String sdate) { 59 | try { 60 | return dateFormater.get().parse(sdate); 61 | } catch (ParseException e) { 62 | return null; 63 | } 64 | } 65 | 66 | /** 67 | * 以友好的方式显示时间 68 | * 69 | * @param sdate 70 | * @return 71 | */ 72 | public static String friendly_time(String sdate) { 73 | Date time = toDate(sdate); 74 | if (time == null) { 75 | return "Unknown"; 76 | } 77 | String ftime = ""; 78 | Calendar cal = Calendar.getInstance(); 79 | 80 | // 判断是否是同一天 81 | String curDate = dateFormater2.get().format(cal.getTime()); 82 | String paramDate = dateFormater2.get().format(time); 83 | if (curDate.equals(paramDate)) { 84 | int hour = (int) ((cal.getTimeInMillis() - time.getTime()) / 3600000); 85 | if (hour == 0) 86 | ftime = Math.max( 87 | (cal.getTimeInMillis() - time.getTime()) / 60000, 1) 88 | + "分钟前"; 89 | else 90 | ftime = hour + "小时前"; 91 | return ftime; 92 | } 93 | 94 | long lt = time.getTime() / 86400000; 95 | long ct = cal.getTimeInMillis() / 86400000; 96 | int days = (int) (ct - lt); 97 | if (days == 0) { 98 | int hour = (int) ((cal.getTimeInMillis() - time.getTime()) / 3600000); 99 | if (hour == 0) 100 | ftime = Math.max( 101 | (cal.getTimeInMillis() - time.getTime()) / 60000, 1) 102 | + "分钟前"; 103 | else 104 | ftime = hour + "小时前"; 105 | } else if (days == 1) { 106 | ftime = "昨天"; 107 | } else if (days == 2) { 108 | ftime = "前天"; 109 | } else if (days > 2 && days <= 10) { 110 | ftime = days + "天前"; 111 | } else if (days > 10) { 112 | ftime = dateFormater2.get().format(time); 113 | } 114 | return ftime; 115 | } 116 | 117 | /** 118 | * 判断给定字符串时间是否为今日 119 | * 120 | * @param sdate 121 | * @return boolean 122 | */ 123 | public static boolean isToday(String sdate) { 124 | boolean b = false; 125 | Date time = toDate(sdate); 126 | Date today = new Date(); 127 | if (time != null) { 128 | String nowDate = dateFormater2.get().format(today); 129 | String timeDate = dateFormater2.get().format(time); 130 | if (nowDate.equals(timeDate)) { 131 | b = true; 132 | } 133 | } 134 | return b; 135 | } 136 | 137 | /** 138 | * 返回long类型的今天的日期 139 | * @return 140 | */ 141 | public static long getToday() { 142 | Calendar cal = Calendar.getInstance(); 143 | String curDate = dateFormater2.get().format(cal.getTime()); 144 | curDate = curDate.replace("-", ""); 145 | return Long.parseLong(curDate); 146 | } 147 | 148 | /** 149 | * 判断给定字符串是否空白串。 空白串是指由空格、制表符、回车符、换行符组成的字符串 若输入字符串为null或空字符串,返回true 150 | * 151 | * @param input 152 | * @return boolean 153 | */ 154 | public static boolean isEmpty(String input) { 155 | if (input == null || "".equals(input)) 156 | return true; 157 | 158 | for (int i = 0; i < input.length(); i++) { 159 | char c = input.charAt(i); 160 | if (c != ' ' && c != '\t' && c != '\r' && c != '\n') { 161 | return false; 162 | } 163 | } 164 | return true; 165 | } 166 | 167 | /** 168 | * 判断是不是一个合法的电子邮件地址 169 | * 170 | * @param email 171 | * @return 172 | */ 173 | public static boolean isEmail(String email) { 174 | if (email == null || email.trim().length() == 0) 175 | return false; 176 | return emailer.matcher(email).matches(); 177 | } 178 | 179 | /** 180 | * 判断是不是合法的IP地址 181 | * @param ip 182 | * @return 183 | */ 184 | public static boolean isValidIPAddress(String ip) { 185 | if (ip == null || ip.equals("")) { 186 | return false; 187 | } 188 | return IPAddresser.matcher(ip).matches(); 189 | } 190 | 191 | /** 192 | * 字符串转整数 193 | * 194 | * @param str 195 | * @param defValue 196 | * @return 197 | */ 198 | public static int toInt(String str, int defValue) { 199 | try { 200 | return Integer.parseInt(str); 201 | } catch (Exception e) { 202 | } 203 | return defValue; 204 | } 205 | 206 | /** 207 | * 对象转整数 208 | * 209 | * @param obj 210 | * @return 转换异常返回 0 211 | */ 212 | public static int toInt(Object obj) { 213 | if (obj == null) 214 | return 0; 215 | return toInt(obj.toString(), 0); 216 | } 217 | 218 | /** 219 | * 对象转整数 220 | * 221 | * @param obj 222 | * @return 转换异常返回 0 223 | */ 224 | public static long toLong(String obj) { 225 | try { 226 | return Long.parseLong(obj); 227 | } catch (Exception e) { 228 | } 229 | return 0; 230 | } 231 | 232 | /** 233 | * 字符串转布尔值 234 | * 235 | * @param b 236 | * @return 转换异常返回 false 237 | */ 238 | public static boolean toBool(String b) { 239 | try { 240 | return Boolean.parseBoolean(b); 241 | } catch (Exception e) { 242 | } 243 | return false; 244 | } 245 | 246 | /** 247 | * 将一个InputStream流转换成字符串 248 | * @param is 249 | * @return 250 | */ 251 | public static String toConvertString(InputStream is) { 252 | StringBuffer res = new StringBuffer(); 253 | InputStreamReader isr = new InputStreamReader(is); 254 | BufferedReader read = new BufferedReader(isr); 255 | try { 256 | String line; 257 | line = read.readLine(); 258 | while (line != null) { 259 | res.append(line); 260 | line = read.readLine(); 261 | } 262 | } catch (IOException e) { 263 | e.printStackTrace(); 264 | } finally { 265 | try { 266 | if (null != isr) { 267 | isr.close(); 268 | isr.close(); 269 | } 270 | if (null != read) { 271 | read.close(); 272 | read = null; 273 | } 274 | if (null != is) { 275 | is.close(); 276 | is = null; 277 | } 278 | } catch (IOException e) { 279 | } 280 | } 281 | return res.toString(); 282 | } 283 | 284 | /** 285 | * Convert time to a string 286 | * 287 | * @param millis 288 | * e.g.time/length from file 289 | * @return formated string (hh:)mm:ss 290 | */ 291 | public static String millisToString(long millis) { 292 | boolean negative = millis < 0; 293 | millis = java.lang.Math.abs(millis); 294 | 295 | millis /= 1000; 296 | int sec = (int) (millis % 60); 297 | millis /= 60; 298 | int min = (int) (millis % 60); 299 | millis /= 60; 300 | int hours = (int) millis; 301 | 302 | String time; 303 | DecimalFormat format = (DecimalFormat) NumberFormat 304 | .getInstance(Locale.US); 305 | format.applyPattern("00"); 306 | if (millis > 0) { 307 | time = (negative ? "-" : "") + hours + ":" + format.format(min) 308 | + ":" + format.format(sec); 309 | } else { 310 | time = (negative ? "-" : "") + min + ":" + format.format(sec); 311 | } 312 | return time; 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /app/src/main/java/com/yuantops/tvplayer/util/UIRobot.java: -------------------------------------------------------------------------------- 1 | package com.yuantops.tvplayer.util; 2 | 3 | import android.os.Bundle; 4 | import com.yuantops.tvplayer.ui.*; 5 | 6 | import android.app.Activity; 7 | import android.content.Context; 8 | import android.content.Intent; 9 | import android.util.Log; 10 | import android.widget.Toast; 11 | 12 | /** 13 | * 处理与UI相关的工具类,包括Activity跳转、显示Dialog、显示Toast 14 | * @author admin (Email: yuan.tops@gmail.com) 15 | * @date 2015-1-7 16 | */ 17 | public class UIRobot { 18 | public static final String TAG = UIRobot.class.getSimpleName(); 19 | 20 | public static void gotoMainPage(Context context, String url) { 21 | Intent intent = new Intent(context, MainActivity.class); 22 | Bundle bundle = new Bundle(); 23 | bundle.putString("url", url); 24 | intent.putExtras(bundle); 25 | // intent.putExtra("url", url); 26 | context.startActivity(intent); 27 | ((Activity)context).finish(); 28 | } 29 | 30 | /** 31 | * 显示提示信息 32 | * @param mContext 33 | * @param string 34 | */ 35 | public static void showToast(Context mContext, String string) { 36 | if(!StringUtils.isEmpty(string)) { 37 | Toast.makeText(mContext, string, Toast.LENGTH_SHORT).show(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/yuantops/tvplayer/util/VolleySingleton.java: -------------------------------------------------------------------------------- 1 | package com.yuantops.tvplayer.util; 2 | 3 | import android.content.Context; 4 | import com.android.volley.Request; 5 | import com.android.volley.RequestQueue; 6 | import com.android.volley.VolleyError; 7 | import com.android.volley.toolbox.Volley; 8 | import org.apache.http.protocol.RequestExpectContinue; 9 | 10 | /** 11 | * Created by yuan on 9/5/15. 12 | */ 13 | public class VolleySingleton { 14 | private static final String TAG = VolleySingleton.class.getSimpleName(); 15 | 16 | private static VolleySingleton mInstance; 17 | private RequestQueue mRequestQueue; 18 | private Context mCtx; 19 | 20 | private VolleySingleton(Context context) { 21 | mCtx = context; 22 | mRequestQueue = getRequestQueue(); 23 | } 24 | 25 | public static synchronized VolleySingleton getInstance(Context context) { 26 | if (mInstance == null) { 27 | mInstance = new VolleySingleton(context); 28 | } 29 | return mInstance; 30 | } 31 | 32 | private RequestQueue getRequestQueue() { 33 | if (mRequestQueue == null) { 34 | mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext()); 35 | } 36 | return mRequestQueue; 37 | } 38 | 39 | public void addToRequestQueue(Request req) { 40 | getRequestQueue().add(req); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuantops/TopsTVPlayer/ccbe8d28f43cf5aa0724a3bd7c5c8e53edb1e427/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuantops/TopsTVPlayer/ccbe8d28f43cf5aa0724a3bd7c5c8e53edb1e427/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_seekbar_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuantops/TopsTVPlayer/ccbe8d28f43cf5aa0724a3bd7c5c8e53edb1e427/app/src/main/res/drawable-mdpi/ic_seekbar_thumb.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuantops/TopsTVPlayer/ccbe8d28f43cf5aa0724a3bd7c5c8e53edb1e427/app/src/main/res/drawable-mdpi/pause.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuantops/TopsTVPlayer/ccbe8d28f43cf5aa0724a3bd7c5c8e53edb1e427/app/src/main/res/drawable-mdpi/play.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ratingbar_full.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/star_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuantops/TopsTVPlayer/ccbe8d28f43cf5aa0724a3bd7c5c8e53edb1e427/app/src/main/res/drawable-mdpi/star_selected.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/star_unselect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuantops/TopsTVPlayer/ccbe8d28f43cf5aa0724a3bd7c5c8e53edb1e427/app/src/main/res/drawable-mdpi/star_unselect.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/vlc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuantops/TopsTVPlayer/ccbe8d28f43cf5aa0724a3bd7c5c8e53edb1e427/app/src/main/res/drawable-mdpi/vlc.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuantops/TopsTVPlayer/ccbe8d28f43cf5aa0724a3bd7c5c8e53edb1e427/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuantops/TopsTVPlayer/ccbe8d28f43cf5aa0724a3bd7c5c8e53edb1e427/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | 20 | 21 | 26 | 27 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_player.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 18 | 19 | 24 | 25 | 26 | 31 | 32 | 38 | 39 | 45 | 46 | 53 | 54 | 62 | 63 | 70 | 71 | 72 | 76 | 77 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_videoplay.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 18 | 19 | 24 | 25 | 26 | 31 | 32 | 38 | 39 | 45 | 46 | 53 | 54 | 62 | 63 | 70 | 71 | 72 | 76 | 77 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_web_server.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 13 | 14 | 18 | 19 | 23 | 24 |