From f9a83e743663b19c3a91d0441cefc13a8c94078c Mon Sep 17 00:00:00 2001
From: SkyD666 <1161046314@qq.com>
Date: Fri, 14 Jan 2022 21:54:09 +0800
Subject: [PATCH] =?UTF-8?q?[feature|optimize|fix]=E6=95=B4=E7=90=86?=
=?UTF-8?q?=E7=94=B3=E8=AF=B7=E6=9D=83=E9=99=90=E4=BB=A3=E7=A0=81=EF=BC=9B?=
=?UTF-8?q?=E6=95=B4=E7=90=86=E9=83=A8=E5=88=86=E6=8A=95=E5=B1=8F=E4=BB=A3?=
=?UTF-8?q?=E7=A0=81=EF=BC=9B=E6=94=AF=E6=8C=81=E8=B0=83=E8=8A=82=E5=BC=B9?=
=?UTF-8?q?=E5=B9=95=E5=AD=97=E5=8F=B7=EF=BC=9B=E4=BC=98=E5=8C=96=E6=92=AD?=
=?UTF-8?q?=E6=94=BE=E7=95=8C=E9=9D=A2Toolbar=E6=98=BE=E7=A4=BA=E9=80=BB?=
=?UTF-8?q?=E8=BE=91=E4=BB=A3=E7=A0=81=EF=BC=9B=E5=AF=BC=E5=85=A5=E6=95=B0?=
=?UTF-8?q?=E6=8D=AE=E6=BA=90=E5=89=8D=E7=94=B3=E8=AF=B7=E5=AD=98=E5=82=A8?=
=?UTF-8?q?=E6=9D=83=E9=99=90=EF=BC=8C=E9=81=BF=E5=85=8D=E5=9C=A8=E6=9F=90?=
=?UTF-8?q?=E4=BA=9B=E6=89=8B=E6=9C=BA=E4=B8=8A=E5=B4=A9=E6=BA=83=EF=BC=9B?=
=?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=95=B0=E6=8D=AE=E6=BA=90=E5=BC=B9=E5=87=BA?=
=?UTF-8?q?Toast=E6=97=B6=E5=B4=A9=E6=BA=83=E7=9A=84=E9=97=AE=E9=A2=98?=
=?UTF-8?q?=EF=BC=88=E6=B2=A1=E6=9C=89=E6=8A=8Acom.skyd.imomoe.util.ToastK?=
=?UTF-8?q?t=E5=8A=A0=E5=85=A5=E6=B7=B7=E6=B7=86=E8=A7=84=E5=88=99?=
=?UTF-8?q?=EF=BC=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.idea/misc.xml | 7 +-
README.md | 19 +-
app/proguard-rules.pro | 1 +
.../skyd/imomoe/model/DataSourceManager.kt | 10 +-
.../skyd/imomoe/net/{DoH.kt => DnsServer.kt} | 2 +-
.../main/java/com/skyd/imomoe/net/Okhttp.kt | 2 +-
.../main/java/com/skyd/imomoe/util/Number.kt | 12 +
.../java/com/skyd/imomoe/util/Permission.kt | 18 +
.../com/skyd/imomoe/util/dlna/CastObject.java | 201 -----------
.../com/skyd/imomoe/util/dlna/CastObject.kt | 90 +++++
.../imomoe/util/dlna/dmc/DLNACastService.java | 58 ---
.../imomoe/util/dlna/dmc/DLNACastService.kt | 52 +++
.../com/skyd/imomoe/util/dlna/dmc/ICast.java | 40 ---
.../com/skyd/imomoe/util/dlna/dmc/ICast.kt | 28 ++
.../skyd/imomoe/util/dlna/dmc/ILogger.java | 77 ----
.../com/skyd/imomoe/util/dlna/dmc/ILogger.kt | 56 +++
.../dlna/dmc/OnDeviceRegistryListener.java | 14 -
.../util/dlna/dmc/OnDeviceRegistryListener.kt | 12 +
...Servlet.java => ContentResourceServlet.kt} | 35 +-
.../util/dlna/dms/IMediaContentDao.java | 20 --
.../imomoe/util/dlna/dms/IMediaContentDao.kt | 10 +
.../imomoe/util/dlna/dms/IResourceServer.java | 7 -
.../imomoe/util/dlna/dms/IResourceServer.kt | 6 +
.../util/dlna/dms/IResourceServerFactory.java | 30 --
.../util/dlna/dms/IResourceServerFactory.kt | 17 +
.../imomoe/util/dlna/dms/MediaContentDao.kt | 4 +
.../util/downloadanime/AnimeDownloadHelper.kt | 45 +--
.../view/activity/AnimeDetailActivity.kt | 8 +-
.../view/activity/AnimeDownloadActivity.kt | 30 +-
.../view/activity/ConfigDataSourceActivity.kt | 34 +-
.../skyd/imomoe/view/activity/PlayActivity.kt | 71 ++--
.../imomoe/view/activity/SettingActivity.kt | 3 +-
.../imomoe/view/component/AnimeToolbar.kt | 11 +-
.../view/component/player/AnimeVideoPlayer.kt | 69 +++-
.../component/player/DanmakuVideoPlayer.kt | 52 ++-
.../player/DetailPlayerActivity.java | 338 ------------------
.../component/player/DetailPlayerActivity.kt | 224 ++++++++++++
.../danmaku/bili/BiliBiliDanmakuParser.kt | 40 ++-
.../skyd/imomoe/view/fragment/HomeFragment.kt | 22 +-
.../listener/dsl/OnPermissionsCallback.kt | 63 ++++
.../main/res/layout/activity_anime_detail.xml | 9 +-
app/src/main/res/layout/activity_favorite.xml | 3 +-
.../layout/layout_anime_video_player_land.xml | 210 ++++++-----
app/src/main/res/values/strings.xml | 1 +
doc/customdatasource/README.md | 17 +-
45 files changed, 983 insertions(+), 1095 deletions(-)
rename app/src/main/java/com/skyd/imomoe/net/{DoH.kt => DnsServer.kt} (99%)
create mode 100644 app/src/main/java/com/skyd/imomoe/util/Number.kt
create mode 100644 app/src/main/java/com/skyd/imomoe/util/Permission.kt
delete mode 100644 app/src/main/java/com/skyd/imomoe/util/dlna/CastObject.java
create mode 100644 app/src/main/java/com/skyd/imomoe/util/dlna/CastObject.kt
delete mode 100644 app/src/main/java/com/skyd/imomoe/util/dlna/dmc/DLNACastService.java
create mode 100644 app/src/main/java/com/skyd/imomoe/util/dlna/dmc/DLNACastService.kt
delete mode 100644 app/src/main/java/com/skyd/imomoe/util/dlna/dmc/ICast.java
create mode 100644 app/src/main/java/com/skyd/imomoe/util/dlna/dmc/ICast.kt
delete mode 100644 app/src/main/java/com/skyd/imomoe/util/dlna/dmc/ILogger.java
create mode 100644 app/src/main/java/com/skyd/imomoe/util/dlna/dmc/ILogger.kt
delete mode 100644 app/src/main/java/com/skyd/imomoe/util/dlna/dmc/OnDeviceRegistryListener.java
create mode 100644 app/src/main/java/com/skyd/imomoe/util/dlna/dmc/OnDeviceRegistryListener.kt
rename app/src/main/java/com/skyd/imomoe/util/dlna/dms/{ContentResourceServlet.java => ContentResourceServlet.kt} (60%)
delete mode 100644 app/src/main/java/com/skyd/imomoe/util/dlna/dms/IMediaContentDao.java
create mode 100644 app/src/main/java/com/skyd/imomoe/util/dlna/dms/IMediaContentDao.kt
delete mode 100644 app/src/main/java/com/skyd/imomoe/util/dlna/dms/IResourceServer.java
create mode 100644 app/src/main/java/com/skyd/imomoe/util/dlna/dms/IResourceServer.kt
delete mode 100644 app/src/main/java/com/skyd/imomoe/util/dlna/dms/IResourceServerFactory.java
create mode 100644 app/src/main/java/com/skyd/imomoe/util/dlna/dms/IResourceServerFactory.kt
delete mode 100644 app/src/main/java/com/skyd/imomoe/view/component/player/DetailPlayerActivity.java
create mode 100644 app/src/main/java/com/skyd/imomoe/view/component/player/DetailPlayerActivity.kt
create mode 100644 app/src/main/java/com/skyd/imomoe/view/listener/dsl/OnPermissionsCallback.kt
diff --git a/.idea/misc.xml b/.idea/misc.xml
index b76fc880..88c68f5e 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -13,13 +13,14 @@
+
-
-
+
+
@@ -44,7 +45,7 @@
-
+
diff --git a/README.md b/README.md
index 50998aa0..0a3592e4 100644
--- a/README.md
+++ b/README.md
@@ -42,14 +42,15 @@
4. 支持**双指缩放**、**移动**、**旋转**视频
5. 支持视频**投屏**到电视
6. 支持部分视频**显示**、**发送弹幕**(需要数据源支持弹幕)
-7. 支持**缓存视频**到本地(暂不支持m3u8格式资源缓存)
-8. 支持**追番**(数据保存在本地)
-9. 支持显示**观看历史**记录
-10. 支持显示**搜索历史**记录
-11. 支持改变视频**播放速度**
-12. 支持改变**视频**显示**比例**(16:9, 4:3, 全屏等)
-13. [支持**自定义**显示**数据源**](doc/customdatasource/README.md)
-14. ......
+7. 支持输入某站弹幕链接播放网络弹幕(例如https://api.bilibili.com/x/v1/dm/list.so?oid=97495910)
+8. 支持**缓存视频**到本地(暂不支持m3u8格式资源缓存)
+9. 支持**追番**(数据保存在本地)
+10. 支持显示**观看历史**记录
+11. 支持显示**搜索历史**记录
+12. 支持改变视频**播放速度**
+13. 支持改变**视频**显示**比例**(16:9, 4:3, 全屏等)
+14. [支持**自定义**显示**数据源**](doc/customdatasource/README.md)
+15. ......
## 运行截图
@@ -67,7 +68,7 @@
## 安全说明
-**请勿**私自**传播APK**安装包,Github仓库为唯一长期仓库,**请仅在Github仓库下载安装包**,请勿下载来历不明的应用与Jar包,谨防隐私泄露,谨防受骗!
+**请勿**私自**传播APK**安装包,Github仓库为唯一长期仓库,**请仅在Github仓库下载安装包**,请勿下载来历不明的应用与ads包,谨防隐私泄露,谨防受骗!
### 已发现未知来源的APK
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index ca584616..a0fe8b79 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -105,6 +105,7 @@ public static final int *;
-keep class com.skyd.imomoe.model.util.** { *; }
-keep class com.skyd.imomoe.util.html.source.** { *; }
-keep class com.skyd.imomoe.util.eventbus.** { *; }
+-keep class com.skyd.imomoe.util.ToastKt { *; }
# 与自定义数据源相关的库不应该被混淆
-keep class org.jsoup.** { *; }
-keep class org.greenrobot.eventbus.** { *; }
diff --git a/app/src/main/java/com/skyd/imomoe/model/DataSourceManager.kt b/app/src/main/java/com/skyd/imomoe/model/DataSourceManager.kt
index 031f0aeb..9d839751 100644
--- a/app/src/main/java/com/skyd/imomoe/model/DataSourceManager.kt
+++ b/app/src/main/java/com/skyd/imomoe/model/DataSourceManager.kt
@@ -7,7 +7,6 @@ import com.skyd.imomoe.BuildConfig
import com.skyd.imomoe.model.interfaces.IConst
import com.skyd.imomoe.model.interfaces.IRouteProcessor
import com.skyd.imomoe.model.interfaces.IUtil
-import com.skyd.imomoe.util.showToast
import com.skyd.imomoe.util.editor
import com.skyd.imomoe.util.sharedPreferences
import dalvik.system.DexClassLoader
@@ -15,13 +14,6 @@ import java.io.File
object DataSourceManager {
-// var useCustomDataSource: Boolean
-// get() {
-// return App.context.sharedPreferences().getBoolean("useCustomDataSource", false)
-// }
-// set(value) {
-// App.context.sharedPreferences().editor { putBoolean("useCustomDataSource", value) }
-// }
const val DEFAULT_DATA_SOURCE = ""
@@ -97,7 +89,7 @@ object DataSourceManager {
@Suppress("UNCHECKED_CAST")
fun create(clazz: Class): T? {
// 如果不使用自定义数据,直接返回null
- if (dataSourceName == DEFAULT_DATA_SOURCE) return null
+ if (dataSourceName == DEFAULT_DATA_SOURCE && !BuildConfig.DEBUG) return null
cache[clazz]?.let {
return it.newInstance() as T
}
diff --git a/app/src/main/java/com/skyd/imomoe/net/DoH.kt b/app/src/main/java/com/skyd/imomoe/net/DnsServer.kt
similarity index 99%
rename from app/src/main/java/com/skyd/imomoe/net/DoH.kt
rename to app/src/main/java/com/skyd/imomoe/net/DnsServer.kt
index 5aeb9566..22be14d3 100644
--- a/app/src/main/java/com/skyd/imomoe/net/DoH.kt
+++ b/app/src/main/java/com/skyd/imomoe/net/DnsServer.kt
@@ -12,7 +12,7 @@ import com.skyd.imomoe.util.showToast
import okhttp3.HttpUrl.Companion.toHttpUrl
import java.lang.Exception
-object DoH {
+object DnsServer {
val defaultDnsServer = hashMapOf(
0 to "",
1 to "https://223.5.5.5/dns-query",
diff --git a/app/src/main/java/com/skyd/imomoe/net/Okhttp.kt b/app/src/main/java/com/skyd/imomoe/net/Okhttp.kt
index 13189193..c44021e3 100644
--- a/app/src/main/java/com/skyd/imomoe/net/Okhttp.kt
+++ b/app/src/main/java/com/skyd/imomoe/net/Okhttp.kt
@@ -11,7 +11,7 @@ import java.io.File
private val okhttpCache = Cache(File("cacheDir", "okhttpcache"), 10 * 1024 * 1024)
private val bootstrapClient = OkHttpClient.Builder().cache(okhttpCache).build()
-var dns: DnsOverHttps? = DoH.dnsServer.let {
+var dns: DnsOverHttps? = DnsServer.dnsServer.let {
if (it.isNullOrBlank()) null else {
try {
DnsOverHttps.Builder().client(bootstrapClient)
diff --git a/app/src/main/java/com/skyd/imomoe/util/Number.kt b/app/src/main/java/com/skyd/imomoe/util/Number.kt
new file mode 100644
index 00000000..ea96f78a
--- /dev/null
+++ b/app/src/main/java/com/skyd/imomoe/util/Number.kt
@@ -0,0 +1,12 @@
+package com.skyd.imomoe.util
+
+/**
+ * 只拼接百分号%
+ */
+inline val Int.percentage: String
+ get() = "${this}%"
+
+/**
+ * 乘100后拼接百分号
+ */
+fun Int.toPercentage(): String = "${this * 100}%"
\ No newline at end of file
diff --git a/app/src/main/java/com/skyd/imomoe/util/Permission.kt b/app/src/main/java/com/skyd/imomoe/util/Permission.kt
new file mode 100644
index 00000000..67a6451a
--- /dev/null
+++ b/app/src/main/java/com/skyd/imomoe/util/Permission.kt
@@ -0,0 +1,18 @@
+package com.skyd.imomoe.util
+
+import android.app.Activity
+import androidx.fragment.app.Fragment
+import com.hjq.permissions.Permission
+import com.hjq.permissions.XXPermissions
+import com.skyd.imomoe.view.listener.dsl.OnSinglePermissionCallback
+import com.skyd.imomoe.view.listener.dsl.requestSinglePermission
+
+fun Activity.requestManageExternalStorage(init: OnSinglePermissionCallback.() -> Unit) {
+ XXPermissions.with(this).permission(Permission.MANAGE_EXTERNAL_STORAGE)
+ .requestSinglePermission(init)
+}
+
+fun Fragment.requestManageExternalStorage(init: OnSinglePermissionCallback.() -> Unit) {
+ XXPermissions.with(this).permission(Permission.MANAGE_EXTERNAL_STORAGE)
+ .requestSinglePermission(init)
+}
diff --git a/app/src/main/java/com/skyd/imomoe/util/dlna/CastObject.java b/app/src/main/java/com/skyd/imomoe/util/dlna/CastObject.java
deleted file mode 100644
index dd033c15..00000000
--- a/app/src/main/java/com/skyd/imomoe/util/dlna/CastObject.java
+++ /dev/null
@@ -1,201 +0,0 @@
-package com.skyd.imomoe.util.dlna;
-
-import androidx.annotation.NonNull;
-
-import com.skyd.imomoe.util.dlna.dmc.ICast;
-
-public class CastObject {
- private CastObject() {
- }
-
- public static ICast newInstance(String url, String id, String name) {
- if (url.endsWith(".mp4")) {
- return CastVideo.newInstance(url, id, name);
- } else if (url.endsWith(".mp3")) {
- return CastAudio.newInstance(url, id, name);
- } else if (url.endsWith(".jpg")) {
- return CastImage.newInstance(url, id, name);
- } else {
- return null;
- }
- }
-
- /**
- *
- */
- public static class CastAudio implements ICast.ICastVideo {
- public static CastAudio newInstance(String url, String id, String name) {
- return new CastAudio(url, id, name);
- }
-
- public final String url;
-
- public final String id;
-
- public final String name;
-
- private long duration;
-
- public CastAudio(String url, String id, String name) {
- this.url = url;
- this.id = id;
- this.name = name;
- }
-
- /**
- * @param duration the total time of video (ms)
- */
- public CastAudio setDuration(long duration) {
- this.duration = duration;
- return this;
- }
-
- @NonNull
- @Override
- public String getId() {
- return id;
- }
-
- @NonNull
- @Override
- public String getUri() {
- return url;
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public long getDurationMillSeconds() {
- return duration;
- }
-
- @Override
- public long getSize() {
- return 0;
- }
-
- @Override
- public long getBitrate() {
- return 0;
- }
- }
-
- /**
- *
- */
- public static class CastImage implements ICast.ICastVideo {
- public static CastImage newInstance(String url, String id, String name) {
- return new CastImage(url, id, name);
- }
-
- public final String url;
-
- public final String id;
-
- public final String name;
-
- public CastImage(String url, String id, String name) {
- this.url = url;
- this.id = id;
- this.name = name;
- }
-
- @NonNull
- @Override
- public String getId() {
- return id;
- }
-
- @NonNull
- @Override
- public String getUri() {
- return url;
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public long getDurationMillSeconds() {
- return -1L;
- }
-
- @Override
- public long getSize() {
- return 0;
- }
-
- @Override
- public long getBitrate() {
- return 0;
- }
- }
-
- /**
- *
- */
- public static class CastVideo implements ICast.ICastVideo {
- public static CastVideo newInstance(String url, String id, String name) {
- return new CastVideo(url, id, name);
- }
-
- public final String url;
-
- public final String id;
-
- public final String name;
-
- private long duration;
-
- public CastVideo(String url, String id, String name) {
- this.url = url;
- this.id = id;
- this.name = name;
- }
-
- /**
- * @param duration the total time of video (ms)
- */
- public CastVideo setDuration(long duration) {
- this.duration = duration;
- return this;
- }
-
- @NonNull
- @Override
- public String getId() {
- return id;
- }
-
- @NonNull
- @Override
- public String getUri() {
- return url;
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public long getDurationMillSeconds() {
- return duration;
- }
-
- @Override
- public long getSize() {
- return 0;
- }
-
- @Override
- public long getBitrate() {
- return 0;
- }
- }
-}
diff --git a/app/src/main/java/com/skyd/imomoe/util/dlna/CastObject.kt b/app/src/main/java/com/skyd/imomoe/util/dlna/CastObject.kt
new file mode 100644
index 00000000..bec1d17a
--- /dev/null
+++ b/app/src/main/java/com/skyd/imomoe/util/dlna/CastObject.kt
@@ -0,0 +1,90 @@
+package com.skyd.imomoe.util.dlna
+
+import com.skyd.imomoe.util.dlna.dmc.ICast
+import com.skyd.imomoe.util.dlna.dmc.ICast.ICastVideo
+
+object CastObject {
+ fun newInstance(url: String, id: String, name: String?): ICast? {
+ return when {
+ url.endsWith(".mp4") -> CastVideo.newInstance(url, id, name)
+ url.endsWith(".mp3") -> CastAudio.newInstance(url, id, name)
+ url.endsWith(".jpg") -> CastImage.newInstance(url, id, name)
+ else -> null
+ }
+ }
+
+ class CastAudio(
+ override val uri: String,
+ override val id: String,
+ override val name: String?
+ ) : ICastVideo {
+ override var durationMillSeconds: Long = 0
+ private set
+
+ /**
+ * @param duration the total time of video (ms)
+ */
+ fun setDuration(duration: Long): CastAudio {
+ durationMillSeconds = duration
+ return this
+ }
+
+ override val size: Long
+ get() = 0
+ override val bitrate: Long
+ get() = 0
+
+ companion object {
+ @JvmStatic
+ fun newInstance(url: String, id: String, name: String?): CastAudio {
+ return CastAudio(url, id, name)
+ }
+ }
+ }
+
+ class CastImage(
+ override val uri: String,
+ override val id: String,
+ override val name: String?
+ ) : ICastVideo {
+ override val durationMillSeconds: Long
+ get() = -1L
+ override val size: Long
+ get() = 0
+ override val bitrate: Long
+ get() = 0
+
+ companion object {
+ @JvmStatic
+ fun newInstance(url: String, id: String, name: String?): CastImage {
+ return CastImage(url, id, name)
+ }
+ }
+ }
+
+ class CastVideo(override val uri: String, override val id: String, override val name: String?) :
+ ICastVideo {
+ override var durationMillSeconds: Long = 0
+ private set
+
+ /**
+ * @param duration the total time of video (ms)
+ */
+ fun setDuration(duration: Long): CastVideo {
+ durationMillSeconds = duration
+ return this
+ }
+
+ override val size: Long
+ get() = 0
+ override val bitrate: Long
+ get() = 0
+
+ companion object {
+ @JvmStatic
+ fun newInstance(url: String, id: String, name: String?): CastVideo {
+ return CastVideo(url, id, name)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/DLNACastService.java b/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/DLNACastService.java
deleted file mode 100644
index 778e0113..00000000
--- a/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/DLNACastService.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package com.skyd.imomoe.util.dlna.dmc;
-
-import android.content.Intent;
-
-import org.fourthline.cling.UpnpServiceConfiguration;
-import org.fourthline.cling.android.AndroidUpnpServiceConfiguration;
-import org.fourthline.cling.android.AndroidUpnpServiceImpl;
-import org.fourthline.cling.android.FixedAndroidLogHandler;
-
-/**
- *
- */
-public class DLNACastService extends AndroidUpnpServiceImpl {
- private final ILogger mLogger = new ILogger.DefaultLoggerImpl(this);
-
- @Override
- public void onCreate() {
- mLogger.i(String.format("[%s] onCreate", getClass().getName()));
- org.seamless.util.logging.LoggingUtil.resetRootHandler(new FixedAndroidLogHandler());
- super.onCreate();
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- mLogger.i(String.format("[%s] onStartCommand: %s , %s", getClass().getName(), intent, flags));
- return super.onStartCommand(intent, flags, startId);
- }
-
- @Override
- public void onDestroy() {
- mLogger.w(String.format("[%s] onDestroy", getClass().getName()));
- super.onDestroy();
- }
-
- @Override
- protected UpnpServiceConfiguration createConfiguration() {
- return new DLNACastServiceConfiguration();
- }
-
- // ----------------------------------------------------------------
- // ---- configuration
- // ----------------------------------------------------------------
- private static final class DLNACastServiceConfiguration extends AndroidUpnpServiceConfiguration {
-
- @Override
- public int getRegistryMaintenanceIntervalMillis() {
- return 5000; //default is 3000!
- }
-
- // @Override
- // public ServiceType[] getExclusiveServiceTypes() {
- // return new ServiceType[]{
- // DLNACastManager.SERVICE_RENDERING_CONTROL,
- // DLNACastManager.SERVICE_AV_TRANSPORT,
- // DLNACastManager.SERVICE_CONNECTION_MANAGER};
- // }
- }
-}
diff --git a/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/DLNACastService.kt b/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/DLNACastService.kt
new file mode 100644
index 00000000..b9637564
--- /dev/null
+++ b/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/DLNACastService.kt
@@ -0,0 +1,52 @@
+package com.skyd.imomoe.util.dlna.dmc
+
+import org.fourthline.cling.android.AndroidUpnpServiceImpl
+import com.skyd.imomoe.util.dlna.dmc.ILogger.DefaultLoggerImpl
+import org.fourthline.cling.android.FixedAndroidLogHandler
+import android.content.Intent
+import org.fourthline.cling.UpnpServiceConfiguration
+import org.fourthline.cling.android.AndroidUpnpServiceConfiguration
+import org.seamless.util.logging.LoggingUtil
+
+/**
+ *
+ */
+class DLNACastService : AndroidUpnpServiceImpl() {
+ private val mLogger: ILogger = DefaultLoggerImpl(this)
+ override fun onCreate() {
+ mLogger.i(String.format("[%s] onCreate", javaClass.name))
+ LoggingUtil.resetRootHandler(FixedAndroidLogHandler())
+ super.onCreate()
+ }
+
+ override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
+ mLogger.i(String.format("[%s] onStartCommand: %s , %s", javaClass.name, intent, flags))
+ return super.onStartCommand(intent, flags, startId)
+ }
+
+ override fun onDestroy() {
+ mLogger.w(String.format("[%s] onDestroy", javaClass.name))
+ super.onDestroy()
+ }
+
+ override fun createConfiguration(): UpnpServiceConfiguration {
+ return DLNACastServiceConfiguration()
+ }
+
+ // ----------------------------------------------------------------
+ // ---- configuration
+ // ----------------------------------------------------------------
+ private class DLNACastServiceConfiguration : AndroidUpnpServiceConfiguration() {
+ override fun getRegistryMaintenanceIntervalMillis(): Int {
+ return 5000 //default is 3000!
+ }
+
+ // @Override
+ // public ServiceType[] getExclusiveServiceTypes() {
+ // return new ServiceType[]{
+ // DLNACastManager.SERVICE_RENDERING_CONTROL,
+ // DLNACastManager.SERVICE_AV_TRANSPORT,
+ // DLNACastManager.SERVICE_CONNECTION_MANAGER};
+ // }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/ICast.java b/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/ICast.java
deleted file mode 100644
index 57ebb927..00000000
--- a/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/ICast.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package com.skyd.imomoe.util.dlna.dmc;
-
-import androidx.annotation.NonNull;
-
-public interface ICast {
-
- @NonNull
- String getId();
-
- @NonNull
- String getUri();
-
- String getName();
-
- interface ICastVideo extends ICast {
-
- /**
- * @return video duration, ms
- */
- long getDurationMillSeconds();
-
- long getSize();
-
- long getBitrate();
- }
-
- interface ICastAudio extends ICast {
- /**
- * @return audio duration, ms
- */
- long getDurationMillSeconds();
-
- long getSize();
- }
-
- interface ICastImage extends ICast {
- long getSize();
- }
-
-}
diff --git a/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/ICast.kt b/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/ICast.kt
new file mode 100644
index 00000000..c6e0b593
--- /dev/null
+++ b/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/ICast.kt
@@ -0,0 +1,28 @@
+package com.skyd.imomoe.util.dlna.dmc
+
+interface ICast {
+ val id: String
+ val uri: String
+ val name: String?
+
+ interface ICastVideo : ICast {
+ /**
+ * @return video duration, ms
+ */
+ val durationMillSeconds: Long
+ val size: Long
+ val bitrate: Long
+ }
+
+ interface ICastAudio : ICast {
+ /**
+ * @return audio duration, ms
+ */
+ val durationMillSeconds: Long
+ val size: Long
+ }
+
+ interface ICastImage : ICast {
+ val size: Long
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/ILogger.java b/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/ILogger.java
deleted file mode 100644
index c01ec4b9..00000000
--- a/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/ILogger.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package com.skyd.imomoe.util.dlna.dmc;
-
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.skyd.imomoe.BuildConfig;
-
-public interface ILogger {
- String PREFIX_TAG = "DLNACast_";
-
- void v(String msg);
-
- void d(String msg);
-
- void i(String msg);
-
- void w(String msg);
-
- void e(String msg);
-
- class DefaultLoggerImpl implements ILogger {
- private final String TAG;
- private final boolean DEBUG;
-
- public DefaultLoggerImpl(Object object) {
- this(object, BuildConfig.DEBUG);
- }
-
- public DefaultLoggerImpl(Object object, boolean debug) {
- String className = object.getClass().getSimpleName();
- if (TextUtils.isEmpty(className)) {
- if (object.getClass().getSuperclass() != null) {
- className = object.getClass().getSuperclass().getSimpleName();
- } else {
- className = "$1";
- }
- }
- TAG = PREFIX_TAG + className;
- DEBUG = debug;
- }
-
- @Override
- public void v(String msg) {
- if (DEBUG) {
- Log.v(TAG, msg);
- }
- }
-
- @Override
- public void d(String msg) {
- if (DEBUG) {
- Log.d(TAG, msg);
- }
- }
-
- @Override
- public void i(String msg) {
- if (DEBUG) {
- Log.i(TAG, msg);
- }
- }
-
- @Override
- public void w(String msg) {
- if (DEBUG) {
- Log.w(TAG, msg);
- }
- }
-
- @Override
- public void e(String msg) {
- if (DEBUG) {
- Log.e(TAG, msg);
- }
- }
- }
-}
diff --git a/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/ILogger.kt b/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/ILogger.kt
new file mode 100644
index 00000000..7a824c48
--- /dev/null
+++ b/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/ILogger.kt
@@ -0,0 +1,56 @@
+package com.skyd.imomoe.util.dlna.dmc
+
+import android.text.TextUtils
+import android.util.Log
+import com.skyd.imomoe.BuildConfig
+
+interface ILogger {
+ fun v(msg: String?)
+ fun d(msg: String?)
+ fun i(msg: String?)
+ fun w(msg: String?)
+ fun e(msg: String?)
+ class DefaultLoggerImpl @JvmOverloads constructor(
+ `object`: Any,
+ debug: Boolean = BuildConfig.DEBUG
+ ) : ILogger {
+ private val TAG: String
+ private val DEBUG: Boolean
+ override fun v(msg: String?) {
+ if (DEBUG) Log.v(TAG, msg.toString())
+ }
+
+ override fun d(msg: String?) {
+ if (DEBUG) Log.d(TAG, msg.toString())
+ }
+
+ override fun i(msg: String?) {
+ if (DEBUG) Log.i(TAG, msg.toString())
+ }
+
+ override fun w(msg: String?) {
+ if (DEBUG) Log.w(TAG, msg.toString())
+ }
+
+ override fun e(msg: String?) {
+ if (DEBUG) Log.e(TAG, msg.toString())
+ }
+
+ init {
+ var className = `object`.javaClass.simpleName
+ if (TextUtils.isEmpty(className)) {
+ className = if (`object`.javaClass.superclass != null) {
+ `object`.javaClass.superclass.simpleName
+ } else {
+ "$1"
+ }
+ }
+ TAG = PREFIX_TAG + className
+ DEBUG = debug
+ }
+ }
+
+ companion object {
+ const val PREFIX_TAG = "DLNACast_"
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/OnDeviceRegistryListener.java b/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/OnDeviceRegistryListener.java
deleted file mode 100644
index ce358dd3..00000000
--- a/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/OnDeviceRegistryListener.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.skyd.imomoe.util.dlna.dmc;
-
-import org.fourthline.cling.model.meta.Device;
-
-/**
- * this listener call in UI thread.
- */
-public interface OnDeviceRegistryListener {
- void onDeviceAdded(Device, ?, ?> device);
-
- void onDeviceUpdated(Device, ?, ?> device);
-
- void onDeviceRemoved(Device, ?, ?> device);
-}
diff --git a/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/OnDeviceRegistryListener.kt b/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/OnDeviceRegistryListener.kt
new file mode 100644
index 00000000..f25ebc4f
--- /dev/null
+++ b/app/src/main/java/com/skyd/imomoe/util/dlna/dmc/OnDeviceRegistryListener.kt
@@ -0,0 +1,12 @@
+package com.skyd.imomoe.util.dlna.dmc
+
+import org.fourthline.cling.model.meta.Device
+
+/**
+ * this listener call in UI thread.
+ */
+interface OnDeviceRegistryListener {
+ fun onDeviceAdded(device: Device<*, *, *>?)
+ fun onDeviceUpdated(device: Device<*, *, *>?)
+ fun onDeviceRemoved(device: Device<*, *, *>?)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/skyd/imomoe/util/dlna/dms/ContentResourceServlet.java b/app/src/main/java/com/skyd/imomoe/util/dlna/dms/ContentResourceServlet.kt
similarity index 60%
rename from app/src/main/java/com/skyd/imomoe/util/dlna/dms/ContentResourceServlet.java
rename to app/src/main/java/com/skyd/imomoe/util/dlna/dms/ContentResourceServlet.kt
index 83cf1320..13c80057 100644
--- a/app/src/main/java/com/skyd/imomoe/util/dlna/dms/ContentResourceServlet.java
+++ b/app/src/main/java/com/skyd/imomoe/util/dlna/dms/ContentResourceServlet.kt
@@ -13,34 +13,29 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.skyd.imomoe.util.dlna.dms;
+package com.skyd.imomoe.util.dlna.dms
-import org.eclipse.jetty.servlet.DefaultServlet;
-import org.eclipse.jetty.util.resource.FileResource;
-import org.eclipse.jetty.util.resource.Resource;
+import org.eclipse.jetty.servlet.DefaultServlet
+import org.eclipse.jetty.util.resource.FileResource
+import org.eclipse.jetty.util.resource.Resource
+import java.io.File
+import java.lang.Exception
-import java.io.File;
-
-public class ContentResourceServlet extends DefaultServlet {
-
- @Override
- public Resource getResource(String pathInContext) {
+open class ContentResourceServlet : DefaultServlet() {
+ override fun getResource(pathInContext: String?): Resource? {
// String id = Utils.parseResourceId(pathInContext);
// content://media/external/video/media/1611127029319529
// Uri uri = ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, Long.parseLong(id));
// Logger.i("ContentResourceServlet, path: %s", pathInContext);
try {
- File file = new File(pathInContext);
- if (file.exists()) return FileResource.newResource(file);
- } catch (Exception e) {
- e.printStackTrace();
+ val file = File(pathInContext!!)
+ if (file.exists()) return FileResource.newResource(file)
+ } catch (e: Exception) {
+ e.printStackTrace()
}
- return null;
+ return null
}
- public static class VideoResourceServlet extends ContentResourceServlet {
- }
-
- public static class AudioResourceServlet extends ContentResourceServlet {
- }
+ class VideoResourceServlet : ContentResourceServlet()
+ class AudioResourceServlet : ContentResourceServlet()
}
\ No newline at end of file
diff --git a/app/src/main/java/com/skyd/imomoe/util/dlna/dms/IMediaContentDao.java b/app/src/main/java/com/skyd/imomoe/util/dlna/dms/IMediaContentDao.java
deleted file mode 100644
index 6f3581c9..00000000
--- a/app/src/main/java/com/skyd/imomoe/util/dlna/dms/IMediaContentDao.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.skyd.imomoe.util.dlna.dms;
-
-import android.content.Context;
-
-import androidx.annotation.NonNull;
-
-import org.fourthline.cling.support.model.item.Item;
-
-import java.util.List;
-
-interface IMediaContentDao {
- @NonNull
- List- getImageItems(@NonNull Context context);
-
- @NonNull
- List
- getAudioItems(@NonNull Context context);
-
- @NonNull
- List
- getVideoItems(@NonNull Context context);
-}
diff --git a/app/src/main/java/com/skyd/imomoe/util/dlna/dms/IMediaContentDao.kt b/app/src/main/java/com/skyd/imomoe/util/dlna/dms/IMediaContentDao.kt
new file mode 100644
index 00000000..0a0d7c35
--- /dev/null
+++ b/app/src/main/java/com/skyd/imomoe/util/dlna/dms/IMediaContentDao.kt
@@ -0,0 +1,10 @@
+package com.skyd.imomoe.util.dlna.dms
+
+import android.content.Context
+import org.fourthline.cling.support.model.item.Item
+
+internal interface IMediaContentDao {
+ fun getImageItems(context: Context): List
-
+ fun getAudioItems(context: Context): List
-
+ fun getVideoItems(context: Context): List
-
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/skyd/imomoe/util/dlna/dms/IResourceServer.java b/app/src/main/java/com/skyd/imomoe/util/dlna/dms/IResourceServer.java
deleted file mode 100644
index 53ba4c95..00000000
--- a/app/src/main/java/com/skyd/imomoe/util/dlna/dms/IResourceServer.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.skyd.imomoe.util.dlna.dms;
-
-interface IResourceServer {
- void startServer();
-
- void stopServer();
-}
diff --git a/app/src/main/java/com/skyd/imomoe/util/dlna/dms/IResourceServer.kt b/app/src/main/java/com/skyd/imomoe/util/dlna/dms/IResourceServer.kt
new file mode 100644
index 00000000..369e30e2
--- /dev/null
+++ b/app/src/main/java/com/skyd/imomoe/util/dlna/dms/IResourceServer.kt
@@ -0,0 +1,6 @@
+package com.skyd.imomoe.util.dlna.dms
+
+internal interface IResourceServer {
+ fun startServer()
+ fun stopServer()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/skyd/imomoe/util/dlna/dms/IResourceServerFactory.java b/app/src/main/java/com/skyd/imomoe/util/dlna/dms/IResourceServerFactory.java
deleted file mode 100644
index 2969322b..00000000
--- a/app/src/main/java/com/skyd/imomoe/util/dlna/dms/IResourceServerFactory.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.skyd.imomoe.util.dlna.dms;
-
-interface IResourceServerFactory {
- int getPort();
-
- IResourceServer getInstance();
-
- // ----------------------------------------------------------------------------
- // ---- implement
- // ----------------------------------------------------------------------------
- final class DefaultResourceServerFactoryImpl implements IResourceServerFactory {
- private final int port;
-
- public DefaultResourceServerFactoryImpl(int port) {
- this.port = port;
- }
-
- @Override
- public int getPort() {
- return port;
- }
-
- @Override
- public IResourceServer getInstance() {
- // TODO:
- // return new JettyHttpServer(port);
- return new NanoHttpServer(port);
- }
- }
-}
diff --git a/app/src/main/java/com/skyd/imomoe/util/dlna/dms/IResourceServerFactory.kt b/app/src/main/java/com/skyd/imomoe/util/dlna/dms/IResourceServerFactory.kt
new file mode 100644
index 00000000..2262bdae
--- /dev/null
+++ b/app/src/main/java/com/skyd/imomoe/util/dlna/dms/IResourceServerFactory.kt
@@ -0,0 +1,17 @@
+package com.skyd.imomoe.util.dlna.dms
+
+
+internal interface IResourceServerFactory {
+ val port: Int
+ val instance: IResourceServer
+
+ // ----------------------------------------------------------------------------
+ // ---- implement
+ // ----------------------------------------------------------------------------
+ class DefaultResourceServerFactoryImpl(override val port: Int) : IResourceServerFactory {
+ override val instance: IResourceServer
+ // TODO:
+ // return new JettyHttpServer(port);
+ get() = NanoHttpServer(port)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/skyd/imomoe/util/dlna/dms/MediaContentDao.kt b/app/src/main/java/com/skyd/imomoe/util/dlna/dms/MediaContentDao.kt
index 11a68235..ba0c0c47 100644
--- a/app/src/main/java/com/skyd/imomoe/util/dlna/dms/MediaContentDao.kt
+++ b/app/src/main/java/com/skyd/imomoe/util/dlna/dms/MediaContentDao.kt
@@ -1,5 +1,6 @@
package com.skyd.imomoe.util.dlna.dms
+import android.annotation.SuppressLint
import android.content.Context
import android.provider.MediaStore.*
import org.fourthline.cling.support.model.PersonWithRole
@@ -12,6 +13,7 @@ import java.io.File
import java.util.*
internal class MediaContentDao(private val mBaseUrl: String) : IMediaContentDao {
+ @SuppressLint("Range")
override fun getImageItems(context: Context): List
- {
val items: MutableList
- = ArrayList()
context.contentResolver.query(
@@ -39,6 +41,7 @@ internal class MediaContentDao(private val mBaseUrl: String) : IMediaContentDao
return items
}
+ @SuppressLint("Range")
override fun getAudioItems(context: Context): List
- {
val items: MutableList
- = ArrayList()
context.contentResolver.query(
@@ -75,6 +78,7 @@ internal class MediaContentDao(private val mBaseUrl: String) : IMediaContentDao
return items
}
+ @SuppressLint("Range")
override fun getVideoItems(context: Context): List
- {
val items: MutableList
- = ArrayList()
context.contentResolver.query(
diff --git a/app/src/main/java/com/skyd/imomoe/util/downloadanime/AnimeDownloadHelper.kt b/app/src/main/java/com/skyd/imomoe/util/downloadanime/AnimeDownloadHelper.kt
index abf759be..ac144a6c 100644
--- a/app/src/main/java/com/skyd/imomoe/util/downloadanime/AnimeDownloadHelper.kt
+++ b/app/src/main/java/com/skyd/imomoe/util/downloadanime/AnimeDownloadHelper.kt
@@ -4,14 +4,12 @@ import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
-import com.hjq.permissions.OnPermissionCallback
-import com.hjq.permissions.Permission
-import com.hjq.permissions.XXPermissions
import com.skyd.imomoe.App
import com.skyd.imomoe.R
import com.skyd.imomoe.config.Const
import com.skyd.imomoe.config.Const.DownloadAnime.Companion.animeFilePath
import com.skyd.imomoe.database.entity.AnimeDownloadEntity
+import com.skyd.imomoe.util.requestManageExternalStorage
import com.skyd.imomoe.util.showToast
import org.w3c.dom.Element
import org.w3c.dom.Node
@@ -244,32 +242,23 @@ class AnimeDownloadHelper private constructor() {
.showToast()
return
}
- XXPermissions.with(activity).permission(Permission.MANAGE_EXTERNAL_STORAGE).request(
- object : OnPermissionCallback {
- override fun onGranted(permissions: MutableList?, all: Boolean) {
- if (downloadHashMap[key]?.value == AnimeDownloadStatus.DOWNLOADING) {
- "已经在下载啦...".showToast()
- return
- } /*else if (downloadHashMap[key]?.value == AnimeDownloadStatus.COMPLETE) {
- "已经下载好啦...".showToast()
- return
- }*/
- val status = MutableLiveData()
- status.value = AnimeDownloadStatus.DOWNLOADING
- downloadHashMap[key] = status
- activity.startService(
- Intent(activity, AnimeDownloadService::class.java)
- .putExtra("url", url)
- .putExtra("key", key)
- .putExtra("folderAndFileName", folderAndFileName)
- )
- }
-
- override fun onDenied(permissions: MutableList?, never: Boolean) {
- super.onDenied(permissions, never)
- "未获取存储权限,无法下载".showToast()
+ activity.requestManageExternalStorage {
+ onGranted {
+ if (downloadHashMap[key]?.value == AnimeDownloadStatus.DOWNLOADING) {
+ "已经在下载啦...".showToast()
+ return@onGranted
}
+ val status = MutableLiveData()
+ status.value = AnimeDownloadStatus.DOWNLOADING
+ downloadHashMap[key] = status
+ activity.startService(
+ Intent(activity, AnimeDownloadService::class.java)
+ .putExtra("url", url)
+ .putExtra("key", key)
+ .putExtra("folderAndFileName", folderAndFileName)
+ )
}
- )
+ onDenied { "未获取存储权限,无法下载".showToast() }
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/skyd/imomoe/view/activity/AnimeDetailActivity.kt b/app/src/main/java/com/skyd/imomoe/view/activity/AnimeDetailActivity.kt
index b66ae7d2..1a5974d0 100644
--- a/app/src/main/java/com/skyd/imomoe/view/activity/AnimeDetailActivity.kt
+++ b/app/src/main/java/com/skyd/imomoe/view/activity/AnimeDetailActivity.kt
@@ -13,7 +13,6 @@ import com.skyd.imomoe.config.Const
import com.skyd.imomoe.database.getAppDataBase
import com.skyd.imomoe.databinding.ActivityAnimeDetailBinding
import com.skyd.imomoe.util.Util.getSkinResourceId
-import com.skyd.imomoe.util.Util.getStatusBarHeight
import com.skyd.imomoe.util.Util.setTransparentStatusBar
import com.skyd.imomoe.util.showToast
import com.skyd.imomoe.util.coil.DarkBlurTransformation
@@ -41,12 +40,7 @@ class AnimeDetailActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setTransparentStatusBar(window, isDark = true)
-
- val statusBarLinearParams =
- mBinding.viewAnimeDetailActivityStatusBar.layoutParams //取控件当前的布局参数
- statusBarLinearParams.height = getStatusBarHeight()
- mBinding.viewAnimeDetailActivityStatusBar.layoutParams = statusBarLinearParams
+ setTransparentStatusBar(window, isDark = false)
viewModel = ViewModelProvider(this).get(AnimeDetailViewModel::class.java)
adapter = AnimeDetailAdapter(this, viewModel.animeDetailList)
diff --git a/app/src/main/java/com/skyd/imomoe/view/activity/AnimeDownloadActivity.kt b/app/src/main/java/com/skyd/imomoe/view/activity/AnimeDownloadActivity.kt
index 6720633f..0b5e0456 100644
--- a/app/src/main/java/com/skyd/imomoe/view/activity/AnimeDownloadActivity.kt
+++ b/app/src/main/java/com/skyd/imomoe/view/activity/AnimeDownloadActivity.kt
@@ -7,13 +7,11 @@ import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.afollestad.materialdialogs.MaterialDialog
-import com.hjq.permissions.OnPermissionCallback
-import com.hjq.permissions.Permission
-import com.hjq.permissions.XXPermissions
import com.skyd.imomoe.R
import com.skyd.imomoe.databinding.ActivityAnimeDownloadBinding
import com.skyd.imomoe.util.showToast
import com.skyd.imomoe.util.gone
+import com.skyd.imomoe.util.requestManageExternalStorage
import com.skyd.imomoe.util.visible
import com.skyd.imomoe.view.adapter.AnimeDownloadAdapter
import com.skyd.imomoe.viewmodel.AnimeDownloadViewModel
@@ -70,23 +68,19 @@ class AnimeDownloadActivity : BaseActivity() {
}
})
- XXPermissions.with(this).permission(Permission.MANAGE_EXTERNAL_STORAGE)
- .request(object : OnPermissionCallback {
- override fun onGranted(permissions: MutableList?, all: Boolean) {
- if (mode == 0) viewModel.getAnimeCover()
- else if (mode == 1) {
- mBinding.layoutAnimeDownloadLoading.layoutCircleProgressTextTip1.visible()
- viewModel.getAnimeCoverEpisode(directoryName, path)
- }
- }
-
- override fun onDenied(permissions: MutableList?, never: Boolean) {
- super.onDenied(permissions, never)
- "无存储权限,无法播放本地缓存视频".showToast(Toast.LENGTH_LONG)
- finish()
+ requestManageExternalStorage {
+ onGranted {
+ if (mode == 0) viewModel.getAnimeCover()
+ else if (mode == 1) {
+ mBinding.layoutAnimeDownloadLoading.layoutCircleProgressTextTip1.visible()
+ viewModel.getAnimeCoverEpisode(directoryName, path)
}
}
- )
+ onDenied {
+ "无存储权限,无法播放本地缓存视频".showToast(Toast.LENGTH_LONG)
+ finish()
+ }
+ }
}
override fun getBinding(): ActivityAnimeDownloadBinding =
diff --git a/app/src/main/java/com/skyd/imomoe/view/activity/ConfigDataSourceActivity.kt b/app/src/main/java/com/skyd/imomoe/view/activity/ConfigDataSourceActivity.kt
index 564f42f7..d63f96c9 100644
--- a/app/src/main/java/com/skyd/imomoe/view/activity/ConfigDataSourceActivity.kt
+++ b/app/src/main/java/com/skyd/imomoe/view/activity/ConfigDataSourceActivity.kt
@@ -49,17 +49,29 @@ class ConfigDataSourceActivity : BaseActivity()
}
private fun callToImport(intent: Intent) {
- if (Intent.ACTION_VIEW == intent.action) {
- intent.data?.let { uri ->
- importDataSource(uri,
- onSuccess = {
- getString(R.string.import_data_source_success, uri.path).showSnackbar(this)
- viewModel.getDataSourceList()
- },
- onFailed = {
- getString(R.string.import_data_source_failed, it.message).showSnackbar(this)
- }
- )
+ val uri = intent.data
+ if (Intent.ACTION_VIEW == intent.action && uri != null) {
+ requestManageExternalStorage {
+ onGranted {
+ importDataSource(uri,
+ onSuccess = {
+ getString(
+ R.string.import_data_source_success,
+ uri.path
+ ).showSnackbar(this@ConfigDataSourceActivity)
+ viewModel.getDataSourceList()
+ },
+ onFailed = {
+ getString(
+ R.string.import_data_source_failed,
+ it.message
+ ).showSnackbar(this@ConfigDataSourceActivity)
+ }
+ )
+ }
+ onDenied {
+ "无存储权限,无法导入".showSnackbar(this@ConfigDataSourceActivity, Toast.LENGTH_LONG)
+ }
}
}
}
diff --git a/app/src/main/java/com/skyd/imomoe/view/activity/PlayActivity.kt b/app/src/main/java/com/skyd/imomoe/view/activity/PlayActivity.kt
index ea8f6e50..44352ab8 100644
--- a/app/src/main/java/com/skyd/imomoe/view/activity/PlayActivity.kt
+++ b/app/src/main/java/com/skyd/imomoe/view/activity/PlayActivity.kt
@@ -92,9 +92,11 @@ class PlayActivity : DetailPlayerActivity
when {
abs(verticalOffset) > ctlPlayActivity.scrimVisibleHeightTrigger -> {
- tvPlayActivityToolbarTitle?.visible()
+ tvPlayActivityToolbarVideoTitle.gone()
+ tvPlayActivityToolbarTitle?.visible(animate = true, dur = 200L)
}
else -> {
+ tvPlayActivityToolbarVideoTitle.visible(animate = true, dur = 200L)
tvPlayActivityToolbarTitle?.gone()
}
}
@@ -285,19 +287,16 @@ class PlayActivity : DetailPlayerActivity
danmakuParamMap.clear()
danmakuParamMap.putAll(paramMap)
danmakuUrl = paramMap[SnifferVideo.DANMU_URL] ?: ""
- setUp(videoUrl, false, title)
+ setUp(videoUrl, false, episodeDataBean.title)
// 开始播放
startPlayLogic()
}
@@ -409,13 +408,6 @@ class PlayActivity : DetailPlayerActivity
-// videoPlayer.startPlay(videoUrl, viewModel.animeEpisodeDataBean.title)
-// }
}
override fun onQuitFullscreen(url: String?, vararg objects: Any?) {
@@ -448,8 +440,12 @@ class PlayActivity : DetailPlayerActivity = ArrayList()
private val buttonLayoutParams = LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.MATCH_PARENT
+ ViewGroup.LayoutParams.WRAP_CONTENT
)
private lateinit var titleTextView: TextView
private lateinit var backButton: ImageView
@@ -85,6 +88,7 @@ class AnimeToolbar : LinearLayout {
backButton = ImageView(context, attrs).apply {
this.layoutParams = buttonLayoutParams
adjustViewBounds = true
+ minimumHeight = BUTTON_MIN_HEIGHT.dp
context.obtainStyledAttributes(intArrayOf(android.R.attr.selectableItemBackground))
.also {
background = it.getDrawable(0)
@@ -152,6 +156,7 @@ class AnimeToolbar : LinearLayout {
addView(ImageView(context).apply {
layoutParams = params
adjustViewBounds = true
+ minimumHeight = BUTTON_MIN_HEIGHT.dp
context.obtainStyledAttributes(intArrayOf(android.R.attr.selectableItemBackground))
.also { background = it.getDrawable(0) }.recycle()
setPadding(12.dp, 12.dp, 12.dp, 12.dp)
diff --git a/app/src/main/java/com/skyd/imomoe/view/component/player/AnimeVideoPlayer.kt b/app/src/main/java/com/skyd/imomoe/view/component/player/AnimeVideoPlayer.kt
index 7dc63cf8..1d6f95f2 100644
--- a/app/src/main/java/com/skyd/imomoe/view/component/player/AnimeVideoPlayer.kt
+++ b/app/src/main/java/com/skyd/imomoe/view/component/player/AnimeVideoPlayer.kt
@@ -374,8 +374,8 @@ open class AnimeVideoPlayer : StandardGSYVideoPlayer {
override fun hideAllWidget() {
super.hideAllWidget()
- setViewShowState(vgRightContainer, INVISIBLE)
- setViewShowState(vgSettingContainer, INVISIBLE)
+// setViewShowState(vgRightContainer, INVISIBLE)
+// setViewShowState(vgSettingContainer, INVISIBLE)
setViewShowState(tvRestoreScreen, View.GONE)
setViewShowState(viewTopContainerShadow, View.INVISIBLE)
}
@@ -726,25 +726,58 @@ open class AnimeVideoPlayer : StandardGSYVideoPlayer {
override fun onClick(v: View) {
super.onClick(v)
- val i = v.id
- // bigger_surface代替原有的surface_container执行点击动作
- if (i == R.id.bigger_surface && mCurrentState == GSYVideoView.CURRENT_STATE_ERROR) {
- if (mVideoAllCallBack != null) {
- Debuger.printfLog("onClickStartError")
- mVideoAllCallBack.onClickStartError(mOriginUrl, mTitle, this)
- }
- prepareVideo()
- } else if (i == R.id.bigger_surface) {
- if (mVideoAllCallBack != null && isCurrentMediaListener) {
- if (mIfCurrentIsFullscreen) {
- Debuger.printfLog("onClickBlankFullscreen")
- mVideoAllCallBack.onClickBlankFullscreen(mOriginUrl, mTitle, this)
+ when (v.id) {
+ // bigger_surface代替原有的surface_container执行点击动作
+ R.id.bigger_surface -> {
+ vgSettingContainer?.gone()
+ vgRightContainer?.gone()
+ if (mCurrentState == GSYVideoView.CURRENT_STATE_ERROR) {
+ if (mVideoAllCallBack != null) {
+ Debuger.printfLog("onClickStartError")
+ mVideoAllCallBack.onClickStartError(mOriginUrl, mTitle, this)
+ }
+ prepareVideo()
} else {
- Debuger.printfLog("onClickBlank")
- mVideoAllCallBack.onClickBlank(mOriginUrl, mTitle, this)
+ if (mVideoAllCallBack != null && isCurrentMediaListener) {
+ if (mIfCurrentIsFullscreen) {
+ Debuger.printfLog("onClickBlankFullscreen")
+ mVideoAllCallBack.onClickBlankFullscreen(mOriginUrl, mTitle, this)
+ } else {
+ Debuger.printfLog("onClickBlank")
+ mVideoAllCallBack.onClickBlank(mOriginUrl, mTitle, this)
+ }
+ }
+ startDismissControlViewTimer()
}
}
- startDismissControlViewTimer()
+ R.id.thumb -> {
+ vgSettingContainer?.gone()
+ vgRightContainer?.gone()
+ }
+ }
+ }
+
+ /**
+ * 双击的时候调用此方法
+ */
+ override fun touchDoubleUp(e: MotionEvent?) {
+ // 处理双击前的逻辑
+ val oldUiVisibilityState = mBottomContainer?.visibility ?: VISIBLE
+
+ // 处理双击
+ super.touchDoubleUp(e)
+
+ // 下面是处理完双击后的逻辑
+ if (mCurrentState == CURRENT_STATE_PLAYING) { // 若双击后是播放状态
+ //双击前Ui是什么可见性状态,则双击后Ui还是什么可见性状态,避免双击后Ui突然显示出来
+ if (oldUiVisibilityState == VISIBLE) changeUiToPlayingShow()
+ else changeUiToPlayingClear()
+// cancelDismissControlViewTimer()
+ } else if (mCurrentState == CURRENT_STATE_PAUSE) { // 若双击后是暂停状态
+ //双击前Ui是什么可见性状态,则双击后Ui还是什么可见性状态,避免双击后Ui突然显示出来
+ if (oldUiVisibilityState == VISIBLE) changeUiToPauseShow()
+ else changeUiToPauseClear()
+// cancelDismissControlViewTimer()
}
}
diff --git a/app/src/main/java/com/skyd/imomoe/view/component/player/DanmakuVideoPlayer.kt b/app/src/main/java/com/skyd/imomoe/view/component/player/DanmakuVideoPlayer.kt
index e4d872e2..4cd291f2 100644
--- a/app/src/main/java/com/skyd/imomoe/view/component/player/DanmakuVideoPlayer.kt
+++ b/app/src/main/java/com/skyd/imomoe/view/component/player/DanmakuVideoPlayer.kt
@@ -9,6 +9,7 @@ import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.widget.EditText
import android.widget.ImageView
+import android.widget.SeekBar
import android.widget.TextView
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.input.input
@@ -35,6 +36,7 @@ import com.skyd.imomoe.view.component.player.danmaku.Const
import com.skyd.imomoe.view.component.player.danmaku.anime.AnimeDanmakuParser
import com.skyd.imomoe.view.component.player.danmaku.anime.AnimeDanmakuSender
import com.skyd.imomoe.view.component.player.danmaku.bili.BiliBiliDanmakuParser
+import com.skyd.imomoe.view.listener.dsl.setOnSeekBarChangeListener
import java.net.HttpURLConnection
import java.net.URL
import java.util.zip.Inflater
@@ -93,6 +95,21 @@ open class DanmakuVideoPlayer : AnimeVideoPlayer {
// 弹幕进度delta
private var mDanmakuProgressDelta: Long = 0L
+ // 弹幕字号缩放百分比SeekBar
+ private var sbDanmakuTextScale: SeekBar? = null
+
+ // "弹幕字号"TextView
+ private var tvDanmakuTextScaleHeader: TextView? = null
+
+ // 显示弹幕字号缩放百分比TextView
+ private var tvDanmakuTextScale: TextView? = null
+
+ // 弹幕字号缩放最小百分比
+ private val mDanmakuTextScaleMinPercent: Int = 50
+
+ // 弹幕字号百分比
+ private var mDanmakuTextScalePercent: Int = mDanmakuTextScaleMinPercent + 70
+
constructor(context: Context, fullFlag: Boolean?) : super(context, fullFlag)
constructor(context: Context) : super(context)
@@ -112,6 +129,9 @@ open class DanmakuVideoPlayer : AnimeVideoPlayer {
tvRewindDanmakuProgress = findViewById(R.id.tv_player_rewind_danmaku_progress)
tvResetDanmakuProgress = findViewById(R.id.tv_player_reset_danmaku_progress)
tvForwardDanmakuProgress = findViewById(R.id.tv_player_forward_danmaku_progress)
+ sbDanmakuTextScale = findViewById(R.id.sb_danmaku_text_size_scale)
+ tvDanmakuTextScaleHeader = findViewById(R.id.tv_danmaku_text_size_scale_header)
+ tvDanmakuTextScale = findViewById(R.id.tv_danmaku_text_size_scale)
etDanmakuInput?.gone()
ivShowDanmaku?.gone()
// 设置高度是0
@@ -204,6 +224,15 @@ open class DanmakuVideoPlayer : AnimeVideoPlayer {
mDanmakuProgressDelta = 0L
seekDanmaku(currentPlayer.currentPositionWhenPlaying.toLong())
}
+
+ sbDanmakuTextScale?.setOnSeekBarChangeListener {
+ onProgressChanged { seekBar, progress, _ ->
+ seekBar ?: return@onProgressChanged
+ mDanmakuTextScalePercent = progress + mDanmakuTextScaleMinPercent
+ setTextSizeScale(mDanmakuTextScalePercent / 100f)
+ tvDanmakuTextScale?.text = mDanmakuTextScalePercent.percentage
+ }
+ }
}
override fun onCompletion() {
@@ -296,6 +325,8 @@ open class DanmakuVideoPlayer : AnimeVideoPlayer {
super.startWindowFullscreen(context, actionBar, statusBar) as DanmakuVideoPlayer
player.ivShowDanmaku?.visibility = ivShowDanmaku?.visibility ?: View.GONE
player.etDanmakuInput?.visibility = etDanmakuInput?.visibility ?: View.GONE
+ player.sbDanmakuTextScale?.progress = mDanmakuTextScalePercent - mDanmakuTextScaleMinPercent
+ player.setTextSizeScale(mDanmakuTextScalePercent / 100f)
player.mDanmakuShow = mDanmakuShow
player.resolveDanmakuShow()
@@ -325,10 +356,11 @@ open class DanmakuVideoPlayer : AnimeVideoPlayer {
val player = it as DanmakuVideoPlayer
if (player.etDanmakuInput?.visibility == View.VISIBLE) showBottomDanmakuController()
else hideBottomDanmakuController()
- ivShowDanmaku?.visibility =
- player.ivShowDanmaku?.visibility ?: View.GONE
- etDanmakuInput?.visibility =
- player.etDanmakuInput?.visibility ?: View.GONE
+ ivShowDanmaku?.visibility = player.ivShowDanmaku?.visibility ?: View.GONE
+ etDanmakuInput?.visibility = player.etDanmakuInput?.visibility ?: View.GONE
+ mDanmakuTextScalePercent =
+ (player.sbDanmakuTextScale?.progress ?: 0) + mDanmakuTextScaleMinPercent
+ setTextSizeScale(player.mDanmakuTextScalePercent / 100f)
mDanmakuShow = player.mDanmakuShow
resolveDanmakuShow()
@@ -434,6 +466,9 @@ open class DanmakuVideoPlayer : AnimeVideoPlayer {
tvRewindDanmakuProgress?.visible()
tvResetDanmakuProgress?.visible()
tvForwardDanmakuProgress?.visible()
+ sbDanmakuTextScale?.visible()
+ tvDanmakuTextScaleHeader?.visible()
+ tvDanmakuTextScale?.visible()
if (mDanmakuParamMap.size > 0) {
etDanmakuInput?.enable()
etDanmakuInput?.hint = mContext.getString(R.string.send_a_danmaku)
@@ -493,6 +528,15 @@ open class DanmakuVideoPlayer : AnimeVideoPlayer {
stopDanmaku()
}
+ /**
+ * 更改弹幕字号缩放百分比
+ * @param scale 缩放倍数,例如2.7f指的是弹幕字号乘2.7
+ */
+ private fun setTextSizeScale(scale: Float) {
+ config = config.copy(textSizeScale = scale)
+ mDanmakuPlayer.updateConfig(config)
+ }
+
/**
* 释放弹幕控件
*/
diff --git a/app/src/main/java/com/skyd/imomoe/view/component/player/DetailPlayerActivity.java b/app/src/main/java/com/skyd/imomoe/view/component/player/DetailPlayerActivity.java
deleted file mode 100644
index f0c2b635..00000000
--- a/app/src/main/java/com/skyd/imomoe/view/component/player/DetailPlayerActivity.java
+++ /dev/null
@@ -1,338 +0,0 @@
-package com.skyd.imomoe.view.component.player;
-
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.view.View;
-
-import androidx.viewbinding.ViewBinding;
-
-import com.skyd.imomoe.view.activity.BaseActivity;
-import com.skyd.skin.core.SkinBaseActivity;
-import com.shuyu.gsyvideoplayer.GSYVideoManager;
-import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder;
-import com.shuyu.gsyvideoplayer.utils.OrientationOption;
-import com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer;
-
-import org.jetbrains.annotations.NotNull;
-
-import static com.shuyu.gsyvideoplayer.video.base.GSYVideoView.CURRENT_STATE_PAUSE;
-
-/**
- * 详情模式播放页面基础类
- */
-public abstract class DetailPlayerActivity extends BaseActivity implements MyVideoAllCallBack {
-
- protected boolean isPlay;
-
- // 是否是在onPause方法里自动暂停的
- protected boolean isPause;
-
- protected AnimeOrientationUtils orientationUtils;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-
- /**
- * 选择普通模式
- */
- public void initVideo() {
- //外部辅助的旋转,帮助全屏
- orientationUtils = new AnimeOrientationUtils(this, getGSYVideoPlayer(), getOrientationOption());
- //初始化不打开外部的旋转
- orientationUtils.setEnable(false);
- if (getGSYVideoPlayer().getFullscreenButton() != null) {
- getGSYVideoPlayer().getFullscreenButton().setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- showFull();
- clickForFullScreen();
- }
- });
- }
- // 退出全屏监听,避免平板退出全屏后变成竖屏
- getGSYVideoPlayer().setBackFromFullScreenListener(view -> {
- onBackPressed();
- });
- }
-
- /**
- * 选择builder模式
- */
- public void initVideoBuilderMode() {
- initVideo();
- getGSYVideoOptionBuilder().
- setVideoAllCallBack(this)
- .build(getGSYVideoPlayer());
- }
-
- public void showFull() {
- if (orientationUtils.getIsLand() != 1) {
- //直接横屏
- orientationUtils.resolveByClick();
- }
- //第一个true是否需要隐藏actionbar,第二个true是否需要隐藏statusBar
- getGSYVideoPlayer().startWindowFullscreen(DetailPlayerActivity.this, hideActionBarWhenFull(), hideStatusBarWhenFull());
-
- }
-
- @Override
- public void onBackPressed() {
- if (orientationUtils != null) {
- orientationUtils.backToProtVideo2();
- }
- if (GSYVideoManager.backFromWindowFull(this)) {
- return;
- }
- super.onBackPressed();
- }
-
-
- @Override
- protected void onPause() {
- super.onPause();
- if (getGSYVideoPlayer().getCurrentPlayer().getCurrentState() != CURRENT_STATE_PAUSE) {
- getGSYVideoPlayer().getCurrentPlayer().onVideoPause();
- if (orientationUtils != null) {
- orientationUtils.setIsPause(true);
- }
- isPause = true;
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- if (isPause) {
- getGSYVideoPlayer().getCurrentPlayer().onVideoResume();
- if (orientationUtils != null) {
- orientationUtils.setIsPause(false);
- }
- isPause = false;
- }
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- if (isPlay) {
- getGSYVideoPlayer().getCurrentPlayer().release();
- }
- if (orientationUtils != null)
- orientationUtils.releaseListener();
- }
-
- /**
- * orientationUtils 和 detailPlayer.onConfigurationChanged 方法是用于触发屏幕旋转的
- */
- @Override
- public void onConfigurationChanged(@NotNull Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- //如果旋转了就全屏
- if (isPlay && !isPause) {
- getGSYVideoPlayer().onConfigurationChanged(this, newConfig, orientationUtils, hideActionBarWhenFull(), hideStatusBarWhenFull());
- }
- }
-
- @Override
- public void onStartPrepared(String url, Object... objects) {
- videoPlayStatusChanged(true);
- needShowToolbar(true);
- }
-
- @Override
- public void onPrepared(String url, Object... objects) {
-
- if (orientationUtils == null) {
- throw new NullPointerException("initVideo() or initVideoBuilderMode() first");
- }
- //开始播放了才能旋转和全屏
- orientationUtils.setEnable(getDetailOrientationRotateAuto() && !isAutoFullWithSize());
- isPlay = true;
- isPause = false;
- videoPlayStatusChanged(true);
- }
-
- @Override
- public void onClickStartIcon(String url, Object... objects) {
-
- }
-
- @Override
- public void onClickStartError(String url, Object... objects) {
-
- }
-
- @Override
- public void onClickStop(String url, Object... objects) {
- videoPlayStatusChanged(false);
- }
-
- @Override
- public void onClickStopFullscreen(String url, Object... objects) {
- videoPlayStatusChanged(false);
- }
-
- @Override
- public void onClickResume(String url, Object... objects) {
- videoPlayStatusChanged(true);
- }
-
- @Override
- public void onClickResumeFullscreen(String url, Object... objects) {
- videoPlayStatusChanged(true);
- }
-
- @Override
- public void onClickSeekbar(String url, Object... objects) {
-
- }
-
- @Override
- public void onClickSeekbarFullscreen(String url, Object... objects) {
-
- }
-
- @Override
- public void onAutoComplete(String url, Object... objects) {
- videoPlayStatusChanged(false);
- needShowToolbar(true);
- }
-
- @Override
- public void onEnterFullscreen(String url, Object... objects) {
-
- }
-
- @Override
- public void onQuitFullscreen(String url, Object... objects) {
- if (orientationUtils != null) {
- orientationUtils.backToProtVideo();
- }
- }
-
- @Override
- public void onQuitSmallWidget(String url, Object... objects) {
-
- }
-
- @Override
- public void onEnterSmallWidget(String url, Object... objects) {
-
- }
-
- @Override
- public void onTouchScreenSeekVolume(String url, Object... objects) {
-
- }
-
- @Override
- public void onTouchScreenSeekPosition(String url, Object... objects) {
-
- }
-
- @Override
- public void onTouchScreenSeekLight(String url, Object... objects) {
-
- }
-
- @Override
- public void onPlayError(String url, Object... objects) {
- videoPlayStatusChanged(false);
- needShowToolbar(true);
- }
-
- @Override
- public void onClickStartThumb(String url, Object... objects) {
-
- }
-
- @Override
- public void onClickBlank(String url, Object... objects) {
-
- }
-
- @Override
- public void onClickBlankFullscreen(String url, Object... objects) {
-
- }
-
- @Override
- public void onComplete(String url, Object... objects) {
- videoPlayStatusChanged(false);
- needShowToolbar(true);
- }
-
- public boolean hideActionBarWhenFull() {
- return true;
- }
-
- public boolean hideStatusBarWhenFull() {
- return true;
- }
-
- /**
- * 可配置旋转 OrientationUtils
- */
- public OrientationOption getOrientationOption() {
- return null;
- }
-
- /**
- * 播放控件
- */
- public abstract T getGSYVideoPlayer();
-
- /**
- * 配置播放器
- */
- public abstract GSYVideoOptionBuilder getGSYVideoOptionBuilder();
-
- /**
- * 点击了全屏
- */
- public abstract void clickForFullScreen();
-
- /**
- * 是否启动旋转横屏,true表示启动
- */
- public abstract boolean getDetailOrientationRotateAuto();
-
- /**
- * 是否根据视频尺寸,自动选择竖屏全屏或者横屏全屏,注意,这时候默认旋转无效
- */
- public boolean isAutoFullWithSize() {
- return false;
- }
-
- @Override
- public void onVideoPause() {
- videoPlayStatusChanged(false);
- needShowToolbar(true);
- }
-
- @Override
- public void onVideoResume() {
- videoPlayStatusChanged(true);
- needShowToolbar(false);
- }
-
- /**
- * 视频播放状态变化
- *
- * @param playing false:未在播放(包括播放失败暂停等等);true:正在播放(包括正在准备加载、缓冲等等)
- */
- protected void videoPlayStatusChanged(boolean playing) {
-
- }
-
- /**
- * 是否需要必须显示工具栏
- *
- * @param show false:不需要显示;true:需要显示
- */
- protected void needShowToolbar(boolean show) {
-
- }
-}
diff --git a/app/src/main/java/com/skyd/imomoe/view/component/player/DetailPlayerActivity.kt b/app/src/main/java/com/skyd/imomoe/view/component/player/DetailPlayerActivity.kt
new file mode 100644
index 00000000..1630d2b2
--- /dev/null
+++ b/app/src/main/java/com/skyd/imomoe/view/component/player/DetailPlayerActivity.kt
@@ -0,0 +1,224 @@
+package com.skyd.imomoe.view.component.player
+
+import android.content.res.Configuration
+import com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer
+import androidx.viewbinding.ViewBinding
+import com.skyd.imomoe.view.activity.BaseActivity
+import com.shuyu.gsyvideoplayer.GSYVideoManager
+import com.shuyu.gsyvideoplayer.video.base.GSYVideoView
+import com.shuyu.gsyvideoplayer.utils.OrientationOption
+import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
+import java.lang.NullPointerException
+
+/**
+ * 详情模式播放页面基础类
+ */
+abstract class DetailPlayerActivity : BaseActivity(),
+ MyVideoAllCallBack {
+ protected open var isPlay = false
+
+ // 是否是在onPause方法里自动暂停的
+ protected open var isPause = false
+ protected open var orientationUtils: AnimeOrientationUtils? = null
+
+ /**
+ * 选择普通模式
+ */
+ protected open fun initVideo() {
+ //外部辅助的旋转,帮助全屏
+ orientationUtils = AnimeOrientationUtils(this, getGSYVideoPlayer(), orientationOption).apply {
+ // 初始化不打开外部的旋转
+ isEnable = false
+ }
+ if (getGSYVideoPlayer().fullscreenButton != null) {
+ getGSYVideoPlayer().fullscreenButton.setOnClickListener {
+ showFull()
+ clickForFullScreen()
+ }
+ }
+ // 退出全屏监听,避免平板退出全屏后变成竖屏
+ getGSYVideoPlayer().setBackFromFullScreenListener { onBackPressed() }
+ }
+
+ /**
+ * 选择builder模式
+ */
+ fun initVideoBuilderMode() {
+ initVideo()
+ gsyVideoOptionBuilder.setVideoAllCallBack(this).build(getGSYVideoPlayer())
+ }
+
+ protected open fun showFull() {
+ if (orientationUtils?.isLand != 1) {
+ //直接横屏
+ orientationUtils?.resolveByClick()
+ }
+ //第一个true是否需要隐藏actionbar,第二个true是否需要隐藏statusBar
+ getGSYVideoPlayer().startWindowFullscreen(
+ this@DetailPlayerActivity,
+ hideActionBarWhenFull(),
+ hideStatusBarWhenFull()
+ )
+ }
+
+ override fun onBackPressed() {
+ orientationUtils?.backToProtVideo2()
+ if (GSYVideoManager.backFromWindowFull(this)) {
+ return
+ }
+ super.onBackPressed()
+ }
+
+ override fun onPause() {
+ super.onPause()
+ if (getGSYVideoPlayer().currentPlayer.currentState != GSYVideoView.CURRENT_STATE_PAUSE) {
+ getGSYVideoPlayer().currentPlayer.onVideoPause()
+ orientationUtils?.setIsPause(true)
+ isPause = true
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ if (isPause) {
+ getGSYVideoPlayer().currentPlayer.onVideoResume()
+ orientationUtils?.setIsPause(false)
+ isPause = false
+ }
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ if (isPlay) {
+ getGSYVideoPlayer().currentPlayer.release()
+ }
+ orientationUtils?.releaseListener()
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ //如果旋转了就全屏
+ if (isPlay && !isPause) {
+ getGSYVideoPlayer().onConfigurationChanged(
+ this,
+ newConfig,
+ orientationUtils,
+ hideActionBarWhenFull(),
+ hideStatusBarWhenFull()
+ )
+ }
+ }
+
+ override fun onStartPrepared(url: String?, vararg objects: Any?) {
+ videoPlayStatusChanged(true)
+ }
+
+ override fun onPrepared(url: String?, vararg objects: Any?) {
+ orientationUtils.let {
+ if (it == null) {
+ throw NullPointerException("initVideo() or initVideoBuilderMode() first")
+ }
+ // 开始播放了才能旋转和全屏
+ it.isEnable = detailOrientationRotateAuto && !isAutoFullWithSize
+ isPlay = true
+ isPause = false
+ videoPlayStatusChanged(true)
+ }
+ }
+
+ override fun onClickStartIcon(url: String?, vararg objects: Any?) {}
+ override fun onClickStartError(url: String?, vararg objects: Any?) {}
+ override fun onClickStop(url: String?, vararg objects: Any?) {
+ videoPlayStatusChanged(false)
+ }
+
+ override fun onClickStopFullscreen(url: String?, vararg objects: Any?) {
+ videoPlayStatusChanged(false)
+ }
+
+ override fun onClickResume(url: String?, vararg objects: Any?) {
+ videoPlayStatusChanged(true)
+ }
+
+ override fun onClickResumeFullscreen(url: String?, vararg objects: Any?) {
+ videoPlayStatusChanged(true)
+ }
+
+ override fun onClickSeekbar(url: String?, vararg objects: Any?) {}
+ override fun onClickSeekbarFullscreen(url: String?, vararg objects: Any?) {}
+ override fun onAutoComplete(url: String?, vararg objects: Any?) {
+ videoPlayStatusChanged(false)
+ }
+
+ override fun onEnterFullscreen(url: String?, vararg objects: Any?) {}
+ override fun onQuitFullscreen(url: String?, vararg objects: Any?) {
+ orientationUtils?.backToProtVideo()
+ }
+
+ override fun onQuitSmallWidget(url: String?, vararg objects: Any?) {}
+ override fun onEnterSmallWidget(url: String?, vararg objects: Any?) {}
+ override fun onTouchScreenSeekVolume(url: String?, vararg objects: Any?) {}
+ override fun onTouchScreenSeekPosition(url: String?, vararg objects: Any?) {}
+ override fun onTouchScreenSeekLight(url: String?, vararg objects: Any?) {}
+ override fun onPlayError(url: String?, vararg objects: Any?) {
+ videoPlayStatusChanged(false)
+ }
+
+ override fun onClickStartThumb(url: String?, vararg objects: Any?) {}
+ override fun onClickBlank(url: String?, vararg objects: Any?) {}
+ override fun onClickBlankFullscreen(url: String?, vararg objects: Any?) {}
+ override fun onComplete(url: String?, vararg objects: Any?) {
+ videoPlayStatusChanged(false)
+ }
+
+ protected open fun hideActionBarWhenFull(): Boolean = true
+
+ protected open fun hideStatusBarWhenFull(): Boolean = true
+
+ /**
+ * 可配置旋转 OrientationUtils
+ */
+ protected open val orientationOption: OrientationOption?
+ get() = null
+
+ /**
+ * 播放控件
+ */
+ abstract fun getGSYVideoPlayer(): T
+
+ /**
+ * 配置播放器
+ */
+ abstract val gsyVideoOptionBuilder: GSYVideoOptionBuilder
+
+ /**
+ * 点击了全屏
+ */
+ abstract fun clickForFullScreen()
+
+ /**
+ * 是否启动旋转横屏,true表示启动
+ */
+ abstract val detailOrientationRotateAuto: Boolean
+
+ /**
+ * 是否根据视频尺寸,自动选择竖屏全屏或者横屏全屏,注意,这时候默认旋转无效
+ */
+ protected open val isAutoFullWithSize: Boolean
+ get() = false
+
+ override fun onVideoPause() {
+ videoPlayStatusChanged(false)
+ }
+
+ override fun onVideoResume() {
+ videoPlayStatusChanged(true)
+ }
+
+ /**
+ * 视频播放状态变化
+ *
+ * @param playing false:未在播放(包括播放失败暂停等等);true:正在播放(包括正在准备加载、缓冲等等)
+ */
+ protected open fun videoPlayStatusChanged(playing: Boolean) {}
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/skyd/imomoe/view/component/player/danmaku/bili/BiliBiliDanmakuParser.kt b/app/src/main/java/com/skyd/imomoe/view/component/player/danmaku/bili/BiliBiliDanmakuParser.kt
index f14990d9..3191eb91 100644
--- a/app/src/main/java/com/skyd/imomoe/view/component/player/danmaku/bili/BiliBiliDanmakuParser.kt
+++ b/app/src/main/java/com/skyd/imomoe/view/component/player/danmaku/bili/BiliBiliDanmakuParser.kt
@@ -10,6 +10,7 @@ import org.xml.sax.helpers.DefaultHandler
import org.xml.sax.helpers.XMLReaderFactory
import java.io.IOException
import java.io.InputStream
+import java.lang.NumberFormatException
import java.util.*
import kotlin.collections.ArrayList
@@ -80,14 +81,18 @@ class BiliBiliDanmakuParser {
val values = pValue.split(",").toTypedArray()
if (values.isNotEmpty()) {
- item = DanmakuItemData(
- content = "",
- danmakuId = values[7].toLong(),
- textSize = (values[2].toFloat() * 2f).dp.toInt(),
- textColor = values[3].toInt(),
- position = (values[0].toFloat() * 1000).toLong(),
- mode = getType(values[1].toInt())
- )
+ try {
+ item = DanmakuItemData(
+ content = "",
+ danmakuId = values[7].toLong(),
+ textSize = getTextSize(values[2].toInt()).toInt(),
+ textColor = values[3].toInt(),
+ position = (values[0].toFloat() * 1000).toLong(),
+ mode = getType(values[1].toInt())
+ )
+ } catch (e: NumberFormatException) {
+ e.printStackTrace()
+ }
}
}
}
@@ -133,6 +138,25 @@ class BiliBiliDanmakuParser {
}
companion object {
+ /**
+ * 获取真实的字体大小px
+ * 弹幕库限制字体最小12f最大25f
+ * @param n 12非常小,16特小,18小,25中,36大,45很大,64特别大
+ * @return 以px为单位的字体大小
+ */
+ private fun getTextSize(n: Int): Float {
+ return when (n) {
+ 12 -> 12f
+ 16 -> 14f
+ 18 -> 17f
+ 25 -> 19f
+ 36 -> 21f
+ 45 -> 23f
+ 64 -> 25f
+ else -> 19f
+ }
+ }
+
private fun getType(s: Int): Int {
// 类型(1从右至左滚动弹幕|6从左至右滚动弹幕|5顶端固定弹幕|4底端固定弹幕|7高级弹幕|8脚本弹幕)
return when (s) {
diff --git a/app/src/main/java/com/skyd/imomoe/view/fragment/HomeFragment.kt b/app/src/main/java/com/skyd/imomoe/view/fragment/HomeFragment.kt
index 14869a63..ea6fabbe 100644
--- a/app/src/main/java/com/skyd/imomoe/view/fragment/HomeFragment.kt
+++ b/app/src/main/java/com/skyd/imomoe/view/fragment/HomeFragment.kt
@@ -9,14 +9,9 @@ import android.view.ViewStub
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
-import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.viewpager2.adapter.FragmentStateAdapter
-import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
-import com.hjq.permissions.OnPermissionCallback
-import com.hjq.permissions.Permission
-import com.hjq.permissions.XXPermissions
import com.skyd.imomoe.R
import com.skyd.imomoe.databinding.FragmentHomeBinding
import com.skyd.imomoe.model.DataSourceManager
@@ -27,6 +22,7 @@ import com.skyd.imomoe.util.eventbus.EventBusSubscriber
import com.skyd.imomoe.util.eventbus.MessageEvent
import com.skyd.imomoe.util.eventbus.RefreshEvent
import com.skyd.imomoe.util.eventbus.SelectHomeTabEvent
+import com.skyd.imomoe.util.requestManageExternalStorage
import com.skyd.imomoe.view.activity.*
import com.skyd.imomoe.view.listener.dsl.addOnTabSelectedListener
import com.skyd.imomoe.viewmodel.HomeViewModel
@@ -92,18 +88,10 @@ class HomeFragment : BaseFragment(), EventBusSubscriber {
ivHomeFragmentAnimeDownload.setOnClickListener {
it.clickScale(0.8f, 70)
- XXPermissions.with(this@HomeFragment).permission(Permission.MANAGE_EXTERNAL_STORAGE)
- .request(object : OnPermissionCallback {
- override fun onGranted(permissions: MutableList?, all: Boolean) {
- startActivity(Intent(activity, AnimeDownloadActivity::class.java))
- }
-
- override fun onDenied(permissions: MutableList?, never: Boolean) {
- super.onDenied(permissions, never)
- "无存储权限,无法播放本地缓存视频".showToast(Toast.LENGTH_LONG)
- }
- }
- )
+ requestManageExternalStorage {
+ onGranted { startActivity(Intent(activity, AnimeDownloadActivity::class.java)) }
+ onDenied { "无存储权限,无法播放本地缓存视频".showToast(Toast.LENGTH_LONG) }
+ }
}
ivHomeFragmentFavorite.setOnClickListener {
diff --git a/app/src/main/java/com/skyd/imomoe/view/listener/dsl/OnPermissionsCallback.kt b/app/src/main/java/com/skyd/imomoe/view/listener/dsl/OnPermissionsCallback.kt
new file mode 100644
index 00000000..3869b2cf
--- /dev/null
+++ b/app/src/main/java/com/skyd/imomoe/view/listener/dsl/OnPermissionsCallback.kt
@@ -0,0 +1,63 @@
+package com.skyd.imomoe.view.listener.dsl
+
+import com.hjq.permissions.XXPermissions
+
+fun XXPermissions.requestPermissions(init: OnPermissionsCallback.() -> Unit) {
+ val listener = OnPermissionsCallback()
+ listener.init()
+ this.request(listener)
+}
+
+fun XXPermissions.requestSinglePermission(init: OnSinglePermissionCallback.() -> Unit) {
+ val listener = OnSinglePermissionCallback()
+ listener.init()
+ this.request(listener)
+}
+
+private typealias Granted = (permissions: MutableList?, all: Boolean) -> Unit
+private typealias Denied = (permissions: MutableList?, never: Boolean) -> Unit
+
+class OnPermissionsCallback : com.hjq.permissions.OnPermissionCallback {
+ private var granted: Granted? = null
+ private var denied: Denied? = null
+
+ fun onGranted(granted: Granted?) {
+ this.granted = granted
+ }
+
+ fun onDenied(denied: Denied?) {
+ this.denied = denied
+ }
+
+ override fun onGranted(permissions: MutableList?, all: Boolean) {
+ granted?.invoke(permissions, all)
+ }
+
+ override fun onDenied(permissions: MutableList?, never: Boolean) {
+ denied?.invoke(permissions, never)
+ }
+}
+
+private typealias SingleGranted = () -> Unit
+private typealias SingleDenied = (never: Boolean) -> Unit
+
+class OnSinglePermissionCallback : com.hjq.permissions.OnPermissionCallback {
+ private var singleGranted: SingleGranted? = null
+ private var singleDenied: SingleDenied? = null
+
+ fun onGranted(singleGranted: SingleGranted?) {
+ this.singleGranted = singleGranted
+ }
+
+ fun onDenied(singleDenied: SingleDenied?) {
+ this.singleDenied = singleDenied
+ }
+
+ override fun onGranted(permissions: MutableList?, all: Boolean) {
+ singleGranted?.invoke()
+ }
+
+ override fun onDenied(permissions: MutableList?, never: Boolean) {
+ singleDenied?.invoke(never)
+ }
+}
diff --git a/app/src/main/res/layout/activity_anime_detail.xml b/app/src/main/res/layout/activity_anime_detail.xml
index 5dddfcf2..2d3b2dd9 100644
--- a/app/src/main/res/layout/activity_anime_detail.xml
+++ b/app/src/main/res/layout/activity_anime_detail.xml
@@ -19,16 +19,11 @@
android:layout_height="match_parent"
android:background="@color/transparent_skin" />
-
-
diff --git a/app/src/main/res/layout/activity_favorite.xml b/app/src/main/res/layout/activity_favorite.xml
index 9fda5935..8ef1382f 100644
--- a/app/src/main/res/layout/activity_favorite.xml
+++ b/app/src/main/res/layout/activity_favorite.xml
@@ -1,9 +1,9 @@
@@ -26,6 +26,7 @@
android:id="@+id/rv_favorite_activity"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:clipToPadding="false"
android:overScrollMode="never"
android:paddingHorizontal="16dp"
android:paddingTop="6dp" />
diff --git a/app/src/main/res/layout/layout_anime_video_player_land.xml b/app/src/main/res/layout/layout_anime_video_player_land.xml
index 1e314f7c..8da8caa4 100644
--- a/app/src/main/res/layout/layout_anime_video_player_land.xml
+++ b/app/src/main/res/layout/layout_anime_video_player_land.xml
@@ -384,81 +384,14 @@
android:paddingHorizontal="12dp"
android:paddingVertical="12dp">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:textColor="@color/foreground_white_skin"
+ android:textSize="12sp" />
-
-
+ android:textColor="@color/foreground_white_skin"
+ android:textSize="12sp" />
-
+ android:layout_marginTop="10dp">
-
+
-
+ android:orientation="horizontal">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 28b23e39..a0d5da1b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -192,4 +192,5 @@
无效数据源文件后缀:%s
正在使用此数据源!
关闭
+ 弹幕字号
diff --git a/doc/customdatasource/README.md b/doc/customdatasource/README.md
index a88edae0..98f9e499 100644
--- a/doc/customdatasource/README.md
+++ b/doc/customdatasource/README.md
@@ -6,15 +6,15 @@
## **快捷导航:大部分普通用户可以只看①-④**
-## [①使用已有的自定义数据源](#一.使用已有的自定义数据源)
+## [①使用已有的自定义数据源](#一-使用已有的自定义数据源)
## [②示例代码和ads包](#例子)
-## [③删除数据源](#二.删除数据源)
+## [③删除数据源](#二-删除数据源)
-## [④恢复默认数据源](#三.恢复默认数据源)
+## [④恢复默认数据源](#三-恢复默认数据源)
-## [⑤自制数据源(高级)](#四.自制数据源)
+## [⑤自制数据源(高级)](#四-自制数据源)
## 什么是自定义数据源?
@@ -26,7 +26,7 @@
## 如何使用自定义数据源功能?
-### 一.使用已有的自定义数据源
+### 一 使用已有的自定义数据源
### **>>已有的ads包可从[此处](#例子)找到<<**
@@ -48,7 +48,7 @@
-### 二.删除数据源
+### 二 删除数据源
#### 1.进入配置自定义数据源页面,长按列表中的数据源
@@ -58,7 +58,7 @@
-### 三.恢复默认数据源
+### 三 恢复默认数据源
#### 1.进入配置自定义数据源页面,点击右上角恢复按钮
@@ -68,7 +68,7 @@
-### 四.自制数据源
+### 四 自制数据源
自己编写ads包(熟悉**Kotlin**,最好能够了解Android的基础知识)
@@ -135,6 +135,7 @@ com.skyd.imomoe.model.util.**
com.skyd.imomoe.util.html.source.**
com.skyd.imomoe.util.eventbus.**
com.skyd.imomoe.util.Util
+com.skyd.imomoe.util.ToastKt
com.skyd.imomoe.bean.**
com.skyd.imomoe.config.**
org.jsoup.**