├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── LICENSE
├── README.English.md
├── README.md
├── SUMMARY.md
├── _config.yml
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── assets
│ ├── assets_video.mp4
│ └── video.json
│ ├── java
│ └── org
│ │ └── salient
│ │ └── artvideoplayer
│ │ ├── BaseActivity.kt
│ │ ├── BaseApplication.kt
│ │ ├── DensityUtil.kt
│ │ ├── activity
│ │ ├── FullscreenActivity.kt
│ │ ├── MainActivity.kt
│ │ └── TinyWindowActivity.kt
│ │ └── bean
│ │ ├── MovieData.kt
│ │ └── VideoBean.kt
│ └── res
│ ├── layout
│ ├── activity_api.xml
│ ├── activity_api_common.xml
│ ├── activity_api_raw_assets.xml
│ ├── activity_extension.xml
│ ├── activity_fullscreen.xml
│ ├── activity_list.xml
│ ├── activity_main.xml
│ ├── activity_orientation.xml
│ ├── activity_recycler_view.xml
│ ├── activity_tiny.xml
│ ├── content_main.xml
│ └── item_video_view.xml
│ ├── menu
│ └── menu_bar_setting.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxxhdpi
│ └── ic_launcher.png
│ ├── raw
│ └── raw_video.mp4
│ └── values
│ ├── colors.xml
│ ├── strings.xml
│ └── styles.xml
├── artPlayer-arm64
├── .gitignore
├── bintary_push.gradle
├── build.gradle
├── gradle.properties
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── res
│ └── values
│ └── strings.xml
├── artPlayer-armv5
├── .gitignore
├── bintary_push.gradle
├── build.gradle
├── gradle.properties
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── res
│ └── values
│ └── strings.xml
├── artPlayer-armv7a
├── .gitignore
├── bintary_push.gradle
├── build.gradle
├── gradle.properties
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── res
│ └── values
│ └── strings.xml
├── artPlayer-x86
├── .gitignore
├── bintary_push.gradle
├── build.gradle
├── gradle.properties
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── res
│ └── values
│ └── strings.xml
├── artPlayer-x86_64
├── .gitignore
├── bintary_push.gradle
├── build.gradle
├── gradle.properties
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── res
│ └── values
│ └── strings.xml
├── artplayer-core
├── .gitignore
├── bintary_push.gradle
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── org
│ │ └── salient
│ │ └── artplayer
│ │ ├── MediaPlayerManager.kt
│ │ ├── audio
│ │ ├── DefaultAudioFocusChangeListener.kt
│ │ ├── DefaultAudioManager.kt
│ │ └── IAudioManager.kt
│ │ ├── bean
│ │ ├── VideoInfo.kt
│ │ └── VideoSize.kt
│ │ ├── conduction
│ │ ├── PlayerState.kt
│ │ ├── ScaleType.kt
│ │ └── WindowType.kt
│ │ ├── extend
│ │ └── Utils.kt
│ │ ├── player
│ │ ├── IMediaPlayer.kt
│ │ └── SystemMediaPlayer.kt
│ │ └── ui
│ │ ├── FullscreenVideoView.kt
│ │ ├── IFullscreenVideoView.kt
│ │ ├── ITinyVideoView.kt
│ │ ├── IVideoView.kt
│ │ ├── ResizeTextureView.kt
│ │ ├── TinyVideoView.kt
│ │ ├── VideoView.kt
│ │ └── extend
│ │ ├── FullscreenGestureListener.kt
│ │ ├── OrientationEventManager.kt
│ │ └── TinyViewGestureListener.kt
│ └── res
│ └── values
│ ├── attrs.xml
│ ├── ids.xml
│ └── strings.xml
├── artplayer-exo
├── .gitignore
├── bintary_push.gradle
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── org
│ │ └── salient
│ │ └── artplayer
│ │ └── exo
│ │ ├── ExoMediaPlayer.kt
│ │ ├── ExoSourceBuilder.kt
│ │ └── ExoSourceManager.kt
│ └── res
│ └── values
│ └── strings.xml
├── artplayer-ijk
├── .gitignore
├── bintary_push.gradle
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── org
│ │ └── salient
│ │ └── artplayer
│ │ └── ijk
│ │ ├── IjkPlayer.kt
│ │ └── RawDataSourceProvider.kt
│ └── res
│ └── values
│ └── strings.xml
├── artplayer-ui
├── .gitignore
├── bintary_push.gradle
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── org
│ │ └── salient
│ │ └── artplayer
│ │ └── ui
│ │ ├── ControlPanel.kt
│ │ ├── VideoGestureListener.kt
│ │ └── listener
│ │ └── OrientationChangeListener.kt
│ └── res
│ ├── drawable-hdpi
│ ├── salient_brightness.png
│ └── salient_volume.png
│ ├── drawable-mdpi
│ ├── salient_brightness.png
│ └── salient_volume.png
│ ├── drawable-xhdpi
│ ├── salient_bg_loading.png
│ ├── salient_brightness.png
│ ├── salient_icon_back.png
│ ├── salient_icon_full_screen.png
│ ├── salient_icon_narrow.png
│ ├── salient_icon_pause.png
│ ├── salient_icon_start.png
│ ├── salient_icon_volume.png
│ ├── salient_icon_volume_hover.png
│ └── salient_volume.png
│ ├── drawable-xxhdpi
│ ├── salient_bg_loading.png
│ ├── salient_bottom_pause.png
│ ├── salient_bottom_play.png
│ ├── salient_brightness.png
│ ├── salient_icon_back.png
│ ├── salient_icon_full_screen.png
│ ├── salient_icon_narrow.png
│ ├── salient_icon_pause.png
│ ├── salient_icon_start.png
│ ├── salient_icon_volume.png
│ ├── salient_icon_volume_hover.png
│ └── salient_volume.png
│ ├── drawable
│ ├── progress_custom.xml
│ ├── salient_bg_btn_corner_stroke_white.xml
│ ├── salient_seek_bar_video_white.xml
│ ├── salient_seek_thumb_video_white.xml
│ ├── salient_selector_bottom_video_play.xml
│ ├── salient_selector_video_play.xml
│ ├── salient_selector_volume.xml
│ └── salient_video_loading.xml
│ └── layout
│ └── salient_layout_video_control_panel.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── pic
├── api.png
├── apkqrcode.png
├── extension.png
├── list.png
├── main.png
├── mediaplayer.png
└── recyclerview.png
└── settings.gradle
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 |
5 | ---
6 |
7 | **Describe the bug**
8 | A clear and concise description of what the bug is.
9 |
10 | **To Reproduce**
11 | Steps to reproduce the behavior:
12 | 1. Go to '...'
13 | 2. Click on '....'
14 | 3. Scroll down to '....'
15 | 4. See error
16 |
17 | **Expected behavior**
18 | A clear and concise description of what you expected to happen.
19 |
20 | **Screenshots**
21 | If applicable, add screenshots to help explain your problem.
22 |
23 | **Desktop (please complete the following information):**
24 | - OS: [e.g. iOS]
25 | - Browser [e.g. chrome, safari]
26 | - Version [e.g. 22]
27 |
28 | **Smartphone (please complete the following information):**
29 | - Device: [e.g. iPhone6]
30 | - OS: [e.g. iOS8.1]
31 | - Browser [e.g. stock browser, safari]
32 | - Version [e.g. 22]
33 |
34 | **Additional context**
35 | Add any other context about the problem here.
36 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 |
5 | ---
6 |
7 | **Is your feature request related to a problem? Please describe.**
8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
9 |
10 | **Describe the solution you'd like**
11 | A clear and concise description of what you want to happen.
12 |
13 | **Describe alternatives you've considered**
14 | A clear and concise description of any alternative solutions or features you've considered.
15 |
16 | **Additional context**
17 | Add any other context or screenshots about the feature request here.
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/libraries
5 | /.idea/modules.xml
6 | /.idea/workspace.xml
7 | .DS_Store
8 | /build
9 | /captures
10 | .externalNativeBuild
11 | /.idea
12 |
--------------------------------------------------------------------------------
/README.English.md:
--------------------------------------------------------------------------------
1 | English | [简体中文](https://github.com/maiwenchang/ArtVideoPlayer/blob/master/README.md)
2 | # ArtPlayer
3 |
4 | 
5 | [](https://github.com/maiwenchang/ArtVideoPlayer/blob/master/LICENSE)
6 | [](https://android-arsenal.com/api?level=21)
7 | 
8 |
9 | ### Introduction
10 | This is a flexible video player. MediaPlayer is completely separate from VideoView and can be replaced with other player kernels such as ExoPlayer and ijkPlayer. Developers can fully customize the player view, which we call the control panel. In addition, developers can use MediaPlayerManager to control playback behaviours, such as full-screen mode, small screen mode, and smart matching modes in RecyclerView.
11 |
12 |
13 |
14 |
15 |
16 | [Full Wiki of ArtPlayer](https://github.com/maiwenchang/ArtPlayer/wiki)
17 |
18 | ### Features
19 | - Fullscreen,TinyWindow play
20 | - Support for playing in RecyclerView
21 | - Custom UI
22 | - Global playback in APP
23 | - Mute
24 | - Loop Playback
25 | - Gesture manipulation (small window: single finger drag, double finger zoom; full screen: volume, brightness, fast forward)
26 | - ijkPlayer support
27 | - ExoPlayer support
28 | - Gravity sensor support
29 | - Raw/Assets, and local playback support
30 |
31 | ### Getting started
32 | basic dependency
33 | ```
34 | implementation 'org.salient.artvideoplayer:artplayer-core:1.1.0'
35 | ```
36 |
37 | using ExoPlayer
38 | ```
39 | implementation "org.salient.artvideoplayer:artplayer-exo:1.1.0"
40 | ```
41 |
42 | using IjkPlayer
43 | ```
44 | implementation 'org.salient.artvideoplayer:artplayer-ijk:1.1.0'
45 | implementation "org.salient.artvideoplayer:artplayer-armv7a:1.1.0"
46 | ```
47 |
48 | support different cpu architecture
49 | ```
50 | implementation "org.salient.artvideoplayer:artplayer-armv5:1.1.0"
51 | implementation "org.salient.artvideoplayer:artplayer-x86:1.1.0"
52 | //required minSdk version >= 21
53 | implementation "org.salient.artvideoplayer:artplayer-arm64:1.1.0"
54 | implementation "org.salient.artvideoplayer:artplayer-x86_64:1.1.0"
55 | ```
56 |
57 | ### 使用方法
58 |
59 | kotlin
60 | ``` kotlin
61 | import org.salient.artplayer.VideoView
62 |
63 | val videoView = VideoView(context)
64 | videoView.mediaPlayer = SystemMediaPlayer().apply{
65 | setDataSource(context, Uri.parse("http://vfx.mtime.cn/Video/2018/07/06/mp4/180706094003288023.mp4"))
66 | }
67 | videoView.prepare()
68 | ```
69 |
70 | xml
71 | ``` xml
72 |
76 | ```
77 |
78 | `AndroidManifest.xml`
79 | ``` xml
80 |
83 | ```
84 |
85 | Activity
86 | ``` kotlin
87 | // block the backpress event of the fullscreen playback
88 | override fun onBackPressed() {
89 | if (MediaPlayerManager.blockBackPress(this)) {
90 | return
91 | }
92 | super.onBackPressed()
93 | }
94 | ```
95 |
96 | setup cover
97 | ``` java
98 | //If using the ControlPanel,we can get the ImageView of the cover by `findViewById()` method
99 | //of the ControlPanel which extends FrameLayout:
100 | Glide.with(context)
101 | .load("http://img5.mtime.cn/mg/2018/07/06/093947.51483272.jpg")
102 | .into(videoView.cover);
103 | ```
104 |
105 |
106 | ### Support
107 | - Public technical discussion on github is preferred.[Technical problems](https://github.com/maiwenchang/ArtPlayer/issues)
108 | - [Wiki](https://github.com/maiwenchang/ArtPlayer/wiki)
109 |
110 | ### My Build Environment
111 | - Java 1.8
112 | - Android Studio 3.6.0
113 | - Gradle 5.6.4
114 | - IjkPlayer 0.8.8
115 | - ExoPlayer 2.11.3
116 |
117 | ### Authors
118 | - [maiwenchang](https://github.com/maiwenchang)
119 | - [ironman6121](https://github.com/ironman6121)
120 |
121 | ### Contact
122 | - cv.stronger@gmail.com
123 |
124 | ### License
125 |
126 | ```
127 | Copyright 2018 maiwenchang
128 |
129 | Licensed under the Apache License, Version 2.0 (the "License");
130 | you may not use this file except in compliance with the License.
131 | You may obtain a copy of the License at
132 |
133 | http://www.apache.org/licenses/LICENSE-2.0
134 |
135 | Unless required by applicable law or agreed to in writing, software
136 | distributed under the License is distributed on an "AS IS" BASIS,
137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138 | See the License for the specific language governing permissions and
139 | limitations under the License.
140 | ```
141 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [English](https://github.com/maiwenchang/ArtVideoPlayer/blob/master/README.English.md) | 简体中文
2 | # ArtPlayer
3 |
4 | 
5 | [](https://github.com/maiwenchang/ArtVideoPlayer/blob/master/LICENSE)
6 | [](https://android-arsenal.com/api?level=21)
7 | 
8 |
9 | ### 简介
10 | Kotlin实现的视频播放器,将MediaPlayer与VideoView解耦合,支持切换播放器内核(如ExoPlayer和ijkPlayer),支持自定义控制视图,提供MediaPlayerManager实现全屏模式,小屏幕模式等。
11 |
12 |
13 |
14 |
15 |
16 | [查看详细文档](https://github.com/maiwenchang/ArtPlayer/wiki)
17 |
18 | ### 特点
19 | - ijkPlayer支持
20 | - ExoPlayer支持
21 | - 全屏,小屏播放
22 | - 完全自定义UI
23 | - 静音
24 | - 循环播放
25 | - 倍速播放(IjkPlayer支持)
26 | - 多播放器同时播放
27 | - Raw/Assets,本地视频文件播放支持
28 | - Activity生命周期感知,实现了onPause暂停播放,onDestory停止播放并释放资源
29 | - 手势操作支持(小窗:单指拖动,双指缩放;全屏:音量,亮度,快进)
30 | - 重力感应支持
31 |
32 | ### 开始使用
33 | 核心依赖
34 | ```
35 | implementation 'org.salient.artvideoplayer:artplayer-core:1.1.0'
36 | ```
37 |
38 | 选择使用ExoPlayer
39 | ```
40 | implementation "org.salient.artvideoplayer:artplayer-exo:1.1.0"
41 | ```
42 |
43 | 选择使用IjkPlayer
44 | ```
45 | implementation 'org.salient.artvideoplayer:artplayer-ijk:1.1.0'
46 | implementation "org.salient.artvideoplayer:artplayer-armv7a:1.1.0"
47 | ```
48 |
49 | 需要支持不同的cpu架构
50 | ```
51 | implementation "org.salient.artvideoplayer:artplayer-armv5:1.1.0"
52 | implementation "org.salient.artvideoplayer:artplayer-x86:1.1.0"
53 | //下面两个需要minSdk version >= 21
54 | implementation "org.salient.artvideoplayer:artplayer-arm64:1.1.0"
55 | implementation "org.salient.artvideoplayer:artplayer-x86_64:1.1.0"
56 | ```
57 |
58 | ### 使用方法
59 |
60 | kotlin
61 | ``` kotlin
62 | import org.salient.artplayer.VideoView
63 |
64 | val videoView = VideoView(context)
65 | videoView.mediaPlayer = SystemMediaPlayer().apply{
66 | setDataSource(context, Uri.parse("http://vfx.mtime.cn/Video/2018/07/06/mp4/180706094003288023.mp4"))
67 | }
68 | videoView.prepare()
69 | ```
70 |
71 | xml
72 | ``` xml
73 |
77 | ```
78 |
79 | `AndroidManifest.xml`
80 | ``` xml
81 |
84 | ```
85 |
86 | Activity
87 | ``` kotlin
88 | //拦截全屏时的返回事件
89 | override fun onBackPressed() {
90 | if (MediaPlayerManager.blockBackPress(this)) {
91 | return
92 | }
93 | super.onBackPressed()
94 | }
95 | ```
96 |
97 | 设置封面
98 | ``` java
99 | //绑定封面图片资源到VideoView的`cover`字段
100 | Glide.with(context)
101 | .load("http://img5.mtime.cn/mg/2018/07/06/093947.51483272.jpg")
102 | .into(videoView.cover);
103 | ```
104 |
105 | ### 支持
106 | - 请在 github 上公开讨论[技术问题](https://github.com/maiwenchang/ArtPlayer/issues)
107 | - 详细的说明文档请查看[Wiki](https://github.com/maiwenchang/ArtPlayer/wiki)
108 |
109 | ### 构建环境
110 | - Kotlin 1.37.2
111 | - Java 1.8
112 | - Android Studio 3.6.0
113 | - Gradle 5.6.4
114 | - IjkPlayer 0.8.8
115 | - ExoPlayer 2.11.3
116 |
117 | ### 作者
118 | - [maiwenchang](https://github.com/maiwenchang)
119 | - [ironman6121](https://github.com/ironman6121)
120 |
121 | ### 联系方式
122 | - cv.stronger@gmail.com
123 |
124 | ### License
125 |
126 | ```
127 | Copyright 2018 maiwenchang
128 | Licensed under the Apache License, Version 2.0 (the "License");
129 | you may not use this file except in compliance with the License.
130 | You may obtain a copy of the License at
131 | http://www.apache.org/licenses/LICENSE-2.0
132 | Unless required by applicable law or agreed to in writing, software
133 | distributed under the License is distributed on an "AS IS" BASIS,
134 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135 | See the License for the specific language governing permissions and
136 | limitations under the License.
137 | ```
138 |
--------------------------------------------------------------------------------
/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # Table of contents
2 |
3 | * [Initial page](README.md)
4 |
5 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 | apply plugin: 'kotlin-kapt'
5 |
6 | android {
7 | compileSdkVersion rootProject.compileSdkVersion
8 | defaultConfig {
9 | applicationId "org.salient.artvideoplayer"
10 | minSdkVersion rootProject.minSdkVersion
11 | targetSdkVersion rootProject.targetSdkVersion
12 | versionCode 1
13 | versionName "1.0"
14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
15 | ndk {
16 | //设置支持的SO库架构
17 | abiFilters 'armeabi-v7a', 'armeabi', 'x86', 'arm64-v8a', 'x86_64'
18 | }
19 | }
20 | buildTypes {
21 | debug {
22 | minifyEnabled false
23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
24 | }
25 | release {
26 | minifyEnabled false
27 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
28 | }
29 | android.applicationVariants.all {
30 | variant ->
31 | variant.outputs.all {
32 | output ->
33 | def outputFile = output.outputFile
34 | if (outputFile.name.contains("release")) {
35 | outputFileName = new File("./", "artplayer-release.apk")
36 | } else if (outputFile.name.contains("debug")) {
37 | outputFileName = new File("./", "artplayer-debug.apk")
38 | }
39 | }
40 | }
41 | }
42 |
43 | sourceSets {
44 | main {
45 | jniLibs.srcDirs = ['libs']
46 | }
47 | }
48 |
49 | compileOptions {
50 | targetCompatibility JavaVersion.VERSION_1_8
51 | sourceCompatibility JavaVersion.VERSION_1_8
52 | }
53 |
54 | lintOptions {
55 | abortOnError false
56 | }
57 |
58 | dependencies {
59 | implementation fileTree(include: ['*.jar'], dir: 'libs')
60 | implementation "androidx.appcompat:appcompat:$compactVersion"
61 | //design
62 | implementation "com.google.android.material:material:1.5.0"
63 | implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
64 | //FlexboxLayout
65 | implementation 'com.google.android.flexbox:flexbox:3.0.0'
66 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
67 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
68 | implementation 'com.google.code.gson:gson:2.8.6'
69 | implementation 'com.github.bumptech.glide:glide:4.9.0'
70 | debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1'
71 | releaseImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1'
72 | //releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4'
73 | testImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4'
74 |
75 | //required
76 | implementation project(':artplayer-core')
77 | implementation project(':artplayer-exo')
78 | implementation project(':artplayer-ijk')
79 | implementation project(':artplayer-armv7a')
80 | // implementation "org.salient.artvideoplayer:artplayer-armv7a:1.0.0"
81 | // implementation "org.salient.artvideoplayer:artplayer-armv5:1.0.0"
82 | // implementation "org.salient.artvideoplayer:artplayer-x86:1.0.0"
83 | // implementation "org.salient.artvideoplayer:artplayer-arm64:1.0.0"
84 | // implementation "org.salient.artvideoplayer:artplayer-x86_64:1.0.0"
85 |
86 | }
87 | }
88 | dependencies {
89 | implementation "androidx.core:core-ktx:$ktx_version"
90 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
91 | }
92 |
93 | configurations.all {
94 | resolutionStrategy {
95 | force "androidx.core:core-ktx:$ktx_version"
96 | }
97 | }
98 |
99 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
17 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
36 |
41 |
45 |
49 |
54 |
58 |
63 |
67 |
72 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/app/src/main/assets/assets_video.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/app/src/main/assets/assets_video.mp4
--------------------------------------------------------------------------------
/app/src/main/java/org/salient/artvideoplayer/BaseActivity.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artvideoplayer
2 |
3 | import android.content.Context
4 | import android.content.res.Configuration
5 | import android.os.Bundle
6 | import android.util.Log
7 | import android.view.Menu
8 | import android.view.MenuItem
9 | import android.view.View
10 | import android.view.inputmethod.InputMethodManager
11 | import androidx.appcompat.app.AppCompatActivity
12 | import org.salient.artplayer.MediaPlayerManager
13 | import org.salient.artplayer.MediaPlayerManager.blockBackPress
14 | import org.salient.artplayer.exo.ExoMediaPlayer
15 | import org.salient.artplayer.ijk.IjkPlayer
16 | import org.salient.artplayer.player.SystemMediaPlayer
17 | import org.salient.artvideoplayer.bean.VideoBean
18 | import java.util.*
19 |
20 | /**
21 | * description: Activity基类
22 | *
23 | * @author Maiwenchang
24 | * email: cv.stronger@gmail.com
25 | * date: 2020-05-20 09:06 AM.
26 | */
27 | abstract class BaseActivity : AppCompatActivity() {
28 |
29 | val allComing: List
30 | get() {
31 | val list: MutableList = ArrayList()
32 | val mMovieData = BaseApplication.movieData
33 | if (mMovieData != null) {
34 | val moviecomings = mMovieData.moviecomings ?: emptyList()
35 | for (moviecomingsBean in moviecomings) {
36 | val videos = moviecomingsBean.videos
37 | if (videos != null && videos.size > 0) {
38 | list.add(videos[0])
39 | }
40 | }
41 | }
42 | return list
43 | }
44 |
45 | val allAttention: List
46 | get() {
47 | val list: MutableList = ArrayList()
48 | val mMovieData = BaseApplication.movieData
49 | if (mMovieData != null) {
50 | val attentions = mMovieData.attention ?: emptyList()
51 | for (attentionBean in attentions) {
52 | val videos = attentionBean.videos
53 | if (videos != null && videos.size > 0) {
54 | list.add(videos[0])
55 | }
56 | }
57 | }
58 | return list
59 | }
60 |
61 | val randomVideo: VideoBean?
62 | get() {
63 | val mMovieData = BaseApplication.movieData
64 | if (mMovieData != null) {
65 | val allAttention = allAttention
66 | return allAttention[getRandomInt(0, allAttention.size)]
67 | }
68 | return null
69 | }
70 |
71 | fun getRandomInt(min: Int, max: Int): Int {
72 | var i = (System.currentTimeMillis() % max).toInt()
73 | if (i < min) {
74 | i = i + min
75 | }
76 | Log.d("BaseActivity", "#getRandomInt():$i")
77 | return i
78 | }
79 |
80 | override fun onBackPressed() {
81 | if (MediaPlayerManager.blockBackPress(this)) {
82 | return
83 | }
84 | super.onBackPressed()
85 | }
86 |
87 | override fun onPause() {
88 | super.onPause()
89 | hideSoftInput()
90 | }
91 |
92 | //显示软键盘
93 | fun showSoftInput(view: View) {
94 | val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
95 | if (imm != null) {
96 | view.requestFocus()
97 | imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT)
98 | }
99 | }
100 |
101 | //收起软键盘
102 | fun hideSoftInput() {
103 | val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
104 | imm?.hideSoftInputFromWindow(window.decorView.windowToken, 0)
105 | }
106 |
107 | override fun onConfigurationChanged(newConfig: Configuration) {
108 | super.onConfigurationChanged(newConfig)
109 | hideSoftInput()
110 | }
111 |
112 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/salient/artvideoplayer/BaseApplication.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artvideoplayer
2 |
3 | import android.app.Application
4 | import android.text.TextUtils
5 | import com.google.gson.Gson
6 | import org.salient.artvideoplayer.bean.MovieData
7 | import java.io.BufferedReader
8 | import java.io.IOException
9 | import java.io.InputStreamReader
10 | import java.util.concurrent.Executors
11 |
12 | /**
13 | * Created by Mai on 2018/7/17
14 | * *
15 | * Description:
16 | * *
17 | */
18 | class BaseApplication : Application() {
19 | override fun onCreate() {
20 | super.onCreate()
21 | Executors.newSingleThreadExecutor().submit { jsonString = readAssetsFile("video.json") }
22 | }
23 |
24 | /**
25 | * 读取assets中的文件
26 | *
27 | * @param path File Path
28 | * @return File Content String
29 | */
30 | fun readAssetsFile(path: String?): String {
31 | var result = ""
32 | try { // read file content from file
33 | val sb = StringBuilder("")
34 | val reader = InputStreamReader(resources.assets.open(path!!))
35 | val br = BufferedReader(reader)
36 | var str: String?
37 | while (br.readLine().also { str = it } != null) {
38 | sb.append(str)
39 | }
40 | result = sb.toString()
41 | br.close()
42 | reader.close()
43 | } catch (e: IOException) {
44 | e.printStackTrace()
45 | }
46 | return result
47 | }
48 |
49 | companion object {
50 | private var jsonString = ""
51 | val movieData: MovieData?
52 | get() = if (TextUtils.isEmpty(jsonString)) {
53 | null
54 | } else {
55 | Gson().fromJson(jsonString, MovieData::class.java)
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/salient/artvideoplayer/DensityUtil.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artvideoplayer
2 |
3 | import android.app.Activity
4 | import android.content.Context
5 | import android.util.DisplayMetrics
6 |
7 | /**
8 | * Created by Mai on 2018/5/18
9 | * *
10 | * Description:屏幕像素转换类
11 | * *
12 | */
13 | object DensityUtil {
14 | /**
15 | * dip转像素
16 | */
17 | fun dip2px(context: Context, dip: Int): Int {
18 | val SCALE = context.resources.displayMetrics.density
19 | return (dip.toFloat() * SCALE + 0.5f).toInt()
20 | }
21 |
22 | /**
23 | * 像素转dip
24 | */
25 | fun px2dip(context: Context, Pixels: Int): Float {
26 | val SCALE = context.resources.displayMetrics.density
27 | return Pixels / SCALE
28 | }
29 |
30 | /**
31 | * 屏幕分辨率宽
32 | */
33 | fun getWindowWidth(activity: Activity): Int {
34 | val dm = DisplayMetrics()
35 | activity.windowManager.defaultDisplay.getMetrics(dm)
36 | return dm.widthPixels
37 | }
38 |
39 | /**
40 | * 屏幕分辩类高
41 | */
42 | fun getWindowHeight(activity: Activity): Int {
43 | val dm = DisplayMetrics()
44 | activity.windowManager.defaultDisplay.getMetrics(dm)
45 | return dm.heightPixels
46 | }
47 |
48 | /**
49 | * 屏幕的dpi
50 | */
51 | fun getDmDensityDpi(activity: Activity): Float {
52 | val dm = DisplayMetrics()
53 | activity.windowManager.defaultDisplay.getMetrics(dm)
54 | return dm.density
55 | }
56 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/salient/artvideoplayer/activity/FullscreenActivity.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artvideoplayer.activity
2 |
3 | import android.content.res.Configuration
4 | import android.net.Uri
5 | import android.os.Bundle
6 | import android.view.Gravity
7 | import android.view.View
8 | import android.widget.FrameLayout
9 | import androidx.lifecycle.Observer
10 | import kotlinx.android.synthetic.main.activity_fullscreen.*
11 | import kotlinx.android.synthetic.main.activity_tiny.*
12 | import org.salient.artplayer.MediaPlayerManager
13 | import org.salient.artplayer.player.SystemMediaPlayer
14 | import org.salient.artplayer.ui.FullscreenVideoView
15 | import org.salient.artplayer.ui.TinyVideoView
16 | import org.salient.artplayer.ui.VideoView
17 | import org.salient.artplayer.ui.extend.OrientationEventManager
18 | import org.salient.artvideoplayer.BaseActivity
19 | import org.salient.artvideoplayer.DensityUtil.getWindowHeight
20 | import org.salient.artvideoplayer.DensityUtil.getWindowWidth
21 | import org.salient.artvideoplayer.R
22 |
23 | /**
24 | * description:全屏播放demo
25 | *
26 | * @author Maiwenchang
27 | * email: cv.stronger@gmail.com
28 | * date: 2020-05-20 09:06 AM.
29 | */
30 | class FullscreenActivity : BaseActivity() {
31 |
32 | private val orientationEventManager = OrientationEventManager()
33 | private val orientationEventListener = object : OrientationEventManager.OnOrientationChangeListener {
34 | override fun onOrientationLandscape(videoView: VideoView?) {
35 | //横屏
36 | videoView?.let {
37 | MediaPlayerManager.startFullscreen(this@FullscreenActivity, it as FullscreenVideoView)
38 | }
39 | }
40 |
41 | override fun onOrientationReverseLandscape(videoView: VideoView?) {
42 | //反向横屏
43 | videoView?.let {
44 | MediaPlayerManager.startFullscreenReverse(this@FullscreenActivity, it as FullscreenVideoView)
45 | }
46 | }
47 |
48 | override fun onOrientationPortrait(videoView: VideoView?) {
49 | //竖屏
50 | videoView?.let {
51 | MediaPlayerManager.dismissFullscreen(this@FullscreenActivity)
52 | }
53 | }
54 |
55 | }
56 |
57 | override fun onCreate(savedInstanceState: Bundle?) {
58 | super.onCreate(savedInstanceState)
59 | setContentView(R.layout.activity_fullscreen)
60 | }
61 |
62 | override fun onConfigurationChanged(newConfig: Configuration) {
63 | super.onConfigurationChanged(newConfig)
64 | }
65 |
66 | fun onClick(view: View) {
67 | when (view.id) {
68 | R.id.start -> {
69 | val fullScreenVideoView = FullscreenVideoView(context = this, origin = video_view).apply {
70 | this.isVolumeGestureEnable = cb_volume_gesture_enable.isChecked
71 | this.isBrightnessGestureEnable = cb_brightness_gesture_enable.isChecked
72 | this.isProgressGestureEnable = cb_progress_gesture_enable.isChecked
73 | }
74 | val systemMediaPlayer = SystemMediaPlayer()
75 | systemMediaPlayer.setDataSource(this, Uri.parse(randomVideo?.url))
76 | fullScreenVideoView.mediaPlayer = systemMediaPlayer
77 | video_view.mediaPlayer = systemMediaPlayer
78 | if (cb_auto_orientate_enable.isChecked) {
79 | orientationEventManager.orientationEnable(this, fullScreenVideoView, orientationEventListener)
80 | } else {
81 | orientationEventManager.orientationDisable()
82 | }
83 | //开始播放
84 | fullScreenVideoView.prepare()
85 | MediaPlayerManager.startFullscreen(this, fullScreenVideoView)
86 | }
87 | }
88 | }
89 |
90 | override fun onResume() {
91 | super.onResume()
92 | }
93 |
94 | override fun onPause() {
95 | super.onPause()
96 | }
97 |
98 | override fun onBackPressed() {
99 | super.onBackPressed()
100 | }
101 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/salient/artvideoplayer/activity/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artvideoplayer.activity
2 |
3 | import android.content.Intent
4 | import android.net.Uri
5 | import android.os.Bundle
6 | import android.view.Menu
7 | import android.view.MenuItem
8 | import android.view.View
9 | import androidx.appcompat.widget.Toolbar
10 | import kotlinx.android.synthetic.main.content_main.*
11 | import org.salient.artplayer.MediaPlayerManager
12 | import org.salient.artplayer.conduction.PlayerState
13 | import org.salient.artplayer.exo.ExoMediaPlayer
14 | import org.salient.artplayer.exo.ExoSourceBuilder
15 | import org.salient.artplayer.ijk.IjkPlayer
16 | import org.salient.artplayer.player.IMediaPlayer
17 | import org.salient.artplayer.player.SystemMediaPlayer
18 | import org.salient.artplayer.ui.FullscreenVideoView
19 | import org.salient.artplayer.ui.TinyVideoView
20 | import org.salient.artvideoplayer.BaseActivity
21 | import org.salient.artvideoplayer.R
22 | import java.io.IOException
23 |
24 | /**
25 | * description: 首页
26 | *
27 | * @author Maiwenchang
28 | * email: cv.stronger@gmail.com
29 | * date: 2020-05-20 09:06 AM.
30 | */
31 | class MainActivity : BaseActivity() {
32 |
33 | override fun onCreate(savedInstanceState: Bundle?) {
34 | super.onCreate(savedInstanceState)
35 | setContentView(R.layout.activity_main)
36 | val toolbar = findViewById(R.id.toolbar) as Toolbar
37 | setSupportActionBar(toolbar)
38 |
39 | setupMediaPlayer(SystemMediaPlayer())
40 |
41 | btn_start.setOnClickListener {
42 | //开始播放
43 | if (artVideoView.playerState == PlayerState.INITIALIZED) {
44 | artVideoView.prepare()
45 | } else if (artVideoView.playerState == PlayerState.STOPPED) {
46 | artVideoView.prepare()
47 | } else if (!artVideoView.isPlaying) {
48 | artVideoView.start()
49 | }
50 | }
51 |
52 | btn_pause.setOnClickListener {
53 | artVideoView.pause()
54 | }
55 |
56 | btn_stop.setOnClickListener {
57 | artVideoView.stop()
58 | }
59 |
60 | btn_fullscreen.setOnClickListener {
61 | //开启全屏
62 | val fullScreenVideoView = FullscreenVideoView(this, origin = artVideoView)
63 | fullScreenVideoView.mediaPlayer = artVideoView.mediaPlayer
64 | MediaPlayerManager.startFullscreen(this, fullScreenVideoView)
65 |
66 | fullScreenVideoView.setOnClickListener {
67 | if (!fullScreenVideoView.isPlaying) {
68 | fullScreenVideoView.start()
69 | }
70 | }
71 | }
72 |
73 | btn_tiny.setOnClickListener {
74 | //开启小窗
75 | val tinyVideoView = TinyVideoView(this, origin = artVideoView)
76 | tinyVideoView.mediaPlayer = artVideoView.mediaPlayer
77 | MediaPlayerManager.startTinyWindow(this, tinyVideoView)
78 |
79 | tinyVideoView.setOnClickListener {
80 | if (!tinyVideoView.isPlaying) {
81 | tinyVideoView.start()
82 | }
83 | }
84 | }
85 |
86 | }
87 |
88 | private fun setupMediaPlayer(mediaPlayer: IMediaPlayer<*>) {
89 | artVideoView.mediaPlayer?.release()
90 | artVideoView.mediaPlayer = mediaPlayer
91 | when (mediaPlayer) {
92 | is SystemMediaPlayer -> {
93 | mediaPlayer.setDataSource(this, Uri.parse("http://vfx.mtime.cn/Video/2018/07/06/mp4/180706094003288023.mp4"))
94 | }
95 | is IjkPlayer -> {
96 | mediaPlayer.setDataSource(this, Uri.parse("http://vfx.mtime.cn/Video/2018/07/06/mp4/180706094003288023.mp4"))
97 | }
98 | is ExoMediaPlayer -> {
99 | val mediaSource = ExoSourceBuilder(this, "http://vfx.mtime.cn/Video/2018/07/06/mp4/180706094003288023.mp4")
100 | .apply {
101 | this.isLooping = false
102 | this.cacheEnable = true
103 | }
104 | .build()
105 | mediaPlayer.mediaSource = mediaSource
106 | }
107 | }
108 |
109 |
110 | }
111 |
112 | fun onClick(view: View) {
113 | when (view.id) {
114 | R.id.play -> {
115 | val url = edUrl!!.text.toString()
116 | SystemMediaPlayer().also {
117 | try {
118 | it.impl.setDataSource(this, Uri.parse(url))
119 | } catch (e: IOException) {
120 | e.printStackTrace()
121 | }
122 | }.let {
123 | artVideoView.mediaPlayer = it
124 | }
125 | artVideoView.prepare()
126 | }
127 | R.id.fullWindow -> {
128 | startActivity(Intent(this, FullscreenActivity::class.java))
129 | }
130 | R.id.tinyWindow -> {
131 | startActivity(Intent(this, TinyWindowActivity::class.java))
132 | }
133 | }
134 | }
135 |
136 | protected var mMenu: Menu? = null
137 |
138 | override fun onCreateOptionsMenu(menu: Menu): Boolean { // Inflate the menu; this adds items to the action bar if it is present.
139 | menuInflater.inflate(R.menu.menu_bar_setting, menu)
140 | mMenu = menu
141 | refreshMenuState()
142 | return true
143 | }
144 |
145 | override fun onMenuOpened(featureId: Int, menu: Menu): Boolean {
146 | if (mMenu == null) return super.onMenuOpened(featureId, menu)
147 | refreshMenuState()
148 | return super.onMenuOpened(featureId, mMenu!!)
149 | }
150 |
151 | override fun onOptionsItemSelected(item: MenuItem): Boolean {
152 | if (item.isChecked) return super.onOptionsItemSelected(item)
153 | val id = item.itemId
154 | when (id) {
155 | R.id.menu_MediaPlayer -> {
156 | mMenu?.getItem(0)?.title = "Using: MediaPlayer"
157 | setupMediaPlayer(SystemMediaPlayer())
158 | }
159 | R.id.menu_IjkPlayer -> {
160 | mMenu?.getItem(0)?.title = "Using: IjkPlayer"
161 | setupMediaPlayer(IjkPlayer())
162 | }
163 | R.id.menu_ExoPlayer -> {
164 | mMenu?.getItem(0)?.title = "Using: ExoPlayer"
165 | setupMediaPlayer(ExoMediaPlayer(this))
166 | }
167 | }
168 | return super.onOptionsItemSelected(item)
169 | }
170 |
171 | /**
172 | * 刷新标题栏菜单状态
173 | */
174 | private fun refreshMenuState() {
175 | mMenu?.also {
176 | when (artVideoView.mediaPlayer) {
177 | is SystemMediaPlayer -> {
178 | it.getItem(1).getSubMenu().getItem(0).setChecked(true);
179 | it.getItem(0).setTitle("Using: MediaPlayer");
180 | }
181 | is IjkPlayer -> {
182 | it.getItem(1).getSubMenu().getItem(1).setChecked(true);
183 | it.getItem(0).setTitle("Using: IjkPlayer");
184 | }
185 | is ExoMediaPlayer -> {
186 | it.getItem(1).getSubMenu().getItem(2).setChecked(true);
187 | it.getItem(0).setTitle("Using: ExoPlayer");
188 | }
189 | }
190 | }
191 | }
192 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/salient/artvideoplayer/activity/TinyWindowActivity.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artvideoplayer.activity
2 |
3 | import android.content.res.Configuration
4 | import android.net.Uri
5 | import android.os.Bundle
6 | import android.view.Gravity
7 | import android.view.View
8 | import android.widget.FrameLayout
9 | import kotlinx.android.synthetic.main.activity_tiny.*
10 | import org.salient.artplayer.MediaPlayerManager
11 | import org.salient.artplayer.player.SystemMediaPlayer
12 | import org.salient.artplayer.ui.TinyVideoView
13 | import org.salient.artvideoplayer.BaseActivity
14 | import org.salient.artvideoplayer.DensityUtil.getWindowHeight
15 | import org.salient.artvideoplayer.DensityUtil.getWindowWidth
16 | import org.salient.artvideoplayer.R
17 |
18 | /**
19 | * description: 小窗播放demo
20 | *
21 | * @author Maiwenchang
22 | * email: cv.stronger@gmail.com
23 | * date: 2020-05-20 09:06 AM.
24 | */
25 | class TinyWindowActivity : BaseActivity() {
26 | override fun onCreate(savedInstanceState: Bundle?) {
27 | super.onCreate(savedInstanceState)
28 | setContentView(R.layout.activity_tiny)
29 | }
30 |
31 | override fun onConfigurationChanged(newConfig: Configuration) {
32 | super.onConfigurationChanged(newConfig)
33 | hideSoftInput()
34 | }
35 |
36 | fun onClick(view: View) {
37 | when (view.id) {
38 | R.id.start -> {
39 | hideSoftInput()
40 | //set LayoutParams
41 | val windowWidth = getWindowWidth(this)
42 | val windowHeight = getWindowHeight(this)
43 | var width = Integer.valueOf(width.text.toString())
44 | var height = Integer.valueOf(height.text.toString())
45 | if (width > windowWidth) {
46 | width = windowWidth
47 | }
48 | if (height > windowHeight) {
49 | height = windowHeight
50 | }
51 | val layoutParams = FrameLayout.LayoutParams(width, height)
52 | var leftRight = Gravity.END
53 | if (left.isChecked) {
54 | leftRight = Gravity.START
55 | }
56 | var topBottom = Gravity.BOTTOM
57 | if (top.isChecked) {
58 | topBottom = Gravity.TOP
59 | }
60 | layoutParams.gravity = leftRight or topBottom
61 | var marginLeft = Integer.valueOf(marginLeft.text.toString())
62 | var marginTop = Integer.valueOf(marginTop.text.toString())
63 | var marginRight = Integer.valueOf(marginRight.text.toString())
64 | var marginBottom = Integer.valueOf(marginBottom.text.toString())
65 | if (marginLeft > windowWidth - width) {
66 | marginLeft = windowWidth - width
67 | }
68 | if (marginRight > windowWidth - width) {
69 | marginRight = windowWidth - width
70 | }
71 | if (marginTop > windowHeight - height) {
72 | marginTop = windowHeight - height
73 | }
74 | if (marginBottom > windowHeight - height) {
75 | marginBottom = windowHeight - height
76 | }
77 | layoutParams.setMargins(marginLeft, marginTop, marginRight, marginBottom)
78 | val tinyVideoView = TinyVideoView(context = this, params = layoutParams).apply {
79 | mediaPlayer = SystemMediaPlayer().apply {
80 | val uri = Uri.parse(randomVideo?.url)
81 | setDataSource(this@TinyWindowActivity, uri)
82 | }
83 | isMovable = cb_isMovable.isChecked
84 | isScalable = cb_isScalable.isChecked
85 | }
86 | tinyVideoView.prepare()
87 | MediaPlayerManager.startTinyWindow(this@TinyWindowActivity, tinyVideoView)
88 | }
89 | }
90 | }
91 |
92 | override fun onResume() {
93 | super.onResume()
94 | }
95 |
96 | override fun onPause() {
97 | super.onPause()
98 | }
99 |
100 | override fun onBackPressed() {
101 | super.onBackPressed()
102 | }
103 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/salient/artvideoplayer/bean/MovieData.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artvideoplayer.bean
2 |
3 | /**
4 | * Created by Mai on 2018/7/17
5 | * *
6 | * Description:
7 | * *
8 | */
9 | class MovieData {
10 | var attention: List? = null
11 | var moviecomings: List? = null
12 |
13 | class AttentionBean {
14 | /**
15 | * actor1 : 道恩·强森
16 | * actor2 : 内芙·坎贝尔
17 | * director : 罗森·马歇尔·瑟伯
18 | * id : 234573
19 | * image : http://img5.mtime.cn/mt/2018/07/11/151451.75772708_1280X720X2.jpg
20 | * isFilter : false
21 | * isTicket : false
22 | * isVideo : true
23 | * locationName : 美国
24 | * rDay : 20
25 | * rMonth : 7
26 | * rYear : 2018
27 | * releaseDate : 7月20日上映
28 | * title : 摩天营救
29 | * type : 动作 / 冒险 / 剧情
30 | * videoCount : 3
31 | * videos : [{"hightUrl":"","image":"http://img5.mtime.cn/mg/2018/06/27/094527.12278962.jpg","length":61,"title":"摩天营救 定档预告片","url":"http://vfx.mtime.cn/Video/2018/06/27/mp4/180627094726195356.mp4","videoId":71043},{"hightUrl":"","image":"http://img5.mtime.cn/mg/2018/02/05/144143.61155408.jpg","length":159,"title":"摩天营救 中文版预告片","url":"http://vfx.mtime.cn/Video/2018/02/05/mp4/180205170620160029.mp4","videoId":69545},{"hightUrl":"","image":"http://img5.mtime.cn/mg/2018/02/03/085022.69184529.jpg","length":123,"title":"摩天营救 先导预告片","url":"http://vfx.mtime.cn/Video/2018/02/03/mp4/180203084924515223.mp4","videoId":69520}]
32 | * wantedCount : 853
33 | */
34 | var actor1: String? = null
35 | var actor2: String? = null
36 | var director: String? = null
37 | var id = 0
38 | var image: String? = null
39 | var isIsFilter = false
40 | private set
41 | var isIsTicket = false
42 | private set
43 | var isIsVideo = false
44 | private set
45 | var locationName: String? = null
46 | var rDay = 0
47 | var rMonth = 0
48 | var rYear = 0
49 | var releaseDate: String? = null
50 | var title: String? = null
51 | var type: String? = null
52 | var videoCount = 0
53 | var wantedCount = 0
54 | var videos: List? = null
55 |
56 | fun setIsFilter(isFilter: Boolean) {
57 | isIsFilter = isFilter
58 | }
59 |
60 | fun setIsTicket(isTicket: Boolean) {
61 | isIsTicket = isTicket
62 | }
63 |
64 | fun setIsVideo(isVideo: Boolean) {
65 | isIsVideo = isVideo
66 | }
67 |
68 | }
69 |
70 | class MoviecomingsBean {
71 | /**
72 | * actor1 : 方城淞
73 | * actor2 :
74 | * director : 高建国
75 | * id : 258415
76 | * image : http://img5.mtime.cn/mt/2018/06/20/113940.34629012_1280X720X2.jpg
77 | * isFilter : false
78 | * isTicket : false
79 | * isVideo : false
80 | * locationName : 中国
81 | * rDay : 19
82 | * rMonth : 7
83 | * rYear : 2018
84 | * releaseDate : 7月19日上映
85 | * title : 八只鸡
86 | * type :
87 | * videoCount : 0
88 | * videos : []
89 | * wantedCount : 25
90 | */
91 | var actor1: String? = null
92 | var actor2: String? = null
93 | var director: String? = null
94 | var id = 0
95 | var image: String? = null
96 | var isIsFilter = false
97 | private set
98 | var isIsTicket = false
99 | private set
100 | var isIsVideo = false
101 | private set
102 | var locationName: String? = null
103 | var rDay = 0
104 | var rMonth = 0
105 | var rYear = 0
106 | var releaseDate: String? = null
107 | var title: String? = null
108 | var type: String? = null
109 | var videoCount = 0
110 | var wantedCount = 0
111 | var videos: List? = null
112 |
113 | fun setIsFilter(isFilter: Boolean) {
114 | isIsFilter = isFilter
115 | }
116 |
117 | fun setIsTicket(isTicket: Boolean) {
118 | isIsTicket = isTicket
119 | }
120 |
121 | fun setIsVideo(isVideo: Boolean) {
122 | isIsVideo = isVideo
123 | }
124 |
125 | }
126 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/salient/artvideoplayer/bean/VideoBean.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artvideoplayer.bean
2 |
3 | /**
4 | * Created by Mai on 2018/7/17
5 | * *
6 | * Description:
7 | * *
8 | */
9 | class VideoBean {
10 | /**
11 | * hightUrl :
12 | * image : http://img5.mtime.cn/mg/2018/06/27/094527.12278962.jpg
13 | * length : 61
14 | * title : 摩天营救 定档预告片
15 | * url : http://vfx.mtime.cn/Video/2018/06/27/mp4/180627094726195356.mp4
16 | * videoId : 71043
17 | */
18 | var hightUrl: String? = null
19 | var image: String? = null
20 | var length = 0
21 | var title: String? = null
22 | var url: String? = null
23 | var videoId = 0
24 | var listPosition = 0
25 |
26 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_api.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
24 |
25 |
32 |
33 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_api_common.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
22 |
23 |
29 |
30 |
31 |
34 |
35 |
41 |
42 |
43 |
47 |
48 |
55 |
56 |
63 |
64 |
71 |
72 |
79 |
80 |
87 |
88 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_api_raw_assets.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
25 |
26 |
32 |
33 |
34 |
41 |
42 |
50 |
51 |
57 |
58 |
59 |
62 |
63 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_extension.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
24 |
25 |
32 |
33 |
40 |
41 |
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_fullscreen.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
19 |
20 |
25 |
26 |
32 |
33 |
34 |
37 |
38 |
43 |
44 |
50 |
51 |
52 |
55 |
56 |
61 |
62 |
68 |
69 |
70 |
73 |
74 |
79 |
80 |
86 |
87 |
88 |
94 |
95 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
16 |
17 |
24 |
25 |
32 |
33 |
40 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_orientation.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
20 |
21 |
22 |
29 |
30 |
37 |
38 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_recycler_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
21 |
22 |
25 |
26 |
30 |
31 |
35 |
36 |
40 |
41 |
47 |
48 |
53 |
54 |
59 |
60 |
61 |
65 |
66 |
70 |
71 |
77 |
78 |
83 |
84 |
89 |
90 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/content_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
20 |
21 |
29 |
30 |
36 |
37 |
38 |
41 |
42 |
50 |
51 |
58 |
59 |
66 |
67 |
74 |
75 |
82 |
83 |
90 |
91 |
92 |
96 |
97 |
105 |
106 |
107 |
114 |
115 |
122 |
123 |
131 |
132 |
140 |
141 |
149 |
150 |
151 |
152 |
153 |
154 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_video_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_bar_setting.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/raw/raw_video.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/app/src/main/res/raw/raw_video.mp4
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ArtPlayer
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
14 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/artPlayer-arm64/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/artPlayer-arm64/bintary_push.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.novoda.bintray-release'
2 |
3 | publish {
4 | userOrg = 'cv1948'//bintray.com用户名
5 | groupId = 'org.salient.artvideoplayer'//jcenter上的路径
6 | artifactId = "artplayer-arm64"//项目名称
7 | publishVersion = android.defaultConfig.versionName//版本号
8 | desc = 'ABI for ArtVideoPlayer'//描述
9 | website = 'https://github.com/maiwenchang/ArtVideoPlayer'//网站
10 | }
--------------------------------------------------------------------------------
/artPlayer-arm64/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 |
5 | compileSdkVersion rootProject.compileSdkVersion
6 |
7 | defaultConfig {
8 | minSdkVersion rootProject.minSdkVersion
9 | targetSdkVersion rootProject.targetSdkVersion
10 | }
11 |
12 | buildTypes {
13 | debug {
14 | minifyEnabled false
15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
16 | }
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 |
23 | compileOptions {
24 | targetCompatibility JavaVersion.VERSION_1_8
25 | sourceCompatibility JavaVersion.VERSION_1_8
26 | }
27 |
28 | sourceSets {
29 | main {
30 | jniLibs.srcDirs = ['libs']
31 | }
32 | }
33 | }
34 |
35 | dependencies {
36 | implementation fileTree(dir: 'libs', include: ['*.jar'])
37 | implementation "tv.danmaku.ijk.media:ijkplayer-arm64:$ijkPlayerVersion"
38 | }
39 | //apply from: './bintary_push.gradle'
--------------------------------------------------------------------------------
/artPlayer-arm64/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 | PROJ_NAME=gsyVideoPlayer-armv7a
19 | PROJ_ARTIFACTID=gsyVideoPlayer-armv7a
20 |
21 |
--------------------------------------------------------------------------------
/artPlayer-arm64/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/guoshuyu/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/artPlayer-arm64/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/artPlayer-arm64/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | artplayer-armv7a
3 |
4 |
--------------------------------------------------------------------------------
/artPlayer-armv5/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/artPlayer-armv5/bintary_push.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.novoda.bintray-release'
2 |
3 | publish {
4 | userOrg = 'cv1948'//bintray.com用户名
5 | groupId = 'org.salient.artvideoplayer'//jcenter上的路径
6 | artifactId = "artplayer-armv5"//项目名称
7 | publishVersion = android.defaultConfig.versionName//版本号
8 | desc = 'ABI for ArtVideoPlayer'//描述
9 | website = 'https://github.com/maiwenchang/ArtVideoPlayer'//网站
10 | }
--------------------------------------------------------------------------------
/artPlayer-armv5/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 |
5 | compileSdkVersion rootProject.compileSdkVersion
6 |
7 | defaultConfig {
8 | minSdkVersion rootProject.minSdkVersion
9 | targetSdkVersion rootProject.targetSdkVersion
10 | }
11 |
12 | buildTypes {
13 | debug {
14 | minifyEnabled false
15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
16 | }
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 |
23 | compileOptions {
24 | targetCompatibility JavaVersion.VERSION_1_8
25 | sourceCompatibility JavaVersion.VERSION_1_8
26 | }
27 |
28 | sourceSets {
29 | main {
30 | jniLibs.srcDirs = ['libs']
31 | }
32 | }
33 | }
34 |
35 | dependencies {
36 | implementation fileTree(dir: 'libs', include: ['*.jar'])
37 | implementation "tv.danmaku.ijk.media:ijkplayer-armv5:$ijkPlayerVersion"
38 | }
39 | //apply from: './bintary_push.gradle'
--------------------------------------------------------------------------------
/artPlayer-armv5/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 | PROJ_NAME=gsyVideoPlayer-armv7a
19 | PROJ_ARTIFACTID=gsyVideoPlayer-armv7a
20 |
21 |
--------------------------------------------------------------------------------
/artPlayer-armv5/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/guoshuyu/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/artPlayer-armv5/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/artPlayer-armv5/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | artplayer-armv7a
3 |
4 |
--------------------------------------------------------------------------------
/artPlayer-armv7a/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/artPlayer-armv7a/bintary_push.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.novoda.bintray-release'
2 |
3 | publish {
4 | userOrg = 'cv1948'//bintray.com用户名
5 | groupId = 'org.salient.artvideoplayer'//jcenter上的路径
6 | artifactId = "artplayer-armv7a"//项目名称
7 | publishVersion = android.defaultConfig.versionName//版本号
8 | desc = 'ABI for ArtVideoPlayer'//描述
9 | website = 'https://github.com/maiwenchang/ArtVideoPlayer'//网站
10 | }
--------------------------------------------------------------------------------
/artPlayer-armv7a/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 |
5 | compileSdkVersion rootProject.compileSdkVersion
6 |
7 | defaultConfig {
8 | minSdkVersion rootProject.minSdkVersion
9 | targetSdkVersion rootProject.targetSdkVersion
10 | }
11 |
12 | buildTypes {
13 | debug {
14 | minifyEnabled false
15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
16 | }
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 |
23 | compileOptions {
24 | targetCompatibility JavaVersion.VERSION_1_8
25 | sourceCompatibility JavaVersion.VERSION_1_8
26 | }
27 |
28 | sourceSets {
29 | main {
30 | jniLibs.srcDirs = ['libs']
31 | }
32 | }
33 | }
34 |
35 | dependencies {
36 | implementation fileTree(dir: 'libs', include: ['*.jar'])
37 | implementation "tv.danmaku.ijk.media:ijkplayer-armv7a:$ijkPlayerVersion"
38 | }
39 | //apply from: './bintary_push.gradle'
--------------------------------------------------------------------------------
/artPlayer-armv7a/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 | PROJ_NAME=gsyVideoPlayer-armv7a
19 | PROJ_ARTIFACTID=gsyVideoPlayer-armv7a
20 |
21 |
--------------------------------------------------------------------------------
/artPlayer-armv7a/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/guoshuyu/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/artPlayer-armv7a/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/artPlayer-armv7a/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | artplayer-armv7a
3 |
4 |
--------------------------------------------------------------------------------
/artPlayer-x86/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/artPlayer-x86/bintary_push.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.novoda.bintray-release'
2 |
3 | publish {
4 | userOrg = 'cv1948'//bintray.com用户名
5 | groupId = 'org.salient.artvideoplayer'//jcenter上的路径
6 | artifactId = "artplayer-x86"//项目名称
7 | publishVersion = android.defaultConfig.versionName//版本号
8 | desc = 'ABI for ArtVideoPlayer'//描述
9 | website = 'https://github.com/maiwenchang/ArtVideoPlayer'//网站
10 | }
--------------------------------------------------------------------------------
/artPlayer-x86/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 |
5 | compileSdkVersion rootProject.compileSdkVersion
6 |
7 | defaultConfig {
8 | minSdkVersion rootProject.minSdkVersion
9 | targetSdkVersion rootProject.targetSdkVersion
10 | }
11 |
12 | buildTypes {
13 | debug {
14 | minifyEnabled false
15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
16 | }
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 |
23 | compileOptions {
24 | targetCompatibility JavaVersion.VERSION_1_8
25 | sourceCompatibility JavaVersion.VERSION_1_8
26 | }
27 |
28 | sourceSets {
29 | main {
30 | jniLibs.srcDirs = ['libs']
31 | }
32 | }
33 | }
34 |
35 | dependencies {
36 | implementation fileTree(dir: 'libs', include: ['*.jar'])
37 | implementation "tv.danmaku.ijk.media:ijkplayer-x86:$ijkPlayerVersion"
38 | }
39 | //apply from: './bintary_push.gradle'
--------------------------------------------------------------------------------
/artPlayer-x86/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 | PROJ_NAME=gsyVideoPlayer-armv7a
19 | PROJ_ARTIFACTID=gsyVideoPlayer-armv7a
20 |
21 |
--------------------------------------------------------------------------------
/artPlayer-x86/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/guoshuyu/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/artPlayer-x86/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/artPlayer-x86/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | artplayer-armv7a
3 |
4 |
--------------------------------------------------------------------------------
/artPlayer-x86_64/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/artPlayer-x86_64/bintary_push.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.novoda.bintray-release'
2 |
3 | publish {
4 | userOrg = 'cv1948'//bintray.com用户名
5 | groupId = 'org.salient.artvideoplayer'//jcenter上的路径
6 | artifactId = "artplayer-x86_64"//项目名称
7 | publishVersion = android.defaultConfig.versionName//版本号
8 | desc = 'ABI for ArtVideoPlayer'//描述
9 | website = 'https://github.com/maiwenchang/ArtVideoPlayer'//网站
10 | }
--------------------------------------------------------------------------------
/artPlayer-x86_64/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 |
5 | compileSdkVersion rootProject.compileSdkVersion
6 |
7 | defaultConfig {
8 | minSdkVersion rootProject.minSdkVersion
9 | targetSdkVersion rootProject.targetSdkVersion
10 | versionCode rootProject.libraryVersionCode
11 | versionName rootProject.libraryVersionName
12 | }
13 |
14 | buildTypes {
15 | debug {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 |
25 | compileOptions {
26 | targetCompatibility JavaVersion.VERSION_1_8
27 | sourceCompatibility JavaVersion.VERSION_1_8
28 | }
29 |
30 | sourceSets {
31 | main {
32 | jniLibs.srcDirs = ['libs']
33 | }
34 | }
35 | }
36 |
37 | dependencies {
38 | implementation fileTree(dir: 'libs', include: ['*.jar'])
39 | implementation "tv.danmaku.ijk.media:ijkplayer-x86_64:$ijkPlayerVersion"
40 | }
41 | //apply from: './bintary_push.gradle'
--------------------------------------------------------------------------------
/artPlayer-x86_64/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 | PROJ_NAME=gsyVideoPlayer-armv7a
19 | PROJ_ARTIFACTID=gsyVideoPlayer-armv7a
20 |
21 |
--------------------------------------------------------------------------------
/artPlayer-x86_64/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/guoshuyu/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/artPlayer-x86_64/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/artPlayer-x86_64/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | artplayer-armv7a
3 |
4 |
--------------------------------------------------------------------------------
/artplayer-core/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/artplayer-core/bintary_push.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.novoda.bintray-release'
2 |
3 | publish {
4 | userOrg = 'cv1948'//bintray.com用户名
5 | groupId = 'org.salient.artvideoplayer'//jcenter上的路径
6 | artifactId = 'artplayer-core'//项目名称
7 | publishVersion = android.defaultConfig.versionName//版本号
8 | desc = 'An android video player'//描述
9 | website = 'https://github.com/maiwenchang/ArtVideoPlayer'//网站
10 | }
--------------------------------------------------------------------------------
/artplayer-core/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 | apply plugin: 'kotlin-kapt'
5 |
6 | android {
7 | compileSdkVersion rootProject.compileSdkVersion
8 |
9 | defaultConfig {
10 | minSdkVersion rootProject.minSdkVersion
11 | targetSdkVersion rootProject.targetSdkVersion
12 |
13 | }
14 |
15 | resourcePrefix("artplayer_java")
16 |
17 | buildTypes {
18 | debug {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
21 | }
22 | release {
23 | minifyEnabled false
24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
25 | }
26 | }
27 |
28 | compileOptions {
29 | targetCompatibility JavaVersion.VERSION_1_8
30 | sourceCompatibility JavaVersion.VERSION_1_8
31 | }
32 |
33 | sourceSets {
34 | main {
35 | jniLibs.srcDirs = ['libs']
36 | }
37 | }
38 |
39 | lintOptions {
40 | abortOnError false
41 | }
42 | }
43 |
44 | dependencies {
45 | implementation fileTree(dir: 'libs', include: ['*.jar'])
46 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
47 |
48 | implementation "androidx.appcompat:appcompat:$compactVersion"
49 | implementation "androidx.core:core-ktx:$ktx_version"
50 | implementation "androidx.activity:activity-ktx:$ktx_version"
51 | }
52 | configurations.all {
53 | resolutionStrategy {
54 | force "androidx.core:core-ktx:$ktx_version"
55 | }
56 | }
57 | //apply from: './bintary_push.gradle'
58 |
59 |
--------------------------------------------------------------------------------
/artplayer-core/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/artplayer-core/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/artplayer-core/src/main/java/org/salient/artplayer/MediaPlayerManager.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer
2 |
3 | import android.app.Activity
4 | import android.content.pm.ActivityInfo
5 | import android.view.OrientationEventListener
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import android.view.Window
9 | import org.salient.artplayer.conduction.WindowType
10 | import org.salient.artplayer.extend.Utils
11 | import org.salient.artplayer.ui.FullscreenVideoView
12 | import org.salient.artplayer.ui.TinyVideoView
13 | import org.salient.artplayer.ui.VideoView
14 |
15 | /**
16 | * description: 视频播放器管理类
17 | *
18 | * @author Maiwenchang
19 | * email: cv.stronger@gmail.com
20 | * date: 2020-05-04 10:06 AM.
21 | */
22 | object MediaPlayerManager {
23 |
24 | /**
25 | * 拦截返回事件
26 | */
27 | fun blockBackPress(activity: Activity): Boolean {
28 | val decorView = activity.findViewById(Window.ID_ANDROID_CONTENT)
29 | val videoView = decorView.findViewWithTag(WindowType.FULLSCREEN)
30 | videoView?.let {
31 | dismissFullscreen(activity)
32 | return true
33 | }
34 | return false
35 | }
36 |
37 | /**
38 | * 开启全屏
39 | */
40 | fun startFullscreen(activity: Activity, fullscreenVideoView: FullscreenVideoView, orientation: Int = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
41 | Utils.hideSupportActionBar(activity)
42 | val decorView = activity.findViewById(Window.ID_ANDROID_CONTENT)
43 | decorView.findViewWithTag(WindowType.FULLSCREEN)?.let {
44 | decorView.removeView(it)
45 | }
46 | decorView.addView(fullscreenVideoView, fullscreenVideoView.layoutParams)
47 | fullscreenVideoView.origin?.getBitmap()?.let {
48 | if (!fullscreenVideoView.isPlaying) {
49 | fullscreenVideoView.cover.setImageBitmap(it)
50 | fullscreenVideoView.cover.visibility = View.VISIBLE
51 | }
52 | }
53 | Utils.setRequestedOrientation(activity, orientation)
54 | Utils.setRequestedOrientation(activity, ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE)
55 | }
56 |
57 | /**
58 | * 开启反向全屏
59 | */
60 | fun startFullscreenReverse(activity: Activity, fullscreenVideoView: FullscreenVideoView) {
61 | startFullscreen(activity, fullscreenVideoView, ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE)
62 | }
63 |
64 | /**
65 | * 取消全屏
66 | */
67 | fun dismissFullscreen(activity: Activity) {
68 | val decorView = activity.findViewById(Window.ID_ANDROID_CONTENT)
69 | val fullscreenVideoView = decorView.findViewWithTag(WindowType.FULLSCREEN)
70 | fullscreenVideoView?.let {
71 | it.origin?.attach()
72 | Utils.setRequestedOrientation(activity, ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
73 | decorView.removeView(it)
74 | Utils.showSupportActionBar(activity)
75 | }
76 | }
77 |
78 | /**
79 | * 开启小窗
80 | */
81 | fun startTinyWindow(activity: Activity, tinyVideoView: TinyVideoView) {
82 | val decorView = activity.findViewById(Window.ID_ANDROID_CONTENT)
83 | decorView.findViewWithTag(WindowType.TINY)?.let {
84 | decorView.removeView(it)
85 | }
86 | decorView.addView(tinyVideoView)
87 | tinyVideoView.origin?.getBitmap()?.let {
88 | if (!tinyVideoView.isPlaying) {
89 | tinyVideoView.cover.setImageBitmap(it)
90 | tinyVideoView.cover.visibility = View.VISIBLE
91 | }
92 | }
93 | }
94 |
95 | /**
96 | * 取消小窗
97 | */
98 | fun dismissTinyWindow(activity: Activity) {
99 | val decorView = activity.findViewById(Window.ID_ANDROID_CONTENT)
100 | val tinyVideoView = decorView.findViewWithTag(WindowType.TINY)
101 | tinyVideoView?.let {
102 | it.origin?.attach()
103 | decorView.removeView(it)
104 | }
105 | }
106 |
107 |
108 | }
--------------------------------------------------------------------------------
/artplayer-core/src/main/java/org/salient/artplayer/audio/DefaultAudioFocusChangeListener.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer.audio
2 |
3 | import android.app.Service
4 | import android.content.Context
5 | import android.media.AudioManager
6 | import android.media.AudioManager.OnAudioFocusChangeListener
7 | import org.salient.artplayer.player.IMediaPlayer
8 | import java.lang.ref.WeakReference
9 |
10 | /**
11 | * description: 声音焦点变化管理类
12 | *
13 | * @author Maiwenchang
14 | * email: cv.stronger@gmail.com
15 | * date: 2020-05-04 10:06 AM.
16 | */
17 | open class DefaultAudioFocusChangeListener(
18 | private val contextReference: WeakReference,
19 | private val audioManager: IAudioManager,
20 | private val mediaPlayer: IMediaPlayer<*>?
21 | ) : OnAudioFocusChangeListener {
22 | private var playOnAudioFocus = true;
23 | override fun onAudioFocusChange(focusChange: Int) {
24 | when (focusChange) {
25 | AudioManager.AUDIOFOCUS_GAIN -> {
26 | val context = contextReference.get() ?: return
27 | // 重新获得焦点,恢复正常音量,恢复播放
28 | if (playOnAudioFocus && mediaPlayer?.isPlaying != true) {
29 | mediaPlayer?.start();
30 | } else if (mediaPlayer?.isPlaying == true) {
31 | mediaPlayer.setVolume(getCurrentVolume(context));
32 | }
33 | playOnAudioFocus = false;
34 | }
35 | AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
36 | //短暂失去焦点,无须停止播放,只适当降低播放器音量
37 | val context = contextReference.get() ?: return
38 | val duckVolume = getCurrentVolume(context) * 0.8f
39 | mediaPlayer?.setVolume(duckVolume);
40 | }
41 | AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
42 | //暂时失去焦点,暂停
43 | if (mediaPlayer?.isPlaying == true) {
44 | playOnAudioFocus = true;
45 | mediaPlayer.pause()
46 | }
47 | }
48 | AudioManager.AUDIOFOCUS_LOSS -> {
49 | //失去焦点,停止播放,如播放其他多媒体
50 | audioManager.abandonAudioFocus()
51 | playOnAudioFocus = false;
52 | mediaPlayer?.stop()
53 | }
54 | }
55 | }
56 |
57 | /**
58 | * 获取当前系统音量
59 | */
60 | fun getCurrentVolume(context: Context): Float {
61 | val audioManager = context.getSystemService(Service.AUDIO_SERVICE) as AudioManager
62 | val streamVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC).toFloat()
63 | val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC).toFloat()
64 | return streamVolume * 1.000f / maxVolume
65 | }
66 | }
--------------------------------------------------------------------------------
/artplayer-core/src/main/java/org/salient/artplayer/audio/DefaultAudioManager.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer.audio
2 |
3 | import android.app.Service
4 | import android.content.Context
5 | import android.media.AudioAttributes
6 | import android.media.AudioFocusRequest
7 | import android.media.AudioManager
8 | import android.os.Build
9 | import org.salient.artplayer.player.IMediaPlayer
10 | import java.lang.ref.WeakReference
11 |
12 | /**
13 | * description: 音频管理
14 | *
15 | * @author Maiwenchang
16 | * email: cv.stronger@gmail.com
17 | * date: 2020-05-04 10:06 AM.
18 | */
19 | open class DefaultAudioManager(context: Context, mediaPlayer: IMediaPlayer<*>?) : IAudioManager {
20 |
21 | private val audioManager: AudioManager = context.getSystemService(Service.AUDIO_SERVICE) as AudioManager
22 | private val audioAttributes = AudioAttributes.Builder()
23 | .setUsage(AudioAttributes.USAGE_MEDIA)
24 | .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
25 | .build()
26 | private var audioFocusRequest: AudioFocusRequest? = null
27 |
28 | override var onAudioFocusChangeListener: AudioManager.OnAudioFocusChangeListener = DefaultAudioFocusChangeListener(WeakReference(context), this, mediaPlayer)
29 |
30 | override fun mute() {
31 | audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, AudioManager.FLAG_PLAY_SOUND)
32 | }
33 |
34 | override fun setVolume(volume: Int) {
35 | val maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC).toFloat()
36 | val level = volume % maxVolume
37 | audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, level.toInt(), AudioManager.FLAG_PLAY_SOUND)
38 | }
39 |
40 | override fun getVolume(): Int {
41 | return audioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
42 | }
43 |
44 | override fun getMaxVolume(): Int {
45 | return audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
46 | }
47 |
48 | override fun requestAudioFocus() {
49 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
50 | audioFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
51 | .setAudioAttributes(audioAttributes)
52 | .setAcceptsDelayedFocusGain(false)
53 | .setOnAudioFocusChangeListener(onAudioFocusChangeListener)
54 | .build()
55 | .also {
56 | audioManager.requestAudioFocus(it)
57 | }
58 | } else {
59 | audioManager.requestAudioFocus(onAudioFocusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN)
60 | }
61 | }
62 |
63 | override fun abandonAudioFocus() {
64 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
65 | audioFocusRequest?.let {
66 | audioManager.abandonAudioFocusRequest(it)
67 | }
68 | } else {
69 | audioManager.abandonAudioFocus(onAudioFocusChangeListener)
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/artplayer-core/src/main/java/org/salient/artplayer/audio/IAudioManager.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer.audio
2 |
3 | import android.media.AudioManager
4 |
5 | /**
6 | * description: 音频管理
7 | *
8 | * @author Maiwenchang
9 | * email: cv.stronger@gmail.com
10 | * date: 2020-05-04 10:06 AM.
11 | */
12 | interface IAudioManager {
13 |
14 | /**
15 | * 焦点变化监听
16 | */
17 | var onAudioFocusChangeListener: AudioManager.OnAudioFocusChangeListener
18 |
19 | /**
20 | * 静音
21 | */
22 | fun mute()
23 |
24 | /**
25 | * 设置音量
26 | * @param volume 0 ~ maxVolume
27 | */
28 | fun setVolume(volume: Int)
29 |
30 | /**
31 | * 设置音量
32 | * @param volume 0 ~ maxVolume
33 | */
34 | fun getVolume(): Int
35 |
36 | /**
37 | * 获取最大音量
38 | */
39 | fun getMaxVolume(): Int
40 |
41 | /**
42 | * 申请音频焦点
43 | */
44 | fun requestAudioFocus()
45 |
46 | /**
47 | * 放弃音频焦点
48 | */
49 | fun abandonAudioFocus()
50 |
51 | }
--------------------------------------------------------------------------------
/artplayer-core/src/main/java/org/salient/artplayer/bean/VideoInfo.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer.bean
2 |
3 | /**
4 | * description: 视频信息
5 | *
6 | * @author Maiwenchang
7 | * email: cv.stronger@gmail.com
8 | * date: 2020-05-04 10:06 AM.
9 | */
10 | class VideoInfo(val what: Int = 0, val extra: Int = 0)
--------------------------------------------------------------------------------
/artplayer-core/src/main/java/org/salient/artplayer/bean/VideoSize.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer.bean
2 |
3 | /**
4 | * description: 视频尺寸信息
5 | *
6 | * @author Maiwenchang
7 | * email: cv.stronger@gmail.com
8 | * date: 2020-05-04 10:06 AM.
9 | */
10 | class VideoSize(val width: Int = 0, val height: Int = 0)
--------------------------------------------------------------------------------
/artplayer-core/src/main/java/org/salient/artplayer/conduction/PlayerState.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer.conduction
2 |
3 | /**
4 | * description: 播放状态
5 | * 参考:@see https://developer.android.google.cn/reference/android/media/MediaPlayer#state-diagram
6 | *
7 | * @author Maiwenchang
8 | * email: cv.stronger@gmail.com
9 | * date: 2020-05-04 10:06 AM.
10 | */
11 | sealed class PlayerState(val code: Int) {
12 |
13 | object IDLE :
14 | PlayerState(1 shl 1)
15 |
16 | object INITIALIZED :
17 | PlayerState(1 shl 2)
18 |
19 | object PREPARING :
20 | PlayerState(1 shl 3)
21 |
22 | object PREPARED :
23 | PlayerState(1 shl 4)
24 |
25 | object STARTED :
26 | PlayerState(1 shl 5)
27 |
28 | object PAUSED :
29 | PlayerState(1 shl 6)
30 |
31 | object STOPPED :
32 | PlayerState(1 shl 7)
33 |
34 | object COMPLETED :
35 | PlayerState(1 shl 8)
36 |
37 | object ERROR :
38 | PlayerState(1 shl 9)
39 |
40 | object END :
41 | PlayerState(1 shl 10)
42 |
43 | }
--------------------------------------------------------------------------------
/artplayer-core/src/main/java/org/salient/artplayer/conduction/ScaleType.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer.conduction
2 |
3 | /**
4 | * description: 缩放模式
5 | *
6 | * @author Maiwenchang
7 | * email: cv.stronger@gmail.com
8 | * date: 2020-05-04 10:06 AM.
9 | */
10 | sealed class ScaleType {
11 |
12 | object DEFAULT : ScaleType()
13 |
14 | object SCALE_ORIGINAL : ScaleType()
15 |
16 | object SCALE_16_9 : ScaleType()
17 |
18 | object SCALE_4_3 : ScaleType()
19 |
20 | object SCALE_MATCH_PARENT : ScaleType()
21 |
22 | object SCALE_CENTER_CROP : ScaleType()
23 | }
--------------------------------------------------------------------------------
/artplayer-core/src/main/java/org/salient/artplayer/conduction/WindowType.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer.conduction
2 |
3 | /**
4 | * description: 窗口模式
5 | *
6 | * @author Maiwenchang
7 | * email: cv.stronger@gmail.com
8 | * date: 2020-05-04 10:06 AM.
9 | */
10 | sealed class WindowType {
11 |
12 | object NORMAL : WindowType()
13 |
14 | object LIST : WindowType()
15 |
16 | object FULLSCREEN : WindowType()
17 |
18 | object TINY : WindowType()
19 |
20 | }
--------------------------------------------------------------------------------
/artplayer-core/src/main/java/org/salient/artplayer/extend/Utils.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer.extend
2 |
3 | import android.annotation.SuppressLint
4 | import android.app.Activity
5 | import android.content.Context
6 | import android.content.ContextWrapper
7 | import android.view.Window
8 | import android.view.WindowManager
9 | import androidx.appcompat.app.AppCompatActivity
10 | import androidx.appcompat.view.ContextThemeWrapper
11 |
12 | /**
13 | * description: 工具类
14 | *
15 | * @author Maiwenchang
16 | * email: cv.stronger@gmail.com
17 | * date: 2020-05-04 10:06 AM.
18 | */
19 | object Utils {
20 |
21 | /**
22 | * Get activity from context object
23 | *
24 | * @param context context
25 | * @return object of Activity or null if it is not Activity
26 | */
27 | fun scanForActivity(context: Context?): Activity? {
28 | if (context == null) return null
29 | if (context is Activity) {
30 | return context
31 | } else if (context is ContextWrapper) {
32 | return scanForActivity(context.baseContext)
33 | }
34 | return null
35 | }
36 |
37 | /**
38 | * Get AppCompatActivity from context
39 | *
40 | * @param context context
41 | * @return AppCompatActivity if it's not null
42 | */
43 | fun getAppCompActivity(context: Context?): AppCompatActivity? {
44 | if (context == null) return null
45 | if (context is AppCompatActivity) {
46 | return context
47 | } else if (context is ContextThemeWrapper) {
48 | return getAppCompActivity(context.baseContext)
49 | }
50 | return null
51 | }
52 |
53 | fun getRequestedOrientation(context: Context?): Int {
54 | return if (getAppCompActivity(context) != null) {
55 | getAppCompActivity(context)?.requestedOrientation ?: 0
56 | } else {
57 | scanForActivity(context)?.requestedOrientation ?: 0
58 | }
59 | }
60 |
61 | fun setRequestedOrientation(context: Context?, orientation: Int) {
62 | if (getAppCompActivity(context) != null) {
63 | getAppCompActivity(context)?.requestedOrientation = orientation
64 | } else {
65 | scanForActivity(context)?.requestedOrientation = orientation
66 | }
67 | }
68 |
69 | private fun getWindow(context: Context?): Window? {
70 | return if (getAppCompActivity(context) != null) {
71 | getAppCompActivity(context)?.window
72 | } else {
73 | scanForActivity(context)?.window
74 | }
75 | }
76 |
77 | @SuppressLint("RestrictedApi")
78 | fun hideSupportActionBar(context: Context?) {
79 | if (getAppCompActivity(context) != null) {
80 | val ab = getAppCompActivity(context)?.supportActionBar
81 | if (ab != null) {
82 | ab.setShowHideAnimationEnabled(false)
83 | ab.hide()
84 | }
85 | }
86 | getWindow(context)?.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
87 | WindowManager.LayoutParams.FLAG_FULLSCREEN)
88 | }
89 |
90 | @SuppressLint("RestrictedApi")
91 | fun showSupportActionBar(context: Context?) {
92 | if (getAppCompActivity(context) != null) {
93 | val ab = getAppCompActivity(context)?.supportActionBar
94 | if (ab != null) {
95 | ab.setShowHideAnimationEnabled(false)
96 | ab.show()
97 | }
98 | }
99 | getWindow(context)?.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
100 | }
101 | }
--------------------------------------------------------------------------------
/artplayer-core/src/main/java/org/salient/artplayer/player/IMediaPlayer.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer.player
2 |
3 | import android.view.Surface
4 | import android.view.SurfaceHolder
5 | import androidx.lifecycle.LiveData
6 | import org.salient.artplayer.bean.VideoInfo
7 | import org.salient.artplayer.bean.VideoSize
8 | import org.salient.artplayer.conduction.PlayerState
9 |
10 | /**
11 | * description: 视频播放器的抽象基类
12 | *
13 | * @author Maiwenchang
14 | * email: cv.stronger@gmail.com
15 | * date: 2020-05-04 10:06 AM.
16 | */
17 | interface IMediaPlayer {
18 |
19 | /**
20 | * 播放器实例
21 | */
22 | var impl: T
23 | /**
24 | * 准备后是否播放
25 | */
26 | var playWhenReady: Boolean
27 | /**
28 | * 是否正在播放
29 | */
30 | val isPlaying: Boolean
31 | /**
32 | * 当前位置
33 | */
34 | val currentPosition: Long
35 | /**
36 | * 视频长度
37 | */
38 | val duration: Long
39 | /**
40 | * 视频高度
41 | */
42 | val videoHeight: Int
43 | /**
44 | * 视频宽度
45 | */
46 | val videoWidth: Int
47 | /**
48 | * 当前播放器状态
49 | */
50 | val playerState: PlayerState
51 | /**
52 | * 播放器状态监听
53 | */
54 | val playerStateLD: LiveData
55 | /**
56 | * 播放器尺寸
57 | */
58 | val videoSizeLD: LiveData
59 | /**
60 | * 加载进度
61 | */
62 | val bufferingProgressLD: LiveData
63 | /**
64 | * 是否跳转完成
65 | */
66 | val seekCompleteLD: LiveData
67 | /**
68 | * 视频信息
69 | */
70 | val videoInfoLD: LiveData
71 | /**
72 | * 视频报错
73 | */
74 | val videoErrorLD: LiveData
75 |
76 | /**
77 | * 开始
78 | */
79 | fun start()
80 |
81 | /**
82 | * 准备
83 | */
84 | fun prepare()
85 |
86 | /**
87 | * 异步准备
88 | */
89 | fun prepareAsync()
90 |
91 | /**
92 | * 暂停
93 | */
94 | fun pause()
95 |
96 | /**
97 | * 停止
98 | */
99 | fun stop()
100 |
101 | /**
102 | * 跳转到指定到位置
103 | */
104 | fun seekTo(time: Long)
105 |
106 | /**
107 | * 重置
108 | */
109 | fun reset()
110 |
111 | /**
112 | * 释放
113 | */
114 | fun release()
115 |
116 | /**
117 | * 设置音量
118 | */
119 | fun setVolume(volume: Float)
120 |
121 | /**
122 | * 设置循环播放
123 | */
124 | fun setLooping(isLoop: Boolean)
125 |
126 | /**
127 | * 设置播放容器
128 | */
129 | fun setSurface(surface: Surface?)
130 |
131 | /**
132 | * 设置播放容器
133 | */
134 | fun setDisplay(surfaceHolder: SurfaceHolder)
135 |
136 | }
--------------------------------------------------------------------------------
/artplayer-core/src/main/java/org/salient/artplayer/ui/FullscreenVideoView.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer.ui
2 |
3 | import android.content.Context
4 | import android.view.GestureDetector
5 | import android.view.ViewGroup
6 | import org.salient.artplayer.conduction.WindowType
7 | import org.salient.artplayer.ui.extend.FullscreenGestureListener
8 |
9 | /**
10 | * description: 视频播放视容器 - 全屏
11 | * 特性:全屏播放,支持经典的亮度(左方),音量(右方),快进(下方)手势
12 | *
13 | * @author Maiwenchang
14 | * email: cv.stronger@gmail.com
15 | * date: 2020-05-04 10:06 AM.
16 | */
17 | open class FullscreenVideoView(context: Context,
18 | override val origin: VideoView? = null,
19 | final override val params: ViewGroup.LayoutParams? = null
20 | ) : VideoView(context), IFullscreenVideoView {
21 |
22 | var isVolumeGestureEnable: Boolean = true //是否开启音量手势
23 | set(value) {
24 | field = value
25 | gestureListener?.isVolumeGestureEnable = value
26 | }
27 | var isBrightnessGestureEnable: Boolean = true //是否开启亮度手势
28 | set(value) {
29 | field = value
30 | gestureListener?.isBrightnessGestureEnable = value
31 | }
32 | var isProgressGestureEnable: Boolean = true //是否开启进度拖动手势
33 | set(value) {
34 | field = value
35 | gestureListener?.isProgressGestureEnable = value
36 | }
37 |
38 | private val gestureListener: FullscreenGestureListener?
39 |
40 | init {
41 | tag = WindowType.FULLSCREEN
42 | layoutParams = params
43 | ?: ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
44 |
45 | gestureListener = FullscreenGestureListener(this, isVolumeGestureEnable, isBrightnessGestureEnable, isProgressGestureEnable)
46 | val gestureDetector = GestureDetector(getContext(), gestureListener)
47 | setOnTouchListener { v, event ->
48 | if (gestureDetector.onTouchEvent(event)) true
49 | else gestureListener.onTouch(v, event)
50 | }
51 | }
52 |
53 |
54 | }
--------------------------------------------------------------------------------
/artplayer-core/src/main/java/org/salient/artplayer/ui/IFullscreenVideoView.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer.ui
2 |
3 | import android.view.ViewGroup
4 |
5 | /**
6 | * description: 视频容器-小窗模式抽象
7 | *
8 | * @author Maiwenchang
9 | * email: cv.stronger@gmail.com
10 | * date: 2020-05-22 9:06 AM.
11 | */
12 | interface IFullscreenVideoView {
13 |
14 | /**
15 | * 源视图
16 | */
17 | val origin: VideoView?
18 |
19 | /**
20 | * 布局参数
21 | */
22 | val params: ViewGroup.LayoutParams?
23 |
24 |
25 | }
--------------------------------------------------------------------------------
/artplayer-core/src/main/java/org/salient/artplayer/ui/ITinyVideoView.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer.ui
2 |
3 | /**
4 | * description: 视频容器-小窗模式抽象
5 | *
6 | * @author Maiwenchang
7 | * email: cv.stronger@gmail.com
8 | * date: 2020-05-22 9:06 AM.
9 | */
10 | interface ITinyVideoView {
11 |
12 | /**
13 | * 源视图
14 | */
15 | var origin: VideoView?
16 |
17 | }
--------------------------------------------------------------------------------
/artplayer-core/src/main/java/org/salient/artplayer/ui/IVideoView.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer.ui
2 |
3 | import android.graphics.Bitmap
4 | import android.view.TextureView
5 | import android.widget.ImageView
6 | import androidx.lifecycle.LifecycleObserver
7 | import org.salient.artplayer.audio.IAudioManager
8 | import org.salient.artplayer.conduction.PlayerState
9 | import org.salient.artplayer.player.IMediaPlayer
10 |
11 | /**
12 | * description: 视频播放容器抽象类
13 | *
14 | * @author Maiwenchang
15 | * email: cv.stronger@gmail.com
16 | * date: 2020-05-04 10:06 AM.
17 | */
18 | interface IVideoView : TextureView.SurfaceTextureListener, LifecycleObserver {
19 |
20 | /**
21 | * 封面
22 | */
23 | val cover: ImageView
24 |
25 | /**
26 | * 播放器内核
27 | */
28 | var mediaPlayer: IMediaPlayer<*>?
29 |
30 | /**
31 | * 是否正在播放
32 | */
33 | val isPlaying: Boolean
34 |
35 | /**
36 | * 当前位置
37 | */
38 | val currentPosition: Long
39 |
40 | /**
41 | * 视频时长
42 | */
43 | val duration: Long
44 |
45 | /**
46 | * 视频高度
47 | */
48 | val videoHeight: Int
49 |
50 | /**
51 | * 视频宽度
52 | */
53 | val videoWidth: Int
54 |
55 | /**
56 | * 音频管理器
57 | */
58 | var audioManager : IAudioManager
59 |
60 | /**
61 | * 播放器状态
62 | */
63 | val playerState: PlayerState
64 |
65 | /**
66 | * 初始化播放
67 | */
68 | fun prepare()
69 |
70 | /**
71 | * 播放
72 | */
73 | fun start()
74 |
75 | /**
76 | * 重播
77 | */
78 | fun replay()
79 |
80 | /**
81 | * 暂停
82 | */
83 | fun pause()
84 |
85 | /**
86 | * 停止
87 | */
88 | fun stop()
89 |
90 | /**
91 | * 释放资源
92 | */
93 | fun release()
94 |
95 | /**
96 | * 重置
97 | */
98 | fun reset()
99 |
100 | /**
101 | * 跳转
102 | */
103 | fun seekTo(time: Long)
104 |
105 | /**
106 | * 设置音量
107 | *
108 | */
109 | fun setVolume(volume: Int)
110 |
111 | /**
112 | * 绑定视图
113 | */
114 | fun attach()
115 |
116 | /**
117 | * 获取当前视图Bitmap
118 | */
119 | fun getBitmap(): Bitmap?
120 | }
--------------------------------------------------------------------------------
/artplayer-core/src/main/java/org/salient/artplayer/ui/ResizeTextureView.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer.ui
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.TextureView
6 | import android.view.View
7 | import org.salient.artplayer.conduction.ScaleType
8 |
9 | /**
10 | * description: 可改变大小的视频播放容器,可设置缩放模式
11 | *
12 | * @author Maiwenchang
13 | * email: cv.stronger@gmail.com
14 | * date: 2020-05-04 10:06 AM.
15 | */
16 | class ResizeTextureView : TextureView {
17 | private var mVideoWidth = 0
18 | private var mVideoHeight = 0
19 | private var screenType: ScaleType = ScaleType.DEFAULT
20 |
21 | constructor(context: Context) : super(context) {
22 |
23 | }
24 | constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {}
25 |
26 | fun setVideoSize(width: Int, height: Int) {
27 | mVideoWidth = width
28 | mVideoHeight = height
29 | requestLayout()
30 | }
31 |
32 | fun setScreenScale(type: ScaleType) {
33 | screenType = type
34 | requestLayout()
35 | }
36 |
37 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
38 | var widthSpec: Int = widthMeasureSpec
39 | var heightSpec = heightMeasureSpec
40 | if (rotation == 90f || rotation == 270f) {
41 | // 软解码时处理旋转信息,交换宽高
42 | widthSpec = heightMeasureSpec
43 | heightSpec = widthMeasureSpec
44 | }
45 | var width = View.getDefaultSize(mVideoWidth, widthSpec)
46 | var height = View.getDefaultSize(mVideoHeight, heightSpec)
47 | when (screenType) {
48 | ScaleType.SCALE_ORIGINAL -> {
49 | width = mVideoWidth
50 | height = mVideoHeight
51 | }
52 | ScaleType.SCALE_16_9 -> if (height > width / 16 * 9) {
53 | height = width / 16 * 9
54 | } else {
55 | width = height / 9 * 16
56 | }
57 | ScaleType.SCALE_4_3 -> if (height > width / 4 * 3) {
58 | height = width / 4 * 3
59 | } else {
60 | width = height / 3 * 4
61 | }
62 | ScaleType.SCALE_MATCH_PARENT -> {
63 | width = widthSpec
64 | height = heightSpec
65 | }
66 | ScaleType.SCALE_CENTER_CROP -> if (mVideoWidth > 0 && mVideoHeight > 0) {
67 | if (mVideoWidth * height > width * mVideoHeight) {
68 | width = height * mVideoWidth / mVideoHeight
69 | } else {
70 | height = width * mVideoHeight / mVideoWidth
71 | }
72 | }
73 | ScaleType.DEFAULT -> if (mVideoWidth > 0 && mVideoHeight > 0) {
74 | val widthSpecMode = MeasureSpec.getMode(widthSpec)
75 | val widthSpecSize = MeasureSpec.getSize(widthSpec)
76 | val heightSpecMode = MeasureSpec.getMode(heightSpec)
77 | val heightSpecSize = MeasureSpec.getSize(heightSpec)
78 | if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) { // the size is fixed
79 | width = widthSpecSize
80 | height = heightSpecSize
81 | // for compatibility, we adjust size based on aspect ratio
82 | if (mVideoWidth * height < width * mVideoHeight) { //Log.i("@@@", "image too wide, correcting");
83 | width = height * mVideoWidth / mVideoHeight
84 | } else if (mVideoWidth * height > width * mVideoHeight) { //Log.i("@@@", "image too tall, correcting");
85 | height = width * mVideoHeight / mVideoWidth
86 | }
87 | } else if (widthSpecMode == MeasureSpec.EXACTLY) { // only the width is fixed, adjust the height to match aspect ratio if possible
88 | width = widthSpecSize
89 | height = width * mVideoHeight / mVideoWidth
90 | if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) { // couldn't match aspect ratio within the constraints
91 | height = heightSpecSize
92 | }
93 | } else if (heightSpecMode == MeasureSpec.EXACTLY) { // only the height is fixed, adjust the width to match aspect ratio if possible
94 | height = heightSpecSize
95 | width = height * mVideoWidth / mVideoHeight
96 | if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) { // couldn't match aspect ratio within the constraints
97 | width = widthSpecSize
98 | }
99 | } else { // neither the width nor the height are fixed, try to use actual video size
100 | width = mVideoWidth
101 | height = mVideoHeight
102 | if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) { // too tall, decrease both width and height
103 | height = heightSpecSize
104 | width = height * mVideoWidth / mVideoHeight
105 | }
106 | if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) { // too wide, decrease both width and height
107 | width = widthSpecSize
108 | height = width * mVideoHeight / mVideoWidth
109 | }
110 | }
111 | }
112 | }
113 | setMeasuredDimension(width, height)
114 | }
115 | }
--------------------------------------------------------------------------------
/artplayer-core/src/main/java/org/salient/artplayer/ui/TinyVideoView.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer.ui
2 |
3 | import android.content.Context
4 | import android.view.GestureDetector
5 | import android.view.Gravity
6 | import android.view.ViewGroup
7 | import org.salient.artplayer.conduction.WindowType
8 | import org.salient.artplayer.ui.extend.TinyViewGestureListener
9 |
10 | /**
11 | * description: 视频播放视容器 - 小窗模式
12 | * 特性:单指拖动,双指缩放,双击暂停/恢复播放
13 | *
14 | * @author Maiwenchang
15 | * email: cv.stronger@gmail.com
16 | * date: 2020-05-04 10:06 AM.
17 | */
18 | open class TinyVideoView(
19 | context: Context,
20 | params: ViewGroup.LayoutParams? = null,
21 | override var origin: VideoView? = null
22 | ) : VideoView(context), ITinyVideoView {
23 |
24 | private val gestureListener: TinyViewGestureListener
25 |
26 | /**
27 | * 是否可以拖动
28 | */
29 | var isMovable: Boolean = true
30 | set(value) {
31 | field = value
32 | gestureListener.isMovable = value
33 | }
34 |
35 | /**
36 | * 是否可以缩放
37 | */
38 | var isScalable: Boolean = true
39 | set(value) {
40 | field = value
41 | gestureListener.isScalable = value
42 | }
43 |
44 | init {
45 | tag = WindowType.TINY
46 | if (params != null) {
47 | layoutParams = params
48 | } else {
49 | layoutParams = LayoutParams(16 * 40, 9 * 40).apply {
50 | gravity = Gravity.BOTTOM or Gravity.END
51 | setMargins(30, 30, 30, 30)
52 | }
53 | }
54 |
55 | gestureListener = TinyViewGestureListener(this, isMovable, isScalable)
56 | val gestureDetector = GestureDetector(getContext(), gestureListener)
57 | setOnTouchListener { v, event ->
58 | if (gestureDetector.onTouchEvent(event)) true
59 | else gestureListener.onTouch(v, event)
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/artplayer-core/src/main/java/org/salient/artplayer/ui/extend/FullscreenGestureListener.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer.ui.extend
2 |
3 | import android.view.GestureDetector.SimpleOnGestureListener
4 | import android.view.MotionEvent
5 | import android.view.View
6 | import org.salient.artplayer.conduction.PlayerState
7 | import org.salient.artplayer.extend.Utils
8 | import org.salient.artplayer.ui.VideoView
9 | import kotlin.math.abs
10 |
11 |
12 | /**
13 | * description: 全屏的手势监听,支持经典的亮度(左方),音量(右方),快进(下方)手势
14 | *
15 | * @author Maiwenchang
16 | * email: cv.stronger@gmail.com
17 | * date: 2020-05-22 10:06 PM.
18 | */
19 | class FullscreenGestureListener(
20 | private val target: VideoView,
21 | var isVolumeGestureEnable:Boolean, //是否开启音量手势
22 | var isBrightnessGestureEnable: Boolean, //是否开启亮度手势
23 | var isProgressGestureEnable: Boolean //是否开启进度拖动手势
24 | ) : SimpleOnGestureListener(), View.OnTouchListener {
25 | private var currentX = 0f
26 | private var currentY = 0f
27 | private var currentWidth = 0f
28 | private var currentHeight = 0f
29 |
30 | private var currentVolume: Int = 0
31 | private var currentBrightness: Float = 0f
32 | private var currentProgress: Long = 0
33 | private var progressOffset: Long = 0
34 |
35 | private var isFirstTouch = false //按住屏幕不放的第一次点击,则为true
36 | private var isChangeProgress = false//判断是改变进度条则为true,否则为false
37 | private var isChangeBrightness = false //判断是不是改变亮度的操作
38 | private var isChangeVolume = false //判断是不是改变音量的操作
39 |
40 | override fun onDown(e: MotionEvent): Boolean {
41 | currentX = target.x
42 | currentY = target.y
43 | currentWidth = target.width.toFloat()
44 | currentHeight = target.height.toFloat()
45 |
46 | isFirstTouch = true
47 | isChangeProgress = false
48 | isChangeVolume = false
49 | isChangeBrightness = false
50 |
51 | currentVolume = target.audioManager.getVolume()
52 | currentBrightness = Utils.scanForActivity(target.context)?.window?.attributes?.screenBrightness
53 | ?: 0f
54 | currentProgress = if (target.isPlaying) target.currentPosition else 0
55 | return true
56 | }
57 |
58 | override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
59 | //全屏
60 | if (e2.pointerCount == 1) { //单指移动
61 | val mOldX = e1.x
62 | val mOldY = e1.y
63 | val x = e2.rawX.toInt()
64 | val y = e2.rawY.toInt()
65 | if (isFirstTouch) {
66 | isChangeProgress = abs(distanceX) >= abs(distanceY)
67 | if (!isChangeProgress) {
68 | if (mOldX > currentWidth * 2.0 / 3) { //右边三分之一区域滑动
69 | isChangeVolume = true && isVolumeGestureEnable
70 | } else if (mOldX < currentWidth / 3.0) { //左边三分之一区域滑动
71 | isChangeBrightness = true && isBrightnessGestureEnable
72 | }
73 | }
74 | isChangeProgress = isChangeProgress && isProgressGestureEnable
75 | isFirstTouch = false
76 | }
77 | if (isChangeProgress) {
78 | onSeekProgressControl((x - mOldX) / currentWidth)
79 | } else if (isChangeBrightness) {
80 | onBrightnessSlide((mOldY - y) / currentHeight)
81 | } else if (isChangeVolume) {
82 | onVolumeSlide((mOldY - y) / currentHeight)
83 | }
84 | return true
85 | }
86 | return false
87 | }
88 |
89 | /**
90 | * 滑动改变播放的快进快退
91 | *
92 | * @param seekDistance
93 | */
94 | private fun onSeekProgressControl(percent: Float) {
95 | val duration = target.duration
96 | val offset = (currentProgress + percent * duration).toLong()
97 | progressOffset = if (offset <= 100) 100 else if (offset > duration) duration else offset
98 | }
99 |
100 | /**
101 | * 滑动改变声音大小
102 | *
103 | * @param percent
104 | */
105 | private fun onVolumeSlide(percent: Float) {
106 | val maxVolume = target.audioManager.getMaxVolume()
107 | var index: Float = currentVolume + percent * maxVolume
108 | index = if (index > maxVolume) maxVolume.toFloat() else if (index < 0) 0f else index
109 | // 变更声音
110 | target.audioManager.setVolume(index.toInt())
111 | }
112 |
113 | /**
114 | * 滑动改变亮度
115 | *
116 | * @param percent
117 | */
118 | private fun onBrightnessSlide(percent: Float) {
119 | val activity = Utils.scanForActivity(target.context) ?: return
120 | val attributes = activity.window.attributes
121 | var brightness = currentBrightness + percent
122 | brightness = if (brightness > 1.0f) 1.0f else if (brightness < 0.1f) 0.1f else brightness
123 | attributes.screenBrightness = brightness
124 | activity.window.attributes = attributes
125 | }
126 |
127 | override fun onTouch(v: View, event: MotionEvent): Boolean {
128 | val action = event.action
129 | if (action == MotionEvent.ACTION_UP) {
130 | if (isChangeProgress) {
131 | target.seekTo(progressOffset)
132 | }
133 | isChangeProgress = false
134 | isChangeVolume = false
135 | isChangeBrightness = false
136 | }
137 | return false
138 | }
139 |
140 | override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
141 | return target.performClick()
142 | }
143 |
144 | override fun onDoubleTap(e: MotionEvent): Boolean {
145 | //双击播放或暂停
146 | if (target.isPlaying) {
147 | target.pause()
148 | } else if (target.playerState.code > PlayerState.PREPARED.code) {
149 | target.start()
150 | }
151 | return true
152 | }
153 | }
--------------------------------------------------------------------------------
/artplayer-core/src/main/java/org/salient/artplayer/ui/extend/OrientationEventManager.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer.ui.extend
2 |
3 | import android.content.Context
4 | import android.content.pm.ActivityInfo
5 | import android.provider.Settings
6 | import android.provider.Settings.SettingNotFoundException
7 | import android.util.Log
8 | import android.view.OrientationEventListener
9 | import org.salient.artplayer.extend.Utils
10 | import org.salient.artplayer.ui.VideoView
11 |
12 | /**
13 | * description: 重力感应方向管理器
14 | *
15 | * @author Maiwenchang
16 | * email: cv.stronger@gmail.com
17 | * date: 2020-05-04 10:06 AM.
18 | */
19 | class OrientationEventManager {
20 | private var currentOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
21 | private var orientationListenerDelayTime: Long = 0
22 | private var isRotateLocked = 0 //0 代表方向锁定,1 代表没方向锁定
23 | private var mOrientationChangeListener: OnOrientationChangeListener? = null
24 | private var orientationEventListener: OrientationEventListener? = null //加速度传感器监听
25 |
26 | fun orientationDisable() {
27 | orientationEventListener?.disable()
28 | }
29 |
30 | fun orientationEnable(context: Context, videoView: VideoView, orientationChangeListener: OnOrientationChangeListener?) {
31 | mOrientationChangeListener = orientationChangeListener
32 | orientationEventListener = object : OrientationEventListener(context, 5) {
33 | // 加速度传感器监听,用于自动旋转屏幕
34 | override fun onOrientationChanged(orientation: Int) {
35 | Log.d(javaClass.name, "onOrientationChanged() called with orientation: $orientation");
36 | try {
37 | //系统是否开启方向锁定
38 | isRotateLocked = Settings.System.getInt(context.contentResolver, Settings.System.ACCELEROMETER_ROTATION)
39 | } catch (e: SettingNotFoundException) {
40 | e.printStackTrace()
41 | }
42 | if (isRotateLocked == 0) return //方向被锁定,直接返回
43 | val operationDelay = System.currentTimeMillis() - orientationListenerDelayTime > 500
44 | if ((orientation >= 300 || orientation <= 30) && operationDelay) {
45 | //屏幕顶部朝上
46 | onOrientationPortrait(videoView)
47 | orientationListenerDelayTime = System.currentTimeMillis()
48 | } else if (orientation in 260..280 && operationDelay) {
49 | //屏幕左边朝上
50 | onOrientationLandscape(videoView)
51 | orientationListenerDelayTime = System.currentTimeMillis()
52 | } else if (orientation in 70..90 && operationDelay) {
53 | //屏幕右边朝上
54 | onOrientationReverseLandscape(videoView)
55 | orientationListenerDelayTime = System.currentTimeMillis()
56 | }
57 | }
58 | }
59 | currentOrientation = Utils.getRequestedOrientation(context)
60 | orientationEventListener?.enable()
61 | }
62 |
63 | /**
64 | * 横屏(屏幕左边朝上)
65 | */
66 | private fun onOrientationLandscape(videoView: VideoView) {
67 | if (currentOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) return
68 | if (currentOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
69 | currentOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
70 | return
71 | }
72 | currentOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
73 | mOrientationChangeListener?.onOrientationLandscape(videoView)
74 | }
75 |
76 | /**
77 | * 反向横屏(屏幕右边朝上)
78 | */
79 | private fun onOrientationReverseLandscape(videoView: VideoView) {
80 | if (currentOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) return
81 | if (currentOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
82 | currentOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
83 | return
84 | }
85 | currentOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
86 | mOrientationChangeListener?.onOrientationReverseLandscape(videoView)
87 | }
88 |
89 | /**
90 | * 竖屏
91 | */
92 | private fun onOrientationPortrait(videoView: VideoView) {
93 | if (currentOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
94 | return
95 | }
96 | if ((currentOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
97 | || currentOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE)) {
98 | currentOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
99 | return
100 | }
101 | currentOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
102 | mOrientationChangeListener?.onOrientationPortrait(videoView)
103 | }
104 |
105 | /**
106 | * 设置重力感应横竖屏管理
107 | */
108 | fun setOnOrientationChangeListener(orientationChangeListener: OnOrientationChangeListener?) {
109 | mOrientationChangeListener = orientationChangeListener
110 | }
111 |
112 | interface OnOrientationChangeListener {
113 | fun onOrientationLandscape(videoView: VideoView?)
114 | fun onOrientationReverseLandscape(videoView: VideoView?)
115 | fun onOrientationPortrait(videoView: VideoView?)
116 | }
117 | }
--------------------------------------------------------------------------------
/artplayer-core/src/main/java/org/salient/artplayer/ui/extend/TinyViewGestureListener.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer.ui.extend
2 |
3 | import android.view.GestureDetector.SimpleOnGestureListener
4 | import android.view.MotionEvent
5 | import android.view.View
6 | import android.view.View.OnTouchListener
7 | import android.view.ViewGroup
8 | import org.salient.artplayer.conduction.PlayerState
9 | import org.salient.artplayer.ui.VideoView
10 |
11 |
12 | /**
13 | * description: 小窗的手势监听,支持单指拖动,双指缩放
14 | *
15 | * @author Maiwenchang
16 | * email: cv.stronger@gmail.com
17 | * date: 2020-05-22 10:06 PM.
18 | */
19 | class TinyViewGestureListener(
20 | private val target: VideoView,
21 | var isMovable: Boolean = true, //是否可以拖动
22 | var isScalable: Boolean = true //是否可以缩放
23 | ) : SimpleOnGestureListener(), OnTouchListener {
24 | private var currentX = 0f
25 | private var currentY = 0f
26 | private var currentWidth = 0f
27 | private var currentHeight = 0f
28 | private var initDistance = 0f //缩放时,两指间初始距离
29 |
30 | override fun onDown(e: MotionEvent): Boolean {
31 | currentX = target.x
32 | currentY = target.y
33 | currentWidth = target.width.toFloat()
34 | currentHeight = target.height.toFloat()
35 | initDistance = 0f
36 | return true
37 | }
38 |
39 | override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
40 | if (e2.pointerCount == 1) { //单指移动
41 | return moveWindow(target, e1, e2)
42 | } else if (e2.pointerCount == 2) { //双指缩放
43 | return zoomWindow(target, e1, e2)
44 | }
45 | return false
46 | }
47 |
48 | /**
49 | * Move the window according to the finger position
50 | * 根据手指移动窗口
51 | */
52 | private fun moveWindow(target: View, e1: MotionEvent, e2: MotionEvent): Boolean {
53 | if (!isMovable) return false
54 | val viewParent = target.parent as ViewGroup
55 | val parentWidth = viewParent.width
56 | val parentHeight = viewParent.height
57 | when (e2.action) {
58 | MotionEvent.ACTION_MOVE -> {
59 | var x = currentX + e2.rawX - e1.rawX
60 | var y = currentY + e2.rawY - e1.rawY
61 | if (x < 0) {
62 | x = 0f
63 | }
64 | if (x > parentWidth - target.width) {
65 | x = parentWidth - target.width.toFloat()
66 | }
67 | if (y < 0) {
68 | y = 0f
69 | }
70 | if (y > parentHeight - target.height) {
71 | y = parentHeight - target.height.toFloat()
72 | }
73 | target.y = y
74 | target.x = x
75 | }
76 | }
77 | return true
78 | }
79 |
80 | /**
81 | * Zoom window according to two fingers
82 | * @param e1 The first down motion event that started the scrolling.
83 | * @param e2 The move motion event that triggered the current onScroll.
84 | * 根据两个手指缩放窗口
85 | */
86 | private fun zoomWindow(target: View, e1: MotionEvent, e2: MotionEvent): Boolean {
87 | if (!isScalable) return false
88 | if (e2.pointerCount == 2 && e2.action == MotionEvent.ACTION_MOVE) {
89 | val x = e2.getX(0) - e2.getX(1)
90 | val y = e2.getY(0) - e2.getY(1)
91 | val value = Math.sqrt(x * x + y * y.toDouble()).toFloat() // 计算两点的距离
92 | if (initDistance == 0f) {
93 | initDistance = value
94 | } else if (Math.abs(value - initDistance) >= 2) {
95 | val scale = value / initDistance // 当前两点间的距离除以手指落下时两点间的距离就是需要缩放的比例。
96 | if (Math.abs(scale) > 0.05) {
97 | val layoutParams = target.layoutParams
98 | var height = currentHeight * scale
99 | var width = currentWidth * scale
100 | val WH = width / height
101 | if (width < 400 * WH) {
102 | width = 400 * WH
103 | }
104 | val viewParent = target.parent as ViewGroup
105 | val parentWidth = viewParent.width
106 | if (width > parentWidth) {
107 | width = parentWidth.toFloat()
108 | }
109 | if (height < 400 / WH) {
110 | height = 400 / WH
111 | }
112 | val parentHeight = viewParent.height
113 | if (height > parentHeight) {
114 | height = parentHeight.toFloat()
115 | }
116 | val parentWH = parentWidth / parentHeight
117 | if (WH > parentWH) {
118 | height = width / WH
119 | } else {
120 | width = height * WH
121 | }
122 | layoutParams.width = width.toInt()
123 | layoutParams.height = height.toInt()
124 | target.requestLayout()
125 | }
126 | }
127 | }
128 | return true
129 | }
130 |
131 | /**
132 | * revise position
133 | * 修正位置
134 | */
135 | private fun revisePosition(target: View) {
136 | var X = target.x
137 | var Y = target.y
138 | if (X < 0) {
139 | X = 0f
140 | }
141 | val viewParent = target.parent as ViewGroup
142 | val parentWidth = viewParent.width
143 | if (X > parentWidth - target.width) {
144 | X = parentWidth - target.width.toFloat()
145 | }
146 | if (Y < 0) {
147 | Y = 0f
148 | }
149 | val parentHeight = viewParent.height
150 | if (Y > parentHeight - target.height) {
151 | Y = parentHeight - target.height.toFloat()
152 | }
153 | target.y = Y
154 | target.x = X
155 | }
156 |
157 | override fun onTouch(v: View, event: MotionEvent): Boolean {
158 | val action = event.action
159 | if (action == MotionEvent.ACTION_UP) {
160 | revisePosition(target)
161 | initDistance = 0f
162 | }
163 | return false
164 | }
165 |
166 |
167 | override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
168 | target.performClick()
169 | return false
170 | }
171 |
172 | override fun onDoubleTap(e: MotionEvent): Boolean {
173 | //双击播放或暂停
174 | if (target.isPlaying) {
175 | target.pause()
176 | } else if (target.playerState.code > PlayerState.PREPARED.code) {
177 | target.start()
178 | }
179 | return true
180 | }
181 | }
--------------------------------------------------------------------------------
/artplayer-core/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/artplayer-core/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/artplayer-core/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/artplayer-exo/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/artplayer-exo/bintary_push.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.novoda.bintray-release'
2 |
3 | publish {
4 | userOrg = 'cv1948'//bintray.com用户名
5 | groupId = 'org.salient.artvideoplayer'//jcenter上的路径
6 | artifactId = "artplayer-exo"//项目名称
7 | publishVersion = android.defaultConfig.versionName//版本号
8 | desc = 'ExoPlayer for ArtVideoPlayer'//描述
9 | website = 'https://github.com/maiwenchang/ArtVideoPlayer'//网站
10 | }
--------------------------------------------------------------------------------
/artplayer-exo/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 |
5 | android {
6 | compileSdkVersion rootProject.compileSdkVersion
7 |
8 | defaultConfig {
9 | minSdkVersion rootProject.minSdkVersion
10 | targetSdkVersion rootProject.targetSdkVersion
11 |
12 | }
13 |
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 |
21 | compileOptions {
22 | targetCompatibility JavaVersion.VERSION_1_8
23 | sourceCompatibility JavaVersion.VERSION_1_8
24 | }
25 |
26 | kotlinOptions {
27 | jvmTarget = JavaVersion.VERSION_1_8
28 | }
29 |
30 | lintOptions {
31 | abortOnError false
32 | }
33 |
34 | }
35 |
36 | dependencies {
37 | implementation fileTree(dir: 'libs', include: ['*.jar'])
38 |
39 | implementation("androidx.appcompat:appcompat:$compactVersion")
40 |
41 | api("com.google.android.exoplayer:exoplayer:$exoPlayerVersion")
42 | api("com.google.android.exoplayer:extension-rtmp:$exoPlayerVersion")
43 |
44 | implementation project(':artplayer-core')
45 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
46 | }
47 | //apply from: './bintary_push.gradle'
--------------------------------------------------------------------------------
/artplayer-exo/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/artplayer-exo/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/artplayer-exo/src/main/java/org/salient/artplayer/exo/ExoSourceBuilder.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer.exo
2 |
3 | import android.content.Context
4 | import android.net.Uri
5 | import com.google.android.exoplayer2.C
6 | import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory
7 | import com.google.android.exoplayer2.source.LoopingMediaSource
8 | import com.google.android.exoplayer2.source.MediaSource
9 | import com.google.android.exoplayer2.source.ProgressiveMediaSource
10 | import com.google.android.exoplayer2.source.dash.DashMediaSource
11 | import com.google.android.exoplayer2.source.dash.DefaultDashChunkSource
12 | import com.google.android.exoplayer2.source.hls.HlsMediaSource
13 | import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource
14 | import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource
15 | import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter
16 | import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
17 | import com.google.android.exoplayer2.upstream.TransferListener
18 | import java.io.File
19 |
20 | /**
21 | * description: 构建ExoPlayer的MediaSource
22 | *
23 | * @author Maiwenchang
24 | * email: cv.stronger@gmail.com
25 | * date: 2020-05-08 3:05 PM.
26 | */
27 | class ExoSourceBuilder(private val context: Context, private val dataSource: String) {
28 |
29 | /**
30 | * 缓存地址
31 | */
32 | var cacheDir: File? = null
33 |
34 | /**
35 | * header
36 | */
37 | var headers: Map? = null
38 |
39 | /**
40 | * 是否缓存
41 | */
42 | var cacheEnable: Boolean = true
43 |
44 | /**
45 | * 是否循环播放
46 | */
47 | var isLooping: Boolean = false
48 |
49 | /**
50 | * 最大缓存大小
51 | */
52 | var maxCacheSize: Long = ExoSourceManager.EXO_CACHE_DEFAULT_MAX_SIZE
53 |
54 | /**
55 | * A listener of data transfer events.
56 | */
57 | var transferListener: TransferListener? = DefaultBandwidthMeter.Builder(context).build()
58 |
59 | fun build(): MediaSource {
60 | val contentUri = Uri.parse(dataSource)
61 | val contentType = ExoSourceManager.inferContentType(dataSource)
62 | val mediaSource: MediaSource
63 | val cache = ExoSourceManager.getCacheInstance(context, cacheDir, maxCacheSize)
64 | val dataSourceFactoryCache = cache?.let {
65 | ExoSourceManager.getCacheDataSourceFactory(context, headers, transferListener, cache)
66 | } ?: ExoSourceManager.getDefaultDataSourceFactory(context, headers, transferListener)
67 | mediaSource = when (contentType) {
68 | C.TYPE_SS -> {
69 | val httpDataSourceFactory = ExoSourceManager.getHttpDataSourceFactory(context, headers, transferListener)
70 | val defaultDataSourceFactory = DefaultDataSourceFactory(context, transferListener, httpDataSourceFactory)
71 | SsMediaSource.Factory(
72 | DefaultSsChunkSource.Factory(dataSourceFactoryCache),
73 | defaultDataSourceFactory).createMediaSource(contentUri)
74 | }
75 | C.TYPE_DASH -> {
76 | val httpDataSourceFactory = ExoSourceManager.getHttpDataSourceFactory(context, headers, transferListener)
77 | val defaultDataSourceFactory = DefaultDataSourceFactory(context, transferListener, httpDataSourceFactory)
78 | DashMediaSource.Factory(DefaultDashChunkSource.Factory(dataSourceFactoryCache),
79 | defaultDataSourceFactory).createMediaSource(contentUri)
80 | }
81 | C.TYPE_HLS -> HlsMediaSource.Factory(dataSourceFactoryCache).createMediaSource(contentUri)
82 | C.TYPE_OTHER -> {
83 | ProgressiveMediaSource.Factory(dataSourceFactoryCache, DefaultExtractorsFactory())
84 | .createMediaSource(contentUri)
85 | }
86 | else -> {
87 | ProgressiveMediaSource.Factory(dataSourceFactoryCache, DefaultExtractorsFactory())
88 | .createMediaSource(contentUri)
89 | }
90 | }
91 | return if (isLooping) {
92 | LoopingMediaSource(mediaSource)
93 | } else mediaSource
94 | }
95 |
96 |
97 | }
--------------------------------------------------------------------------------
/artplayer-exo/src/main/java/org/salient/artplayer/exo/ExoSourceManager.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer.exo
2 |
3 | import android.content.Context
4 | import android.net.Uri
5 | import android.text.TextUtils
6 | import com.google.android.exoplayer2.C
7 | import com.google.android.exoplayer2.database.ExoDatabaseProvider
8 | import com.google.android.exoplayer2.upstream.*
9 | import com.google.android.exoplayer2.upstream.cache.*
10 | import com.google.android.exoplayer2.util.Util
11 | import java.io.File
12 |
13 | /**
14 | * description: EXO视频播放器的缓存管理类
15 | *
16 | * @author Maiwenchang
17 | * email: cv.stronger@gmail.com
18 | * date: 2020-05-04 10:06 AM.
19 | */
20 | object ExoSourceManager {
21 |
22 | private const val TAG = "ExoSourceManager"
23 | /**
24 | * 默认最大缓存 512M
25 | */
26 | const val EXO_CACHE_DEFAULT_MAX_SIZE = 512 * 1024 * 1024.toLong()
27 |
28 | /**
29 | * 获取DataSourceFactory
30 | */
31 | fun getCacheDataSourceFactory(context: Context, headers: Map?, transferListener: TransferListener?, cache: Cache): DataSource.Factory {
32 | return CacheDataSourceFactory(cache, getDefaultDataSourceFactory(context, headers, transferListener), CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR)
33 | }
34 |
35 | /**
36 | * 获取DefaultDataSourceFactory
37 | */
38 | fun getDefaultDataSourceFactory(context: Context, headers: Map?, transferListener: TransferListener?): DataSource.Factory {
39 | return DefaultDataSourceFactory(context, transferListener, getHttpDataSourceFactory(context, headers, transferListener))
40 | }
41 |
42 | fun getHttpDataSourceFactory(context: Context, headers: Map?, transferListener: TransferListener?): DataSource.Factory {
43 | val dataSourceFactory = DefaultHttpDataSourceFactory(
44 | Util.getUserAgent(context, TAG),
45 | transferListener)
46 | headers?.forEach { key, value ->
47 | dataSourceFactory.defaultRequestProperties[key] = value
48 | }
49 | return dataSourceFactory
50 | }
51 |
52 |
53 | //@C.ContentType
54 | fun inferContentType(fileName: String): Int {
55 | Util.toLowerInvariant(fileName).let {
56 | return if (it.endsWith(".mpd")) {
57 | C.TYPE_DASH
58 | } else if (it.endsWith(".m3u8")) {
59 | C.TYPE_HLS
60 | } else if (it.endsWith(".ism") || it.endsWith(".isml")
61 | || it.endsWith(".ism/manifest") || it.endsWith(".isml/manifest")) {
62 | C.TYPE_SS
63 | } else {
64 | C.TYPE_OTHER
65 | }
66 | }
67 |
68 | }
69 |
70 | /**
71 | * 获取本地缓存
72 | */
73 | @Synchronized
74 | fun getCacheInstance(context: Context, cacheDir: File?, maxCacheSize: Long = EXO_CACHE_DEFAULT_MAX_SIZE): Cache? {
75 | val dirs = if (cacheDir != null) {
76 | cacheDir.absolutePath
77 | } else {
78 | context.cacheDir.absolutePath
79 | }
80 | val path = dirs + File.separator + "exo"
81 | val isLocked = SimpleCache.isCacheFolderLocked(File(path))
82 | if (!isLocked) {
83 | return SimpleCache(File(path), LeastRecentlyUsedCacheEvictor(maxCacheSize), ExoDatabaseProvider(context))
84 | } else {
85 | return null
86 | }
87 | }
88 |
89 | /**
90 | * 释放缓存
91 | */
92 | fun release(mCache: Cache? = null) {
93 | try {
94 | mCache?.release()
95 | } catch (e: Exception) {
96 | e.printStackTrace()
97 | }
98 | }
99 |
100 |
101 | /**
102 | * 清理缓存
103 | * Cache需要release之后才能clear
104 | */
105 | fun clearCache(context: Context, cacheDir: File?, url: String?) {
106 | try {
107 | val cache = getCacheInstance(context, cacheDir)
108 | if (!TextUtils.isEmpty(url)) {
109 | if (cache != null) {
110 | CacheUtil.remove(cache, CacheUtil.generateKey(Uri.parse(url)))
111 | }
112 | } else {
113 | if (cache != null) {
114 | for (key in cache.keys) {
115 | CacheUtil.remove(cache, key)
116 | }
117 | }
118 | }
119 | } catch (e: Exception) {
120 | e.printStackTrace()
121 | }
122 | }
123 |
124 | /**
125 | * 是否缓存成功
126 | */
127 | fun isCached(context: Context, cacheDir: File?, url: String?): Boolean {
128 | return resolveCacheState(getCacheInstance(context, cacheDir), url)
129 | }
130 |
131 | /**
132 | * 根据缓存块判断是否缓存成功
133 | *
134 | * @param cache
135 | */
136 | private fun resolveCacheState(cache: Cache?, url: String?): Boolean {
137 | var isCache = false
138 | if (url?.isNotEmpty() == true) {
139 | val key = CacheUtil.generateKey(Uri.parse(url))
140 | if (key.isNotEmpty()) {
141 | val cachedSpans = cache!!.getCachedSpans(key)
142 | if (cachedSpans.size == 0) {
143 | isCache = false
144 | } else {
145 | val contentLength = ContentMetadata.getContentLength(cache.getContentMetadata(key))
146 | var currentLength: Long = 0
147 | for (cachedSpan in cachedSpans) {
148 | currentLength += cache.getCachedLength(key, cachedSpan.position, cachedSpan.length)
149 | }
150 | isCache = currentLength >= contentLength
151 | }
152 | } else {
153 | isCache = false
154 | }
155 | }
156 | return isCache
157 | }
158 | }
--------------------------------------------------------------------------------
/artplayer-exo/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | artplayer-exo
3 |
4 |
--------------------------------------------------------------------------------
/artplayer-ijk/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/artplayer-ijk/bintary_push.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.novoda.bintray-release'
2 |
3 | publish {
4 | userOrg = 'cv1948'//bintray.com用户名
5 | groupId = 'org.salient.artvideoplayer'//jcenter上的路径
6 | artifactId = "artplayer-ijk"//项目名称
7 | publishVersion = android.defaultConfig.versionName//版本号
8 | desc = 'IjkPlayer for ArtVideoPlayer'//描述
9 | website = 'https://github.com/maiwenchang/ArtVideoPlayer'//网站
10 | }
--------------------------------------------------------------------------------
/artplayer-ijk/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 | apply plugin: 'kotlin-kapt'
5 |
6 | android {
7 | compileSdkVersion rootProject.compileSdkVersion
8 |
9 | defaultConfig {
10 | minSdkVersion rootProject.minSdkVersion
11 | targetSdkVersion rootProject.targetSdkVersion
12 |
13 | }
14 |
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 |
22 | compileOptions {
23 | targetCompatibility JavaVersion.VERSION_1_8
24 | sourceCompatibility JavaVersion.VERSION_1_8
25 | }
26 |
27 | lintOptions {
28 | abortOnError false
29 | }
30 |
31 | }
32 |
33 | dependencies {
34 | implementation fileTree(dir: 'libs', include: ['*.jar'])
35 | implementation "androidx.appcompat:appcompat:$compactVersion"
36 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
37 |
38 | implementation project(':artplayer-core')
39 |
40 | api "tv.danmaku.ijk.media:ijkplayer-java:$ijkPlayerVersion"
41 | }
42 |
43 | //apply from: './bintary_push.gradle'
--------------------------------------------------------------------------------
/artplayer-ijk/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/artplayer-ijk/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/artplayer-ijk/src/main/java/org/salient/artplayer/ijk/RawDataSourceProvider.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer.ijk
2 |
3 | import android.content.Context
4 | import android.content.res.AssetFileDescriptor
5 | import android.net.Uri
6 | import tv.danmaku.ijk.media.player.misc.IMediaDataSource
7 | import java.io.ByteArrayOutputStream
8 | import java.io.FileNotFoundException
9 | import java.io.IOException
10 | import java.io.InputStream
11 |
12 | /**
13 | * description: 构建Raw播放DataSource帮助类
14 | *
15 | * @author Maiwenchang
16 | * email: cv.stronger@gmail.com
17 | * date: 2020-05-04 10:06 AM.
18 | */
19 | class RawDataSourceProvider(private var mDescriptor: AssetFileDescriptor) : IMediaDataSource {
20 | private var mMediaBytes: ByteArray? = null
21 | override fun readAt(position: Long, buffer: ByteArray, offset: Int, size: Int): Int {
22 | val bytes = mMediaBytes ?: return 0
23 | if (position >= bytes.size) {
24 | return -1
25 | }
26 | var length: Int
27 | if (position + size < bytes.size) {
28 | length = size
29 | } else {
30 | length = (bytes.size - position).toInt()
31 | if (length > buffer.size) length = buffer.size
32 | }
33 | System.arraycopy(bytes, position.toInt(), buffer, offset, length)
34 | return length
35 | }
36 |
37 | @Throws(IOException::class)
38 | override fun getSize(): Long {
39 | val length = mDescriptor.length
40 | if (mMediaBytes == null) {
41 | val inputStream: InputStream = mDescriptor.createInputStream()
42 | mMediaBytes = readBytes(inputStream)
43 | }
44 | return length
45 | }
46 |
47 | @Throws(IOException::class)
48 | override fun close() {
49 | mDescriptor.close()
50 | mMediaBytes = null
51 | }
52 |
53 | @Throws(IOException::class)
54 | private fun readBytes(inputStream: InputStream): ByteArray {
55 | val byteBuffer = ByteArrayOutputStream()
56 | val bufferSize = 1024
57 | val buffer = ByteArray(bufferSize)
58 | var len: Int
59 | while (inputStream.read(buffer).also { len = it } != -1) {
60 | byteBuffer.write(buffer, 0, len)
61 | }
62 | return byteBuffer.toByteArray()
63 | }
64 |
65 | companion object {
66 | fun create(context: Context, uri: Uri): RawDataSourceProvider? {
67 | try {
68 | val fileDescriptor = context.contentResolver.openAssetFileDescriptor(uri, "r")
69 | ?: return null
70 | return RawDataSourceProvider(fileDescriptor)
71 | } catch (e: FileNotFoundException) {
72 | e.printStackTrace()
73 | }
74 | return null
75 | }
76 | }
77 |
78 | }
--------------------------------------------------------------------------------
/artplayer-ijk/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | artplayer-ijk
3 |
4 |
--------------------------------------------------------------------------------
/artplayer-ui/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/artplayer-ui/bintary_push.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.novoda.bintray-release'
2 |
3 | publish {
4 | userOrg = 'cv1948'//bintray.com用户名
5 | groupId = 'org.salient.artvideoplayer'//jcenter上的路径
6 | artifactId = 'artplayer-ui'//项目名称
7 | publishVersion = android.defaultConfig.versionName//版本号
8 | desc = 'Controller panel for org.salient.artplayer'//描述
9 | website = 'https://github.com/maiwenchang/ArtVideoPlayer'//网站
10 | }
--------------------------------------------------------------------------------
/artplayer-ui/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 | apply plugin: 'kotlin-kapt'
5 |
6 | android {
7 | compileSdkVersion rootProject.compileSdkVersion
8 |
9 | defaultConfig {
10 | minSdkVersion rootProject.minSdkVersion
11 | targetSdkVersion rootProject.targetSdkVersion
12 | versionCode rootProject.libraryVersionCode
13 | versionName rootProject.libraryVersionName
14 | }
15 |
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | debug {
22 | minifyEnabled false
23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
24 | }
25 | }
26 |
27 | lintOptions {
28 | abortOnError false
29 | }
30 |
31 | }
32 |
33 | dependencies {
34 | implementation fileTree(dir: 'libs', include: ['*.jar'])
35 | compileOnly "androidx.appcompat:appcompat:$compactVersion"
36 | compileOnly project(':artplayer-core')
37 | }
38 |
39 | ////apply from: './bintary_push.gradle'
40 |
--------------------------------------------------------------------------------
/artplayer-ui/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/artplayer-ui/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/artplayer-ui/src/main/java/org/salient/artplayer/ui/listener/OrientationChangeListener.kt:
--------------------------------------------------------------------------------
1 | package org.salient.artplayer.ui.listener
2 |
3 | import OrientationEventManager.OnOrientationChangeListener
4 | import android.content.pm.ActivityInfo
5 | import org.salient.artplayer.AbsControlPanel
6 | import org.salient.artplayer.OrientationEventManager
7 | import org.salient.artplayer.Utils
8 | import org.salient.artplayer.VideoView
9 |
10 | /**
11 | * Created by Mai on 2018/11/14
12 | * *
13 | * Description: 处理重力感应横竖屏切换事件
14 | * *
15 | */
16 | class OrientationChangeListener : OnOrientationChangeListener {
17 | fun onOrientationLandscape(videoView: VideoView?) { //横屏(屏幕左边朝上)
18 | if (videoView == null) return
19 | val controlPanel: AbsControlPanel = videoView.getControlPanel() ?: return
20 | controlPanel.enterFullScreen(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE)
21 | }
22 |
23 | fun onOrientationReverseLandscape(videoView: VideoView?) { //反向横屏(屏幕右边朝上)
24 | if (videoView == null) return
25 | val controlPanel: AbsControlPanel = videoView.getControlPanel() ?: return
26 | controlPanel.enterFullScreen(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE)
27 | Utils.setRequestedOrientation(videoView.getContext(), ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE)
28 | }
29 |
30 | fun onOrientationPortrait(videoView: VideoView) { //竖屏
31 | if (videoView.getWindowType() === VideoView.WindowType.FULLSCREEN) {
32 | videoView.exitFullscreen()
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-hdpi/salient_brightness.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-hdpi/salient_brightness.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-hdpi/salient_volume.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-hdpi/salient_volume.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-mdpi/salient_brightness.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-mdpi/salient_brightness.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-mdpi/salient_volume.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-mdpi/salient_volume.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-xhdpi/salient_bg_loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-xhdpi/salient_bg_loading.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-xhdpi/salient_brightness.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-xhdpi/salient_brightness.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-xhdpi/salient_icon_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-xhdpi/salient_icon_back.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-xhdpi/salient_icon_full_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-xhdpi/salient_icon_full_screen.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-xhdpi/salient_icon_narrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-xhdpi/salient_icon_narrow.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-xhdpi/salient_icon_pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-xhdpi/salient_icon_pause.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-xhdpi/salient_icon_start.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-xhdpi/salient_icon_start.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-xhdpi/salient_icon_volume.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-xhdpi/salient_icon_volume.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-xhdpi/salient_icon_volume_hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-xhdpi/salient_icon_volume_hover.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-xhdpi/salient_volume.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-xhdpi/salient_volume.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-xxhdpi/salient_bg_loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-xxhdpi/salient_bg_loading.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-xxhdpi/salient_bottom_pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-xxhdpi/salient_bottom_pause.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-xxhdpi/salient_bottom_play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-xxhdpi/salient_bottom_play.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-xxhdpi/salient_brightness.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-xxhdpi/salient_brightness.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-xxhdpi/salient_icon_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-xxhdpi/salient_icon_back.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-xxhdpi/salient_icon_full_screen.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-xxhdpi/salient_icon_full_screen.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-xxhdpi/salient_icon_narrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-xxhdpi/salient_icon_narrow.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-xxhdpi/salient_icon_pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-xxhdpi/salient_icon_pause.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-xxhdpi/salient_icon_start.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-xxhdpi/salient_icon_start.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-xxhdpi/salient_icon_volume.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-xxhdpi/salient_icon_volume.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-xxhdpi/salient_icon_volume_hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-xxhdpi/salient_icon_volume_hover.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable-xxhdpi/salient_volume.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/artplayer-ui/src/main/res/drawable-xxhdpi/salient_volume.png
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable/progress_custom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
14 |
15 |
16 |
17 | -
18 |
19 |
20 |
21 |
28 |
29 |
30 |
31 |
32 | -
33 |
34 |
35 |
36 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable/salient_bg_btn_corner_stroke_white.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable/salient_seek_bar_video_white.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
10 | -
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | -
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable/salient_seek_thumb_video_white.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable/salient_selector_bottom_video_play.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable/salient_selector_video_play.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable/salient_selector_volume.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/artplayer-ui/src/main/res/drawable/salient_video_loading.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 |
4 | ext.kotlin_version = '1.3.72'
5 |
6 | ext {
7 | // App dependencies
8 | compileSdkVersion = 31
9 | minSdkVersion = 21
10 | targetSdkVersion = 31
11 | compactVersion = '1.1.0'
12 | ktx_version = '1.1.0'
13 | ijkPlayerVersion = '0.8.8'
14 | exoPlayerVersion = '2.11.3'
15 | artPlayerVersion = "0.8.0"
16 | }
17 |
18 | ext.libraryVersionCode = 10100
19 | ext.libraryVersionName = "1.1.0"
20 |
21 | repositories {
22 | google()
23 | mavenCentral()
24 | jcenter()
25 | }
26 | dependencies {
27 | classpath 'com.android.tools.build:gradle:7.1.2'
28 | // classpath 'com.novoda:bintray-release:0.9.2'
29 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
30 | // NOTE: Do not place your application dependencies here; they belong
31 | // in the individual module build.gradle files
32 | }
33 | }
34 |
35 | allprojects {
36 | repositories {
37 | google()
38 | mavenCentral()
39 | jcenter()
40 | }
41 |
42 | // 解决 : bintray 上传如果注释中有中文,可能会出现:编码GBK的不可映射字符
43 | // tasks.withType(Javadoc) {
44 | // options {
45 | // encoding "UTF-8"
46 | // charSet 'UTF-8'
47 | // links "http://docs.oracle.com/javase/8/docs/api"
48 | // }
49 | // options.addStringOption('Xdoclint:none', '-quiet')
50 | // options.addStringOption('encoding', 'UTF-8')
51 | // }
52 | }
53 |
54 | task clean(type: Delete) {
55 | delete rootProject.buildDir
56 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | android.enableJetifier=true
10 | android.useAndroidX=true
11 | org.gradle.jvmargs=-Xmx1536m
12 | # When configured, Gradle will run in incubating parallel mode.
13 | # This option should only be used with decoupled projects. More details, visit
14 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
15 | # org.gradle.parallel=true
16 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jul 13 11:36:31 CST 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/pic/api.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/pic/api.png
--------------------------------------------------------------------------------
/pic/apkqrcode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/pic/apkqrcode.png
--------------------------------------------------------------------------------
/pic/extension.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/pic/extension.png
--------------------------------------------------------------------------------
/pic/list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/pic/list.png
--------------------------------------------------------------------------------
/pic/main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/pic/main.png
--------------------------------------------------------------------------------
/pic/mediaplayer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/pic/mediaplayer.png
--------------------------------------------------------------------------------
/pic/recyclerview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maiwenchang/ArtPlayer/0cf40d65493852881bda6deaed94eef4c642d821/pic/recyclerview.png
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | include ':artplayer-core'
3 | include ':artplayer-ijk'
4 | include ':artplayer-exo'
5 | include ':artplayer-arm64'
6 | include ':artplayer-armv5'
7 | include ':artplayer-armv7a'
8 | include ':artplayer-x86'
9 | //include ':artplayer-ui'
10 |
11 |
12 |
--------------------------------------------------------------------------------