From b50f6d22e4e1e90b7c967095581517d6d9f560ea Mon Sep 17 00:00:00 2001 From: Administrator <981964879@qq.com> Date: Mon, 1 Mar 2021 10:01:53 +0800 Subject: [PATCH] add library --- .gitignore | 1 + library/build.gradle | 31 + library/src/main/AndroidManifest.xml | 6 + .../src/main/java/cn/jzvd/JZDataSource.java | 88 ++ .../main/java/cn/jzvd/JZMediaInterface.java | 47 + .../src/main/java/cn/jzvd/JZMediaSystem.java | 203 +++ .../src/main/java/cn/jzvd/JZTextureView.java | 161 +++ library/src/main/java/cn/jzvd/JZUtils.java | 199 +++ library/src/main/java/cn/jzvd/Jzvd.java | 1135 +++++++++++++++++ library/src/main/java/cn/jzvd/JzvdStd.java | 979 ++++++++++++++ .../main/java/org/jzvd/jzvideo/JZVideoA.kt | 37 + .../main/res/anim/pop_from_bottom_anim_in.xml | 6 + .../res/anim/pop_from_bottom_anim_out.xml | 5 + .../main/res/drawable-xhdpi/jz_add_volume.png | Bin 0 -> 1306 bytes .../res/drawable-xhdpi/jz_back_normal.png | Bin 0 -> 625 bytes .../res/drawable-xhdpi/jz_back_pressed.png | Bin 0 -> 378 bytes .../drawable-xhdpi/jz_back_tiny_normal.png | Bin 0 -> 531 bytes .../drawable-xhdpi/jz_back_tiny_pressed.png | Bin 0 -> 486 bytes .../res/drawable-xhdpi/jz_backward_icon.png | Bin 0 -> 1392 bytes .../drawable-xhdpi/jz_battery_level_10.png | Bin 0 -> 176 bytes .../drawable-xhdpi/jz_battery_level_100.png | Bin 0 -> 169 bytes .../drawable-xhdpi/jz_battery_level_30.png | Bin 0 -> 169 bytes .../drawable-xhdpi/jz_battery_level_50.png | Bin 0 -> 168 bytes .../drawable-xhdpi/jz_battery_level_70.png | Bin 0 -> 169 bytes .../drawable-xhdpi/jz_battery_level_90.png | Bin 0 -> 168 bytes .../drawable-xhdpi/jz_brightness_video.png | Bin 0 -> 1058 bytes .../jz_clarity_popwindow_bg.9.png | Bin 0 -> 469 bytes .../res/drawable-xhdpi/jz_close_volume.png | Bin 0 -> 717 bytes .../main/res/drawable-xhdpi/jz_enlarge.png | Bin 0 -> 164 bytes .../res/drawable-xhdpi/jz_forward_icon.png | Bin 0 -> 1381 bytes .../main/res/drawable-xhdpi/jz_loading_bg.png | Bin 0 -> 4400 bytes .../res/drawable-xhdpi/jz_pause_normal.png | Bin 0 -> 1092 bytes .../res/drawable-xhdpi/jz_pause_pressed.png | Bin 0 -> 958 bytes .../res/drawable-xhdpi/jz_play_normal.png | Bin 0 -> 1480 bytes .../res/drawable-xhdpi/jz_play_pressed.png | Bin 0 -> 1272 bytes .../res/drawable-xhdpi/jz_restart_normal.png | Bin 0 -> 1215 bytes .../res/drawable-xhdpi/jz_restart_pressed.png | Bin 0 -> 1118 bytes .../res/drawable-xhdpi/jz_share_normal.png | Bin 0 -> 4059 bytes .../res/drawable-xhdpi/jz_share_pressed.png | Bin 0 -> 4004 bytes .../src/main/res/drawable-xhdpi/jz_shrink.png | Bin 0 -> 224 bytes .../res/drawable-xhdpi/jz_volume_icon.png | Bin 0 -> 922 bytes .../src/main/res/drawable/jz_bottom_bg.9.png | Bin 0 -> 946 bytes .../main/res/drawable/jz_bottom_progress.xml | 28 + .../res/drawable/jz_bottom_seek_poster.xml | 5 + .../res/drawable/jz_bottom_seek_progress.xml | 28 + .../drawable/jz_clarity_popwindow_bg.9.png | Bin 0 -> 470 bytes .../res/drawable/jz_click_back_selector.xml | 5 + .../drawable/jz_click_back_tiny_selector.xml | 5 + .../res/drawable/jz_click_pause_selector.xml | 6 + .../res/drawable/jz_click_play_selector.xml | 5 + .../res/drawable/jz_click_replay_selector.xml | 5 + .../res/drawable/jz_click_share_selector.xml | 5 + .../main/res/drawable/jz_dialog_progress.xml | 17 + .../res/drawable/jz_dialog_progress_bg.xml | 9 + library/src/main/res/drawable/jz_loading.xml | 7 + library/src/main/res/drawable/jz_retry.xml | 7 + .../res/drawable/jz_seek_poster_normal.xml | 8 + .../res/drawable/jz_seek_poster_pressed.xml | 8 + .../src/main/res/drawable/jz_title_bg.9.png | Bin 0 -> 1314 bytes .../res/drawable/jz_volume_progress_bg.xml | 19 + .../main/res/layout/jz_dialog_brightness.xml | 43 + .../main/res/layout/jz_dialog_progress.xml | 56 + .../src/main/res/layout/jz_dialog_volume.xml | 43 + .../src/main/res/layout/jz_layout_clarity.xml | 10 + .../res/layout/jz_layout_clarity_item.xml | 11 + library/src/main/res/layout/jz_layout_std.xml | 246 ++++ library/src/main/res/values-es/strings.xml | 10 + .../src/main/res/values-ja-rJP/strings.xml | 10 + .../src/main/res/values-ko-rKR/strings.xml | 10 + library/src/main/res/values-pt/strings.xml | 10 + library/src/main/res/values-tr/strings.xml | 10 + library/src/main/res/values-zh/strings.xml | 10 + library/src/main/res/values/dimens.xml | 4 + library/src/main/res/values/ids.xml | 23 + library/src/main/res/values/strings.xml | 10 + library/src/main/res/values/styles.xml | 21 + .../res/xml/jz_network_security_config.xml | 4 + 77 files changed, 3596 insertions(+) create mode 100644 library/build.gradle create mode 100644 library/src/main/AndroidManifest.xml create mode 100644 library/src/main/java/cn/jzvd/JZDataSource.java create mode 100644 library/src/main/java/cn/jzvd/JZMediaInterface.java create mode 100644 library/src/main/java/cn/jzvd/JZMediaSystem.java create mode 100644 library/src/main/java/cn/jzvd/JZTextureView.java create mode 100644 library/src/main/java/cn/jzvd/JZUtils.java create mode 100644 library/src/main/java/cn/jzvd/Jzvd.java create mode 100644 library/src/main/java/cn/jzvd/JzvdStd.java create mode 100644 library/src/main/java/org/jzvd/jzvideo/JZVideoA.kt create mode 100644 library/src/main/res/anim/pop_from_bottom_anim_in.xml create mode 100644 library/src/main/res/anim/pop_from_bottom_anim_out.xml create mode 100644 library/src/main/res/drawable-xhdpi/jz_add_volume.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_back_normal.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_back_pressed.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_back_tiny_normal.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_back_tiny_pressed.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_backward_icon.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_battery_level_10.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_battery_level_100.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_battery_level_30.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_battery_level_50.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_battery_level_70.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_battery_level_90.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_brightness_video.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_clarity_popwindow_bg.9.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_close_volume.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_enlarge.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_forward_icon.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_loading_bg.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_pause_normal.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_pause_pressed.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_play_normal.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_play_pressed.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_restart_normal.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_restart_pressed.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_share_normal.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_share_pressed.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_shrink.png create mode 100644 library/src/main/res/drawable-xhdpi/jz_volume_icon.png create mode 100644 library/src/main/res/drawable/jz_bottom_bg.9.png create mode 100644 library/src/main/res/drawable/jz_bottom_progress.xml create mode 100644 library/src/main/res/drawable/jz_bottom_seek_poster.xml create mode 100644 library/src/main/res/drawable/jz_bottom_seek_progress.xml create mode 100644 library/src/main/res/drawable/jz_clarity_popwindow_bg.9.png create mode 100644 library/src/main/res/drawable/jz_click_back_selector.xml create mode 100644 library/src/main/res/drawable/jz_click_back_tiny_selector.xml create mode 100644 library/src/main/res/drawable/jz_click_pause_selector.xml create mode 100644 library/src/main/res/drawable/jz_click_play_selector.xml create mode 100644 library/src/main/res/drawable/jz_click_replay_selector.xml create mode 100644 library/src/main/res/drawable/jz_click_share_selector.xml create mode 100644 library/src/main/res/drawable/jz_dialog_progress.xml create mode 100644 library/src/main/res/drawable/jz_dialog_progress_bg.xml create mode 100644 library/src/main/res/drawable/jz_loading.xml create mode 100644 library/src/main/res/drawable/jz_retry.xml create mode 100644 library/src/main/res/drawable/jz_seek_poster_normal.xml create mode 100644 library/src/main/res/drawable/jz_seek_poster_pressed.xml create mode 100644 library/src/main/res/drawable/jz_title_bg.9.png create mode 100644 library/src/main/res/drawable/jz_volume_progress_bg.xml create mode 100644 library/src/main/res/layout/jz_dialog_brightness.xml create mode 100644 library/src/main/res/layout/jz_dialog_progress.xml create mode 100644 library/src/main/res/layout/jz_dialog_volume.xml create mode 100644 library/src/main/res/layout/jz_layout_clarity.xml create mode 100644 library/src/main/res/layout/jz_layout_clarity_item.xml create mode 100644 library/src/main/res/layout/jz_layout_std.xml create mode 100644 library/src/main/res/values-es/strings.xml create mode 100644 library/src/main/res/values-ja-rJP/strings.xml create mode 100644 library/src/main/res/values-ko-rKR/strings.xml create mode 100644 library/src/main/res/values-pt/strings.xml create mode 100644 library/src/main/res/values-tr/strings.xml create mode 100644 library/src/main/res/values-zh/strings.xml create mode 100644 library/src/main/res/values/dimens.xml create mode 100644 library/src/main/res/values/ids.xml create mode 100644 library/src/main/res/values/strings.xml create mode 100644 library/src/main/res/values/styles.xml create mode 100644 library/src/main/res/xml/jz_network_security_config.xml diff --git a/.gitignore b/.gitignore index 124894a..531f93c 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ /.idea/ /app/src/test/java/com/uiui/videoplayer/ /app/src/androidTest/java/com/uiui/videoplayer/ +/library/build/ diff --git a/library/build.gradle b/library/build.gradle new file mode 100644 index 0000000..f0ad8f5 --- /dev/null +++ b/library/build.gradle @@ -0,0 +1,31 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + compileSdkVersion 30 + buildToolsVersion = '30.0.3' + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 30 + versionCode 106 + versionName "7.6.0" + } + + compileOptions { + sourceCompatibility 1.8 + targetCompatibility 1.8 + } + + lintOptions { + abortOnError false + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation "androidx.core:core-ktx:1.3.2" + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" +} + +//apply from: '../gradle/build_upload.gradle' \ No newline at end of file diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml new file mode 100644 index 0000000..b20611d --- /dev/null +++ b/library/src/main/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/library/src/main/java/cn/jzvd/JZDataSource.java b/library/src/main/java/cn/jzvd/JZDataSource.java new file mode 100644 index 0000000..9fcad12 --- /dev/null +++ b/library/src/main/java/cn/jzvd/JZDataSource.java @@ -0,0 +1,88 @@ +package cn.jzvd; + +import java.util.HashMap; +import java.util.LinkedHashMap; + +public class JZDataSource { + + public static final String URL_KEY_DEFAULT = "URL_KEY_DEFAULT"; + + public int currentUrlIndex; + public LinkedHashMap urlsMap = new LinkedHashMap(); + public String title = ""; + public HashMap headerMap = new HashMap<>(); + public boolean looping = false; + public Object[] objects; + + public JZDataSource(String url) { + urlsMap.put(URL_KEY_DEFAULT, url); + currentUrlIndex = 0; + } + + public JZDataSource(String url, String title) { + urlsMap.put(URL_KEY_DEFAULT, url); + this.title = title; + currentUrlIndex = 0; + } + + public JZDataSource(Object url) { + urlsMap.put(URL_KEY_DEFAULT, url); + currentUrlIndex = 0; + } + + public JZDataSource(LinkedHashMap urlsMap) { + this.urlsMap.clear(); + this.urlsMap.putAll(urlsMap); + currentUrlIndex = 0; + } + + public JZDataSource(LinkedHashMap urlsMap, String title) { + this.urlsMap.clear(); + this.urlsMap.putAll(urlsMap); + this.title = title; + currentUrlIndex = 0; + } + + public Object getCurrentUrl() { + return getValueFromLinkedMap(currentUrlIndex); + } + + public Object getCurrentKey() { + return getKeyFromDataSource(currentUrlIndex); + } + + public String getKeyFromDataSource(int index) { + int currentIndex = 0; + for (Object key : urlsMap.keySet()) { + if (currentIndex == index) { + return key.toString(); + } + currentIndex++; + } + return null; + } + + public Object getValueFromLinkedMap(int index) { + int currentIndex = 0; + for (Object key : urlsMap.keySet()) { + if (currentIndex == index) { + return urlsMap.get(key); + } + currentIndex++; + } + return null; + } + + public boolean containsTheUrl(Object object) { + if (object != null) { + return urlsMap.containsValue(object); + } + return false; + } + + public JZDataSource cloneMe() { + LinkedHashMap map = new LinkedHashMap(); + map.putAll(urlsMap); + return new JZDataSource(map, title); + } +} diff --git a/library/src/main/java/cn/jzvd/JZMediaInterface.java b/library/src/main/java/cn/jzvd/JZMediaInterface.java new file mode 100644 index 0000000..a8202b2 --- /dev/null +++ b/library/src/main/java/cn/jzvd/JZMediaInterface.java @@ -0,0 +1,47 @@ +package cn.jzvd; + +import android.graphics.SurfaceTexture; +import android.os.Handler; +import android.os.HandlerThread; +import android.view.Surface; +import android.view.TextureView; + +/** + * Created by Nathen on 2017/11/7. + * 自定义播放器 + */ +public abstract class JZMediaInterface implements TextureView.SurfaceTextureListener { + + public static SurfaceTexture SAVED_SURFACE; + public HandlerThread mMediaHandlerThread; + public Handler mMediaHandler; + public Handler handler; + public Jzvd jzvd; + + + public JZMediaInterface(Jzvd jzvd) { + this.jzvd = jzvd; + } + + public abstract void start(); + + public abstract void prepare(); + + public abstract void pause(); + + public abstract boolean isPlaying(); + + public abstract void seekTo(long time); + + public abstract void release(); + + public abstract long getCurrentPosition(); + + public abstract long getDuration(); + + public abstract void setVolume(float leftVolume, float rightVolume); + + public abstract void setSpeed(float speed); + + public abstract void setSurface(Surface surface); +} diff --git a/library/src/main/java/cn/jzvd/JZMediaSystem.java b/library/src/main/java/cn/jzvd/JZMediaSystem.java new file mode 100644 index 0000000..565e44a --- /dev/null +++ b/library/src/main/java/cn/jzvd/JZMediaSystem.java @@ -0,0 +1,203 @@ +package cn.jzvd; + +import android.graphics.SurfaceTexture; +import android.media.AudioManager; +import android.media.MediaPlayer; +import android.media.PlaybackParams; +import android.os.Handler; +import android.os.HandlerThread; +import android.view.Surface; + +import java.lang.reflect.Method; +import java.util.Map; + +/** + * Created by Nathen on 2017/11/8. + * 实现系统的播放引擎 + */ +public class JZMediaSystem extends JZMediaInterface implements MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnBufferingUpdateListener, MediaPlayer.OnSeekCompleteListener, MediaPlayer.OnErrorListener, MediaPlayer.OnInfoListener, MediaPlayer.OnVideoSizeChangedListener { + + public MediaPlayer mediaPlayer; + + public JZMediaSystem(Jzvd jzvd) { + super(jzvd); + } + + @Override + public void prepare() { + release(); + mMediaHandlerThread = new HandlerThread("JZVD"); + mMediaHandlerThread.start(); + mMediaHandler = new Handler(mMediaHandlerThread.getLooper());//主线程还是非主线程,就在这里 + handler = new Handler(); + + mMediaHandler.post(() -> { + try { + mediaPlayer = new MediaPlayer(); + mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); + mediaPlayer.setLooping(jzvd.jzDataSource.looping); + mediaPlayer.setOnPreparedListener(JZMediaSystem.this); + mediaPlayer.setOnCompletionListener(JZMediaSystem.this); + mediaPlayer.setOnBufferingUpdateListener(JZMediaSystem.this); + mediaPlayer.setScreenOnWhilePlaying(true); + mediaPlayer.setOnSeekCompleteListener(JZMediaSystem.this); + mediaPlayer.setOnErrorListener(JZMediaSystem.this); + mediaPlayer.setOnInfoListener(JZMediaSystem.this); + mediaPlayer.setOnVideoSizeChangedListener(JZMediaSystem.this); + Class clazz = MediaPlayer.class; + //如果不用反射,没有url和header参数的setDataSource函数 + Method method = clazz.getDeclaredMethod("setDataSource", String.class, Map.class); + method.invoke(mediaPlayer, jzvd.jzDataSource.getCurrentUrl().toString(), jzvd.jzDataSource.headerMap); + mediaPlayer.prepareAsync(); + mediaPlayer.setSurface(new Surface(SAVED_SURFACE)); + } catch (Exception e) { + e.printStackTrace(); + } + }); + } + + @Override + public void start() { + mMediaHandler.post(() -> mediaPlayer.start()); + } + + @Override + public void pause() { + mMediaHandler.post(() -> mediaPlayer.pause()); + } + + @Override + public boolean isPlaying() { + return mediaPlayer.isPlaying(); + } + + @Override + public void seekTo(long time) { + mMediaHandler.post(() -> { + try { + mediaPlayer.seekTo((int) time); + } catch (IllegalStateException e) { + e.printStackTrace(); + } + }); + } + + @Override + public void release() {//not perfect change you later + if (mMediaHandler != null && mMediaHandlerThread != null && mediaPlayer != null) {//不知道有没有妖孽 + HandlerThread tmpHandlerThread = mMediaHandlerThread; + MediaPlayer tmpMediaPlayer = mediaPlayer; + JZMediaInterface.SAVED_SURFACE = null; + + mMediaHandler.post(() -> { + tmpMediaPlayer.setSurface(null); + tmpMediaPlayer.release(); + tmpHandlerThread.quit(); + }); + mediaPlayer = null; + } + } + + //TODO 测试这种问题是否在threadHandler中是否正常,所有的操作mediaplayer是否不需要thread,挨个测试,是否有问题 + @Override + public long getCurrentPosition() { + if (mediaPlayer != null) { + return mediaPlayer.getCurrentPosition(); + } else { + return 0; + } + } + + @Override + public long getDuration() { + if (mediaPlayer != null) { + return mediaPlayer.getDuration(); + } else { + return 0; + } + } + + @Override + public void setVolume(float leftVolume, float rightVolume) { + if (mMediaHandler == null) return; + mMediaHandler.post(() -> { + if (mediaPlayer != null) mediaPlayer.setVolume(leftVolume, rightVolume); + }); + } + + @Override + public void setSpeed(float speed) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + PlaybackParams pp = mediaPlayer.getPlaybackParams(); + pp.setSpeed(speed); + mediaPlayer.setPlaybackParams(pp); + } + } + + @Override + public void onPrepared(MediaPlayer mediaPlayer) { + handler.post(() -> jzvd.onPrepared());//如果是mp3音频,走这里 + } + + @Override + public void onCompletion(MediaPlayer mediaPlayer) { + handler.post(() -> jzvd.onCompletion()); + } + + @Override + public void onBufferingUpdate(MediaPlayer mediaPlayer, final int percent) { + handler.post(() -> jzvd.setBufferProgress(percent)); + } + + @Override + public void onSeekComplete(MediaPlayer mediaPlayer) { + handler.post(() -> jzvd.onSeekComplete()); + } + + @Override + public boolean onError(MediaPlayer mediaPlayer, final int what, final int extra) { + handler.post(() -> jzvd.onError(what, extra)); + return true; + } + + @Override + public boolean onInfo(MediaPlayer mediaPlayer, final int what, final int extra) { + handler.post(() -> jzvd.onInfo(what, extra)); + return false; + } + + @Override + public void onVideoSizeChanged(MediaPlayer mediaPlayer, int width, int height) { + handler.post(() -> jzvd.onVideoSizeChanged(width, height)); + } + + @Override + public void setSurface(Surface surface) { + mediaPlayer.setSurface(surface); + } + + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { + if (SAVED_SURFACE == null) { + SAVED_SURFACE = surface; + prepare(); + } else { + jzvd.textureView.setSurfaceTexture(SAVED_SURFACE); + } + } + + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { + + } + + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { + return false; + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surface) { + + } +} diff --git a/library/src/main/java/cn/jzvd/JZTextureView.java b/library/src/main/java/cn/jzvd/JZTextureView.java new file mode 100644 index 0000000..0e850c1 --- /dev/null +++ b/library/src/main/java/cn/jzvd/JZTextureView.java @@ -0,0 +1,161 @@ +package cn.jzvd; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; +import android.view.TextureView; +import android.view.View; + +/** + *

参照Android系统的VideoView的onMeasure方法 + *
注意!relativelayout中无法全屏,要嵌套一个linearlayout

+ *

Referring Android system Video View of onMeasure method + *
NOTE! Can not fullscreen relativelayout, to nest a linearlayout

+ * Created by Nathen + * On 2016/06/02 00:01 + */ +public class JZTextureView extends TextureView { + protected static final String TAG = "JZResizeTextureView"; + + public int currentVideoWidth = 0; + public int currentVideoHeight = 0; + + public JZTextureView(Context context) { + super(context); + currentVideoWidth = 0; + currentVideoHeight = 0; + } + + public JZTextureView(Context context, AttributeSet attrs) { + super(context, attrs); + currentVideoWidth = 0; + currentVideoHeight = 0; + } + + public void setVideoSize(int currentVideoWidth, int currentVideoHeight) { + if (this.currentVideoWidth != currentVideoWidth || this.currentVideoHeight != currentVideoHeight) { + this.currentVideoWidth = currentVideoWidth; + this.currentVideoHeight = currentVideoHeight; + requestLayout(); + } + } + + @Override + public void setRotation(float rotation) { + if (rotation != getRotation()) { + super.setRotation(rotation); + requestLayout(); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + Log.i(TAG, "onMeasure " + " [" + this.hashCode() + "] "); + int viewRotation = (int) getRotation(); + int videoWidth = currentVideoWidth; + int videoHeight = currentVideoHeight; + + + int parentHeight = ((View) getParent()).getMeasuredHeight(); + int parentWidth = ((View) getParent()).getMeasuredWidth(); + if (parentWidth != 0 && parentHeight != 0 && videoWidth != 0 && videoHeight != 0) { + if (Jzvd.VIDEO_IMAGE_DISPLAY_TYPE == Jzvd.VIDEO_IMAGE_DISPLAY_TYPE_FILL_PARENT) { + if (viewRotation == 90 || viewRotation == 270) { + int tempSize = parentWidth; + parentWidth = parentHeight; + parentHeight = tempSize; + } + /**强制充满**/ + videoHeight = videoWidth * parentHeight / parentWidth; + } + } + + // 如果判断成立,则说明显示的TextureView和本身的位置是有90度的旋转的,所以需要交换宽高参数。 + if (viewRotation == 90 || viewRotation == 270) { + int tempMeasureSpec = widthMeasureSpec; + widthMeasureSpec = heightMeasureSpec; + heightMeasureSpec = tempMeasureSpec; + } + + int width = getDefaultSize(videoWidth, widthMeasureSpec); + int height = getDefaultSize(videoHeight, heightMeasureSpec); + if (videoWidth > 0 && videoHeight > 0) { + + int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); + int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); + int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); + int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); + + Log.i(TAG, "widthMeasureSpec [" + MeasureSpec.toString(widthMeasureSpec) + "]"); + Log.i(TAG, "heightMeasureSpec [" + MeasureSpec.toString(heightMeasureSpec) + "]"); + + if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) { + // the size is fixed + width = widthSpecSize; + height = heightSpecSize; + // for compatibility, we adjust size based on aspect ratio + if (videoWidth * height < width * videoHeight) { + width = height * videoWidth / videoHeight; + } else if (videoWidth * height > width * videoHeight) { + height = width * videoHeight / videoWidth; + } + } else if (widthSpecMode == MeasureSpec.EXACTLY) { + // only the width is fixed, adjust the height to match aspect ratio if possible + width = widthSpecSize; + height = width * videoHeight / videoWidth; + if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) { + // couldn't match aspect ratio within the constraints + height = heightSpecSize; + width = height * videoWidth / videoHeight; + } + } else if (heightSpecMode == MeasureSpec.EXACTLY) { + // only the height is fixed, adjust the width to match aspect ratio if possible + height = heightSpecSize; + width = height * videoWidth / videoHeight; + if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) { + // couldn't match aspect ratio within the constraints + width = widthSpecSize; + height = width * videoHeight / videoWidth; + } + } else { + // neither the width nor the height are fixed, try to use actual video size + width = videoWidth; + height = videoHeight; + if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) { + // too tall, decrease both width and height + height = heightSpecSize; + width = height * videoWidth / videoHeight; + } + if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) { + // too wide, decrease both width and height + width = widthSpecSize; + height = width * videoHeight / videoWidth; + } + } + } else { + // no size yet, just adopt the given spec sizes + } + if (parentWidth != 0 && parentHeight != 0 && videoWidth != 0 && videoHeight != 0) { + if (Jzvd.VIDEO_IMAGE_DISPLAY_TYPE == Jzvd.VIDEO_IMAGE_DISPLAY_TYPE_ORIGINAL) { + /**原图**/ + height = videoHeight; + width = videoWidth; + } else if (Jzvd.VIDEO_IMAGE_DISPLAY_TYPE == Jzvd.VIDEO_IMAGE_DISPLAY_TYPE_FILL_SCROP) { + if (viewRotation == 90 || viewRotation == 270) { + int tempSize = parentWidth; + parentWidth = parentHeight; + parentHeight = tempSize; + } + /**充满剪切**/ + if (((double) videoHeight / videoWidth) > ((double) parentHeight / parentWidth)) { + height = (int) (((double) parentWidth / (double) width * (double) height)); + width = parentWidth; + } else if (((double) videoHeight / videoWidth) < ((double) parentHeight / parentWidth)) { + width = (int) (((double) parentHeight / (double) height * (double) width)); + height = parentHeight; + } + } + } + setMeasuredDimension(width, height); + } +} diff --git a/library/src/main/java/cn/jzvd/JZUtils.java b/library/src/main/java/cn/jzvd/JZUtils.java new file mode 100644 index 0000000..a14f2a8 --- /dev/null +++ b/library/src/main/java/cn/jzvd/JZUtils.java @@ -0,0 +1,199 @@ +package cn.jzvd; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.Window; +import android.view.WindowManager; + +import java.util.Formatter; +import java.util.Locale; + +/** + * Created by Nathen + * On 2016/02/21 12:25 + */ +public class JZUtils { + public static final String TAG = "JZVD"; + public static int SYSTEM_UI = 0; + + public static String stringForTime(long timeMs) { + if (timeMs <= 0 || timeMs >= 24 * 60 * 60 * 1000) { + return "00:00"; + } + long totalSeconds = timeMs / 1000; + int seconds = (int) (totalSeconds % 60); + int minutes = (int) ((totalSeconds / 60) % 60); + int hours = (int) (totalSeconds / 3600); + StringBuilder stringBuilder = new StringBuilder(); + Formatter mFormatter = new Formatter(stringBuilder, Locale.getDefault()); + if (hours > 0) { + return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString(); + } else { + return mFormatter.format("%02d:%02d", minutes, seconds).toString(); + } + } + + /** + * This method requires the caller to hold the permission ACCESS_NETWORK_STATE. + * + * @param context context + * @return if wifi is connected,return true + */ + public static boolean isWifiConnected(Context context) { + ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); + return networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI; + } + + /** + * Get activity from context object + * + * @param context context + * @return object of Activity or null if it is not Activity + */ + public static Activity scanForActivity(Context context) { + if (context == null) return null; + + if (context instanceof Activity) { + return (Activity) context; + } else if (context instanceof ContextWrapper) { + return scanForActivity(((ContextWrapper) context).getBaseContext()); + } + + return null; + } + + public static void setRequestedOrientation(Context context, int orientation) { + if (JZUtils.scanForActivity(context) != null) { + JZUtils.scanForActivity(context).setRequestedOrientation( + orientation); + } else { + JZUtils.scanForActivity(context).setRequestedOrientation( + orientation); + } + } + + public static Window getWindow(Context context) { + if (JZUtils.scanForActivity(context) != null) { + return JZUtils.scanForActivity(context).getWindow(); + } else { + return JZUtils.scanForActivity(context).getWindow(); + } + } + + public static int dip2px(Context context, float dpValue) { + final float scale = context.getResources().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + public static void saveProgress(Context context, Object url, long progress) { + if (!Jzvd.SAVE_PROGRESS) return; + Log.i(TAG, "saveProgress: " + progress); + if (progress < 5000) { + progress = 0; + } + SharedPreferences spn = context.getSharedPreferences("JZVD_PROGRESS", + Context.MODE_PRIVATE); + SharedPreferences.Editor editor = spn.edit(); + editor.putLong("newVersion:" + url.toString(), progress).apply(); + } + + public static long getSavedProgress(Context context, Object url) { + if (!Jzvd.SAVE_PROGRESS) return 0; + SharedPreferences spn = context.getSharedPreferences("JZVD_PROGRESS", + Context.MODE_PRIVATE); + return spn.getLong("newVersion:" + url.toString(), 0); + } + + /** + * if url == null, clear all progress + * + * @param context context + * @param url if url!=null clear this url progress + */ + public static void clearSavedProgress(Context context, Object url) { + if (url == null) { + SharedPreferences spn = context.getSharedPreferences("JZVD_PROGRESS", + Context.MODE_PRIVATE); + spn.edit().clear().apply(); + } else { + SharedPreferences spn = context.getSharedPreferences("JZVD_PROGRESS", + Context.MODE_PRIVATE); + spn.edit().putLong("newVersion:" + url.toString(), 0).apply(); + } + } + + @SuppressLint("RestrictedApi") + public static void showStatusBar(Context context) { + if (Jzvd.TOOL_BAR_EXIST) { + JZUtils.getWindow(context).clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + } + + //如果是沉浸式的,全屏前就没有状态栏 + @SuppressLint("RestrictedApi") + public static void hideStatusBar(Context context) { + if (Jzvd.TOOL_BAR_EXIST) { + JZUtils.getWindow(context).setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + } + + @SuppressLint("NewApi") + public static void hideSystemUI(Context context) { + int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + ; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { + uiOptions |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + } + SYSTEM_UI = JZUtils.getWindow(context).getDecorView().getSystemUiVisibility(); + JZUtils.getWindow(context).getDecorView().setSystemUiVisibility(uiOptions); + + } + + @SuppressLint("NewApi") + public static void showSystemUI(Context context) { + int uiOptions = View.SYSTEM_UI_FLAG_VISIBLE; + JZUtils.getWindow(context).getDecorView().setSystemUiVisibility(SYSTEM_UI); + } + + //获取状态栏的高度 + public static int getStatusBarHeight(Context context) { + Resources resources = context.getResources(); + int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android"); + int height = resources.getDimensionPixelSize(resourceId); + return height; + } + + //获取NavigationBar的高度 + public static int getNavigationBarHeight(Context context) { + boolean var1 = ViewConfiguration.get(context).hasPermanentMenuKey(); + int var2; + return (var2 = context.getResources().getIdentifier("navigation_bar_height", "dimen", "android")) > 0 && !var1 ? context.getResources().getDimensionPixelSize(var2) : 0; + } + + //获取屏幕的宽度 + public static int getScreenWidth(Context context) { + DisplayMetrics dm = context.getResources().getDisplayMetrics(); + return dm.widthPixels; + } + + //获取屏幕的高度 + public static int getScreenHeight(Context context) { + DisplayMetrics dm = context.getResources().getDisplayMetrics(); + return dm.heightPixels; + } + +} diff --git a/library/src/main/java/cn/jzvd/Jzvd.java b/library/src/main/java/cn/jzvd/Jzvd.java new file mode 100644 index 0000000..2b7ad44 --- /dev/null +++ b/library/src/main/java/cn/jzvd/Jzvd.java @@ -0,0 +1,1135 @@ +package cn.jzvd; + +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.media.AudioManager; +import android.media.MediaPlayer; +import android.provider.Settings; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.view.WindowManager; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.SeekBar; +import android.widget.TextView; +import android.widget.Toast; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.LinkedList; +import java.util.Timer; +import java.util.TimerTask; + +/** + * Created by Nathen on 16/7/30. + */ +public abstract class Jzvd extends FrameLayout implements View.OnClickListener, SeekBar.OnSeekBarChangeListener, View.OnTouchListener { + + public static final String TAG = "JZVD"; + public static final int SCREEN_NORMAL = 0; + public static final int SCREEN_FULLSCREEN = 1; + public static final int SCREEN_TINY = 2; + public static final int STATE_IDLE = -1; + public static final int STATE_NORMAL = 0; + public static final int STATE_PREPARING = 1; + public static final int STATE_PREPARING_CHANGE_URL = 2; + public static final int STATE_PREPARING_PLAYING = 3; + public static final int STATE_PREPARED = 4; + public static final int STATE_PLAYING = 5; + public static final int STATE_PAUSE = 6; + public static final int STATE_AUTO_COMPLETE = 7; + public static final int STATE_ERROR = 8; + public static final int VIDEO_IMAGE_DISPLAY_TYPE_ADAPTER = 0;//DEFAULT + public static final int VIDEO_IMAGE_DISPLAY_TYPE_FILL_PARENT = 1; + public static final int VIDEO_IMAGE_DISPLAY_TYPE_FILL_SCROP = 2; + public static final int VIDEO_IMAGE_DISPLAY_TYPE_ORIGINAL = 3; + public static final int THRESHOLD = 80; + public static Jzvd CURRENT_JZVD; + public static LinkedList CONTAINER_LIST = new LinkedList<>(); + public static boolean TOOL_BAR_EXIST = true; + public static int FULLSCREEN_ORIENTATION = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; + public static int NORMAL_ORIENTATION = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; + public static boolean SAVE_PROGRESS = true; + public static boolean WIFI_TIP_DIALOG_SHOWED = false; + public static int VIDEO_IMAGE_DISPLAY_TYPE = 0; + public static long lastAutoFullscreenTime = 0; + public static int ON_PLAY_PAUSE_TMP_STATE = 0;//这个考虑不放到库里,去自定义 + public static int backUpBufferState = -1; + public static float PROGRESS_DRAG_RATE = 1f;//进度条滑动阻尼系数 越大播放进度条滑动越慢 + public static AudioManager.OnAudioFocusChangeListener onAudioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {//是否新建个class,代码更规矩,并且变量的位置也很尴尬 + @Override + public void onAudioFocusChange(int focusChange) { + switch (focusChange) { + case AudioManager.AUDIOFOCUS_GAIN: + break; + case AudioManager.AUDIOFOCUS_LOSS: + releaseAllVideos(); + Log.d(TAG, "AUDIOFOCUS_LOSS [" + this.hashCode() + "]"); + break; + case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: + try { + Jzvd player = CURRENT_JZVD; + if (player != null && player.state == Jzvd.STATE_PLAYING) { + player.startButton.performClick(); + } + } catch (IllegalStateException e) { + e.printStackTrace(); + } + Log.d(TAG, "AUDIOFOCUS_LOSS_TRANSIENT [" + this.hashCode() + "]"); + break; + case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: + break; + } + } + }; + public int state = -1; + public int screen = -1; + public JZDataSource jzDataSource; + public int widthRatio = 0; + public int heightRatio = 0; + public Class mediaInterfaceClass; + public JZMediaInterface mediaInterface; + public int positionInList = -1;//很想干掉它 + public int videoRotation = 0; + public int seekToManulPosition = -1; + public long seekToInAdvance = 0; + + public ImageView startButton; + public SeekBar progressBar; + public ImageView fullscreenButton; + public TextView currentTimeTextView, totalTimeTextView; + public ViewGroup textureViewContainer; + public ViewGroup topContainer, bottomContainer; + public JZTextureView textureView; + public boolean preloading = false; + protected long gobakFullscreenTime = 0;//这个应该重写一下,刷新列表,新增列表的刷新,不打断播放,应该是个flag + protected long gotoFullscreenTime = 0; + protected Timer UPDATE_PROGRESS_TIMER; + protected int mScreenWidth; + protected int mScreenHeight; + protected AudioManager mAudioManager; + protected ProgressTimerTask mProgressTimerTask; + protected boolean mTouchingProgressBar; + protected float mDownX; + protected float mDownY; + protected boolean mChangeVolume; + protected boolean mChangePosition; + protected boolean mChangeBrightness; + protected long mGestureDownPosition; + protected int mGestureDownVolume; + protected float mGestureDownBrightness; + protected long mSeekTimePosition; + protected Context jzvdContext; + protected long mCurrentPosition; + /** + * 如果不在列表中可以不加block + */ + protected ViewGroup.LayoutParams blockLayoutParams; + protected int blockIndex; + protected int blockWidth; + protected int blockHeight; + + public Jzvd(Context context) { + super(context); + init(context); + } + + public Jzvd(Context context, AttributeSet attrs) { + super(context, attrs); + init(context); + } + + /** + * 增加准备状态逻辑 + */ + public static void goOnPlayOnResume() { + if (CURRENT_JZVD != null) { + if (CURRENT_JZVD.state == Jzvd.STATE_PAUSE) { + if (ON_PLAY_PAUSE_TMP_STATE == STATE_PAUSE) { + CURRENT_JZVD.onStatePause(); + CURRENT_JZVD.mediaInterface.pause(); + } else { + CURRENT_JZVD.onStatePlaying(); + CURRENT_JZVD.mediaInterface.start(); + } + ON_PLAY_PAUSE_TMP_STATE = 0; + } else if (CURRENT_JZVD.state == Jzvd.STATE_PREPARING) { + //准备状态暂停后的 + CURRENT_JZVD.startVideo(); + } + if (CURRENT_JZVD.screen == Jzvd.SCREEN_FULLSCREEN) { + JZUtils.hideStatusBar(CURRENT_JZVD.jzvdContext); + JZUtils.hideSystemUI(CURRENT_JZVD.jzvdContext); + } + } + } + + /** + * 增加准备状态逻辑 + */ + public static void goOnPlayOnPause() { + if (CURRENT_JZVD != null) { + if (CURRENT_JZVD.state == Jzvd.STATE_AUTO_COMPLETE || + CURRENT_JZVD.state == Jzvd.STATE_NORMAL || + CURRENT_JZVD.state == Jzvd.STATE_ERROR) { + Jzvd.releaseAllVideos(); + } else if (CURRENT_JZVD.state == Jzvd.STATE_PREPARING) { + //准备状态暂停的逻辑 + Jzvd.setCurrentJzvd(CURRENT_JZVD); + CURRENT_JZVD.state = STATE_PREPARING; + } else { + ON_PLAY_PAUSE_TMP_STATE = CURRENT_JZVD.state; + CURRENT_JZVD.onStatePause(); + CURRENT_JZVD.mediaInterface.pause(); + } + } + } + + public static void startFullscreenDirectly(Context context, Class _class, String url, String title) { + startFullscreenDirectly(context, _class, new JZDataSource(url, title)); + } + + public static void startFullscreenDirectly(Context context, Class _class, JZDataSource jzDataSource) { + JZUtils.hideStatusBar(context); + JZUtils.setRequestedOrientation(context, FULLSCREEN_ORIENTATION); + JZUtils.hideSystemUI(context); + + ViewGroup vp = (ViewGroup) JZUtils.scanForActivity(context).getWindow().getDecorView(); + try { + Constructor constructor = _class.getConstructor(Context.class); + final Jzvd jzvd = constructor.newInstance(context); + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + vp.addView(jzvd, lp); + jzvd.setUp(jzDataSource, JzvdStd.SCREEN_FULLSCREEN); + jzvd.startVideo(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void releaseAllVideos() { + Log.d(TAG, "releaseAllVideos"); + if (CURRENT_JZVD != null) { + CURRENT_JZVD.reset(); + CURRENT_JZVD = null; + } + } + + public static boolean backPress() { + Log.i(TAG, "backPress"); + if (CONTAINER_LIST.size() != 0 && CURRENT_JZVD != null) {//判断条件,因为当前所有goBack都是回到普通窗口 + CURRENT_JZVD.gotoNormalScreen(); + return true; + } else if (CONTAINER_LIST.size() == 0 && CURRENT_JZVD != null && CURRENT_JZVD.screen != SCREEN_NORMAL) {//退出直接进入的全屏 + CURRENT_JZVD.clearFloatScreen(); + return true; + } + return false; + } + + public static void setCurrentJzvd(Jzvd jzvd) { + if (CURRENT_JZVD != null) CURRENT_JZVD.reset(); + CURRENT_JZVD = jzvd; + } + + public static void setTextureViewRotation(int rotation) { + if (CURRENT_JZVD != null && CURRENT_JZVD.textureView != null) { + CURRENT_JZVD.textureView.setRotation(rotation); + } + } + + public static void setVideoImageDisplayType(int type) { + Jzvd.VIDEO_IMAGE_DISPLAY_TYPE = type; + if (CURRENT_JZVD != null && CURRENT_JZVD.textureView != null) { + CURRENT_JZVD.textureView.requestLayout(); + } + } + + public abstract int getLayoutId(); + + public void init(Context context) { + View.inflate(context, getLayoutId(), this); + jzvdContext = context; + startButton = findViewById(R.id.start); + fullscreenButton = findViewById(R.id.fullscreen); + progressBar = findViewById(R.id.bottom_seek_progress); + currentTimeTextView = findViewById(R.id.current); + totalTimeTextView = findViewById(R.id.total); + bottomContainer = findViewById(R.id.layout_bottom); + textureViewContainer = findViewById(R.id.surface_container); + topContainer = findViewById(R.id.layout_top); + + if (startButton == null) { + startButton = new ImageView(context); + } + if (fullscreenButton == null) { + fullscreenButton = new ImageView(context); + } + if (progressBar == null) { + progressBar = new SeekBar(context); + } + if (currentTimeTextView == null) { + currentTimeTextView = new TextView(context); + } + if (totalTimeTextView == null) { + totalTimeTextView = new TextView(context); + } + if (bottomContainer == null) { + bottomContainer = new LinearLayout(context); + } + if (textureViewContainer == null) { + textureViewContainer = new FrameLayout(context); + } + if (topContainer == null) { + topContainer = new RelativeLayout(context); + } + + startButton.setOnClickListener(this); + fullscreenButton.setOnClickListener(this); + progressBar.setOnSeekBarChangeListener(this); + bottomContainer.setOnClickListener(this); + textureViewContainer.setOnClickListener(this); + textureViewContainer.setOnTouchListener(this); + + mScreenWidth = getContext().getResources().getDisplayMetrics().widthPixels; + mScreenHeight = getContext().getResources().getDisplayMetrics().heightPixels; + + state = STATE_IDLE; + } + + public void setUp(String url, String title) { + setUp(new JZDataSource(url, title), SCREEN_NORMAL); + } + + public void setUp(String url, String title, int screen) { + setUp(new JZDataSource(url, title), screen); + } + + public void setUp(JZDataSource jzDataSource, int screen) { + setUp(jzDataSource, screen, JZMediaSystem.class); + } + + public void setUp(String url, String title, int screen, Class mediaInterfaceClass) { + setUp(new JZDataSource(url, title), screen, mediaInterfaceClass); + } + + public void setUp(JZDataSource jzDataSource, int screen, Class mediaInterfaceClass) { + + + this.jzDataSource = jzDataSource; + this.screen = screen; + onStateNormal(); + this.mediaInterfaceClass = mediaInterfaceClass; + } + + public void setMediaInterface(Class mediaInterfaceClass) { + reset(); + this.mediaInterfaceClass = mediaInterfaceClass; + } + + @Override + public void onClick(View v) { + int i = v.getId(); + if (i == R.id.start) { + clickStart(); + } else if (i == R.id.fullscreen) { + clickFullscreen(); + } + } + + protected void clickFullscreen() { + Log.i(TAG, "onClick fullscreen [" + this.hashCode() + "] "); + if (state == STATE_AUTO_COMPLETE) return; + if (screen == SCREEN_FULLSCREEN) { + //quit fullscreen + backPress(); + } else { + Log.d(TAG, "toFullscreenActivity [" + this.hashCode() + "] "); + gotoFullscreen(); + } + } + + protected void clickStart() { + Log.i(TAG, "onClick start [" + this.hashCode() + "] "); + if (jzDataSource == null || jzDataSource.urlsMap.isEmpty() || jzDataSource.getCurrentUrl() == null) { + Toast.makeText(getContext(), getResources().getString(R.string.no_url), Toast.LENGTH_SHORT).show(); + return; + } + if (state == STATE_NORMAL) { + if (!jzDataSource.getCurrentUrl().toString().startsWith("file") && ! + jzDataSource.getCurrentUrl().toString().startsWith("/") && + !JZUtils.isWifiConnected(getContext()) && !WIFI_TIP_DIALOG_SHOWED) {//这个可以放到std中 + showWifiDialog(); + return; + } + startVideo(); + } else if (state == STATE_PLAYING) { + Log.d(TAG, "pauseVideo [" + this.hashCode() + "] "); + mediaInterface.pause(); + onStatePause(); + } else if (state == STATE_PAUSE) { + mediaInterface.start(); + onStatePlaying(); + } else if (state == STATE_AUTO_COMPLETE) { + startVideo(); + } + } + + + + @Override + public boolean onTouch(View v, MotionEvent event) { + float x = event.getX(); + float y = event.getY(); + int id = v.getId(); + if (id == R.id.surface_container) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + touchActionDown(x, y); + break; + case MotionEvent.ACTION_MOVE: + touchActionMove(x, y); + break; + case MotionEvent.ACTION_UP: + touchActionUp(); + break; + } + } + return false; + } + + protected void touchActionUp() { + Log.i(TAG, "onTouch surfaceContainer actionUp [" + this.hashCode() + "] "); + mTouchingProgressBar = false; + dismissProgressDialog(); + dismissVolumeDialog(); + dismissBrightnessDialog(); + if (mChangePosition) { + mediaInterface.seekTo(mSeekTimePosition); + long duration = getDuration(); + int progress = (int) (mSeekTimePosition * 100 / (duration == 0 ? 1 : duration)); + progressBar.setProgress(progress); + } + if (mChangeVolume) { + //change volume event + } + startProgressTimer(); + } + + protected void touchActionMove(float x, float y) { + Log.i(TAG, "onTouch surfaceContainer actionMove [" + this.hashCode() + "] "); + float deltaX = x - mDownX; + float deltaY = y - mDownY; + float absDeltaX = Math.abs(deltaX); + float absDeltaY = Math.abs(deltaY); + if (screen == SCREEN_FULLSCREEN) { + //拖动的是NavigationBar和状态栏 + if (mDownX > JZUtils.getScreenWidth(getContext()) || mDownY < JZUtils.getStatusBarHeight(getContext())) { + return; + } + if (!mChangePosition && !mChangeVolume && !mChangeBrightness) { + if (absDeltaX > THRESHOLD || absDeltaY > THRESHOLD) { + cancelProgressTimer(); + if (absDeltaX >= THRESHOLD) { + // 全屏模式下的CURRENT_STATE_ERROR状态下,不响应进度拖动事件. + // 否则会因为mediaplayer的状态非法导致App Crash + if (state != STATE_ERROR) { + mChangePosition = true; + mGestureDownPosition = getCurrentPositionWhenPlaying(); + } + } else { + //如果y轴滑动距离超过设置的处理范围,那么进行滑动事件处理 + if (mDownX < mScreenHeight * 0.5f) {//左侧改变亮度 + mChangeBrightness = true; + WindowManager.LayoutParams lp = JZUtils.getWindow(getContext()).getAttributes(); + if (lp.screenBrightness < 0) { + try { + mGestureDownBrightness = Settings.System.getInt(getContext().getContentResolver(), Settings.System.SCREEN_BRIGHTNESS); + Log.i(TAG, "current system brightness: " + mGestureDownBrightness); + } catch (Settings.SettingNotFoundException e) { + e.printStackTrace(); + } + } else { + mGestureDownBrightness = lp.screenBrightness * 255; + Log.i(TAG, "current activity brightness: " + mGestureDownBrightness); + } + } else {//右侧改变声音 + mChangeVolume = true; + mGestureDownVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); + } + } + } + } + } + if (mChangePosition) { + long totalTimeDuration = getDuration(); + if (PROGRESS_DRAG_RATE <= 0) { + Log.d(TAG, "error PROGRESS_DRAG_RATE value"); + PROGRESS_DRAG_RATE = 1f; + } + mSeekTimePosition = (int) (mGestureDownPosition + deltaX * totalTimeDuration / (mScreenWidth * PROGRESS_DRAG_RATE)); + if (mSeekTimePosition > totalTimeDuration) + mSeekTimePosition = totalTimeDuration; + String seekTime = JZUtils.stringForTime(mSeekTimePosition); + String totalTime = JZUtils.stringForTime(totalTimeDuration); + + showProgressDialog(deltaX, seekTime, mSeekTimePosition, totalTime, totalTimeDuration); + } + if (mChangeVolume) { + deltaY = -deltaY; + int max = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + int deltaV = (int) (max * deltaY * 3 / mScreenHeight); + mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, mGestureDownVolume + deltaV, 0); + //dialog中显示百分比 + int volumePercent = (int) (mGestureDownVolume * 100 / max + deltaY * 3 * 100 / mScreenHeight); + showVolumeDialog(-deltaY, volumePercent); + } + + if (mChangeBrightness) { + deltaY = -deltaY; + int deltaV = (int) (255 * deltaY * 3 / mScreenHeight); + WindowManager.LayoutParams params = JZUtils.getWindow(getContext()).getAttributes(); + if (((mGestureDownBrightness + deltaV) / 255) >= 1) {//这和声音有区别,必须自己过滤一下负值 + params.screenBrightness = 1; + } else if (((mGestureDownBrightness + deltaV) / 255) <= 0) { + params.screenBrightness = 0.01f; + } else { + params.screenBrightness = (mGestureDownBrightness + deltaV) / 255; + } + JZUtils.getWindow(getContext()).setAttributes(params); + //dialog中显示百分比 + int brightnessPercent = (int) (mGestureDownBrightness * 100 / 255 + deltaY * 3 * 100 / mScreenHeight); + showBrightnessDialog(brightnessPercent); +// mDownY = y; + } + } + + protected void touchActionDown(float x, float y) { + Log.i(TAG, "onTouch surfaceContainer actionDown [" + this.hashCode() + "] "); + mTouchingProgressBar = true; + + mDownX = x; + mDownY = y; + mChangeVolume = false; + mChangePosition = false; + mChangeBrightness = false; + } + + public void onStateNormal() { + Log.i(TAG, "onStateNormal " + " [" + this.hashCode() + "] "); + state = STATE_NORMAL; + cancelProgressTimer(); + if (mediaInterface != null) mediaInterface.release(); + } + + public void onStatePreparing() { + Log.i(TAG, "onStatePreparing " + " [" + this.hashCode() + "] "); + state = STATE_PREPARING; + resetProgressAndTime(); + } + + public void onStatePreparingPlaying() { + Log.i(TAG, "onStatePreparingPlaying " + " [" + this.hashCode() + "] "); + state = STATE_PREPARING_PLAYING; + } + + public void onStatePreparingChangeUrl() { + Log.i(TAG, "onStatePreparingChangeUrl " + " [" + this.hashCode() + "] "); + state = STATE_PREPARING_CHANGE_URL; + + releaseAllVideos(); + startVideo(); + +// mediaInterface.prepare(); + } + + public void changeUrl(JZDataSource jzDataSource, long seekToInAdvance) { + this.jzDataSource = jzDataSource; + this.seekToInAdvance = seekToInAdvance; + onStatePreparingChangeUrl(); + } + + public void onPrepared() { + Log.i(TAG, "onPrepared " + " [" + this.hashCode() + "] "); + state = STATE_PREPARED; + if (!preloading) { + mediaInterface.start();//这里原来是非县城 + preloading = false; + } + if (jzDataSource.getCurrentUrl().toString().toLowerCase().contains("mp3") || + jzDataSource.getCurrentUrl().toString().toLowerCase().contains("wma") || + jzDataSource.getCurrentUrl().toString().toLowerCase().contains("aac") || + jzDataSource.getCurrentUrl().toString().toLowerCase().contains("m4a") || + jzDataSource.getCurrentUrl().toString().toLowerCase().contains("wav")) { + onStatePlaying(); + } + } + + public void startPreloading() { + preloading = true; + startVideo(); + } + + /** + * 如果STATE_PREPARED就播放,如果没准备完成就走正常的播放函数startVideo(); + */ + public void startVideoAfterPreloading() { + if (state == STATE_PREPARED) { + mediaInterface.start(); + } else { + preloading = false; + startVideo(); + } + } + + public void onStatePlaying() { + Log.i(TAG, "onStatePlaying " + " [" + this.hashCode() + "] "); + if (state == STATE_PREPARED) {//如果是准备完成视频后第一次播放,先判断是否需要跳转进度。 + if (seekToInAdvance != 0) { + mediaInterface.seekTo(seekToInAdvance); + seekToInAdvance = 0; + } else { + long position = JZUtils.getSavedProgress(getContext(), jzDataSource.getCurrentUrl()); + if (position != 0) { + mediaInterface.seekTo(position);//这里为什么区分开呢,第一次的播放和resume播放是不一样的。 这里怎么区分是一个问题。然后 + } + } + } + state = STATE_PLAYING; + startProgressTimer(); + } + + public void onStatePause() { + Log.i(TAG, "onStatePause " + " [" + this.hashCode() + "] "); + state = STATE_PAUSE; + startProgressTimer(); + } + + public void onStateError() { + Log.i(TAG, "onStateError " + " [" + this.hashCode() + "] "); + state = STATE_ERROR; + cancelProgressTimer(); + } + + public void onStateAutoComplete() { + Log.i(TAG, "onStateAutoComplete " + " [" + this.hashCode() + "] "); + state = STATE_AUTO_COMPLETE; + cancelProgressTimer(); + progressBar.setProgress(100); + currentTimeTextView.setText(totalTimeTextView.getText()); + } + + public void onInfo(int what, int extra) { + Log.d(TAG, "onInfo what - " + what + " extra - " + extra); + if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) { + Log.d(TAG, "MEDIA_INFO_VIDEO_RENDERING_START"); + if (state == Jzvd.STATE_PREPARED + || state == Jzvd.STATE_PREPARING_CHANGE_URL + || state == Jzvd.STATE_PREPARING_PLAYING) { + onStatePlaying();//开始渲染图像,真正进入playing状态 + } + } else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_START) { + Log.d(TAG, "MEDIA_INFO_BUFFERING_START"); + backUpBufferState = state; + setState(STATE_PREPARING_PLAYING); + } else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END) { + Log.d(TAG, "MEDIA_INFO_BUFFERING_END"); + if (backUpBufferState != -1) { + setState(backUpBufferState); + backUpBufferState = -1; + } + } + } + + public void onError(int what, int extra) { + Log.e(TAG, "onError " + what + " - " + extra + " [" + this.hashCode() + "] "); + if (what != 38 && extra != -38 && what != -38 && extra != 38 && extra != -19) { + onStateError(); + mediaInterface.release(); + } + } + + public void onCompletion() { + Runtime.getRuntime().gc(); + Log.i(TAG, "onAutoCompletion " + " [" + this.hashCode() + "] "); + cancelProgressTimer(); + dismissBrightnessDialog(); + dismissProgressDialog(); + dismissVolumeDialog(); + onStateAutoComplete(); + mediaInterface.release(); + JZUtils.scanForActivity(getContext()).getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + JZUtils.saveProgress(getContext(), jzDataSource.getCurrentUrl(), 0); + + if (screen == SCREEN_FULLSCREEN) { + if (CONTAINER_LIST.size() == 0) { + clearFloatScreen();//直接进入全屏 + } else { + gotoNormalCompletion(); + } + } + } + + public void gotoNormalCompletion() { + gobakFullscreenTime = System.currentTimeMillis();//退出全屏 + ViewGroup vg = (ViewGroup) (JZUtils.scanForActivity(jzvdContext)).getWindow().getDecorView(); + vg.removeView(this); + textureViewContainer.removeView(textureView); + CONTAINER_LIST.getLast().removeViewAt(blockIndex);//remove block + CONTAINER_LIST.getLast().addView(this, blockIndex, blockLayoutParams); + CONTAINER_LIST.pop(); + setScreenNormal(); + JZUtils.showStatusBar(jzvdContext); + JZUtils.setRequestedOrientation(jzvdContext, NORMAL_ORIENTATION); + JZUtils.showSystemUI(jzvdContext); + } + + /** + * 多数表现为中断当前播放 + */ + public void reset() { + Log.i(TAG, "reset " + " [" + this.hashCode() + "] "); + if (state == STATE_PLAYING || state == STATE_PAUSE) { + long position = getCurrentPositionWhenPlaying(); + JZUtils.saveProgress(getContext(), jzDataSource.getCurrentUrl(), position); + } + cancelProgressTimer(); + dismissBrightnessDialog(); + dismissProgressDialog(); + dismissVolumeDialog(); + onStateNormal(); + textureViewContainer.removeAllViews(); + + AudioManager mAudioManager = (AudioManager) getApplicationContext().getSystemService(Context.AUDIO_SERVICE); + mAudioManager.abandonAudioFocus(onAudioFocusChangeListener); + JZUtils.scanForActivity(getContext()).getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + if (mediaInterface != null) mediaInterface.release(); + } + + /** + * 里面的的onState...()其实就是setState...(),因为要可以被复写,所以参考Activity的onCreate(),onState..()的方式看着舒服一些,老铁们有何高见。 + * + * @param state stateId + */ + public void setState(int state) { + switch (state) { + case STATE_NORMAL: + onStateNormal(); + break; + case STATE_PREPARING: + onStatePreparing(); + break; + case STATE_PREPARING_PLAYING: + onStatePreparingPlaying(); + break; + case STATE_PREPARING_CHANGE_URL: + onStatePreparingChangeUrl(); + break; + case STATE_PLAYING: + onStatePlaying(); + break; + case STATE_PAUSE: + onStatePause(); + break; + case STATE_ERROR: + onStateError(); + break; + case STATE_AUTO_COMPLETE: + onStateAutoComplete(); + break; + } + } + + public void setScreen(int screen) {//特殊的个别的进入全屏的按钮在这里设置 只有setup的时候能用上 + switch (screen) { + case SCREEN_NORMAL: + setScreenNormal(); + break; + case SCREEN_FULLSCREEN: + setScreenFullscreen(); + break; + case SCREEN_TINY: + setScreenTiny(); + break; + } + } + + public void startVideo() { + Log.d(TAG, "startVideo [" + this.hashCode() + "] "); + setCurrentJzvd(this); + try { + Constructor constructor = mediaInterfaceClass.getConstructor(Jzvd.class); + this.mediaInterface = constructor.newInstance(this); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + addTextureView(); + mAudioManager = (AudioManager) getApplicationContext().getSystemService(Context.AUDIO_SERVICE); + mAudioManager.requestAudioFocus(onAudioFocusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); + JZUtils.scanForActivity(getContext()).getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + onStatePreparing(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (screen == SCREEN_FULLSCREEN || screen == SCREEN_TINY) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + return; + } + if (widthRatio != 0 && heightRatio != 0) { + int specWidth = MeasureSpec.getSize(widthMeasureSpec); + int specHeight = (int) ((specWidth * (float) heightRatio) / widthRatio); + setMeasuredDimension(specWidth, specHeight); + + int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(specWidth, MeasureSpec.EXACTLY); + int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(specHeight, MeasureSpec.EXACTLY); + getChildAt(0).measure(childWidthMeasureSpec, childHeightMeasureSpec); + } else { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + } + + public void addTextureView() { + Log.d(TAG, "addTextureView [" + this.hashCode() + "] "); + if (textureView != null) textureViewContainer.removeView(textureView); + textureView = new JZTextureView(getContext().getApplicationContext()); + textureView.setSurfaceTextureListener(mediaInterface); + + FrameLayout.LayoutParams layoutParams = + new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, + Gravity.CENTER); + textureViewContainer.addView(textureView, layoutParams); + } + + public void clearFloatScreen() { + JZUtils.showStatusBar(getContext()); + JZUtils.setRequestedOrientation(getContext(), NORMAL_ORIENTATION); + JZUtils.showSystemUI(getContext()); + + ViewGroup vg = (ViewGroup) (JZUtils.scanForActivity(getContext())).getWindow().getDecorView(); + vg.removeView(this); + if (mediaInterface != null) mediaInterface.release(); + CURRENT_JZVD = null; + } + + public void onVideoSizeChanged(int width, int height) { + Log.i(TAG, "onVideoSizeChanged " + " [" + this.hashCode() + "] "); + if (textureView != null) { + if (videoRotation != 0) { + textureView.setRotation(videoRotation); + } + textureView.setVideoSize(width, height); + } + } + + public void startProgressTimer() { + Log.i(TAG, "startProgressTimer: " + " [" + this.hashCode() + "] "); + cancelProgressTimer(); + UPDATE_PROGRESS_TIMER = new Timer(); + mProgressTimerTask = new ProgressTimerTask(); + UPDATE_PROGRESS_TIMER.schedule(mProgressTimerTask, 0, 300); + } + + public void cancelProgressTimer() { + if (UPDATE_PROGRESS_TIMER != null) { + UPDATE_PROGRESS_TIMER.cancel(); + } + if (mProgressTimerTask != null) { + mProgressTimerTask.cancel(); + } + } + + public void onProgress(int progress, long position, long duration) { +// Log.d(TAG, "onProgress: progress=" + progress + " position=" + position + " duration=" + duration); + mCurrentPosition = position; + if (!mTouchingProgressBar) { + if (seekToManulPosition != -1) { + if (seekToManulPosition > progress) { + return; + } else { + seekToManulPosition = -1;//这个关键帧有没有必要做 + } + } else { + if (progress != 0) progressBar.setProgress(progress); + } + } + if (position != 0) currentTimeTextView.setText(JZUtils.stringForTime(position)); + totalTimeTextView.setText(JZUtils.stringForTime(duration)); + } + + public void setBufferProgress(int bufferProgress) { + if (bufferProgress != 0) progressBar.setSecondaryProgress(bufferProgress); + } + + public void resetProgressAndTime() { + mCurrentPosition = 0; + progressBar.setProgress(0); + progressBar.setSecondaryProgress(0); + currentTimeTextView.setText(JZUtils.stringForTime(0)); + totalTimeTextView.setText(JZUtils.stringForTime(0)); + } + + public long getCurrentPositionWhenPlaying() { + long position = 0; + if (state == STATE_PLAYING || state == STATE_PAUSE || state == STATE_PREPARING_PLAYING) { + try { + position = mediaInterface.getCurrentPosition(); + } catch (IllegalStateException e) { + e.printStackTrace(); + return position; + } + } + return position; + } + + public long getDuration() { + long duration = 0; + try { + duration = mediaInterface.getDuration(); + } catch (IllegalStateException e) { + e.printStackTrace(); + return duration; + } + return duration; + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + Log.i(TAG, "bottomProgress onStartTrackingTouch [" + this.hashCode() + "] "); + cancelProgressTimer(); + ViewParent vpdown = getParent(); + while (vpdown != null) { + vpdown.requestDisallowInterceptTouchEvent(true); + vpdown = vpdown.getParent(); + } + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + Log.i(TAG, "bottomProgress onStopTrackingTouch [" + this.hashCode() + "] "); + startProgressTimer(); + ViewParent vpup = getParent(); + while (vpup != null) { + vpup.requestDisallowInterceptTouchEvent(false); + vpup = vpup.getParent(); + } + if (state != STATE_PLAYING && + state != STATE_PAUSE) return; + long time = seekBar.getProgress() * getDuration() / 100; + seekToManulPosition = seekBar.getProgress(); + mediaInterface.seekTo(time); + Log.i(TAG, "seekTo " + time + " [" + this.hashCode() + "] "); + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + if (fromUser) { + //设置这个progres对应的时间,给textview + long duration = getDuration(); + currentTimeTextView.setText(JZUtils.stringForTime(progress * duration / 100)); + } + } + + public void cloneAJzvd(ViewGroup vg) { + try { + Constructor constructor = (Constructor) Jzvd.this.getClass().getConstructor(Context.class); + Jzvd jzvd = constructor.newInstance(getContext()); + jzvd.setId(getId()); + jzvd.setMinimumWidth(blockWidth); + jzvd.setMinimumHeight(blockHeight); + vg.addView(jzvd, blockIndex, blockLayoutParams); + jzvd.setUp(jzDataSource.cloneMe(), SCREEN_NORMAL, mediaInterfaceClass); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + } + + /** + * 如果全屏或者返回全屏的视图有问题,复写这两个函数gotoScreenNormal(),根据自己布局的情况重新布局。 + */ + public void gotoFullscreen() { + gotoFullscreenTime = System.currentTimeMillis(); + ViewGroup vg = (ViewGroup) getParent(); + jzvdContext = vg.getContext(); + blockLayoutParams = getLayoutParams(); + blockIndex = vg.indexOfChild(this); + blockWidth = getWidth(); + blockHeight = getHeight(); + + vg.removeView(this); + cloneAJzvd(vg); + CONTAINER_LIST.add(vg); + vg = (ViewGroup) (JZUtils.scanForActivity(jzvdContext)).getWindow().getDecorView(); + + ViewGroup.LayoutParams fullLayout = new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + vg.addView(this, fullLayout); + + setScreenFullscreen(); + JZUtils.hideStatusBar(jzvdContext); + JZUtils.setRequestedOrientation(jzvdContext, FULLSCREEN_ORIENTATION); + JZUtils.hideSystemUI(jzvdContext);//华为手机和有虚拟键的手机全屏时可隐藏虚拟键 issue:1326 + + } + + public void gotoNormalScreen() {//goback本质上是goto + gobakFullscreenTime = System.currentTimeMillis();//退出全屏 + ViewGroup vg = (ViewGroup) (JZUtils.scanForActivity(jzvdContext)).getWindow().getDecorView(); + vg.removeView(this); +// CONTAINER_LIST.getLast().removeAllViews(); + CONTAINER_LIST.getLast().removeViewAt(blockIndex);//remove block + CONTAINER_LIST.getLast().addView(this, blockIndex, blockLayoutParams); + CONTAINER_LIST.pop(); + + setScreenNormal();//这块可以放到jzvd中 + JZUtils.showStatusBar(jzvdContext); + JZUtils.setRequestedOrientation(jzvdContext, NORMAL_ORIENTATION); + JZUtils.showSystemUI(jzvdContext); + } + + public void setScreenNormal() {//TODO 这块不对呀,还需要改进,设置flag之后要设置ui,不设置ui这么写没意义呀 + screen = SCREEN_NORMAL; + } + + public void setScreenFullscreen() { + screen = SCREEN_FULLSCREEN; + } + + public void setScreenTiny() { + screen = SCREEN_TINY; + } + + // //重力感应的时候调用的函数,、、这里有重力感应的参数,暂时不能删除 + public void autoFullscreen(float x) {//TODO写道demo中 + if (CURRENT_JZVD != null + && (state == STATE_PLAYING || state == STATE_PAUSE) + && screen != SCREEN_FULLSCREEN + && screen != SCREEN_TINY) { + if (x > 0) { + JZUtils.setRequestedOrientation(getContext(), ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + } else { + JZUtils.setRequestedOrientation(getContext(), ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE); + } + gotoFullscreen(); + } + } + + public void autoQuitFullscreen() { + if ((System.currentTimeMillis() - lastAutoFullscreenTime) > 2000 +// && CURRENT_JZVD != null + && state == STATE_PLAYING + && screen == SCREEN_FULLSCREEN) { + lastAutoFullscreenTime = System.currentTimeMillis(); + backPress(); + } + } + + public void onSeekComplete() { + + } + + public void showWifiDialog() { + } + + public void showProgressDialog(float deltaX, + String seekTime, long seekTimePosition, + String totalTime, long totalTimeDuration) { + } + + public void dismissProgressDialog() { + + } + + public void showVolumeDialog(float deltaY, int volumePercent) { + + } + + public void dismissVolumeDialog() { + + } + + public void showBrightnessDialog(int brightnessPercent) { + + } + + public void dismissBrightnessDialog() { + + } + + public Context getApplicationContext() {//这个函数必要吗 + Context context = getContext(); + if (context != null) { + Context applicationContext = context.getApplicationContext(); + if (applicationContext != null) { + return applicationContext; + } + } + return context; + } + + public static class JZAutoFullscreenListener implements SensorEventListener { + @Override + public void onSensorChanged(SensorEvent event) {//可以得到传感器实时测量出来的变化值 + final float x = event.values[SensorManager.DATA_X]; + float y = event.values[SensorManager.DATA_Y]; + float z = event.values[SensorManager.DATA_Z]; + //过滤掉用力过猛会有一个反向的大数值 + if (x < -12 || x > 12) { + if ((System.currentTimeMillis() - lastAutoFullscreenTime) > 2000) { + if (Jzvd.CURRENT_JZVD != null) Jzvd.CURRENT_JZVD.autoFullscreen(x); + lastAutoFullscreenTime = System.currentTimeMillis(); + } + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + } + } + + public class ProgressTimerTask extends TimerTask { + @Override + public void run() { + if (state == STATE_PLAYING || state == STATE_PAUSE || state == STATE_PREPARING_PLAYING) { +// Log.v(TAG, "onProgressUpdate " + "[" + this.hashCode() + "] "); + post(() -> { + long position = getCurrentPositionWhenPlaying(); + long duration = getDuration(); + int progress = (int) (position * 100 / (duration == 0 ? 1 : duration)); + onProgress(progress, position, duration); + }); + } + } + } + +} diff --git a/library/src/main/java/cn/jzvd/JzvdStd.java b/library/src/main/java/cn/jzvd/JzvdStd.java new file mode 100644 index 0000000..98812b0 --- /dev/null +++ b/library/src/main/java/cn/jzvd/JzvdStd.java @@ -0,0 +1,979 @@ +package cn.jzvd; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.graphics.Color; +import android.net.ConnectivityManager; +import android.util.AttributeSet; +import android.util.Log; +import android.view.GestureDetector; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.PopupWindow; +import android.widget.ProgressBar; +import android.widget.SeekBar; +import android.widget.TextView; +import android.widget.Toast; + +import java.text.SimpleDateFormat; +import java.util.ArrayDeque; +import java.util.Date; +import java.util.Timer; +import java.util.TimerTask; + +/** + * Created by Nathen + * On 2016/04/18 16:15 + */ +public class JzvdStd extends Jzvd { + + public static long LAST_GET_BATTERYLEVEL_TIME = 0; + public static int LAST_GET_BATTERYLEVEL_PERCENT = 70; + protected static Timer DISMISS_CONTROL_VIEW_TIMER; + + public ImageView backButton; + public ProgressBar bottomProgressBar, loadingProgressBar; + public TextView titleTextView; + public ImageView posterImageView; + public ImageView tinyBackImageView; + public LinearLayout batteryTimeLayout; + public ImageView batteryLevel; + public TextView videoCurrentTime; + public TextView replayTextView; + public TextView clarity; + public PopupWindow clarityPopWindow; + public TextView mRetryBtn; + public LinearLayout mRetryLayout; + public BroadcastReceiver battertReceiver = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (Intent.ACTION_BATTERY_CHANGED.equals(action)) { + int level = intent.getIntExtra("level", 0); + int scale = intent.getIntExtra("scale", 100); + int percent = level * 100 / scale; + LAST_GET_BATTERYLEVEL_PERCENT = percent; + setBatteryLevel(); + try { + jzvdContext.unregisterReceiver(battertReceiver); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + }; + protected DismissControlViewTimerTask mDismissControlViewTimerTask; + protected Dialog mProgressDialog; + protected ProgressBar mDialogProgressBar; + protected TextView mDialogSeekTime; + protected TextView mDialogTotalTime; + protected ImageView mDialogIcon; + protected Dialog mVolumeDialog; + protected ProgressBar mDialogVolumeProgressBar; + protected TextView mDialogVolumeTextView; + protected ImageView mDialogVolumeImageView; + protected Dialog mBrightnessDialog; + protected ProgressBar mDialogBrightnessProgressBar; + protected TextView mDialogBrightnessTextView; + protected boolean mIsWifi; + public BroadcastReceiver wifiReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) { + boolean isWifi = JZUtils.isWifiConnected(context); + if (mIsWifi == isWifi) return; + mIsWifi = isWifi; + if (!mIsWifi && !WIFI_TIP_DIALOG_SHOWED && state == STATE_PLAYING) { + startButton.performClick(); + showWifiDialog(); + } + } + } + }; + protected ArrayDeque delayTask = new ArrayDeque<>(); + + public JzvdStd(Context context) { + super(context); + } + + public JzvdStd(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public void init(Context context) { + super.init(context); + batteryTimeLayout = findViewById(R.id.battery_time_layout); + bottomProgressBar = findViewById(R.id.bottom_progress); + titleTextView = findViewById(R.id.title); + backButton = findViewById(R.id.back); + posterImageView = findViewById(R.id.poster); + loadingProgressBar = findViewById(R.id.loading); + tinyBackImageView = findViewById(R.id.back_tiny); + batteryLevel = findViewById(R.id.battery_level); + videoCurrentTime = findViewById(R.id.video_current_time); + replayTextView = findViewById(R.id.replay_text); + clarity = findViewById(R.id.clarity); + mRetryBtn = findViewById(R.id.retry_btn); + mRetryLayout = findViewById(R.id.retry_layout); + + if (batteryTimeLayout == null) { + batteryTimeLayout = new LinearLayout(context); + } + if (bottomProgressBar == null) { + bottomProgressBar = new ProgressBar(context); + } + if (titleTextView == null) { + titleTextView = new TextView(context); + } + if (backButton == null) { + backButton = new ImageView(context); + } + if (posterImageView == null) { + posterImageView = new ImageView(context); + } + if (loadingProgressBar == null) { + loadingProgressBar = new ProgressBar(context); + } + if (tinyBackImageView == null) { + tinyBackImageView = new ImageView(context); + } + if (batteryLevel == null) { + batteryLevel = new ImageView(context); + } + if (videoCurrentTime == null) { + videoCurrentTime = new TextView(context); + } + if (replayTextView == null) { + replayTextView = new TextView(context); + } + if (clarity == null) { + clarity = new TextView(context); + } + if (mRetryBtn == null) { + mRetryBtn = new TextView(context); + } + if (mRetryLayout == null) { + mRetryLayout = new LinearLayout(context); + } + + posterImageView.setOnClickListener(this); + backButton.setOnClickListener(this); + tinyBackImageView.setOnClickListener(this); + clarity.setOnClickListener(this); + mRetryBtn.setOnClickListener(this); + } + + public void setUp(JZDataSource jzDataSource, int screen, Class mediaInterfaceClass) { + if ((System.currentTimeMillis() - gobakFullscreenTime) < 200) { + return; + } + + if ((System.currentTimeMillis() - gotoFullscreenTime) < 200) { + return; + } + + + super.setUp(jzDataSource, screen, mediaInterfaceClass); + titleTextView.setText(jzDataSource.title); + setScreen(screen); + } + + @Override + public void changeUrl(JZDataSource jzDataSource, long seekToInAdvance) { + super.changeUrl(jzDataSource, seekToInAdvance); + titleTextView.setText(jzDataSource.title); + } + + public void changeStartButtonSize(int size) { + ViewGroup.LayoutParams lp = startButton.getLayoutParams(); + lp.height = size; + lp.width = size; + lp = loadingProgressBar.getLayoutParams(); + lp.height = size; + lp.width = size; + } + + @Override + public int getLayoutId() { + return R.layout.jz_layout_std; + } + + @Override + public void onStateNormal() { + super.onStateNormal(); + changeUiToNormal(); + } + + @Override + public void onStatePreparing() { + super.onStatePreparing(); + changeUiToPreparing(); + } + + public void onStatePreparingPlaying() { + super.onStatePreparingPlaying(); + changeUIToPreparingPlaying(); + } + + public void onStatePreparingChangeUrl() { + super.onStatePreparingChangeUrl(); + changeUIToPreparingChangeUrl(); + } + + @Override + public void onStatePlaying() { + super.onStatePlaying(); + changeUiToPlayingClear(); + } + + @Override + public void onStatePause() { + super.onStatePause(); + changeUiToPauseShow(); + cancelDismissControlViewTimer(); + } + + @Override + public void onStateError() { + super.onStateError(); + changeUiToError(); + } + + @Override + public void onStateAutoComplete() { + super.onStateAutoComplete(); + changeUiToComplete(); + cancelDismissControlViewTimer(); + bottomProgressBar.setProgress(100); + } + + @Override + public void startVideo() { + super.startVideo(); + registerWifiListener(getApplicationContext()); + } + + /** + * 双击 + */ + protected GestureDetector gestureDetector = new GestureDetector(getContext().getApplicationContext(), new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onDoubleTap(MotionEvent e) { + if (state == STATE_PLAYING || state == STATE_PAUSE) { + Log.d(TAG, "doublClick [" + this.hashCode() + "] "); + startButton.performClick(); + } + return super.onDoubleTap(e); + } + + @Override + public boolean onSingleTapConfirmed(MotionEvent e) { + if (!mChangePosition && !mChangeVolume) { + onClickUiToggle(); + } + return super.onSingleTapConfirmed(e); + } + + @Override + public void onLongPress(MotionEvent e) { + super.onLongPress(e); + } + }); + + @Override + public boolean onTouch(View v, MotionEvent event) { + int id = v.getId(); + if (id == R.id.surface_container) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_MOVE: + break; + case MotionEvent.ACTION_UP: + startDismissControlViewTimer(); + if (mChangePosition) { + long duration = getDuration(); + int progress = (int) (mSeekTimePosition * 100 / (duration == 0 ? 1 : duration)); + bottomProgressBar.setProgress(progress); + } + break; + } + gestureDetector.onTouchEvent(event); + } else if (id == R.id.bottom_seek_progress) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + cancelDismissControlViewTimer(); + break; + case MotionEvent.ACTION_UP: + startDismissControlViewTimer(); + break; + } + } + return super.onTouch(v, event); + } + + @Override + public void onClick(View v) { + super.onClick(v); + int i = v.getId(); + if (i == R.id.poster) { + clickPoster(); + } else if (i == R.id.surface_container) { + clickSurfaceContainer(); + if (clarityPopWindow != null) { + clarityPopWindow.dismiss(); + } + } else if (i == R.id.back) { + clickBack(); + } else if (i == R.id.back_tiny) { + clickBackTiny(); + } else if (i == R.id.clarity) { + clickClarity(); + } else if (i == R.id.retry_btn) { + clickRetryBtn(); + } + } + + protected void clickRetryBtn() { + if (jzDataSource.urlsMap.isEmpty() || jzDataSource.getCurrentUrl() == null) { + Toast.makeText(jzvdContext, getResources().getString(R.string.no_url), Toast.LENGTH_SHORT).show(); + return; + } + if (!jzDataSource.getCurrentUrl().toString().startsWith("file") && ! + jzDataSource.getCurrentUrl().toString().startsWith("/") && + !JZUtils.isWifiConnected(jzvdContext) && !WIFI_TIP_DIALOG_SHOWED) { + showWifiDialog(); + return; + } + seekToInAdvance = mCurrentPosition; + startVideo(); + } + + protected void clickClarity() { + onCLickUiToggleToClear(); + + LayoutInflater inflater = (LayoutInflater) jzvdContext + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + final LinearLayout layout = (LinearLayout) inflater.inflate(R.layout.jz_layout_clarity, null); + + OnClickListener mQualityListener = v1 -> { + int index = (int) v1.getTag(); + +// this.seekToInAdvance = getCurrentPositionWhenPlaying(); + jzDataSource.currentUrlIndex = index; +// onStatePreparingChangeUrl(); + + changeUrl(jzDataSource, getCurrentPositionWhenPlaying()); + + clarity.setText(jzDataSource.getCurrentKey().toString()); + for (int j = 0; j < layout.getChildCount(); j++) {//设置点击之后的颜色 + if (j == jzDataSource.currentUrlIndex) { + ((TextView) layout.getChildAt(j)).setTextColor(Color.parseColor("#fff85959")); + } else { + ((TextView) layout.getChildAt(j)).setTextColor(Color.parseColor("#ffffff")); + } + } + if (clarityPopWindow != null) { + clarityPopWindow.dismiss(); + } + }; + + for (int j = 0; j < jzDataSource.urlsMap.size(); j++) { + String key = jzDataSource.getKeyFromDataSource(j); + TextView clarityItem = (TextView) View.inflate(jzvdContext, R.layout.jz_layout_clarity_item, null); + clarityItem.setText(key); + clarityItem.setTag(j); + layout.addView(clarityItem, j); + clarityItem.setOnClickListener(mQualityListener); + if (j == jzDataSource.currentUrlIndex) { + clarityItem.setTextColor(Color.parseColor("#fff85959")); + } + } + + clarityPopWindow = new PopupWindow(layout, JZUtils.dip2px(jzvdContext, 240), LayoutParams.MATCH_PARENT, true); + clarityPopWindow.setContentView(layout); + clarityPopWindow.setAnimationStyle(R.style.pop_animation); + clarityPopWindow.showAtLocation(textureViewContainer, Gravity.END, 0, 0); +// int offsetX = clarity.getMeasuredWidth() / 3; +// int offsetY = clarity.getMeasuredHeight() / 3; +// clarityPopWindow.update(clarity, -offsetX, -offsetY, Math.round(layout.getMeasuredWidth() * 2), layout.getMeasuredHeight()); + } + + protected void clickBackTiny() { + clearFloatScreen(); + } + + protected void clickBack() { + backPress(); + } + + protected void clickSurfaceContainer() { + startDismissControlViewTimer(); + } + + protected void clickPoster() { + if (jzDataSource == null || jzDataSource.urlsMap.isEmpty() || jzDataSource.getCurrentUrl() == null) { + Toast.makeText(jzvdContext, getResources().getString(R.string.no_url), Toast.LENGTH_SHORT).show(); + return; + } + if (state == STATE_NORMAL) { + if (!jzDataSource.getCurrentUrl().toString().startsWith("file") && + !jzDataSource.getCurrentUrl().toString().startsWith("/") && + !JZUtils.isWifiConnected(jzvdContext) && !WIFI_TIP_DIALOG_SHOWED) { + showWifiDialog(); + return; + } + startVideo(); + } else if (state == STATE_AUTO_COMPLETE) { + onClickUiToggle(); + } + } + + @Override + public void setScreenNormal() { + super.setScreenNormal(); + fullscreenButton.setImageResource(R.drawable.jz_enlarge); + backButton.setVisibility(View.GONE); + tinyBackImageView.setVisibility(View.INVISIBLE); + changeStartButtonSize((int) getResources().getDimension(R.dimen.jz_start_button_w_h_normal)); + batteryTimeLayout.setVisibility(View.GONE); + clarity.setVisibility(View.GONE); + } + + @Override + public void setScreenFullscreen() { + super.setScreenFullscreen(); + //进入全屏之后要保证原来的播放状态和ui状态不变,改变个别的ui + fullscreenButton.setImageResource(R.drawable.jz_shrink); + backButton.setVisibility(View.VISIBLE); + tinyBackImageView.setVisibility(View.INVISIBLE); + batteryTimeLayout.setVisibility(View.VISIBLE); + if (jzDataSource.urlsMap.size() == 1) { + clarity.setVisibility(GONE); + } else { + clarity.setText(jzDataSource.getCurrentKey().toString()); + clarity.setVisibility(View.VISIBLE); + } + changeStartButtonSize((int) getResources().getDimension(R.dimen.jz_start_button_w_h_fullscreen)); + setSystemTimeAndBattery(); + } + + @Override + public void setScreenTiny() { + super.setScreenTiny(); + tinyBackImageView.setVisibility(View.VISIBLE); + setAllControlsVisiblity(View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, + View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE); + batteryTimeLayout.setVisibility(View.GONE); + clarity.setVisibility(View.GONE); + } + + @Override + public void showWifiDialog() { + super.showWifiDialog(); + AlertDialog.Builder builder = new AlertDialog.Builder(jzvdContext); + builder.setMessage(getResources().getString(R.string.tips_not_wifi)); + builder.setPositiveButton(getResources().getString(R.string.tips_not_wifi_confirm), (dialog, which) -> { + dialog.dismiss(); + WIFI_TIP_DIALOG_SHOWED = true; + if (state == STATE_PAUSE) { + startButton.performClick(); + } else { + startVideo(); + } + + }); + builder.setNegativeButton(getResources().getString(R.string.tips_not_wifi_cancel), (dialog, which) -> { + dialog.dismiss(); + releaseAllVideos(); + clearFloatScreen(); + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + dialog.dismiss(); + releaseAllVideos(); + clearFloatScreen(); + } + }); + + builder.create().show(); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + super.onStartTrackingTouch(seekBar); + cancelDismissControlViewTimer(); + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + super.onStopTrackingTouch(seekBar); + startDismissControlViewTimer(); + } + + public void onClickUiToggle() {//这是事件 + if (bottomContainer.getVisibility() != View.VISIBLE) { + setSystemTimeAndBattery(); + clarity.setText(jzDataSource.getCurrentKey().toString()); + } + if (state == STATE_PREPARING) { + changeUiToPreparing(); + if (bottomContainer.getVisibility() == View.VISIBLE) { + } else { + setSystemTimeAndBattery(); + } + } else if (state == STATE_PLAYING) { + if (bottomContainer.getVisibility() == View.VISIBLE) { + changeUiToPlayingClear(); + } else { + changeUiToPlayingShow(); + } + } else if (state == STATE_PAUSE) { + if (bottomContainer.getVisibility() == View.VISIBLE) { + changeUiToPauseClear(); + } else { + changeUiToPauseShow(); + } + } + } + + public void setSystemTimeAndBattery() { + SimpleDateFormat dateFormater = new SimpleDateFormat("HH:mm"); + Date date = new Date(); + videoCurrentTime.setText(dateFormater.format(date)); + if ((System.currentTimeMillis() - LAST_GET_BATTERYLEVEL_TIME) > 30000) { + LAST_GET_BATTERYLEVEL_TIME = System.currentTimeMillis(); + jzvdContext.registerReceiver( + battertReceiver, + new IntentFilter(Intent.ACTION_BATTERY_CHANGED) + ); + } else { + setBatteryLevel(); + } + } + + public void setBatteryLevel() { + int percent = LAST_GET_BATTERYLEVEL_PERCENT; + if (percent < 15) { + batteryLevel.setBackgroundResource(R.drawable.jz_battery_level_10); + } else if (percent >= 15 && percent < 40) { + batteryLevel.setBackgroundResource(R.drawable.jz_battery_level_30); + } else if (percent >= 40 && percent < 60) { + batteryLevel.setBackgroundResource(R.drawable.jz_battery_level_50); + } else if (percent >= 60 && percent < 80) { + batteryLevel.setBackgroundResource(R.drawable.jz_battery_level_70); + } else if (percent >= 80 && percent < 95) { + batteryLevel.setBackgroundResource(R.drawable.jz_battery_level_90); + } else if (percent >= 95 && percent <= 100) { + batteryLevel.setBackgroundResource(R.drawable.jz_battery_level_100); + } + } + + //** 和onClickUiToggle重复,要干掉 + public void onCLickUiToggleToClear() { + if (state == STATE_PREPARING) { + if (bottomContainer.getVisibility() == View.VISIBLE) { + changeUiToPreparing(); + } else { + } + } else if (state == STATE_PLAYING) { + if (bottomContainer.getVisibility() == View.VISIBLE) { + changeUiToPlayingClear(); + } else { + } + } else if (state == STATE_PAUSE) { + if (bottomContainer.getVisibility() == View.VISIBLE) { + changeUiToPauseClear(); + } else { + } + } else if (state == STATE_AUTO_COMPLETE) { + if (bottomContainer.getVisibility() == View.VISIBLE) { + changeUiToComplete(); + } else { + } + } + } + + @Override + public void onProgress(int progress, long position, long duration) { + super.onProgress(progress, position, duration); + if (progress != 0) bottomProgressBar.setProgress(progress); + } + + @Override + public void setBufferProgress(int bufferProgress) { + super.setBufferProgress(bufferProgress); + if (bufferProgress != 0) bottomProgressBar.setSecondaryProgress(bufferProgress); + } + + @Override + public void resetProgressAndTime() { + super.resetProgressAndTime(); + bottomProgressBar.setProgress(0); + bottomProgressBar.setSecondaryProgress(0); + } + + public void changeUiToNormal() { + switch (screen) { + case SCREEN_NORMAL: + case SCREEN_FULLSCREEN: + setAllControlsVisiblity(View.VISIBLE, View.INVISIBLE, View.VISIBLE, + View.INVISIBLE, View.VISIBLE, View.INVISIBLE, View.INVISIBLE); + updateStartImage(); + break; + case SCREEN_TINY: + break; + } + } + + public void changeUiToPreparing() { + switch (screen) { + case SCREEN_NORMAL: + case SCREEN_FULLSCREEN: + setAllControlsVisiblity(View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, + View.VISIBLE, View.VISIBLE, View.INVISIBLE, View.INVISIBLE); + updateStartImage(); + break; + case SCREEN_TINY: + break; + } + } + + public void changeUIToPreparingPlaying() { + switch (screen) { + case SCREEN_NORMAL: + case SCREEN_FULLSCREEN: + setAllControlsVisiblity(View.VISIBLE, View.VISIBLE, View.INVISIBLE, + View.VISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE); + updateStartImage(); + break; + case SCREEN_TINY: + break; + } + } + + public void changeUIToPreparingChangeUrl() { + switch (screen) { + case SCREEN_NORMAL: + case SCREEN_FULLSCREEN: + setAllControlsVisiblity(View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, + View.VISIBLE, View.VISIBLE, View.INVISIBLE, View.INVISIBLE); + updateStartImage(); + break; + case SCREEN_TINY: + break; + } + } + + public void changeUiToPlayingShow() { + switch (screen) { + case SCREEN_NORMAL: + case SCREEN_FULLSCREEN: + setAllControlsVisiblity(View.VISIBLE, View.VISIBLE, View.VISIBLE, + View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE); + updateStartImage(); + break; + case SCREEN_TINY: + break; + } + + } + + public void changeUiToPlayingClear() { + switch (screen) { + case SCREEN_NORMAL: + case SCREEN_FULLSCREEN: + setAllControlsVisiblity(View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, + View.INVISIBLE, View.INVISIBLE, View.VISIBLE, View.INVISIBLE); + break; + case SCREEN_TINY: + break; + } + + } + + public void changeUiToPauseShow() { + switch (screen) { + case SCREEN_NORMAL: + case SCREEN_FULLSCREEN: + setAllControlsVisiblity(View.VISIBLE, View.VISIBLE, View.VISIBLE, + View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.INVISIBLE); + updateStartImage(); + break; + case SCREEN_TINY: + break; + } + } + + public void changeUiToPauseClear() { + switch (screen) { + case SCREEN_NORMAL: + case SCREEN_FULLSCREEN: + setAllControlsVisiblity(View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, + View.INVISIBLE, View.INVISIBLE, View.VISIBLE, View.INVISIBLE); + break; + case SCREEN_TINY: + break; + } + + } + + public void changeUiToComplete() { + switch (screen) { + case SCREEN_NORMAL: + case SCREEN_FULLSCREEN: + setAllControlsVisiblity(View.VISIBLE, View.INVISIBLE, View.VISIBLE, + View.INVISIBLE, View.VISIBLE, View.INVISIBLE, View.INVISIBLE); + updateStartImage(); + break; + case SCREEN_TINY: + break; + } + + } + + public void changeUiToError() { + switch (screen) { + case SCREEN_NORMAL: + setAllControlsVisiblity(View.INVISIBLE, View.INVISIBLE, View.VISIBLE, + View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.VISIBLE); + updateStartImage(); + break; + case SCREEN_FULLSCREEN: + setAllControlsVisiblity(View.VISIBLE, View.INVISIBLE, View.VISIBLE, + View.INVISIBLE, View.INVISIBLE, View.INVISIBLE, View.VISIBLE); + updateStartImage(); + break; + case SCREEN_TINY: + break; + } + + } + + public void setAllControlsVisiblity(int topCon, int bottomCon, int startBtn, int loadingPro, + int posterImg, int bottomPro, int retryLayout) { + topContainer.setVisibility(topCon); + bottomContainer.setVisibility(bottomCon); + startButton.setVisibility(startBtn); + loadingProgressBar.setVisibility(loadingPro); + posterImageView.setVisibility(posterImg); + bottomProgressBar.setVisibility(bottomPro); + mRetryLayout.setVisibility(retryLayout); + } + + public void updateStartImage() { + if (state == STATE_PLAYING) { + startButton.setVisibility(VISIBLE); + startButton.setImageResource(R.drawable.jz_click_pause_selector); + replayTextView.setVisibility(GONE); + } else if (state == STATE_ERROR) { + startButton.setVisibility(INVISIBLE); + replayTextView.setVisibility(GONE); + } else if (state == STATE_AUTO_COMPLETE) { + startButton.setVisibility(VISIBLE); + startButton.setImageResource(R.drawable.jz_click_replay_selector); + replayTextView.setVisibility(VISIBLE); + } else { + startButton.setImageResource(R.drawable.jz_click_play_selector); + replayTextView.setVisibility(GONE); + } + } + + @Override + public void showProgressDialog(float deltaX, String seekTime, long seekTimePosition, String totalTime, long totalTimeDuration) { + super.showProgressDialog(deltaX, seekTime, seekTimePosition, totalTime, totalTimeDuration); + if (mProgressDialog == null) { + View localView = LayoutInflater.from(jzvdContext).inflate(R.layout.jz_dialog_progress, null); + mDialogProgressBar = localView.findViewById(R.id.duration_progressbar); + mDialogSeekTime = localView.findViewById(R.id.tv_current); + mDialogTotalTime = localView.findViewById(R.id.tv_duration); + mDialogIcon = localView.findViewById(R.id.duration_image_tip); + mProgressDialog = createDialogWithView(localView); + } + if (!mProgressDialog.isShowing()) { + mProgressDialog.show(); + } + + mDialogSeekTime.setText(seekTime); + mDialogTotalTime.setText(" / " + totalTime); + mDialogProgressBar.setProgress(totalTimeDuration <= 0 ? 0 : (int) (seekTimePosition * 100 / totalTimeDuration)); + if (deltaX > 0) { + mDialogIcon.setBackgroundResource(R.drawable.jz_forward_icon); + } else { + mDialogIcon.setBackgroundResource(R.drawable.jz_backward_icon); + } + onCLickUiToggleToClear(); + } + + @Override + public void dismissProgressDialog() { + super.dismissProgressDialog(); + if (mProgressDialog != null) { + mProgressDialog.dismiss(); + } + } + + @Override + public void showVolumeDialog(float deltaY, int volumePercent) { + super.showVolumeDialog(deltaY, volumePercent); + if (mVolumeDialog == null) { + View localView = LayoutInflater.from(jzvdContext).inflate(R.layout.jz_dialog_volume, null); + mDialogVolumeImageView = localView.findViewById(R.id.volume_image_tip); + mDialogVolumeTextView = localView.findViewById(R.id.tv_volume); + mDialogVolumeProgressBar = localView.findViewById(R.id.volume_progressbar); + mVolumeDialog = createDialogWithView(localView); + } + if (!mVolumeDialog.isShowing()) { + mVolumeDialog.show(); + } + if (volumePercent <= 0) { + mDialogVolumeImageView.setBackgroundResource(R.drawable.jz_close_volume); + } else { + mDialogVolumeImageView.setBackgroundResource(R.drawable.jz_add_volume); + } + if (volumePercent > 100) { + volumePercent = 100; + } else if (volumePercent < 0) { + volumePercent = 0; + } + mDialogVolumeTextView.setText(volumePercent + "%"); + mDialogVolumeProgressBar.setProgress(volumePercent); + onCLickUiToggleToClear(); + } + + @Override + public void dismissVolumeDialog() { + super.dismissVolumeDialog(); + if (mVolumeDialog != null) { + mVolumeDialog.dismiss(); + } + } + + @Override + public void showBrightnessDialog(int brightnessPercent) { + super.showBrightnessDialog(brightnessPercent); + if (mBrightnessDialog == null) { + View localView = LayoutInflater.from(jzvdContext).inflate(R.layout.jz_dialog_brightness, null); + mDialogBrightnessTextView = localView.findViewById(R.id.tv_brightness); + mDialogBrightnessProgressBar = localView.findViewById(R.id.brightness_progressbar); + mBrightnessDialog = createDialogWithView(localView); + } + if (!mBrightnessDialog.isShowing()) { + mBrightnessDialog.show(); + } + if (brightnessPercent > 100) { + brightnessPercent = 100; + } else if (brightnessPercent < 0) { + brightnessPercent = 0; + } + mDialogBrightnessTextView.setText(brightnessPercent + "%"); + mDialogBrightnessProgressBar.setProgress(brightnessPercent); + onCLickUiToggleToClear(); + } + + @Override + public void dismissBrightnessDialog() { + super.dismissBrightnessDialog(); + if (mBrightnessDialog != null) { + mBrightnessDialog.dismiss(); + } + } + + public Dialog createDialogWithView(View localView) { + Dialog dialog = new Dialog(jzvdContext, R.style.jz_style_dialog_progress); + dialog.setContentView(localView); + Window window = dialog.getWindow(); + window.addFlags(Window.FEATURE_ACTION_BAR); + window.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); + window.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); + window.setLayout(-2, -2); + WindowManager.LayoutParams localLayoutParams = window.getAttributes(); + localLayoutParams.gravity = Gravity.CENTER; + window.setAttributes(localLayoutParams); + return dialog; + } + + public void startDismissControlViewTimer() { + cancelDismissControlViewTimer(); + DISMISS_CONTROL_VIEW_TIMER = new Timer(); + mDismissControlViewTimerTask = new DismissControlViewTimerTask(); + DISMISS_CONTROL_VIEW_TIMER.schedule(mDismissControlViewTimerTask, 2500); + } + + public void cancelDismissControlViewTimer() { + if (DISMISS_CONTROL_VIEW_TIMER != null) { + DISMISS_CONTROL_VIEW_TIMER.cancel(); + } + if (mDismissControlViewTimerTask != null) { + mDismissControlViewTimerTask.cancel(); + } + + } + + @Override + public void onCompletion() { + super.onCompletion(); + cancelDismissControlViewTimer(); + } + + @Override + public void reset() { + super.reset(); + cancelDismissControlViewTimer(); + unregisterWifiListener(getApplicationContext()); + } + + public void dissmissControlView() { + if (state != STATE_NORMAL + && state != STATE_ERROR + && state != STATE_AUTO_COMPLETE) { + post(() -> { + bottomContainer.setVisibility(View.INVISIBLE); + topContainer.setVisibility(View.INVISIBLE); + startButton.setVisibility(View.INVISIBLE); + + if (screen != SCREEN_TINY) { + bottomProgressBar.setVisibility(View.VISIBLE); + } + cancelProgressTimer(); + }); + } + } + + public void registerWifiListener(Context context) { + if (context == null) return; + mIsWifi = JZUtils.isWifiConnected(context); + IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); + context.registerReceiver(wifiReceiver, intentFilter); + } + + public void unregisterWifiListener(Context context) { + if (context == null) return; + try { + context.unregisterReceiver(wifiReceiver); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } + } + + public class DismissControlViewTimerTask extends TimerTask { + + @Override + public void run() { + dissmissControlView(); + } + } + +} diff --git a/library/src/main/java/org/jzvd/jzvideo/JZVideoA.kt b/library/src/main/java/org/jzvd/jzvideo/JZVideoA.kt new file mode 100644 index 0000000..bdb5e4b --- /dev/null +++ b/library/src/main/java/org/jzvd/jzvideo/JZVideoA.kt @@ -0,0 +1,37 @@ +package org.jzvd.jzvideo + +import android.content.Context +import android.util.AttributeSet +import android.widget.RelativeLayout + +const val TAG = "JZVD" + + +open class JZVideoA : RelativeLayout { + + enum class State { + IDLE, NORMAL, PREPARING, PREPARING_CHANGE_URL, PREPARING_PLAYING, + PREPARED, PLAYING, PAUSE, COMPLETE, ERROR + } + + enum class Screen { + NORMAL, FULLSCREEN, TINY + } + + lateinit var state: State + + + constructor(ctx: Context) : super(ctx) { + } + + constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) { + } + + companion object { + @JvmStatic + fun releaseAllVideos() { + + } + } + +} \ No newline at end of file diff --git a/library/src/main/res/anim/pop_from_bottom_anim_in.xml b/library/src/main/res/anim/pop_from_bottom_anim_in.xml new file mode 100644 index 0000000..7536a52 --- /dev/null +++ b/library/src/main/res/anim/pop_from_bottom_anim_in.xml @@ -0,0 +1,6 @@ + + + diff --git a/library/src/main/res/anim/pop_from_bottom_anim_out.xml b/library/src/main/res/anim/pop_from_bottom_anim_out.xml new file mode 100644 index 0000000..1ee5add --- /dev/null +++ b/library/src/main/res/anim/pop_from_bottom_anim_out.xml @@ -0,0 +1,5 @@ + + diff --git a/library/src/main/res/drawable-xhdpi/jz_add_volume.png b/library/src/main/res/drawable-xhdpi/jz_add_volume.png new file mode 100644 index 0000000000000000000000000000000000000000..868b3080f606bb016e518d740532d4100dca6318 GIT binary patch literal 1306 zcmeAS@N?(olHy`uVBq!ia0vp^3xGI)gBeJ!Z0=)YU|{SC@Ck7Ra{rHl!4U!?pFN#` z@zGKeWxn=$iW`mi>foo(-%Mq$P!|DuJ|+;Ts+aJ5%xKlII<#uPDy z!|%_=q}S&p&A*;leP89(ZPA=xFFn8TTKW8(>c8eG-;36`>pK+xE|Yr@nO3OjaM$|4 z+~u?65A7~jPW|-ct)+qncT_<0PNf(T!ROh5-t#VKtx9B7bzSgoQOU9A`TH3dn09)) zIEG~0dpqlQaFCRY@2eLQz` z)sOS>F!st@9xT3*6>y*FyFa$ z@7S_5$(nn$8f+(S-G3x};JG~m3*R%ws^+!1Z`a*fwOCo?8sCkRGJ>a=|15Q0xT00U zcFoN7-HN+bawuG!7?D>jXS;0j`sLy;K8vtFdHP=dLEn$U+L~r(2PL1S44qAf?-VZz z6^}P!?db`UbXZivW-;^1VRI{{C6hwrCLHtoxIAcabG{8D4{wmF!>k9j0@nq$tYx@+ z+j%PE%Mvk*#i|aAyF(Z>&)%JE#Z;oclJSyA!MB*Log19>_G=}4urYXRkubkKgz+WQ z{L^;rjKYaQ>|Z{wydG5Ru=HHt%$p~U$}%S%Xb5LpV7j|_x}JC%-{;W(6aKSID8Dnc zNl-Dti*12<<<(OgnXkW5UT0jgnZv=`JZ;8?lXfYLJ2crQ9LxXMa`XGc+ylDo6QnBm z#B=v=VJ>H3yyU`AT>iN}gZZ5&(-PGOb#=cPm$xornBI8v&o=%F4Xz34IY+12hgtE> zWlLF*vvZq;L)@d#i{5A6R$i3v{9&N18({y);qQ<6RsQ~$>3FT2x3GuFCz`m&=<-jnV5ayWMGNDrImI#2xHPuBArRa8*r9N&$l}<-a(cm4d#; Tme|JvGZllUtDnm{r-UW|HnS%q literal 0 HcmV?d00001 diff --git a/library/src/main/res/drawable-xhdpi/jz_back_normal.png b/library/src/main/res/drawable-xhdpi/jz_back_normal.png new file mode 100644 index 0000000000000000000000000000000000000000..8b58353f6de6d695e785e69475e683a561429d84 GIT binary patch literal 625 zcmeAS@N?(olHy`uVBq!ia0vp^P9V&|3?#2~eYgdrDgt~$T!Az)U}R*3b5l}M%FE0D z{Q2|i*RP*He}4V?)y2gH$OeMXpFcM?HbU4xe*Ad<{(W!Up{#7VC&YcKyK>ilIK91)JuZ=f*F|DS-1rGc}1n zRN&~&>8j^Xt?JSenlgV@e`~$i=kITF%pJWiF-9{mFgAL+IEF+V-g@J<-(dqDwg*eL zct%RL7PT?nw)pb*{)8*u1xplU-e0dV&J>^jZ;Nl%^*`Ze*KP*+u6SzxH#19%c~(eH z1`Csyv!cL-W2bm}Co~k^;*U>>&|3Onul)Pxm2y#if!Znu>~#Ln+x9UaX8U*`g)@1AvG@9_5dzn z&Tz+qoeBM7E{jCwO`f37EWV=T&|NQmlfa5lE;ca>7a=*19c#sT)C>YS%pD7+Hl1{0 zdDz?1tx%$MP|V5y^n<-en?xSja@w{hII2WcykT}!jF`a5_0jW?01IEB;*2*zH7*Mk zd`vgScf6m+`E$#W;0M>4_ACD=ZnU5KLvCXEE3fUrA3rmhe%LH*fAHh?jz4D3Jums0 z-|#Xd>wXdUnBM$7s^a+a_c3!F7Tndp*L-@ji;iGZw01|XkPE!B_2Y?J}WJ45QOi@%xc{Us&K1Qy90002tNklHwb**Z=?j1DOOtc6RoqOP5$# zSs^O9xVW;ivViQ*pFaalIDh^;3kwU7%gM=k`SRtnXU}qQa9q52@!Yv{KtYHUP~^mk z6F~Ci%a=gyK*61|n_mK@HA{m0f*BYXS(v$n#Rb?o1m*pg7)nYR`HH;$f6!XmX31gO zgGXeXjGRuSeMqx;rr5#=R4nJ|;uvCa`t6m#;wA$gh6j__Rw!t!aE+YHvEujt{RLMx z+gdRldV6&bJq8o10(VTcX%#cy537ts6@H6D^9T zOch_<8=CsvCB(VjT&mZ0{+6Y$a!OUTjtkw`{D0rI-ek?3(&}tk@1)i5k8R?-^K#Y2 z$lY&zmu6eE+KPO2e!lm9R%6fEvo*ETZ(4r-T(;-?)ru`{{YlZ!pM|EcekoluOTN0< zZSvlQ|Gpjb4vt;k7F&L2UBjE_O|PE!I%EZ1`Z?LiteMlaS^4tZ372g*nSXn7FJjxo iX>WJ^{2}c9Sp3>eRppBY*{^`X%i!ti=d#Wzp$P!y=h!U( literal 0 HcmV?d00001 diff --git a/library/src/main/res/drawable-xhdpi/jz_back_tiny_pressed.png b/library/src/main/res/drawable-xhdpi/jz_back_tiny_pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..2ca825247ed2c86fa395747f85f687b9b20c4776 GIT binary patch literal 486 zcmeAS@N?(olHy`uVBq!ia0vp^Iv~u!3?wz9Rv81S%mAMd*Z=?j1DSY0c6RoqOP5$# zS%GS}xVW;ivOa(Q4Agc0{CO4@7N8&}C+FqMm(QL(%fZ2M@#4jE=gvXIf!q@(P5{Z5 zFJA&R##`J5*{xg>SUlYPEQxd5R21quiO@DQV?K!kP>R5%YFI9gN670D;`!0cyv8!SFZlY;>2mOVXN!^ zNF7YtF+)&rb5CTUL2KZd$t%-5rQ8)6&m1b9mcLE-?4;a@@2@Eq8@k?%o6;V*dC4m8 zjrUzy3Ti~0JQh#2_1rq?ILGp%m5a9Oc8esG9{QEEW!t8@f3HK5f2=djtX?(mcY~F5 zY~gJtn|HOjQKyx1O0JhrYrXMecCMjZiFHco&D$5xJ!q&Xmj86`S6i?^&Fr7QG&h$% z^PH~}_W0h-s+H&7u0H4XSbEAg-Yni~=Y8Lgc`sf!Iq$k`!u6GPdV%)&7IUWDNe)U& wGjKMxFup4}CvScE;rFhes$cIr{{KTS``;h(p%DhRrh%f$)78&qol`;+0Pjh`c>n+a literal 0 HcmV?d00001 diff --git a/library/src/main/res/drawable-xhdpi/jz_backward_icon.png b/library/src/main/res/drawable-xhdpi/jz_backward_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..51f5ca3662469264fa3a7e78c5d565edb0be6d03 GIT binary patch literal 1392 zcmeAS@N?(olHy`uVBq!ia0vp^2|(<@!3-qrb}|GqFfe`!@Ck7Ra{rHlkrV=l7H8rShBUMXd^Dp8h|aEkAF2+;_Pnx@1w@n*^m@YWqsQ zvekY!JY##kOK|qTyOA%17AS6iSth^LoGJe^-(=mBfloMo$!xH{$(;8|;B@eF?jC8c ztdB?1-`87To$9|jJ=z#3wstdUHCFr_a39Ov9IPk#y5#AJ?xn( zzRc`imHw%mb$*Yh@J_V4Q2a$xPAc=mjJMO0t0fs2nA$yE977@wZ@qavTsTmG?E~lQ zL*g8Y8WKv3S5^cnZQQs>W15EuqhS_{tFoXAYqfsE4eoDUdw&!y$BQrF3>0v(JLGK858w6%81x<2a5T_`}K|%%RWX!+3sCltJT(M^+Nd z7gZUABX;o^u>LuHH-Sm}m8=AFs5Yb8g$-S^FQ=@{D>=Y&Z4GNrW1Ct7d&RY@ha@8Q zGbS)e*EVPh+`hu2CU8IJpu~+!Oa_f>*EVQg2)oFqwqb4a>|nQCe+HX`;E!hku*j0$Dr7QX7(<&n{(L#4UcaTR(OhgG>0V z9sBhvuTEkxx$>gmf8F~HEGKHUepeT!T+n9Bcrt7D?fZuUIUP#=vY9*lex}Pg;cL$P z&y4Tc zoDN^o=Ex@e-@&rrM|Ah?`{nO-FEadfmbX4Isech??K8(;@r$qd&o|VOcwaA>%>HBR z^Vv_B6u zB|(0{3?1d|6^FKGT}ast6teYnaSX9Ioy^c!w5NfQ$BXr2LqW%^BsQA^a~q`k`uf@w zS1sFfCn4mlK#=i`OZ>T46P_ImQngW-+c?9Bk%41g=Gr})Y|TJ}7(8A5T-G@yGywqI CKu0YA literal 0 HcmV?d00001 diff --git a/library/src/main/res/drawable-xhdpi/jz_battery_level_100.png b/library/src/main/res/drawable-xhdpi/jz_battery_level_100.png new file mode 100644 index 0000000000000000000000000000000000000000..6f0cf512d7c40cde4a2bddd692965ec88054fedb GIT binary patch literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^u0Slu!VDxkUY*keQlbGqAt1s37{KFvE(=hCtt7}V zn4!F*{m^z-F6$bgh^?oKV~EA+WQInQJq?UJK~p|3n0L%dVY6}IS=Gk7-Ivoj#5Zcz x!4(n)B`k#!PC{xWt~$(69As}Jyrk! literal 0 HcmV?d00001 diff --git a/library/src/main/res/drawable-xhdpi/jz_battery_level_30.png b/library/src/main/res/drawable-xhdpi/jz_battery_level_30.png new file mode 100644 index 0000000000000000000000000000000000000000..947e905da541599fcd09dc67c448790406d85a2c GIT binary patch literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^u0Slu!VDxkUY*keQlbGqAt1s37{KFvE(=hCtt7}V zn4!F*{m^z-F6$bgh^?oKV~EA+WQInQJq?UJK~p|3n0L%dVY6}ISuSNZ!fRJ3m>N0W^of)78&qol`;+0A@!$BLDyZ literal 0 HcmV?d00001 diff --git a/library/src/main/res/drawable-xhdpi/jz_battery_level_70.png b/library/src/main/res/drawable-xhdpi/jz_battery_level_70.png new file mode 100644 index 0000000000000000000000000000000000000000..25177bf5988a41906719a0c4803e18a06033dfe0 GIT binary patch literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^u0Slu!VDxkUY*keQlbGqAt1s37{KFvE(=hCtt7}V zn4!F*{m^z-F6$bgh^?oKV~EA+WQInQJq?UJK~p|3n0L%dVY6}IiRG1U^W}67@r{~w xaK$XeC21R$%D-BrIEOu$^kvJ!=2} literal 0 HcmV?d00001 diff --git a/library/src/main/res/drawable-xhdpi/jz_battery_level_90.png b/library/src/main/res/drawable-xhdpi/jz_battery_level_90.png new file mode 100644 index 0000000000000000000000000000000000000000..6c681c5990638d7f8611b4c82e595de02c64d9b5 GIT binary patch literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^u0Slu!VDxkUY*keQlbGqAt1s37{KFvE(=hCtt7}V zn4!F*{m^z-F6$bgh>fRmdKI;Vst0B|5Z#{d8T literal 0 HcmV?d00001 diff --git a/library/src/main/res/drawable-xhdpi/jz_brightness_video.png b/library/src/main/res/drawable-xhdpi/jz_brightness_video.png new file mode 100644 index 0000000000000000000000000000000000000000..df25316e3a3b846f4a2cd805a3d62b5325790b91 GIT binary patch literal 1058 zcmV+-1l{|IP)002-30{{R3se_IN0001fP)t-s00000 z00000000000|NsD1Ox*E1N8Lt{{H?kF){!D|MTFMeJ|Nk=c|7idK04a1*PE!B^1qlog4x{!I`JQ4?Ir5V0>4O;M z*44s^R8B!CCn6H1qkG_oHI#jImH8+D00SyXL_t(&-t5=ga+@#^2H>Dwt-)aM0iTae zlQjSPzhDsptM0a$Uc@rf_Mh>Eg*EzEAqzcyZjvN<8gP;(LnbyhW6W@cEsvVdP=yVU z7S)1=3pB)IjG4@EfdW8=47333fPvfs`9JV)046Sf{0v6lUy?o_u44V`2nlamucTTH(dlwmp$1-FkPC-F1BG-WOOBUIT;K`7$hJcDHluuY z1mF}GNI8N~uLkYQDKbz-SE2F+$<;R!in~hGxQ=D|xG(BXV)*pTJ^O*Zj{10`zmaDE zUm9N=pcd|Yy_}B4`Un&C&AWI$EtE#MH>TyWWRvM;nIc_orjx8O`U`vDS-dZ9ylS^6 z<3e(r&JZ%29;Lgo-!aAUX<;AYdYj@l>9%$-pZLC;bxTsN+Op+Ed9Kf8k+;QMl|r2^ zrcV<|CKZ5nky#OD7O`2e22f3eV)Hc7`NqKbJ+n;RV@GVWch~5IkK%=$qO5@V!LrjO zF3!mIFbArVjfo4?1*})V?9H;*T_>LH+YDIMk1b*2lPxelSmy{3Mwrt3lx{%%dntjX1FF8CBGLt1r0_`>#9G^%-Go?sT0^WT5r zpTQR1;>W&&*?|4rB=vW(+wNl1ZDvAmGZ*_{yuAYc(|Ga@&dTi`k7k-dW&VA9!!30rTh|FgyMDgM0%M?y%x5V7f<*H_?{?=`uh5 cZ-_U}3{=NtMZtdN&w!gF3O}MUBz~?_}G6Mr6r>Bc!h{y4_*G^7r zG7xBexJ#0QQ8eTDw&;8R4SSjS*e$$@#P$AMpVOZcqH<2)&k1+{=vErmZv%<5Dd@G-W~%=L&ZSU+WD;?*izwvw>IfGw9=6KPS^2t9JZ7%2i zC&r>NJ!xm2z4slNweRx#{+2yHz_&Lp;fMU@e+IIL%Wm9KVv>%$<8>slzpH>yg^{*VtJyRqW<9+dB{!ihF}@^hqq|nysvS|HmkcX(72h5_5Rkj& fUPuLdcIHElcR~_sB@^as1VxUgtDnm{r-UW|r*WQz literal 0 HcmV?d00001 diff --git a/library/src/main/res/drawable-xhdpi/jz_close_volume.png b/library/src/main/res/drawable-xhdpi/jz_close_volume.png new file mode 100644 index 0000000000000000000000000000000000000000..1e08431985703580cbc97ec11c8dfe85c39daf33 GIT binary patch literal 717 zcmeAS@N?(olHy`uVBq!ia0vp^3xGI)gBeJ!Z0=(NQZoa5LR^9L|3L|^%Cdw29cNq; zEvr8BcS=HI=KP)4RU;lax9_~G6tO`1f@gC=W1d5CZvl`D z_h2w%KfETrt@${UYt5A9n^jnWMb>N z=|0`8&wdxGaqZ1_<8TO!*yGPA(!yrP`@tjQVK5_qi<+Hmfq%y1V5agGH#^-A?io*m znKR$DrrFsVaGb4CXV`n;OgYyCF}@uN5et6*Pu+W4f#L5@k25V<3%;Lyp5~-$@MGI$ zHvS!7Z*rC%h@U?*fc4JTNKVrOweu$iFxq^%$?=)Fzt-``Br)DIUvFyu(rKx*zcXF? zqki}FI z%4uwP}xiKt@4xkKIbt#2|S^@LFN}nKJ!ic)3XG6o&|eJOMc9H zpK(~T{;KuV$9}6*4pgxo+4wx9^`u_-w&_uq7YJAH>3)6IsKl{psaUG#GI5@JT>ATS zlGm;BpU6Aq+!d=YbyDeaA2Qbmy`3SuyiSmTfvLmO#W5t}@Yb7|!NQILY!7x!FkdI( zz`@AkqSDYg>!?Pc$k8KT9|@Q)68z}2$Mxee;rGA8MBYE1zALO#Oe}QIW!LlHZ!Ry} zUA}Dj@#o3MOtjUnFSq>Tkg#hl$Aj-PCNAeWU~jB3b54AK2h)KyN{j(L0iH8h0;0n7 zs{Y;-RbdDTJGt-uoGc-RXJOt9SI%tqWH8+-bin&C2PZ@6S0M)8&x=(WZZs`mVCWK` z#IQj_kMV(to*JXdv=&x|4O=={8lD(&GU%*VXS6u0t-_F`-7Lef#l({#|7@fy(}7oE zoDA8%T`UvgUUM-d+^rH~Fw2?3aA294kVCa+JB!1vVq228DES&IRe)wHYt$W`A>Iw?EU2o(un0=rPE=2xd!=^?AwU z@Lw^N!D7y8o(11K)^anrE1zPJ>XW_Bx~JE09nXSzX%8)Cb){7YZrob+_0QzoRhd== zUSIvoq>NX2AN?Vfd`kX)oz$$l-nYCbE6ya;2LUhY|~FD!PlwM6O8zT~cCv&m+D|Lj|53mW`r@oQNw__;0Qyz%tj;0KoXPyVxh={n7?WxCql z9~+H60d+j)H;_r#TX*=_$*S2#`Dgk>E_Qx+w>LApl4bE^GspjH1LYSTJNvPJzTM?c zgNu%JT>2XOYDz=%9rmSuT^Hfj`0o9z?E$Rzb!jcZ^^B`m&Xc}2+2tuP3o&@Q`njxg HN@xNAExm6s literal 0 HcmV?d00001 diff --git a/library/src/main/res/drawable-xhdpi/jz_loading_bg.png b/library/src/main/res/drawable-xhdpi/jz_loading_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..b85c0378cb805d49ca484700a038a72da8dedd71 GIT binary patch literal 4400 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 h{&$OC-}L(aF$7j#&_9-)(aeb zcIDl&s=O<_|C{B4`X5d6*SNbISsnfL{dq?EZPRV#e*F8&yFy#a_PyPGBBkc1?4}Fn z)^V#owpru%zUsuCgx zfBP+a+ps7z^8T_+_lca=R~Ct1Sh3c*zBNp~>$3gAo1THY*xxQYnxWoSmKM?RuUn{A zEwhU6uct$N$<)_uhV9=}OmY{O9Ax{&wDk6(mvUMrsxMi!t}J`OsAa1DLSm}X`{nEm zkJFtaXXWOucz&={WkFul{8^y`MQKZ$M^aR!y8#^E*jo23eS}8Vbv}X@8J!9!77oRp2@zZRq_^7c3qoX zDQ{Ww)aBlz7^n`U&FFm=^fB*g$v+omSq@Tn;xchqcy@39#i<9b( z`OE9SG_=1PaY?vt*UlxeO?6)%t(xBHWV1^0dWrlWlSNbHJT82m^3LnR;VE*S7rsvE zQ_V15s_6K*Yg_6Ig@yA^Ur6W7J$a$dsXz6Fn^S+viz+Amlow@AyN$M7Y-E;{Qq0k` za`5k(m!#l&KGE}{-L9Z70{%;1xX`>Mka+YsWUcX%pSc%&ox3sL0no&mn;=$&SZQ zN^IpP1&IUKMM~HM9oa3MbS@uUBG6d+K<`MetP1bNJoY8dJ@*d&4~V?5sP($@o-2<+ z=lq@gr=D-U?c4J@r=vK#B13z;FaGorO6s%vsaKPhxhlUXcb-s<=*-}+ubWNgt=(16 a{fj$vVqfjP6WXPqoagE4=d#Wzp$Py4^P3s~ literal 0 HcmV?d00001 diff --git a/library/src/main/res/drawable-xhdpi/jz_play_normal.png b/library/src/main/res/drawable-xhdpi/jz_play_normal.png new file mode 100644 index 0000000000000000000000000000000000000000..74db6df8e6ece1a6ecb5c1c0ac3071040d549b5b GIT binary patch literal 1480 zcmbW1do&XY9LJX!M#@&K5i7~cBTU$+8HN>0Bah_mT$ER~d4-E|z4C}cWo2@5lE>rq_xDq&)RdHz*w|PU3N=4J z&*$@JW@b7&J1Z(Gva_>EB$B76=hoI%u^-^fb~=VkBsjPNf_>={Cj;D^2nZZjUkR#n zdat|}5e~W{i1rW(SF=Kljnw64`<~uUNuo5>FY!2CkBal-K1__Ws!DUynf}y&008kQ zyd~B#YGiOYjmXp8)4I~TLwb7St=_%sU~pD{et&-5Ke_MSXdJ$jiNVZ^CFShH}dcX&+vKy&WYTHl={^+!SO zj4*GzswnB;iA0aMD)0Hps2(@TlBU{P9HU!ijKQgip(=4;5y!t8grb-XzWK1jjPi(Z z2C7j&RdcyukWsf0-WZGZdXFJH;2|-7GtS0#nK1SaEuDvDo=wii+A;FW%?l#f$gAOw z1!vh*U|f{cM-h$;{E{VyF6n7fd2ZrnCds$pOb2!?d8w2%zo4%-Mw)u7CO(ao)9#M6 zaQk+)VsnT`7bcLn;R@~{C+2=!>Yp9=vwq`@x+dr{Vh*4_Iu8&+3R9N5O9z7MK2T#8 z2DBF??{NB1H+YNWkcwGCbvCOxe&^q-F!U(srRPw?v#Ptz~doRfJb}pT_2#Hl_LIluOBljMwd##T%B%KfRI~Kt9s%ccAFHHT39hYZsrSw$q;f!MwRfjO|>W5 zO6D_-GO3$OsZxS#z6U9BE!LuHLF&DB!4GoVc^HiAV#r;p7WzcYBN-$$c!m)VnYTUP)D+?6b z*6m0Z5Rjz12dAlh&t8%4zx9qRri5KhLR!Mx$Kq;D+f;9wuC{e1H?~TiEL?^`Kx&sk$Ez4M6`Fhqp&UU zk_IYAtMQO}NO@t-t>7XtS7N$t`YXHWlYxG4ZMAY#3IluRFJ%9Se-Q%TP!TA5M4Maw zzU0O}-JQx)n;BQ@?7_&*WZ4mESNP?g&j|f;&5N!DjeR6)M~s_3>nL!+Cu>Qbip|Q= zw^(|zU)NOh!edst+wensH#`a%K5<>C5GoDfdehF?OJ$~Q=rx^2yzRK2Om#7YB&5)Q z=N_u1_gZ4_BftvDpDGG{_aQQA4X!rAFE6L&dO5~IrF6pG!G6s1UoRPtBUo0MdnNq~ Dw@9w# literal 0 HcmV?d00001 diff --git a/library/src/main/res/drawable-xhdpi/jz_play_pressed.png b/library/src/main/res/drawable-xhdpi/jz_play_pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..3aaa183d846e30c13b351a2fcee1e35e49716726 GIT binary patch literal 1272 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 literal 0 HcmV?d00001 diff --git a/library/src/main/res/drawable-xhdpi/jz_share_normal.png b/library/src/main/res/drawable-xhdpi/jz_share_normal.png new file mode 100644 index 0000000000000000000000000000000000000000..73d62e1f89b570e9c71442f12d8a1264a554681b GIT binary patch literal 4059 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 N002ovPDHLkV1kT^yu1Ja literal 0 HcmV?d00001 diff --git a/library/src/main/res/drawable-xhdpi/jz_share_pressed.png b/library/src/main/res/drawable-xhdpi/jz_share_pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..e899314a2afb9fffe5ee4bcc90983cc660ef71ba GIT binary patch literal 4004 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 E0FZ-JQ~&?~ literal 0 HcmV?d00001 diff --git a/library/src/main/res/drawable/jz_bottom_bg.9.png b/library/src/main/res/drawable/jz_bottom_bg.9.png new file mode 100644 index 0000000000000000000000000000000000000000..48a5b47f405a0370c05f9d8116705feac22f1c3b GIT binary patch literal 946 zcmV;j15NyiP)6w%N@^oVBgf`e`C2}liYACxn&(iUxAsDd90!Ad&8a`zyJdbu*tw?kBOOK z;molSb-x8`ew33--*Urxs=fl_62Jfh46v7l%_9$<$H3-}(RDyfU%=+f0vKR`-Cqi6 zlv8M)U2`FQ=Z%>TDjbJW^aZTYY*c76H#}4K6&RNQ1{h%Xm$yfn&vFXG8tT4)y)g@5 zfB|-YSv1D3q3#RVf>{6q46x-R3(fPw@qO7tUx9H6V1NO}9#%3LTd-0iFQxg?v6iYY zV5Mi49oEM5JHu=TP@;ojeFreW0AmkZX`X$@*#64r##Yio-S^V*J*F>UD`o)_z;?_67%6NYx%r*~wr3Uv zwlj=-&HGNXAyt1oHwM_~VLu+(OQUUGUPIYeU|a&&^k5&%0@!q6m9)V0URma<$bJh} zF^dBGU^0&9#sC{V?DLVT@vKYbSWDGcV5Wtx1F8E?*iZp%`mlpxG_5l|hxS{rgS3$? zpO$xuzJMK=1+d;=b>u;HD6D1{E9_u+uBBUWtqJLm^Tq%hE$ql-Y{8D2)Rtm%?$|?L z3v(TKG_0=!`?_0@{m&u>=clrG*b4XV0{L#>A}v-(t@1~qigO=+&_=z z#sHfX?4o&gjlOeT`c{}NmyYvY`vP`h7Ax!=x%s&-7~2~I%op~hdG;MC`YqTOvjFy5 zu&c*LSK_{aU6}>2>A`+73t*(MTjb_93fPTV_``lNi-7%R76JRiEWBa=ah#I;1u1aU U!{gLwt^fc407*qoM6N<$g6aymumAu6 literal 0 HcmV?d00001 diff --git a/library/src/main/res/drawable/jz_bottom_progress.xml b/library/src/main/res/drawable/jz_bottom_progress.xml new file mode 100644 index 0000000..5db2949 --- /dev/null +++ b/library/src/main/res/drawable/jz_bottom_progress.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/library/src/main/res/drawable/jz_bottom_seek_poster.xml b/library/src/main/res/drawable/jz_bottom_seek_poster.xml new file mode 100644 index 0000000..0cd8f9b --- /dev/null +++ b/library/src/main/res/drawable/jz_bottom_seek_poster.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/library/src/main/res/drawable/jz_bottom_seek_progress.xml b/library/src/main/res/drawable/jz_bottom_seek_progress.xml new file mode 100644 index 0000000..212fccc --- /dev/null +++ b/library/src/main/res/drawable/jz_bottom_seek_progress.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/library/src/main/res/drawable/jz_clarity_popwindow_bg.9.png b/library/src/main/res/drawable/jz_clarity_popwindow_bg.9.png new file mode 100644 index 0000000000000000000000000000000000000000..3bb305313a16f529d69f3644d535a93fe463c64e GIT binary patch literal 470 zcmeAS@N?(olHy`uVBq!ia0vp^5kPFt!3-q#zpU*9Qr-bRA+A80THslNRSD26iIO0{ z;Aaev?qo{`oSU9>-BoV+vPdhFX@%Ynho;)}KhbAkU{vySaSVw#{Px;yzg7c*)`yXo z#ZFbqPyJpWw7TQyf@xcf)vRy+H~p!gm?{3S`m>bK(s}=z=E`O*TXg1h#_zAKUrxr& zaCy{y`_Np)jXt6e+dMa=wQ38ki%LjelK9x}LAcQQ{fGOWCYCX7TQY(7g9leH<2uiU zRtJ2z3L89^s{fJy;N#u@`KW({beOfaiU0OS5h>IB32~3ly=Lao=da!xyKrmElx?%G z#?Nl}9Q6Cf@mig2FLuh6b=TehDp7WH9ScwO8++9UJ~x%v-6j`FKRK8Yl&9Cz`M*G2 z<$=vi^(hBd{1D&n#3}Q1Z#38AWA;1DryPD!@w{w8!(y3ONs02!rDtV>4E)j_&r@J@ zx7;1@f&k>AAOk?fXKRd7+1-Xa1Au_u9f!2Z|j}S3j3^P6 + + + + diff --git a/library/src/main/res/drawable/jz_click_back_tiny_selector.xml b/library/src/main/res/drawable/jz_click_back_tiny_selector.xml new file mode 100644 index 0000000..baca67f --- /dev/null +++ b/library/src/main/res/drawable/jz_click_back_tiny_selector.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/library/src/main/res/drawable/jz_click_pause_selector.xml b/library/src/main/res/drawable/jz_click_pause_selector.xml new file mode 100644 index 0000000..93559b8 --- /dev/null +++ b/library/src/main/res/drawable/jz_click_pause_selector.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/library/src/main/res/drawable/jz_click_play_selector.xml b/library/src/main/res/drawable/jz_click_play_selector.xml new file mode 100644 index 0000000..e99799c --- /dev/null +++ b/library/src/main/res/drawable/jz_click_play_selector.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/library/src/main/res/drawable/jz_click_replay_selector.xml b/library/src/main/res/drawable/jz_click_replay_selector.xml new file mode 100644 index 0000000..9b5ca8c --- /dev/null +++ b/library/src/main/res/drawable/jz_click_replay_selector.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/library/src/main/res/drawable/jz_click_share_selector.xml b/library/src/main/res/drawable/jz_click_share_selector.xml new file mode 100644 index 0000000..0de0b6f --- /dev/null +++ b/library/src/main/res/drawable/jz_click_share_selector.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/library/src/main/res/drawable/jz_dialog_progress.xml b/library/src/main/res/drawable/jz_dialog_progress.xml new file mode 100644 index 0000000..35179bd --- /dev/null +++ b/library/src/main/res/drawable/jz_dialog_progress.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/library/src/main/res/drawable/jz_dialog_progress_bg.xml b/library/src/main/res/drawable/jz_dialog_progress_bg.xml new file mode 100644 index 0000000..421a56c --- /dev/null +++ b/library/src/main/res/drawable/jz_dialog_progress_bg.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/library/src/main/res/drawable/jz_loading.xml b/library/src/main/res/drawable/jz_loading.xml new file mode 100644 index 0000000..9eaf8e0 --- /dev/null +++ b/library/src/main/res/drawable/jz_loading.xml @@ -0,0 +1,7 @@ + + diff --git a/library/src/main/res/drawable/jz_retry.xml b/library/src/main/res/drawable/jz_retry.xml new file mode 100644 index 0000000..76c211a --- /dev/null +++ b/library/src/main/res/drawable/jz_retry.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/library/src/main/res/drawable/jz_seek_poster_normal.xml b/library/src/main/res/drawable/jz_seek_poster_normal.xml new file mode 100644 index 0000000..e928712 --- /dev/null +++ b/library/src/main/res/drawable/jz_seek_poster_normal.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/library/src/main/res/drawable/jz_seek_poster_pressed.xml b/library/src/main/res/drawable/jz_seek_poster_pressed.xml new file mode 100644 index 0000000..14e409a --- /dev/null +++ b/library/src/main/res/drawable/jz_seek_poster_pressed.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/library/src/main/res/drawable/jz_title_bg.9.png b/library/src/main/res/drawable/jz_title_bg.9.png new file mode 100644 index 0000000000000000000000000000000000000000..5d9df27dbd814b82185bb2e3d7fe80894387c91a GIT binary patch literal 1314 zcmY*ZZBSBa7`KsdXQ$l?ug!WIL<3TMQo+c4^ z4fm(Z7BK=A*Bsk)FkA5jWz7UAG|VzPwR82H!0Wf0h}l`IR^~2~dvE>eO;IX49Rn8s z=Kn6_$a)1|p_%jN9m=d;-T)omc5g~pyxza`rtVg`_~{eo+PiMBw(I>s;bVuL&PlX6b zeGha+soWPOl#ebqBcdn>Vrcxl+80sN&Ig1;Tr}%OZP_)1s|drkjIpf$_=9+-nrX!| zdSalty{40%cxG!do1ybAm-9?QeKc)B@)M2z`LDd#a)c$JeFg5y{l}(e|L_X(5`6yz zi@;Jc8Ed)Z1q#%&l+;Yl(8(RJbW&@bX7BES%16_SmG8 zaQT#%{&K-;(W@Q}cdSts?85f6Kc1 zvX1ok?WSIN%V-|s!Wc)b^j(BVe=AQbQQ{C+dg;$gml#Pkx0`O9p4|hoitG?4-@p~c zM7Q334;$W?pzBaPB>A;kMnKI-Y#x`8R(;OEIFbP`mj=Q8|79TMiT$|fL!1f2*@12a z)l&)eA3at%97#{psZslO5NfA>?7E0q@BpqdOW~?Gu~Ly&fBv|8by8E=VxLC9$+mVR z%7Yuy+4Fc?kt(UFV!wzkQ3ndxo2>4<6b2=f^4YTeQcEJu&v#q@MFWnR9$~}4m)IT- z9h2(F7E0n;(pPY3$CoMC#wshhTJ=apfiD+eC(+n31if6Nx@VsM`eKCiyZOSle-K#= z8QOE1lV`o9mmW>9cq}x)ajnKqTCNcRP9jd`MTQMzXr-g1KZeY>1UEi=a_Lbo^KNnq zn#hTmxCdLliF=bEf7o}288w)#{n`{|F`#|tHbF_@8iHrFN&BJsz?_?L{p`VE;m^AU smJVt1N{Rz)Q_caPo(Or%$_~tdJ;EUN%pheW^7jGMkjUVw?aYjS0UY~yL;wH) literal 0 HcmV?d00001 diff --git a/library/src/main/res/drawable/jz_volume_progress_bg.xml b/library/src/main/res/drawable/jz_volume_progress_bg.xml new file mode 100644 index 0000000..a388074 --- /dev/null +++ b/library/src/main/res/drawable/jz_volume_progress_bg.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + diff --git a/library/src/main/res/layout/jz_dialog_brightness.xml b/library/src/main/res/layout/jz_dialog_brightness.xml new file mode 100644 index 0000000..90c55e3 --- /dev/null +++ b/library/src/main/res/layout/jz_dialog_brightness.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/library/src/main/res/layout/jz_dialog_progress.xml b/library/src/main/res/layout/jz_dialog_progress.xml new file mode 100644 index 0000000..b16b2e7 --- /dev/null +++ b/library/src/main/res/layout/jz_dialog_progress.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + diff --git a/library/src/main/res/layout/jz_dialog_volume.xml b/library/src/main/res/layout/jz_dialog_volume.xml new file mode 100644 index 0000000..5b4d2a1 --- /dev/null +++ b/library/src/main/res/layout/jz_dialog_volume.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/library/src/main/res/layout/jz_layout_clarity.xml b/library/src/main/res/layout/jz_layout_clarity.xml new file mode 100644 index 0000000..3670c9d --- /dev/null +++ b/library/src/main/res/layout/jz_layout_clarity.xml @@ -0,0 +1,10 @@ + + diff --git a/library/src/main/res/layout/jz_layout_clarity_item.xml b/library/src/main/res/layout/jz_layout_clarity_item.xml new file mode 100644 index 0000000..efeadea --- /dev/null +++ b/library/src/main/res/layout/jz_layout_clarity_item.xml @@ -0,0 +1,11 @@ + + \ No newline at end of file diff --git a/library/src/main/res/layout/jz_layout_std.xml b/library/src/main/res/layout/jz_layout_std.xml new file mode 100644 index 0000000..3bfbd9a --- /dev/null +++ b/library/src/main/res/layout/jz_layout_std.xml @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/library/src/main/res/values-es/strings.xml b/library/src/main/res/values-es/strings.xml new file mode 100644 index 0000000..66ea803 --- /dev/null +++ b/library/src/main/res/values-es/strings.xml @@ -0,0 +1,10 @@ + + + Estás conectado a una red móvil, el reproductor utilizará tus datos si continuas + Reproducir + Cancelar + No hay vídeo + Volver a ver + Haga clic para volver a intentarlo + Error de carga de video + diff --git a/library/src/main/res/values-ja-rJP/strings.xml b/library/src/main/res/values-ja-rJP/strings.xml new file mode 100644 index 0000000..0a5dfcf --- /dev/null +++ b/library/src/main/res/values-ja-rJP/strings.xml @@ -0,0 +1,10 @@ + + + あなたは現在、モバイルネットワークを使用しています + ビデオを再開する + ビデオを止める + URLエラー + リプレイ + 再度試してください + エラーが発生しました + \ No newline at end of file diff --git a/library/src/main/res/values-ko-rKR/strings.xml b/library/src/main/res/values-ko-rKR/strings.xml new file mode 100644 index 0000000..98b752c --- /dev/null +++ b/library/src/main/res/values-ko-rKR/strings.xml @@ -0,0 +1,10 @@ + + + 현재 모바일 네트워크를 사용 중이면 계속 플레이어가 트래픽을 소비합니다 + 재생 다시 시작 + 재생을 중지 + 재생 주소 없음 + 재생 다시 시작 + 다시 시도 하십시오 + 버퍼링 실패 + \ No newline at end of file diff --git a/library/src/main/res/values-pt/strings.xml b/library/src/main/res/values-pt/strings.xml new file mode 100644 index 0000000..87ebf5a --- /dev/null +++ b/library/src/main/res/values-pt/strings.xml @@ -0,0 +1,10 @@ + + + Você está usando a rede móvel, você deseja mesmo ver o vídeo? + Continuar + Parar + Sem vídeo + Replay + Clique para tentar novamente + O carregamento de vídeo falhou + diff --git a/library/src/main/res/values-tr/strings.xml b/library/src/main/res/values-tr/strings.xml new file mode 100644 index 0000000..037f81a --- /dev/null +++ b/library/src/main/res/values-tr/strings.xml @@ -0,0 +1,10 @@ + + + Şu anda mobil veriyi kullanıyorsunuz, yüksek veri kaybına yol açabilir + Devam Et + Durdur + URL Bulunamadı + Tekrar + Tekrar denemek için tıklayın + Video yüklenemedi + diff --git a/library/src/main/res/values-zh/strings.xml b/library/src/main/res/values-zh/strings.xml new file mode 100644 index 0000000..944669c --- /dev/null +++ b/library/src/main/res/values-zh/strings.xml @@ -0,0 +1,10 @@ + + + 您当前正在使用移动网络,继续播放将消耗流量 + 继续播放 + 停止播放 + 播放地址无效 + 重播 + 点击重试 + 视频加载失败 + diff --git a/library/src/main/res/values/dimens.xml b/library/src/main/res/values/dimens.xml new file mode 100644 index 0000000..b6744b0 --- /dev/null +++ b/library/src/main/res/values/dimens.xml @@ -0,0 +1,4 @@ + + 45dp + 62dp + diff --git a/library/src/main/res/values/ids.xml b/library/src/main/res/values/ids.xml new file mode 100644 index 0000000..3d0d301 --- /dev/null +++ b/library/src/main/res/values/ids.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/library/src/main/res/values/strings.xml b/library/src/main/res/values/strings.xml new file mode 100644 index 0000000..2c11f97 --- /dev/null +++ b/library/src/main/res/values/strings.xml @@ -0,0 +1,10 @@ + + + You are currently using the mobile network, the player will continue to consume traffic + Resume + Stop play + No url + Replay + Click to try again + Video loading failed + diff --git a/library/src/main/res/values/styles.xml b/library/src/main/res/values/styles.xml new file mode 100644 index 0000000..86ad9af --- /dev/null +++ b/library/src/main/res/values/styles.xml @@ -0,0 +1,21 @@ + + + + + + + + + + diff --git a/library/src/main/res/xml/jz_network_security_config.xml b/library/src/main/res/xml/jz_network_security_config.xml new file mode 100644 index 0000000..2439f15 --- /dev/null +++ b/library/src/main/res/xml/jz_network_security_config.xml @@ -0,0 +1,4 @@ + + + +