diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..61e8c9b66a --- /dev/null +++ b/.gitignore @@ -0,0 +1,90 @@ +# Built application files +*.apk +*.aar +*.ap_ +*.aab + +proguardMapping.txt + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ +# Uncomment the following line in case you need and you don't have the release build type files in your app +# release/ + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# IntelliJ +*.iml +.idea/workspace.xml +.idea/tasks.xml +.idea/gradle.xml +.idea/assetWizardSettings.xml +.idea/dictionaries +.idea/libraries +# Android Studio 3 in .gitignore file. +.idea/caches +.idea/modules.xml +# Comment next line if keeping position of elements in Navigation Editor is relevant for you +.idea/navEditor.xml + +# Keystore files +# Uncomment the following lines if you do not want to check your keystore files in. +#*.jks +#*.keystore + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild +.cxx/ + +# Google Services (e.g. APIs or Firebase) +# google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md + +# Version control +vcs.xml + +# lint +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +# lint/reports/ +/LTDecodeSpace +/adb.exe +/LTDecodeNew3.exe diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000000..25d48a7c96 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +TVBox \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000000..681f41ae2a --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,116 @@ + + + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
+
+
\ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000000..fb7f4a8a46 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000000..14164b64fe --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000000..470028385e --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000000..e497da9998 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000000..4dc13742f9 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,98 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 29 + buildToolsVersion '29.0.2' + + defaultConfig { + applicationId 'com.github.tvbox.osc' + minSdkVersion 16 + targetSdkVersion 29 + versionCode 33 + versionName '4.3.3' + multiDexEnabled true + //设置room的Schema的位置 + javaCompileOptions { + annotationProcessorOptions { + arguments = ["room.schemaLocation": "$projectDir/schemas".toString()] + } + } + } + + packagingOptions { + exclude 'META-INF/DEPENDENCIES' + } + + buildTypes { + debug { + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + minifyEnabled false + + ndk { + abiFilters 'armeabi-v7a' + } + } + release { + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + minifyEnabled true + + ndk { + abiFilters 'armeabi-v7a' + } + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + lintOptions { + checkReleaseBuilds false + abortOnError false + } + dexOptions { + javaMaxHeapSize "4g" + additionalParameters += '--multi-dex' + additionalParameters += '--set-max-idx-number=48000' + additionalParameters += '--minimal-main-dex' + } +} + +dependencies { + api fileTree(dir: "libs", include: ["*.jar"]) + + + implementation 'org.nanohttpd:nanohttpd:2.3.1' + implementation 'com.google.zxing:core:3.4.1' + implementation 'androidx.appcompat:appcompat:1.3.0' + implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' + implementation 'androidx.recyclerview:recyclerview:1.1.0' + + annotationProcessor 'androidx.room:room-compiler:2.3.0' + implementation 'androidx.room:room-runtime:2.3.0' + implementation 'androidx.multidex:multidex:2.0.1' + implementation 'com.squareup.okhttp3:okhttp:3.12.1' + implementation 'com.squareup.okio:okio:2.6.0' + implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.45-androidx' + implementation 'com.kingja.loadsir:loadsir:1.3.8' + implementation 'com.google.code.gson:gson:2.8.7' + implementation 'com.squareup.picasso:picasso:2.71828' + implementation 'me.jessyan:autosize:1.2.1' + implementation('com.thoughtworks.xstream:xstream:1.4.15') { + exclude group: 'xmlpull', module: 'xmlpull' + } + implementation 'org.greenrobot:eventbus:3.2.0' + implementation 'com.orhanobut:hawk:2.0.1' + + implementation('org.xwalk:xwalk_shared_library:23.53.589.4') { + exclude group: 'com.android.support' + } + implementation 'com.lzy.net:okgo:3.0.4' + // implementation 'com.tencent.bugly:crashreport_upgrade:latest.release' + implementation 'com.github.dueeeke.dkplayer:dkplayer-java:3.2.6' + implementation 'com.github.dueeeke.dkplayer:dkplayer-ui:3.2.6' + implementation 'com.github.dueeeke.dkplayer:player-exo:3.2.6' + implementation 'com.github.dueeeke.dkplayer:player-ijk:3.2.6' + implementation 'com.owen:tv-recyclerview:3.0.0' +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000000..22ad6f6d49 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,194 @@ +############################################# +# +# 对于一些基本指令的添加 +# +############################################# +-optimizationpasses 5 +-dontusemixedcaseclassnames +-dontskipnonpubliclibraryclasses +-dontskipnonpubliclibraryclassmembers +-dontpreverify +-verbose +-printmapping proguardMapping.txt +-optimizations !code/simplification/cast,!field/*,!class/merging/* +-keepattributes *Annotation*,InnerClasses +-keepattributes EnclosingMethod, InnerClasses +-keepattributes *Annotation* +-keepattributes Signature +-keepattributes LineNumberTable +-renamesourcefileattribute SourceFile + +# 重新包装所有重命名的包并放在给定的单一包中 +-flattenpackagehierarchy androidx.base + +# 将包里的类混淆成n个再重新打包到一个统一的package中 会覆盖flattenpackagehierarchy选项 +-repackageclasses androidx.base + +# 把混淆类中的方法名也混淆了 +-useuniqueclassmembernames +############################################# +# +# Android开发中一些需要保留的公共部分 +# +############################################# + +# 保留我们使用的四大组件,自定义的Application等等这些类不被混淆 +# 因为这些子类都有可能被外部调用 +-keep public class * extends android.app.Activity +-keep public class * extends android.app.Appliction +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider +-keep public class * extends android.app.backup.BackupAgentHelper +-keep public class * extends android.preference.Preference +-keep public class * extends android.view.View +-keep public class com.android.vending.licensing.ILicensingService + +# 保留support下的所有类及其内部类 +-keep class android.support.** {*;} +# 保留继承的 +-keep public class * extends android.support.v4.** +-keep public class * extends android.support.v7.** +-keep public class * extends android.support.annotation.** + +-keep class com.google.android.material.** { *; } +-dontwarn com.google.android.material.** +-dontnote com.google.android.material.** +-dontwarn androidx.** +-keep class androidx.** { *; } +-keep interface androidx.** { *; } +#-keep public class * extends androidx.** + +-keep class org.xmlpull.v1.** {*;} + +# 保留R下面的资源 +-keep class **.R$* {*;} + +# 保留本地native方法不被混淆 +-keepclasseswithmembernames class * { + native ; +} + +# 保留在Activity中的方法参数是view的方法, +# 这样以来我们在layout中写的onClick就不会被影响 +-keepclassmembers class * extends android.app.Activity{ + public void *(android.view.View); +} + +# 保留枚举类不被混淆 +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +# 保留我们自定义控件(继承自View)不被混淆 +-keep public class * extends android.view.View{ + *** get*(); + void set*(***); + public (android.content.Context); + public (android.content.Context, android.util.AttributeSet); + public (android.content.Context, android.util.AttributeSet, int); +} + +# 保留Parcelable序列化类不被混淆 +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} + +# 保留Serializable序列化的类不被混淆 +-keepclassmembers class * implements java.io.Serializable { + static final long serialVersionUID; + private static final java.io.ObjectStreamField[] serialPersistentFields; + !static !transient ; + !private ; + !private ; + private void writeObject(java.io.ObjectOutputStream); + private void readObject(java.io.ObjectInputStream); + java.lang.Object writeReplace(); + java.lang.Object readResolve(); +} + +# 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆 +-keepclassmembers class * { + void *(**On*Event); + void *(**On*Listener); +} +#xwalk +-keep class org.xwalk.core.** { *; } +-keep class org.crosswalk.engine.** { *; } +-keep class org.chromium.** { *; } +-dontwarn android.view.** +-dontwarn android.media.** +-dontwarn org.chromium.** +#okhttp +-dontwarn okhttp3.** +-keep class okhttp3.**{*;} +#okio +-dontwarn okio.** +-keep class okio.**{*;} +#loadsir +-dontwarn com.kingja.loadsir.** +-keep class com.kingja.loadsir.** {*;} +#gson +# Gson specific classes +-dontwarn sun.misc.** +#-keep class com.google.gson.stream.** { *; } +# Application classes that will be serialized/deserialized over Gson +-keep class com.google.gson.examples.android.model.** { ; } +# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory, +# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter) +-keep class * extends com.google.gson.TypeAdapter +-keep class * implements com.google.gson.TypeAdapterFactory +-keep class * implements com.google.gson.JsonSerializer +-keep class * implements com.google.gson.JsonDeserializer +# Prevent R8 from leaving Data object members always null +-keepclassmembers,allowobfuscation class * { + @com.google.gson.annotations.SerializedName ; +} +#xstream +-keep class com.thoughtworks.xstream.converters.extended.SubjectConverter { *; } +-keep class com.thoughtworks.xstream.converters.extended.ThrowableConverter { *; } +-keep class com.thoughtworks.xstream.converters.extended.StackTraceElementConverter { *; } +-keep class com.thoughtworks.xstream.converters.extended.CurrencyConverter { *; } +-keep class com.thoughtworks.xstream.converters.extended.RegexPatternConverter { *; } +-keep class com.thoughtworks.xstream.converters.extended.CharsetConverter { *; } +-keep class com.thoughtworks.xstream.** { *; } +#eventbus +-keepclassmembers class * { + @org.greenrobot.eventbus.Subscribe ; +} +-keep enum org.greenrobot.eventbus.ThreadMode { *; } +# And if you use AsyncExecutor: +-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent { + (java.lang.Throwable); +} +#bugly +-dontwarn com.tencent.bugly.** +-keep public class com.tencent.bugly.**{*;} +-keep class android.support.**{*;} + +#dkplayer +-keep class com.dueeeke.videoplayer.** { *; } +-dontwarn com.dueeeke.videoplayer.** + +# IjkPlayer +-keep class tv.danmaku.ijk.** { *; } +-dontwarn tv.danmaku.ijk.** + +# ExoPlayer +-keep class com.google.android.exoplayer2.** { *; } +-dontwarn com.google.android.exoplayer2.** + +# 实体类 +#-keep class com.github.tvbox.osc.bean.** { *; } +#CardView +-keep class com.github.tvbox.osc.ui.tv.widget.card.**{*;} +#ViewObj +-keep class com.github.tvbox.osc.ui.tv.widget.ViewObj{ + ; +} + +#Spider +-keep class * implements com.github.tvbox.osc.spider.Spider { + public ; +} \ No newline at end of file diff --git a/app/schemas/com.github.tvbox.osc.data.AppDataBase/3.json b/app/schemas/com.github.tvbox.osc.data.AppDataBase/3.json new file mode 100644 index 0000000000..f7ebf8c508 --- /dev/null +++ b/app/schemas/com.github.tvbox.osc.data.AppDataBase/3.json @@ -0,0 +1,225 @@ +{ + "formatVersion": 1, + "database": { + "version": 3, + "identityHash": "33b130e0cef69955d9d6fda2087674b0", + "entities": [ + { + "tableName": "cache", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `data` BLOB, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "BLOB", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "vodRecord", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `vodId` TEXT, `updateTime` INTEGER NOT NULL, `sourceKey` TEXT, `data` BLOB, `dataJson` TEXT, `testMigration` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "vodId", + "columnName": "vodId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "updateTime", + "columnName": "updateTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sourceKey", + "columnName": "sourceKey", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "BLOB", + "notNull": false + }, + { + "fieldPath": "dataJson", + "columnName": "dataJson", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "testMigration", + "columnName": "testMigration", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "localSource", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `api` TEXT NOT NULL, `type` INTEGER NOT NULL DEFAULT 0, `playerUrl` TEXT, PRIMARY KEY(`name`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "api", + "columnName": "api", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "playerUrl", + "columnName": "playerUrl", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "name" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "sourceState", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sourceKey` TEXT NOT NULL, `home` INTEGER NOT NULL, `active` INTEGER NOT NULL, `tidSort` TEXT, PRIMARY KEY(`sourceKey`))", + "fields": [ + { + "fieldPath": "sourceKey", + "columnName": "sourceKey", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "home", + "columnName": "home", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "active", + "columnName": "active", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "tidSort", + "columnName": "tidSort", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "sourceKey" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "localParse", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `url` TEXT NOT NULL, PRIMARY KEY(`name`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "name" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "localLive", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `url` TEXT NOT NULL, PRIMARY KEY(`name`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "name" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '33b130e0cef69955d9d6fda2087674b0')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..61d2964cf1 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/cfg.json b/app/src/main/assets/cfg.json new file mode 100644 index 0000000000..1560f90093 --- /dev/null +++ b/app/src/main/assets/cfg.json @@ -0,0 +1,233 @@ +{ + "sources": [ + { + "key": "快播云", + "name": "快播云", + "type": 0, + "api": "http://www.kuaibozy.com/api.php/provide/vod/at/xml/", + "playerUrl": "" + } + ], + "live": [ + { + "channelNum": 1, + "channelName": "CCTV-1", + "channelUrl": [ + "http://111.12.102.68:6610/PLTV/77777777/224/3221225674/index.m3u8?", + "http://111.12.102.68:6610/PLTV/77777777/224/3221225654/index.m3u8?", + "http://111.12.102.68:6610/PLTV/77777777/224/3221225772/index.m3u8?" + ] + } + ], + "parseFlag": [ + "youku", + "qq", + "iqiyi", + "qiyi", + "letv", + "sohu", + "tudou", + "pptv", + "mgtv", + "wasu" + ], + "parseUrl": [ + { + "parseName": "解析1", + "parseUrl": "https://jx.kuluni.cn/?url=" + }, + { + "parseName": "解析2", + "parseUrl": "https://dm.lby.pet/m3u8.php?url=" + }, + { + "parseName": "解析3", + "parseUrl": "http://api.baiyug.vip/index.php?url=" + }, + { + "parseName": "解析4", + "parseUrl": "http://playx.top/?url=" + }, + { + "parseName": "解析5", + "parseUrl": "http://play1.online/?url=" + }, + { + "parseName": "解析6", + "parseUrl": "http://api.baiyug.vip/?url=" + }, + { + "parseName": "解析7", + "parseUrl": "http://bofang.online/?url=" + }, + { + "parseName": "解析8", + "parseUrl": "http://607p.com/?url=" + }, + { + "parseName": "解析9", + "parseUrl": "http://pangujx.cc/?url=" + }, + { + "parseName": "解析10", + "parseUrl": "http://jx.quanmingjiexi.com/?url=" + }, + { + "parseName": "解析11", + "parseUrl": "https://okjx.cc?url=" + }, + { + "parseName": "解析12", + "parseUrl": "http://jqaaa.com/jx.php?url=" + }, + { + "parseName": "解析13", + "parseUrl": "https://www.qianyicp.com/jiexi/index.php?url=" + }, + { + "parseName": "解析14", + "parseUrl": "https://www.xymav.com/?url=" + }, + { + "parseName": "解析15", + "parseUrl": "https://www.1717yun.com/jx/ty.php?url=" + }, + { + "parseName": "解析16", + "parseUrl": "https://jx.mw0.cc/?url=" + }, + { + "parseName": "解析17", + "parseUrl": "https://jx.618g.com/?url=" + }, + { + "parseName": "解析18", + "parseUrl": "https://plamgtvcache.ccyjjd.com/play.php?url=" + }, + { + "parseName": "解析19", + "parseUrl": "https://pay.520yk.cn/256/?url=" + } + ], + "filter": { + "base": [ + "电视剧", + "连续剧", + "电影", + "剧集" + ] + }, + "ijk": { + "option_bak": [ + "4|opensles|0", + "4|overlay-format|842225234", + "4|framedrop|1", + "4|start-on-prepared|1", + "1|http-detect-range-support|0", + "2|skip_loop_filter|48", + "4|reconnect|1", + "4|enable-accurate-seek|1" + ], + "option": [ + "4|opensles|0", + "4|overlay-format|842225234", + "4|framedrop|1", + "4|soundtouch|1", + "4|start-on-prepared|1", + "1|http-detect-range-support|0", + "1|fflags|fastseek", + "2|skip_loop_filter|48", + "4|reconnect|1", + "4|max-buffer-size|5242880", + "4|enable-accurate-seek|0" + ], + "config": [ + { + "name": "软解码", + "option": [ + "4|mediacodec|0", + "4|mediacodec-auto-rotate|0", + "4|mediacodec-handle-resolution-change|0", + "4|mediacodec-hevc|0" + ] + }, + { + "name": "硬解码1", + "option": [ + "4|mediacodec|1", + "4|mediacodec-auto-rotate|1", + "4|mediacodec-handle-resolution-change|1", + "4|mediacodec-hevc|1" + ] + }, + { + "name": "硬解码2", + "option": [ + "4|mediacodec|1", + "4|mediacodec-auto-rotate|1", + "4|mediacodec-handle-resolution-change|1", + "4|mediacodec-hevc|0" + ] + } + ] + }, + "ads": [ + "mimg.0c1q0l.cn", + "www.googletagmanager.com", + "www.google-analytics.com", + "mc.usihnbcq.cn", + "mg.g1mm3d.cn", + "mscs.svaeuzh.cn", + "cnzz.hhttm.top", + "tp.vinuxhome.com", + "cnzz.mmstat.com", + "www.baihuillq.com", + "s23.cnzz.com", + "z3.cnzz.com", + "c.cnzz.com", + "stj.v1vo.top", + "z12.cnzz.com", + "img.mosflower.cn", + "tips.gamevvip.com", + "ehwe.yhdtns.com", + "xdn.cqqc3.com", + "www.jixunkyy.cn", + "sp.chemacid.cn", + "hm.baidu.com", + "s9.cnzz.com", + "z6.cnzz.com", + "um.cavuc.com", + "mav.mavuz.com", + "wofwk.aoidf3.com", + "z5.cnzz.com", + "xc.hubeijieshikj.cn", + "tj.tianwenhu.com", + "xg.gars57.cn", + "k.jinxiuzhilv.com", + "cdn.bootcss.com", + "ppl.xunzhuo123.com", + "xomk.jiangjunmh.top", + "img.xunzhuo123.com", + "z1.cnzz.com", + "s13.cnzz.com", + "xg.huataisangao.cn", + "z7.cnzz.com", + "xg.huataisangao.cn", + "z2.cnzz.com", + "s96.cnzz.com", + "q11.cnzz.com", + "thy.dacedsfa.cn", + "xg.whsbpw.cn", + "s19.cnzz.com", + "z8.cnzz.com", + "s4.cnzz.com", + "f5w.as12df.top", + "ae01.alicdn.com", + "www.92424.cn", + "k.wudejia.com", + "vivovip.mmszxc.top", + "qiu.xixiqiu.com", + "cdnjs.hnfenxun.com", + "cms.qdwght.com" + ] +} \ No newline at end of file diff --git a/app/src/main/assets/ua.db b/app/src/main/assets/ua.db new file mode 100644 index 0000000000..53a2eac591 Binary files /dev/null and b/app/src/main/assets/ua.db differ diff --git a/app/src/main/java/com/github/tvbox/osc/api/ApiConfig.java b/app/src/main/java/com/github/tvbox/osc/api/ApiConfig.java new file mode 100644 index 0000000000..3bf3cccbab --- /dev/null +++ b/app/src/main/java/com/github/tvbox/osc/api/ApiConfig.java @@ -0,0 +1,320 @@ +package com.github.tvbox.osc.api; + +import android.app.Activity; +import android.text.TextUtils; +import android.util.Base64; + +import com.github.tvbox.osc.bean.IJKCode; +import com.github.tvbox.osc.bean.LiveChannel; +import com.github.tvbox.osc.bean.ParseBean; +import com.github.tvbox.osc.bean.SourceBean; +import com.github.tvbox.osc.cache.LocalLive; +import com.github.tvbox.osc.cache.LocalParse; +import com.github.tvbox.osc.cache.LocalSource; +import com.github.tvbox.osc.cache.RoomDataManger; +import com.github.tvbox.osc.cache.SourceState; +import com.github.tvbox.osc.util.AdBlocker; +import com.github.tvbox.osc.util.DefaultConfig; +import com.github.tvbox.osc.util.HawkConfig; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import com.orhanobut.hawk.Hawk; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.zip.GZIPInputStream; + +/** + * @author pj567 + * @date :2020/12/18 + * @description: + */ +public class ApiConfig { + private static ApiConfig instance; + private List sourceBeanList; + private SourceBean mHomeSource; + private ParseBean mDefaultParse; + private List channelList; + private List parseBeanList; + private List vipParseFlags; + private List ijkCodes; + + private ApiConfig() { + sourceBeanList = new ArrayList<>(); + channelList = new ArrayList<>(); + parseBeanList = new ArrayList<>(); + } + + public static ApiConfig get() { + if (instance == null) { + synchronized (ApiConfig.class) { + if (instance == null) { + instance = new ApiConfig(); + } + } + } + return instance; + } + + private String decompressGZIP(byte[] src) throws IOException { + // 创建一个新的输出流 + ByteArrayOutputStream out = new ByteArrayOutputStream(); + // 创建一个 ByteArrayInputStream,使用 buf 作为其缓冲区数组 + ByteArrayInputStream in = new ByteArrayInputStream(src); + // 使用默认缓冲区大小创建新的输入流 + GZIPInputStream gzip = new GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n = 0; + // 将未压缩数据读入字节数组 + while ((n = gzip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + // 使用指定的 charsetName,通过解码字节将缓冲区内容转换为字符串 + return out.toString("utf-8"); + } + + public void loadConfig(LoadConfigCallback callback, Activity activity) { + boolean isSourceModeLocal = Hawk.get(HawkConfig.SOURCE_MODE_LOCAL, true); + if (isSourceModeLocal) { + loadConfigLocal(callback, activity); + } else { + loadConfigServer(callback, activity); + } + } + + private void loadConfigLocal(LoadConfigCallback callback, Activity activity) { + try { + StringBuilder sb = new StringBuilder(); + InputStream is = activity.getAssets().open("cfg.json"); + BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8")); + String str; + while ((str = br.readLine()) != null) { + sb.append(str); + } + br.close(); + parseJson(sb.toString()); + callback.success(); + } catch (IOException e) { + e.printStackTrace(); + callback.error("加载配置失败"); + } + + } + + private void loadConfigLocal1(LoadConfigCallback callback, Activity activity) { + try { + BufferedReader bf = new BufferedReader(new InputStreamReader(activity.getAssets().open("cfg.json"))); + String line = bf.readLine(); + String timestamp = line.substring(line.length() - 13); + String infoString = line.substring(0, line.length() - 13); + byte[] infoBytes = Base64.decode(infoString, Base64.DEFAULT); + for (int i = 0; i < infoBytes.length; i++) { + if (i % 2 == 0) { + int idx = i % timestamp.length(); + int c = timestamp.charAt(idx); + infoBytes[i] ^= c + 10; + } + } + String finalRemoteJson = decompressGZIP(infoBytes); + parseJson(finalRemoteJson); + callback.success(); + } catch (IOException e) { + e.printStackTrace(); + callback.error("加载配置失败"); + } + } + + public static final int REG_CODE_INVALID = -2; + + private void loadConfigServer(LoadConfigCallback callback, Activity activity) { + } + + private void parseJson(String jsonStr) { + JsonObject infoJson = new Gson().fromJson(jsonStr, JsonObject.class); + // 远端站点源 + sourceBeanList = new Gson().fromJson(infoJson.get("sources").toString(), new TypeToken>() { + }.getType()); + // 获取本地站点源 + List localSources = RoomDataManger.getAllLocalSource(); + for (LocalSource sbl : localSources) { + SourceBean local = new SourceBean(); + local.initFromLocal(sbl); + sourceBeanList.add(local); + } + if (sourceBeanList != null && sourceBeanList.size() > 0) { + // 获取启用状态 + HashMap sourceStates = RoomDataManger.getAllSourceState(); + for (SourceBean sb : sourceBeanList) { + if (sourceStates.containsKey(sb.getKey())) + sb.setState(sourceStates.get(sb.getKey())); + if (sb.isHome()) + setSourceBean(sb); + } + // 如果没有home source 使用第一个 + if (mHomeSource == null) + setSourceBean(sourceBeanList.get(0)); + } + // 需要使用vip解析的flag + vipParseFlags = new Gson().fromJson(infoJson.get("parseFlag").toString(), new TypeToken>() { + }.getType()); + // 解析地址 + parseBeanList = new Gson().fromJson(infoJson.get("parseUrl").toString(), new TypeToken>() { + }.getType()); + // 获取本地解析地址 + List localParses = RoomDataManger.getAllLocalParse(); + for (LocalParse lp : localParses) { + ParseBean local = new ParseBean(); + local.initFromLocal(lp); + parseBeanList.add(local); + } + // 获取启用状态 + if (parseBeanList != null && parseBeanList.size() > 0) { + String defaultParse = Hawk.get(HawkConfig.DEFAULT_PARSE, ""); + if (!TextUtils.isEmpty(defaultParse)) + for (ParseBean pb : parseBeanList) { + if (pb.getParseName().equals(defaultParse)) + setDefaultParse(pb); + } + if (mDefaultParse == null) + setDefaultParse(parseBeanList.get(0)); + } + // 直播源 + channelList = new Gson().fromJson(infoJson.get("live").toString(), new TypeToken>() { + }.getType()); + // 获取本地解析地址 + List localLives = RoomDataManger.getAllLocalLive(); + for (LocalLive ll : localLives) { + LiveChannel lc = new LiveChannel(); + lc.initFromLocal(ll); + channelList.add(lc); + } + // 广告地址 + for (JsonElement host : infoJson.getAsJsonArray("ads")) { + AdBlocker.addAdHost(host.getAsString()); + } + // 屏蔽分类 + List rmBase = new ArrayList<>(); + List rmAdolescent = new ArrayList<>(); + for (JsonElement rm : infoJson.get("filter").getAsJsonObject().get("base").getAsJsonArray()) { + rmBase.add(rm.getAsString()); + } + DefaultConfig.initRemove(rmBase); + // IJK解码配置 + boolean foundOldSelect = false; + String ijkCodec = Hawk.get(HawkConfig.IJK_CODEC, ""); + ijkCodes = new ArrayList<>(); + LinkedHashMap baseOpt = new LinkedHashMap<>(); + for (JsonElement opt : infoJson.get("ijk").getAsJsonObject().get("option").getAsJsonArray()) { + String s = opt.getAsString(); + baseOpt.put(s.substring(0, s.lastIndexOf("|")), s.substring(s.lastIndexOf("|") + 1)); + } + for (JsonElement cfg : infoJson.get("ijk").getAsJsonObject().get("config").getAsJsonArray()) { + String name = cfg.getAsJsonObject().get("name").getAsString(); + LinkedHashMap mergeOpt = new LinkedHashMap<>(); + mergeOpt.putAll(baseOpt); + for (JsonElement opt : cfg.getAsJsonObject().get("option").getAsJsonArray()) { + String s = opt.getAsString(); + mergeOpt.put(s.substring(0, s.lastIndexOf("|")), s.substring(s.lastIndexOf("|") + 1)); + } + IJKCode codec = new IJKCode(); + codec.setName(name); + codec.setOption(mergeOpt); + if (name.equals(ijkCodec) || TextUtils.isEmpty(ijkCodec)) { + codec.selected(true); + ijkCodec = name; + foundOldSelect = true; + } else { + codec.selected(false); + } + ijkCodes.add(codec); + } + if (!foundOldSelect && ijkCodes.size() > 0) { + ijkCodes.get(0).selected(true); + } + } + + public interface LoadConfigCallback { + void success(); + + void retry(); + + void error(String msg); + } + + public interface FastParseCallback { + void success(boolean parse, String url, Map header); + + void fail(int code, String msg); + } + + public SourceBean getSource(String key) { + for (SourceBean bean : sourceBeanList) { + if (bean.getKey().equals(key)) + return bean; + } + return null; + } + + public void setSourceBean(SourceBean sourceBean) { + if (this.mHomeSource != null) + this.mHomeSource.setHome(false); + this.mHomeSource = sourceBean; + sourceBean.setHome(true); + } + + public void setDefaultParse(ParseBean parseBean) { + if (this.mDefaultParse != null) + this.mDefaultParse.setDefault(false); + this.mDefaultParse = parseBean; + Hawk.put(HawkConfig.DEFAULT_PARSE, parseBean.getParseName()); + parseBean.setDefault(true); + } + + public ParseBean getDefaultParse() { + return mDefaultParse; + } + + public List getSourceBeanList() { + return sourceBeanList; + } + + public List getParseBeanList() { + return parseBeanList; + } + + public List getVipParseFlags() { + return vipParseFlags; + } + + public SourceBean getHomeSourceBean() { + return mHomeSource; + } + + public List getChannelList() { + return channelList; + } + + public List getIjkCodes() { + return ijkCodes; + } + + public IJKCode getCurrentIJKCode() { + String codeName = Hawk.get(HawkConfig.IJK_CODEC, ""); + for (IJKCode code : ijkCodes) { + if (code.getName().equals(codeName)) + return code; + } + return ijkCodes.get(0); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/tvbox/osc/base/App.java b/app/src/main/java/com/github/tvbox/osc/base/App.java new file mode 100644 index 0000000000..96d10f08e6 --- /dev/null +++ b/app/src/main/java/com/github/tvbox/osc/base/App.java @@ -0,0 +1,60 @@ +package com.github.tvbox.osc.base; + +import androidx.multidex.MultiDexApplication; + +import com.github.tvbox.osc.callback.EmptyCallback; +import com.github.tvbox.osc.callback.LoadingCallback; +import com.github.tvbox.osc.data.AppDataManager; +import com.github.tvbox.osc.server.ControlManager; +import com.github.tvbox.osc.util.HawkConfig; +import com.github.tvbox.osc.util.OkGoHelper; +import com.github.tvbox.osc.util.PlayerHelper; +import com.kingja.loadsir.core.LoadSir; +import com.orhanobut.hawk.Hawk; + +import me.jessyan.autosize.AutoSizeConfig; +import me.jessyan.autosize.unit.Subunits; + +/** + * @author pj567 + * @date :2020/12/17 + * @description: + */ +public class App extends MultiDexApplication { + private static App instance; + + @Override + public void onCreate() { + super.onCreate(); + instance = this; + initParams(); + // OKGo + OkGoHelper.init(); + // 初始化Web服务器 + ControlManager.init(this); + //初始化数据库 + AppDataManager.init(); + LoadSir.beginBuilder() + .addCallback(new EmptyCallback()) + .addCallback(new LoadingCallback()) + .commit(); + AutoSizeConfig.getInstance().getUnitsManager() + .setSupportDP(false) + .setSupportSP(false) + .setSupportSubunits(Subunits.PT); + PlayerHelper.init(); + } + + private void initParams() { + // Hawk + Hawk.init(this).build(); + Hawk.put(HawkConfig.DEBUG_OPEN, false); + if (!Hawk.contains(HawkConfig.PLAY_TYPE)) { + Hawk.put(HawkConfig.PLAY_TYPE, 1); + } + } + + public static App getInstance() { + return instance; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/tvbox/osc/base/BaseActivity.java b/app/src/main/java/com/github/tvbox/osc/base/BaseActivity.java new file mode 100644 index 0000000000..ea498bf24b --- /dev/null +++ b/app/src/main/java/com/github/tvbox/osc/base/BaseActivity.java @@ -0,0 +1,117 @@ +package com.github.tvbox.osc.base; + +import android.content.Context; +import android.content.Intent; +import android.content.res.AssetManager; +import android.os.Bundle; +import android.view.View; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.PermissionChecker; + +import com.kingja.loadsir.callback.Callback; +import com.kingja.loadsir.core.LoadService; +import com.kingja.loadsir.core.LoadSir; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +import com.github.tvbox.osc.callback.EmptyCallback; +import com.github.tvbox.osc.callback.LoadingCallback; +import com.github.tvbox.osc.util.AppManager; + +/** + * @author pj567 + * @date :2020/12/17 + * @description: + */ +public abstract class BaseActivity extends AppCompatActivity { + protected Context mContext; + private LoadService mLoadService; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(getLayoutResID()); + mContext = this; + AppManager.getInstance().addActivity(this); + init(); + } + + public boolean hasPermission(String permission) { + boolean has = true; + try { + has = PermissionChecker.checkSelfPermission(this, permission) == PermissionChecker.PERMISSION_GRANTED; + } catch (Exception e) { + e.printStackTrace(); + } + return has; + } + + protected abstract int getLayoutResID(); + + protected abstract void init(); + + protected void setLoadSir(View view) { + if (mLoadService == null) { + mLoadService = LoadSir.getDefault().register(view, new Callback.OnReloadListener() { + @Override + public void onReload(View v) { + } + }); + } + } + + protected void showLoading() { + if (mLoadService != null) { + mLoadService.showCallback(LoadingCallback.class); + } + } + + protected void showEmpty() { + if (null != mLoadService) { + mLoadService.showCallback(EmptyCallback.class); + } + } + + protected void showSuccess() { + if (null != mLoadService) { + mLoadService.showSuccess(); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + AppManager.getInstance().finishActivity(this); + } + + public void jumpActivity(Class clazz) { + Intent intent = new Intent(mContext, clazz); + startActivity(intent); + } + + public void jumpActivity(Class clazz, Bundle bundle) { + Intent intent = new Intent(mContext, clazz); + intent.putExtras(bundle); + startActivity(intent); + } + + protected String getAssetText(String fileName) { + StringBuilder stringBuilder = new StringBuilder(); + try { + AssetManager assets = getAssets(); + BufferedReader bf = new BufferedReader(new InputStreamReader(assets.open(fileName))); + String line; + while ((line = bf.readLine()) != null) { + stringBuilder.append(line); + } + return stringBuilder.toString(); + } catch (IOException e) { + e.printStackTrace(); + } + return ""; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/tvbox/osc/base/BaseLazyFragment.java b/app/src/main/java/com/github/tvbox/osc/base/BaseLazyFragment.java new file mode 100644 index 0000000000..c7c6760a5a --- /dev/null +++ b/app/src/main/java/com/github/tvbox/osc/base/BaseLazyFragment.java @@ -0,0 +1,283 @@ +package com.github.tvbox.osc.base; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.IdRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; + +import com.kingja.loadsir.callback.Callback; +import com.kingja.loadsir.core.LoadService; +import com.kingja.loadsir.core.LoadSir; + +import java.util.List; + +import com.github.tvbox.osc.callback.EmptyCallback; +import com.github.tvbox.osc.callback.LoadingCallback; + +/** + * Fragment的基类(懒加载) + */ +public abstract class BaseLazyFragment extends Fragment { + /** + * Fragment生命周期 onAttach -> onCreate -> onCreatedView -> onActivityCreated + * -> onStart -> onResume -> onPause -> onStop -> onDestroyView -> onDestroy + * -> onDetach 对于 ViewPager + Fragment 的实现我们需要关注的几个生命周期有: onCreatedView + + * onActivityCreated + onResume + onPause + onDestroyView + */ + protected View rootView = null; + + /** + * 布局是否创建完成 + */ + protected boolean isViewCreated = false; + + /** + * 当前可见状态 + */ + protected boolean currentVisibleState = false; + + /** + * 是否第一次可见 + */ + protected boolean mIsFirstVisible = true; + protected Context mContext; + protected Activity mActivity; + private LoadService mLoadService; + + @Override + public void onAttach(@NonNull Context context) { + super.onAttach(context); + mContext = context; + mActivity = (Activity) context; + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + if (null == rootView) { + rootView = inflater.inflate(getLayoutResID(), container, false); + } + isViewCreated = true; + return rootView; + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + if (!isHidden() && getUserVisibleHint()) { + // 可见状态,进行事件分发 + dispatchUserVisibleHint(true); + } + } + + /** + * 修改Fragment的可见性 setUserVisibleHint 被调用有两种情况: + * 1)在切换tab的时候,会先于所有fragment的其他生命周期,先调用这个函数,可以看log 2) + * 对于之前已经调用过setUserVisibleHint方法的fragment后,让fragment从可见到不可见之间状态的变化 + */ + @Override + public void setUserVisibleHint(boolean isVisibleToUser) { + super.setUserVisibleHint(isVisibleToUser); + // 对于情况1)不予处理,用 isViewCreated 进行判断,如果isViewCreated false,说明它没有被创建 + if (isViewCreated) { + // 对于情况2,需要分情况考虑,如果是不可见 -> 可见 2.1 + // 如果是可见 -> 不可见 2.2 + // 对于2.1)我们需要如何判断呢?首先必须是可见的(isVisibleToUser + // 为true)而且只有当可见状态进行改变的时候才需要切换,否则会出现反复调用的情况 + // 从而导致事件分发带来的多次更新 + if (isVisibleToUser && !currentVisibleState) { + // 从不可见 -> 可见 + dispatchUserVisibleHint(true); + } else if (!isVisibleToUser && currentVisibleState) { + dispatchUserVisibleHint(false); + } + } + } + + /** + * 用FragmentTransaction来控制fragment的hide和show时, + * 那么这个方法就会被调用。每当你对某个Fragment使用hide 或者是show的时候,那么这个Fragment就会自动调用这个方法。 + */ + @Override + public void onHiddenChanged(boolean hidden) { + super.onHiddenChanged(hidden); + // 这里的可见返回为false + if (hidden) { + dispatchUserVisibleHint(false); + } else { + dispatchUserVisibleHint(true); + } + } + + /** + * 统一处理用户可见事件分发 + */ + private void dispatchUserVisibleHint(boolean isVisible) { + // 首先考虑一下fragment嵌套fragment的情况(只考虑2层嵌套) + if (isVisible && isParentInvisible()) { + // 父Fragmnet此时不可见,直接return不做处理 + return; + } + // 为了代码严谨,如果当前状态与需要设置的状态本来就一致了,就不处理了 + if (currentVisibleState == isVisible) { + return; + } + currentVisibleState = isVisible; + if (isVisible) { + if (mIsFirstVisible) { + mIsFirstVisible = false; + // 第一次可见,进行全局初始化 + init(); + } + onFragmentResume(); + // 分发事件给内嵌的Fragment + dispatchChildVisibleState(true); + } else { + onFragmentPause(); + dispatchChildVisibleState(false); + } + } + + /** + * 在双重ViewPager嵌套的情况下,第一次滑到Frgment 嵌套ViewPager(fragment)的场景的时候 + * 此时只会加载外层Fragment的数据,而不会加载内嵌viewPager中的fragment的数据,因此,我们 + * 需要在此增加一个当外层Fragment可见的时候,分发可见事件给自己内嵌的所有Fragment显示 + */ + private void dispatchChildVisibleState(boolean visible) { + FragmentManager fragmentManager = getChildFragmentManager(); + List fragments = fragmentManager.getFragments(); + if (null != fragments) { + for (Fragment fragment : fragments) { + if (fragment instanceof BaseLazyFragment && !fragment.isHidden() && fragment.getUserVisibleHint()) { + ((BaseLazyFragment) fragment).dispatchUserVisibleHint(visible); + } + } + } + + } + + /** + * Fragment真正的Pause,暂停一切网络耗时操作 + */ + protected void onFragmentPause() { + } + + /** + * Fragment真正的Resume,开始处理网络加载等耗时操作 + */ + protected void onFragmentResume() { + } + + private boolean isParentInvisible() { + Fragment parentFragment = getParentFragment(); + if (parentFragment instanceof BaseLazyFragment) { + BaseLazyFragment fragment = (BaseLazyFragment) parentFragment; + return !fragment.isSupportVisible(); + } + return false; + } + + private boolean isSupportVisible() { + return currentVisibleState; + } + + /** + * 在滑动或者跳转的过程中,第一次创建fragment的时候均会调用onResume方法 + */ + @Override + public void onResume() { + super.onResume(); + // 如果不是第一次可见 + if (!mIsFirstVisible) { + // 如果此时进行Activity跳转,会将所有的缓存的fragment进行onResume生命周期的重复 + // 只需要对可见的fragment进行加载, + if (!isHidden() && !currentVisibleState && getUserVisibleHint()) { + dispatchUserVisibleHint(true); + } + } + } + + /** + * 只有当当前页面由可见状态转变到不可见状态时才需要调用 dispatchUserVisibleHint currentVisibleState && + * getUserVisibleHint() 能够限定是当前可见的 Fragment 当前 Fragment 包含子 Fragment 的时候 + * dispatchUserVisibleHint 内部本身就会通知子 Fragment 不可见 子 fragment 走到这里的时候自身又会调用一遍 + */ + @Override + public void onPause() { + super.onPause(); + if (currentVisibleState && getUserVisibleHint()) { + dispatchUserVisibleHint(false); + } + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + isViewCreated = false; + } + + @SuppressWarnings("unchecked") + public T findViewById(@IdRes int viewId) { + View view = null; + if (rootView != null) { + view = rootView.findViewById(viewId); + } + return (T) view; + } + + protected abstract int getLayoutResID(); + + /** + * 第一次可见,根据业务进行初始化操作 + */ + protected abstract void init(); + + protected void setLoadSir(View view) { + if (mLoadService == null) { + mLoadService = LoadSir.getDefault().register(view, new Callback.OnReloadListener() { + @Override + public void onReload(View v) { + } + }); + } + } + + protected void showLoading() { + if (mLoadService != null) { + mLoadService.showCallback(LoadingCallback.class); + } + } + + protected void showEmpty() { + if (null != mLoadService) { + mLoadService.showCallback(EmptyCallback.class); + } + } + + protected void showSuccess() { + if (null != mLoadService) { + mLoadService.showSuccess(); + } + } + + public void jumpActivity(Class clazz) { + Intent intent = new Intent(mContext, clazz); + startActivity(intent); + } + + public void jumpActivity(Class clazz, Bundle bundle) { + Intent intent = new Intent(mContext, clazz); + intent.putExtras(bundle); + startActivity(intent); + } + +} diff --git a/app/src/main/java/com/github/tvbox/osc/bean/AbsJson.java b/app/src/main/java/com/github/tvbox/osc/bean/AbsJson.java new file mode 100644 index 0000000000..f4def07000 --- /dev/null +++ b/app/src/main/java/com/github/tvbox/osc/bean/AbsJson.java @@ -0,0 +1,171 @@ +package com.github.tvbox.osc.bean; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * @author pj567 + * @date :2020/12/18 + * @description: + */ +public class AbsJson implements Serializable { + + public int code; // : 1 + public String limit; // : "20" + public ArrayList list; // : [{vod_id: 71930, type_id: 22, type_id_1: 20, group_id: 0, vod_name: "意式情歌",…},…] + public String msg; // : "数据列表" + public int page; // : "2" + public int pagecount; // : 209 + public int total; // : 4166 + + + public class AbsJsonVod implements Serializable { + public int group_id; //: 0 + public int type_id; //: 32 + public int type_id_1; //: 31 + public String type_name; //: "国产剧" + public String vod_actor; //: "黄小戈,赵旭东,时男,傅隽,张佳琳" + public String vod_area; //: "中国大陆" + public String vod_author; //: "" + public String vod_behind; //: "" + public String vod_blurb; //: "本剧通过讲述咸鱼馆神秘店主灵叔以咸鱼和rose为主角虚构的十六种不同人生故事,展现了在不同故事中的咸鱼和rose犹如千千万万生活在世界上的青年男女一样,拥有着不同的性格和不同背景,怀揣着不同梦想和欲望" + public String vod_class; //: "剧情,爱情,科幻,悬疑,惊悚,国产剧" + public String vod_color; //: "" + public String vod_content; //: "

本剧通过讲述咸鱼馆神秘店主灵叔以咸鱼和rose为主角虚构的十六种不同人生故事,展现了在不同故事中的咸鱼和rose犹如千千万万生活在世界上的青年男女一样,拥有着不同的性格和不同背景,怀揣着不同梦想和欲望,在充满机遇挑战又布满荆棘的人生旅途中,积极面对人生困惑,努力走出困惑和绝境的故事。十六个小故事有神奇,有烦恼,有笑声,有感动,对爱情的向外,对亲情的追忆,对人生的徘徊,对未来的恐惧,构成了一幅咸鱼和rose生命多种可能性的美丽画卷,犹如人生百味,柴米油盐酱醋茶,酸甜苦辣咸,喜怒哀乐怨。人生总是在希望中面临困惑走向绝望,又从绝望中坚强不息走向希望,故事中咸鱼和rose心底善良,拼搏向上,于人生困境中搏出一片青天,体验了生命的各种美好,传递了人生应该坚持希望,积极向上拥抱美好的乐观精神,表达了生命不止,自强不息的内涵思想。

" + public String vod_copyright; //: 0 + public String vod_director; //: "王凯阳,Kaiyang,Wang" + public String vod_douban_id; //: 35373052 + public String vod_douban_score; //: "4.4" + public String vod_down; //: 0 + public String vod_down_from; //: "" + public String vod_down_note; //: "" + public String vod_down_server; //: "" + public String vod_down_url; //: "" + public String vod_duration; //: "10" + public String vod_en; //: "xianyuxianshengRosexiaojiezhihuixinglailiao" + public String vod_hits; //: 0 + public String vod_hits_day; //: 0 + public String vod_hits_month; //: 0 + public String vod_hits_week; //: 0 + public String vod_id; //: 71989 + public String vod_isend; //: 0 + public String vod_jumpurl; //: "" + public String vod_lang; //: "汉语普通话" + public String vod_letter; //: "X" + public String vod_level; //: 0 + public String vod_lock; //: 0 + public String vod_name; //: "咸鱼先生,Rose小姐之彗星来了" + public String vod_pic; //: "https://img.52swat.cn/upload/vod/20210410-1/c8a9342fff893c85e4a255da90fdbe3f.jpg" + public String vod_pic_screenshot; //: null + public String vod_pic_slide; //: "" + public String vod_pic_thumb; //: "" + public String vod_play_from; //: "dbyun$$$dbm3u8" + public String vod_play_note; //: "$$$" + public String vod_play_server; //: "no$$$no" + public String vod_play_url; //: "第01集$https://vod3.buycar5.cn/share/dHsXTOBwbaX4idZb#第02集$https://vod3.buycar5.cn/share/qTlFmVkS3ABl7F4v#第03集$https://vod3.buycar5.cn/share/uNAQVhnro4Xnx4Y1#第04集$https://vod3.buycar5.cn/share/EtGK2XPmuzygMFmE#第05集$https://vod3.buycar5.cn/share/MC1U1bcQrGgVxF6h#第06集$https://vod3.buycar5.cn/share/gEtYSq6IX9KWPykl#第07集$https://vod3.buycar5.cn/share/OEMBq5ujsPaq8Sy7#第08集$https://vod3.buycar5.cn/share/bynmQTMBQwsVHtkn#第09集$https://vod3.buycar5.cn/share/Th7aQDVPOT1p6Cib#第10集$https://vod3.buycar5.cn/share/8AaZzRvh3fFk43Mi#第11集$https://vod3.buycar5.cn/share/YzEk819PQphuqDgL#第12集$https://vod3.buycar5.cn/share/vdAGJhlSg0o1yzcA$$$第01集$https://vod3.buycar5.cn/20210410/iWay2ycC/index.m3u8#第02集$https://vod3.buycar5.cn/20210410/5DpcrSCA/index.m3u8#第03集$https://vod3.buycar5.cn/20210410/wVdGBPgj/index.m3u8#第04集$https://vod3.buycar5.cn/20210410/cUVpM93e/index.m3u8#第05集$https://vod3.buycar5.cn/20210410/NWALmXkH/index.m3u8#第06集$https://vod3.buycar5.cn/20210410/lXZKFL7d/index.m3u8#第07集$https://vod3.buycar5.cn/20210411/3gQEOdxL/index.m3u8#第08集$https://vod3.buycar5.cn/20210411/yMLR7Fsz/index.m3u8#第09集$https://vod3.buycar5.cn/20210411/vMtFz4in/index.m3u8#第10集$https://vod3.buycar5.cn/20210412/EOwKfgwt/index.m3u8#第11集$https://vod3.buycar5.cn/20210412/xRT9FEjR/index.m3u8#第12集$https://vod3.buycar5.cn/20210412/Q6krcXYC/index.m3u8" + public String vod_plot; //: 0 + public String vod_plot_detail; //: "" + public String vod_plot_name; //: "" + public String vod_points; //: 0 + public String vod_points_down; //: 0 + public String vod_points_play; //: 0 + public String vod_pubdate; //: "2021-04-10(中国大陆)" + public String vod_pwd; //: "" + public String vod_pwd_down; //: "" + public String vod_pwd_down_url; //: "" + public String vod_pwd_play; //: "" + public String vod_pwd_play_url; //: "" + public String vod_pwd_url; //: "" + public String vod_rel_art; //: "" + public String vod_rel_vod; //: "" + public String vod_remarks; //: "共30集,更新至12集" + public String vod_reurl; //: "" + public String vod_score; //: "4.4" + public String vod_score_all; //: 460 + public String vod_score_num; //: 291 + public String vod_serial; //: "12" + public String vod_state; //: "" + public String vod_status; //: 1 + public String vod_sub; //: "Mr.Salted Fish Miss Ross 2,咸鱼先生,Rose小姐 第二季,咸鱼先生,Rose小姐之彗星来了" + public String vod_tag; //: "" + public String vod_time; //: "2021-04-12 19:13:27" + public String vod_time_add; //: 1618053726 + public String vod_time_hits; //: 0 + public String vod_time_make; //: 0 + public String vod_total; //: 30 + public String vod_tpl; //: "" + public String vod_tpl_down; //: "" + public String vod_tpl_play; //: "" + public String vod_trysee; //: 0 + public String vod_tv; //: "" + public String vod_up; //: 0 + public String vod_version; //: "" + public String vod_weekday; //: "" + public String vod_writer; //: "周炎青,刘恒,支雅雪,孙露军,李璐,王梦璇" + public String vod_year; //: "2021" + + public Movie.Video toXmlVideo() { + Movie.Video video = new Movie.Video(); + video.last = vod_time; + video.id = vod_id; + video.tid = type_id; + video.name = vod_name; + video.type = type_name; + video.dt = vod_play_from.replace("$$$", ","); + video.pic = vod_pic; + video.lang = vod_lang; + video.area = vod_area; + try { + video.year = Integer.parseInt(vod_year); + } catch (Throwable th) { + video.year = 0; + } + video.state = vod_state; + video.note = vod_remarks; + video.actor = vod_actor; + video.director = vod_director; + Movie.Video.UrlBean urlBean = new Movie.Video.UrlBean(); + String[] playFlags = vod_play_from.split("\\$\\$\\$"); + String[] playUrls = vod_play_url.split("\\$\\$\\$"); + List infoList = new ArrayList<>(); + for (int i = 0; i < playFlags.length; i++) { + Movie.Video.UrlBean.UrlInfo urlInfo = new Movie.Video.UrlBean.UrlInfo(); + urlInfo.flag = playFlags[i]; + if (i < playUrls.length) + urlInfo.urls = playUrls[i]; + else + urlInfo.urls = ""; + infoList.add(urlInfo); + } + urlBean.infoList = infoList; + video.urlBean = urlBean; + video.des = vod_content;// videoList = new ArrayList<>(); + for (AbsJsonVod vod : list) { + try { + videoList.add(vod.toXmlVideo()); + } catch (Throwable th) { + movie.pagesize = 0; + } + } + movie.videoList = videoList; + xml.movie = movie; + return xml; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/tvbox/osc/bean/AbsSortJson.java b/app/src/main/java/com/github/tvbox/osc/bean/AbsSortJson.java new file mode 100644 index 0000000000..7b8fbbdf12 --- /dev/null +++ b/app/src/main/java/com/github/tvbox/osc/bean/AbsSortJson.java @@ -0,0 +1,32 @@ +package com.github.tvbox.osc.bean; + +import com.google.gson.annotations.SerializedName; + +import java.io.Serializable; +import java.util.ArrayList; + +public class AbsSortJson implements Serializable { + + @SerializedName(value = "class") + public ArrayList classList; + + public AbsSortXml toAbsSortXml() { + AbsSortXml absSortXml = new AbsSortXml(); + MovieSort movieSort = new MovieSort(); + movieSort.sortList = new ArrayList<>(); + for (AbsJsonClass cls : classList) { + MovieSort.SortData sortData = new MovieSort.SortData(); + sortData.id = cls.type_id; + sortData.name = cls.type_name; + movieSort.sortList.add(sortData); + } + absSortXml.movieSort = movieSort; + return absSortXml; + } + + public class AbsJsonClass implements Serializable { + public int type_id; + public String type_name; + } + +} diff --git a/app/src/main/java/com/github/tvbox/osc/bean/AbsSortXml.java b/app/src/main/java/com/github/tvbox/osc/bean/AbsSortXml.java new file mode 100644 index 0000000000..4fb3adb878 --- /dev/null +++ b/app/src/main/java/com/github/tvbox/osc/bean/AbsSortXml.java @@ -0,0 +1,16 @@ +package com.github.tvbox.osc.bean; + +import com.thoughtworks.xstream.annotations.XStreamAlias; + +import java.io.Serializable; + +/** + * @author pj567 + * @date :2020/12/18 + * @description: + */ +@XStreamAlias("rss") +public class AbsSortXml implements Serializable { + @XStreamAlias("class") + public MovieSort movieSort; +} \ No newline at end of file diff --git a/app/src/main/java/com/github/tvbox/osc/bean/AbsXml.java b/app/src/main/java/com/github/tvbox/osc/bean/AbsXml.java new file mode 100644 index 0000000000..ebcbbeae00 --- /dev/null +++ b/app/src/main/java/com/github/tvbox/osc/bean/AbsXml.java @@ -0,0 +1,16 @@ +package com.github.tvbox.osc.bean; + +import com.thoughtworks.xstream.annotations.XStreamAlias; + +import java.io.Serializable; + +/** + * @author pj567 + * @date :2020/12/18 + * @description: + */ +@XStreamAlias("rss") +public class AbsXml implements Serializable { + @XStreamAlias("list") + public Movie movie; +} \ No newline at end of file diff --git a/app/src/main/java/com/github/tvbox/osc/bean/IJKCode.java b/app/src/main/java/com/github/tvbox/osc/bean/IJKCode.java new file mode 100644 index 0000000000..3c5ebe4f07 --- /dev/null +++ b/app/src/main/java/com/github/tvbox/osc/bean/IJKCode.java @@ -0,0 +1,45 @@ +package com.github.tvbox.osc.bean; + +import com.orhanobut.hawk.Hawk; + +import java.util.LinkedHashMap; + +import com.github.tvbox.osc.util.HawkConfig; + +/** + * @author pj567 + * @date :2021/3/8 + * @description: + */ +public class IJKCode { + private String name; + private LinkedHashMap option; + private boolean selected; + + public void selected(boolean selected) { + this.selected = selected; + if (selected) { + Hawk.put(HawkConfig.IJK_CODEC, name); + } + } + + public boolean isSelected() { + return selected; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public LinkedHashMap getOption() { + return option; + } + + public void setOption(LinkedHashMap option) { + this.option = option; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/tvbox/osc/bean/LiveChannel.java b/app/src/main/java/com/github/tvbox/osc/bean/LiveChannel.java new file mode 100644 index 0000000000..310b1e6912 --- /dev/null +++ b/app/src/main/java/com/github/tvbox/osc/bean/LiveChannel.java @@ -0,0 +1,80 @@ +package com.github.tvbox.osc.bean; + +import com.github.tvbox.osc.cache.LocalLive; + +import java.io.Serializable; +import java.util.ArrayList; + +/** + * @author pj567 + * @date :2021/1/12 + * @description: + */ +public class LiveChannel implements Serializable { + public static LiveChannel addNewBean = new LiveChannel(); + + static { + addNewBean.channelName = "_add_new_live"; + } + + public boolean isForAdd() { + return this == addNewBean; + } + + /** + * channelNum : 1 + * channelName : CCTV-1 综合 + * channelUrl : http://117.148.187.37/PLTV/88888888/224/3221226154/index.m3u8 + * channelLogo : https://upload.wikimedia.org/wikipedia/zh/6/65/CCTV-1_Logo.png + */ + + private int channelNum; + private String channelName; + private ArrayList channelUrl; + private LocalLive local; + private boolean isDefault; + public int sourceIdx = 0; + + private LocalLive live; + + public void setChannelNum(int channelNum) { + this.channelNum = channelNum; + } + + public int getChannelNum() { + return channelNum; + } + + public String getChannelName() { + return channelName; + } + + public String getChannelUrl() { + if (sourceIdx <= 0 || sourceIdx >= channelUrl.size()) + sourceIdx = 0; + return channelUrl.get(sourceIdx); + } + + public boolean isDefault() { + return isDefault; + } + + public void setDefault(boolean b) { + isDefault = b; + } + + public boolean isInternal() { + return local == null; + } + + public LocalLive getLocal() { + return local; + } + + public void initFromLocal(LocalLive ll) { + channelName = ll.name; + channelUrl = new ArrayList<>(); + channelUrl.add(ll.url); + local = ll; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/tvbox/osc/bean/Movie.java b/app/src/main/java/com/github/tvbox/osc/bean/Movie.java new file mode 100644 index 0000000000..204845c74d --- /dev/null +++ b/app/src/main/java/com/github/tvbox/osc/bean/Movie.java @@ -0,0 +1,96 @@ +package com.github.tvbox.osc.bean; + +import com.thoughtworks.xstream.annotations.XStreamAlias; +import com.thoughtworks.xstream.annotations.XStreamAsAttribute; +import com.thoughtworks.xstream.annotations.XStreamConverter; +import com.thoughtworks.xstream.annotations.XStreamImplicit; +import com.thoughtworks.xstream.converters.extended.ToAttributedValueConverter; + +import java.io.Serializable; +import java.util.List; + +/** + * @author pj567 + * @date :2020/12/18 + * @description: + */ +@XStreamAlias("list") +public class Movie implements Serializable { + @XStreamAsAttribute + public int page; + @XStreamAsAttribute + public int pagecount;//总页数 + @XStreamAsAttribute + public int pagesize; + @XStreamAsAttribute + public int recordcount;//总条数 + @XStreamImplicit(itemFieldName = "video") + public List