diff --git a/.gitignore b/.gitignore index 4e9a1c8aad..8591abf69c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,4 @@ *build *.jks /local.properties -/media -*ser* \ No newline at end of file +/media \ No newline at end of file diff --git a/README.md b/README.md index 92e544a7fe..c12679327a 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ https://github.com/CatVodTVOfficial/CatVodTVJarLoader 電視版 [Jar+Js](https://github.com/FongMi/TV/raw/release/release/leanback-java.apk "TV") [Jar+Js+Py](https://github.com/FongMi/TV/raw/release/release/leanback-python.apk "TV") -[Android 4.4](https://github.com/FongMi/TV/raw/kitkat/release/leanback.apk "TV") +[Android 4.1](https://github.com/FongMi/TV/raw/kitkat/release/leanback.apk "TV") 手機版 [Jar+Js](https://github.com/FongMi/TV/raw/release/release/mobile-java.apk "TV") diff --git a/app/build.gradle b/app/build.gradle index 2b9872739a..1f94fec15d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,18 +11,23 @@ android { minSdk 21 targetSdk 29 ndk { abiFilters "armeabi-v7a" } + javaCompileOptions { + annotationProcessorOptions { + arguments = ["room.schemaLocation": "$projectDir/schemas".toString()] + } + } } productFlavors { leanback { dimension "mode" - versionCode 72 - versionName "1.7.2" + versionCode 73 + versionName "1.7.3" } mobile { dimension "mode" - versionCode 10 - versionName "1.1.0" + versionCode 11 + versionName "1.1.1" } java { dimension "api" @@ -45,6 +50,10 @@ android { } } + packagingOptions { + exclude 'META-INF/beans.xml' + } + compileOptions { sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 @@ -70,17 +79,17 @@ dependencies { implementation project(':forcetech') implementation project(':ijkplayer') pythonImplementation project(':pyramid') - implementation 'androidx.media3:media3-database:1.0.0' - implementation 'androidx.media3:media3-datasource:1.0.0' - implementation 'androidx.media3:media3-datasource-rtmp:1.0.0' - implementation 'androidx.media3:media3-exoplayer:1.0.0' - implementation 'androidx.media3:media3-exoplayer-dash:1.0.0' - implementation 'androidx.media3:media3-exoplayer-hls:1.0.0' - implementation 'androidx.media3:media3-exoplayer-rtsp:1.0.0' - implementation 'androidx.media3:media3-exoplayer-smoothstreaming:1.0.0' - implementation 'androidx.media3:media3-extractor:1.0.0' - implementation 'androidx.media3:media3-session:1.0.0' - implementation 'androidx.media3:media3-ui:1.0.0' + implementation 'androidx.media3:media3-database:1.0.1' + implementation 'androidx.media3:media3-datasource:1.0.1' + implementation 'androidx.media3:media3-datasource-rtmp:1.0.1' + implementation 'androidx.media3:media3-exoplayer:1.0.1' + implementation 'androidx.media3:media3-exoplayer-dash:1.0.1' + implementation 'androidx.media3:media3-exoplayer-hls:1.0.1' + implementation 'androidx.media3:media3-exoplayer-rtsp:1.0.1' + implementation 'androidx.media3:media3-exoplayer-smoothstreaming:1.0.1' + implementation 'androidx.media3:media3-extractor:1.0.1' + implementation 'androidx.media3:media3-session:1.0.1' + implementation 'androidx.media3:media3-ui:1.0.1' implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.lifecycle:lifecycle-viewmodel:2.5.1' implementation 'androidx.preference:preference:1.2.0' @@ -88,11 +97,11 @@ dependencies { implementation 'cat.ereza:customactivityoncrash:2.4.0' implementation 'com.github.bassaer:materialdesigncolors:1.0.0' implementation 'com.github.bumptech.glide:glide:4.14.2' - implementation 'com.google.android.exoplayer:exoplayer:2.18.5' + implementation('com.github.thegrizzlylabs:sardine-android:0.8') { exclude group: 'com.squareup.okhttp3', module: 'okhttp' } implementation 'com.google.android.material:material:1.9.0-beta01' implementation 'com.google.code.gson:gson:2.10.1' implementation 'com.google.net.cronet:cronet-okhttp:0.1.0' - implementation 'com.google.zxing:core:3.3.0' + implementation 'com.google.zxing:core:3.4.1' implementation 'com.guolindev.permissionx:permissionx:1.7.1' implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.10' implementation 'net.java.dev.jna:jna:5.12.1' @@ -104,6 +113,8 @@ dependencies { leanbackImplementation 'androidx.leanback:leanback:1.2.0-alpha02' leanbackImplementation 'me.jessyan:autosize:1.2.1' mobileImplementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' + mobileImplementation 'com.github.devin1014.DLNA-Cast:dlna-dmc:V1.0.0' + mobileImplementation('com.journeyapps:zxing-android-embedded:4.3.0') { transitive = false } annotationProcessor 'androidx.room:room-compiler:2.5.1' annotationProcessor 'com.github.bumptech.glide:compiler:4.14.2' } \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 6fa2874922..a74243cb53 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -2,7 +2,7 @@ -keepattributes Signature -keepattributes *Annotation* -dontwarn sun.misc.** --keep class com.google.gson.**{*;} +-keep class com.google.gson.** { *; } -keep class * extends com.google.gson.TypeAdapter -keep class * implements com.google.gson.TypeAdapterFactory -keep class * implements com.google.gson.JsonSerializer @@ -24,21 +24,28 @@ # OkHttp -dontwarn okhttp3.** +-keep class okio.** { *; } -keep class okhttp3.** { *; } # Cronet --keep class org.chromium.net.**{*;} --keep class com.google.net.cronet.**{*;} +-keep class org.chromium.net.** { *; } +-keep class com.google.net.cronet.** { *; } # CatVod -keep class com.github.catvod.crawler.** { *; } -keep class * extends com.github.catvod.crawler.Spider +# Cling +-keep class org.fourthline.cling.** { *; } + # IJK -keep class tv.danmaku.ijk.media.player.** { *; } -keep class tv.danmaku.ijk.media.player.IjkMediaPlayer { *; } -keep class tv.danmaku.ijk.media.player.ffmpeg.FFmpegApi { *; } +# Sardine +-keep class com.thegrizzlylabs.sardineandroid.** { *; } + # TVBus -keep class com.tvbus.engine.** { *; } diff --git a/app/schemas/com.fongmi.android.tv.db.AppDatabase/22.json b/app/schemas/com.fongmi.android.tv.db.AppDatabase/22.json new file mode 100644 index 0000000000..3dcc90640e --- /dev/null +++ b/app/schemas/com.fongmi.android.tv.db.AppDatabase/22.json @@ -0,0 +1,438 @@ +{ + "formatVersion": 1, + "database": { + "version": 22, + "identityHash": "0d186de42a589253f12a86254e20636e", + "entities": [ + { + "tableName": "Keep", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `siteName` TEXT, `vodName` TEXT, `vodPic` TEXT, `createTime` INTEGER NOT NULL, `type` INTEGER NOT NULL, `cid` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "siteName", + "columnName": "siteName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "vodName", + "columnName": "vodName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "vodPic", + "columnName": "vodPic", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createTime", + "columnName": "createTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "cid", + "columnName": "cid", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "key" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Site", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `name` TEXT, `searchable` INTEGER, `filterable` INTEGER, `changeable` INTEGER, `activated` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "searchable", + "columnName": "searchable", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filterable", + "columnName": "filterable", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "changeable", + "columnName": "changeable", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "activated", + "columnName": "activated", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "key" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Track", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type` INTEGER NOT NULL, `group` INTEGER NOT NULL, `track` INTEGER NOT NULL, `player` INTEGER NOT NULL, `key` TEXT, `name` TEXT, `selected` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "track", + "columnName": "track", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "player", + "columnName": "player", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "selected", + "columnName": "selected", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_Track_key_player_type", + "unique": true, + "columnNames": [ + "key", + "player", + "type" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Track_key_player_type` ON `${TABLE_NAME}` (`key`, `player`, `type`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Config", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type` INTEGER NOT NULL, `time` INTEGER NOT NULL, `url` TEXT, `json` TEXT, `name` TEXT, `home` TEXT, `parse` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "json", + "columnName": "json", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "home", + "columnName": "home", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "parse", + "columnName": "parse", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_Config_url_type", + "unique": true, + "columnNames": [ + "url", + "type" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Config_url_type` ON `${TABLE_NAME}` (`url`, `type`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "Device", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `uuid` TEXT, `name` TEXT, `ip` TEXT, `type` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ip", + "columnName": "ip", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_Device_uuid_name", + "unique": true, + "columnNames": [ + "uuid", + "name" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Device_uuid_name` ON `${TABLE_NAME}` (`uuid`, `name`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "History", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `vodPic` TEXT, `vodName` TEXT, `vodFlag` TEXT, `vodRemarks` TEXT, `episodeUrl` TEXT, `revSort` INTEGER NOT NULL, `revPlay` INTEGER NOT NULL, `createTime` INTEGER NOT NULL, `opening` INTEGER NOT NULL, `ending` INTEGER NOT NULL, `position` INTEGER NOT NULL, `duration` INTEGER NOT NULL, `speed` REAL NOT NULL, `player` INTEGER NOT NULL, `scale` INTEGER NOT NULL, `cid` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "vodPic", + "columnName": "vodPic", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "vodName", + "columnName": "vodName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "vodFlag", + "columnName": "vodFlag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "vodRemarks", + "columnName": "vodRemarks", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "episodeUrl", + "columnName": "episodeUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "revSort", + "columnName": "revSort", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "revPlay", + "columnName": "revPlay", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createTime", + "columnName": "createTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "opening", + "columnName": "opening", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ending", + "columnName": "ending", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "duration", + "columnName": "duration", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "speed", + "columnName": "speed", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "player", + "columnName": "player", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "scale", + "columnName": "scale", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "cid", + "columnName": "cid", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "key" + ] + }, + "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, '0d186de42a589253f12a86254e20636e')" + ] + } +} \ No newline at end of file diff --git a/app/src/leanback/java/com/fongmi/android/tv/Product.java b/app/src/leanback/java/com/fongmi/android/tv/Product.java index 64a956c399..335f115ebf 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/Product.java +++ b/app/src/leanback/java/com/fongmi/android/tv/Product.java @@ -12,6 +12,10 @@ public class Product { + public static int getDeviceType() { + return 0; + } + public static int getColumn() { return Math.abs(Prefers.getSize() - 7); } diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/DetailActivity.java b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/DetailActivity.java index 933f91038a..98cf11ad50 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/DetailActivity.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/DetailActivity.java @@ -104,6 +104,14 @@ public class DetailActivity extends BaseActivity implements CustomKeyDownVod.Lis private Runnable mR1; private Runnable mR2; + public static void cast(Activity activity, History history) { + start(activity, history.getSiteKey(), history.getVodId(), history.getVodName(), true, true); + } + + public static void push(Activity activity, String url, boolean clear) { + start(activity, "push_agent", url, url, clear); + } + public static void start(Activity activity, String id, String name) { start(activity, ApiConfig.get().getHome().getKey(), id, name); } @@ -113,14 +121,23 @@ public static void start(Activity activity, String key, String id, String name) } public static void start(Activity activity, String key, String id, String name, boolean clear) { + start(activity, key, id, name, clear, false); + } + + public static void start(Activity activity, String key, String id, String name, boolean clear, boolean cast) { Intent intent = new Intent(activity, DetailActivity.class); if (clear) intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.putExtra("cast", cast); intent.putExtra("name", name); intent.putExtra("key", key); intent.putExtra("id", id); activity.startActivityForResult(intent, 1000); } + private boolean isCast() { + return getIntent().getBooleanExtra("cast", false); + } + private String getName() { return getIntent().getStringExtra("name"); } @@ -134,7 +151,7 @@ private String getId() { } private String getHistoryKey() { - return getKey().concat(AppDatabase.SYMBOL).concat(getId()); + return getKey().concat(AppDatabase.SYMBOL).concat(getId()).concat(AppDatabase.SYMBOL) + ApiConfig.getCid(); } private Site getSite() { @@ -192,7 +209,6 @@ protected ViewBinding getBinding() { protected void initView() { mKeyDown = CustomKeyDownVod.create(this, mBinding.video); mFrameParams = mBinding.video.getLayoutParams(); - mBinding.progressLayout.showProgress(); mPlayers = new Players().init(); mR1 = this::hideControl; mR2 = this::setTraffic; @@ -201,6 +217,7 @@ protected void initView() { setVideoView(); setViewModel(); getDetail(); + checkCast(); } @Override @@ -230,7 +247,7 @@ protected void initEvent() { mBinding.flag.addOnChildViewHolderSelectedListener(new OnChildViewHolderSelectedListener() { @Override public void onChildViewHolderSelected(@NonNull RecyclerView parent, @Nullable RecyclerView.ViewHolder child, int position, int subposition) { - if (mFlagAdapter.size() > 0) setFlagActivated((Vod.Flag) mFlagAdapter.get(position)); + if (mFlagAdapter.size() > 0) setFlagActivated((Vod.Flag) mFlagAdapter.get(position), false); } }); mBinding.array.addOnChildViewHolderSelectedListener(new OnChildViewHolderSelectedListener() { @@ -244,7 +261,7 @@ public void onChildViewHolderSelected(@NonNull RecyclerView parent, @Nullable Re private void setRecyclerView() { mBinding.flag.setHorizontalSpacing(ResUtil.dp2px(8)); mBinding.flag.setRowHeight(ViewGroup.LayoutParams.WRAP_CONTENT); - mBinding.flag.setAdapter(new ItemBridgeAdapter(mFlagAdapter = new ArrayObjectAdapter(new FlagPresenter(this::setFlagActivated)))); + mBinding.flag.setAdapter(new ItemBridgeAdapter(mFlagAdapter = new ArrayObjectAdapter(new FlagPresenter(item -> setFlagActivated(item, false))))); mBinding.episode.setHorizontalSpacing(ResUtil.dp2px(8)); mBinding.episode.setRowHeight(ViewGroup.LayoutParams.WRAP_CONTENT); mBinding.episode.setAdapter(new ItemBridgeAdapter(mEpisodeAdapter = new ArrayObjectAdapter(mEpisodePresenter = new EpisodePresenter(this::setEpisodeActivated)))); @@ -311,6 +328,11 @@ private void resetFocus() { } } + private void checkCast() { + if (isCast()) onVideo(); + else mBinding.progressLayout.showProgress(); + } + private void getDetail() { mViewModel.detailContent(getKey(), getId()); } @@ -378,13 +400,13 @@ private void setText(TextView view, int resId, String text) { view.setTag(text); } - private void setFlagActivated(Vod.Flag item) { + private void setFlagActivated(Vod.Flag item, boolean force) { if (mFlagAdapter.size() == 0 || item.isActivated()) return; for (int i = 0; i < mFlagAdapter.size(); i++) ((Vod.Flag) mFlagAdapter.get(i)).setActivated(item); mBinding.flag.setSelectedPosition(mFlagAdapter.indexOf(item)); notifyItemChanged(mBinding.flag, mFlagAdapter); setEpisodeAdapter(item.getEpisodes()); - seamless(item); + seamless(item, force); } private void setEpisodeAdapter(List items) { @@ -393,7 +415,8 @@ private void setEpisodeAdapter(List items) { setArray(items.size()); } - private void seamless(Vod.Flag flag) { + private void seamless(Vod.Flag flag, boolean force) { + if (!force && !getSite().isChangeable()) return; Vod.Flag.Episode episode = flag.find(mHistory.getVodRemarks()); if (episode == null || episode.isActivated()) return; mHistory.setVodRemarks(episode.getName()); @@ -623,13 +646,14 @@ private void hideProgress() { } private void showError(String text) { - mBinding.widget.text.setText(text); mBinding.widget.error.setVisibility(View.VISIBLE); + mBinding.widget.text.setText(text); + hideProgress(); } private void hideError() { - mBinding.widget.text.setText(""); mBinding.widget.error.setVisibility(View.GONE); + mBinding.widget.text.setText(""); } private void showInfo() { @@ -693,7 +717,7 @@ private void checkFlag(Vod item) { private void checkHistory(Vod item) { mHistory = History.find(getHistoryKey()); mHistory = mHistory == null ? createHistory(item) : mHistory; - setFlagActivated(mHistory.getFlag()); + setFlagActivated(mHistory.getFlag(), true); if (mHistory.isRevSort()) reverseEpisode(true); mBinding.control.opening.setText(mHistory.getOpening() == 0 ? getString(R.string.play_op) : mPlayers.stringToTime(mHistory.getOpening())); mBinding.control.ending.setText(mHistory.getEnding() == 0 ? getString(R.string.play_ed) : mPlayers.stringToTime(mHistory.getEnding())); @@ -811,7 +835,6 @@ private void onError(ErrorEvent event) { Clock.get().setCallback(null); showError(event.getMsg()); mPlayers.stop(); - hideProgress(); startFlow(); } @@ -859,8 +882,8 @@ private void startSearch(String keyword) { mSearchAdapter.clear(); mExecutor = Executors.newFixedThreadPool(Constant.THREAD_POOL); for (Site site : ApiConfig.get().getSites()) { - if (site.getKey().equals(getKey())) continue; if (isAutoMode() && !site.isChangeable()) continue; + if (isAutoMode() && site.getKey().equals(getKey())) continue; if (site.isSearchable()) mExecutor.execute(() -> search(site, keyword)); } } @@ -904,7 +927,7 @@ private void nextParse(int position) { private void nextFlag(int position) { Vod.Flag flag = (Vod.Flag) mFlagAdapter.get(position + 1); Notify.show(getString(R.string.play_switch_flag, flag.getFlag())); - setFlagActivated(flag); + setFlagActivated(flag, true); } private void nextSite() { @@ -970,10 +993,6 @@ public void setUseParse(boolean useParse) { this.useParse = useParse; } - private void notifyItemChanged(RecyclerView view, ArrayObjectAdapter adapter) { - if (!view.isComputingLayout()) adapter.notifyArrayItemRangeChanged(0, adapter.size()); - } - @Override public boolean dispatchKeyEvent(KeyEvent event) { if (isFullscreen() && Utils.isMenuKey(event)) onToggle(); @@ -1075,8 +1094,8 @@ protected void onResume() { protected void onPause() { super.onPause(); RefreshEvent.history(); - Clock.get().release(); onPause(false); + Clock.stop(); } @Override diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/HomeActivity.java b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/HomeActivity.java index d8d648deef..1ef3f8032b 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/HomeActivity.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/HomeActivity.java @@ -20,12 +20,14 @@ import com.fongmi.android.tv.api.ApiConfig; import com.fongmi.android.tv.api.LiveConfig; import com.fongmi.android.tv.api.WallConfig; +import com.fongmi.android.tv.bean.Config; import com.fongmi.android.tv.bean.Func; import com.fongmi.android.tv.bean.History; import com.fongmi.android.tv.bean.Result; import com.fongmi.android.tv.bean.Site; import com.fongmi.android.tv.bean.Vod; import com.fongmi.android.tv.databinding.ActivityHomeBinding; +import com.fongmi.android.tv.event.CastEvent; import com.fongmi.android.tv.event.RefreshEvent; import com.fongmi.android.tv.event.ServerEvent; import com.fongmi.android.tv.model.SiteViewModel; @@ -304,12 +306,36 @@ public void onServerEvent(ServerEvent event) { CollectActivity.start(this, event.getText(), true); break; case PUSH: - if (ApiConfig.get().getSite("push_agent") == null) return; - DetailActivity.start(this, "push_agent", event.getText(), "", true); + if (ApiConfig.hasPush()) DetailActivity.push(this, event.getText(), true); break; } } + @Subscribe(threadMode = ThreadMode.MAIN) + public void onCastEvent(CastEvent event) { + if (ApiConfig.getUrl().equals(event.getConfig())) { + DetailActivity.cast(this, event.getHistory().update(ApiConfig.getCid())); + } else { + ApiConfig.get().clear().config(Config.find(event.getConfig(), 0)).load(getCallback(event)); + } + } + + private Callback getCallback(CastEvent event) { + return new Callback() { + @Override + public void success() { + RefreshEvent.history(); + RefreshEvent.video(); + onCastEvent(event); + } + + @Override + public void error(int resId) { + Notify.show(resId); + } + }; + } + @Override public boolean dispatchKeyEvent(KeyEvent event) { if (Utils.isMenuKey(event)) showDialog(); @@ -325,7 +351,7 @@ protected void onResume() { @Override protected void onPause() { super.onPause(); - Clock.get().release(); + Clock.stop(); } @Override @@ -339,7 +365,6 @@ public void onBackPressed() { Notify.show(R.string.app_exit); App.post(() -> confirm = false, 2000); } else { - super.onBackPressed(); finish(); } } diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/KeepActivity.java b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/KeepActivity.java index 6d61aff2ef..890f5611cd 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/KeepActivity.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/KeepActivity.java @@ -16,6 +16,7 @@ import com.fongmi.android.tv.ui.adapter.KeepAdapter; import com.fongmi.android.tv.ui.base.BaseActivity; import com.fongmi.android.tv.ui.custom.SpaceItemDecoration; +import com.fongmi.android.tv.utils.Notify; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; @@ -63,7 +64,7 @@ public void success() { @Override public void error(int resId) { - CollectActivity.start(getActivity(), item.getVodName()); + Notify.show(resId); } }); } diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/LiveActivity.java b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/LiveActivity.java index 799120db38..8906e9e816 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/LiveActivity.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/LiveActivity.java @@ -113,6 +113,11 @@ private int getPlayerType() { return getHome().getPlayerType() != -1 ? getHome().getPlayerType() : Prefers.getLivePlayer(); } + @Override + protected boolean customWall() { + return false; + } + @Override protected ViewBinding getBinding() { return mBinding = ActivityLiveBinding.inflate(getLayoutInflater()); @@ -125,11 +130,11 @@ protected void initView() { mR2 = this::hideControl; mR3 = this::setChannelActivated; mR4 = this::setTraffic; + mHides = new ArrayList<>(); mPlayers = new Players().init(); mKeyDown = CustomKeyDownLive.create(this); mFormatDate = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); mFormatTime = new SimpleDateFormat("yyyy-MM-ddHH:mm", Locale.getDefault()); - mHides = new ArrayList<>(); setRecyclerView(); setVideoView(); setViewModel(); @@ -146,13 +151,13 @@ protected void initEvent() { mBinding.control.audio.setOnClickListener(this::onTrack); mBinding.control.video.setOnClickListener(this::onTrack); mBinding.control.home.setOnClickListener(view -> onHome()); + mBinding.control.line.setOnClickListener(view -> onLine()); mBinding.control.scale.setOnClickListener(view -> onScale()); mBinding.control.speed.setOnClickListener(view -> onSpeed()); mBinding.control.invert.setOnClickListener(view -> onInvert()); mBinding.control.across.setOnClickListener(view -> onAcross()); mBinding.control.player.setOnClickListener(view -> onPlayer()); mBinding.control.decode.setOnClickListener(view -> onDecode()); - mBinding.control.line.setOnClickListener(view -> nextLine(false)); mBinding.control.speed.setOnLongClickListener(view -> onSpeedLong()); mBinding.video.setOnTouchListener((view, event) -> mKeyDown.onTouchEvent(event)); mBinding.group.addOnChildViewHolderSelectedListener(new OnChildViewHolderSelectedListener() { @@ -184,10 +189,11 @@ private void setVideoView() { mPlayers.set(getExo(), getIjk()); setScale(Prefers.getLiveScale()); getIjk().setRender(Prefers.getRender()); + findViewById(R.id.timeBar).setNextFocusUpId(R.id.home); mBinding.control.speed.setText(mPlayers.getSpeedText()); - mBinding.control.home.setVisibility(LiveConfig.isOnly() ? View.GONE : View.VISIBLE); mBinding.control.invert.setActivated(Prefers.isInvert()); mBinding.control.across.setActivated(Prefers.isAcross()); + mBinding.control.home.setVisibility(LiveConfig.isOnly() ? View.GONE : View.VISIBLE); } private void setScale(int scale) { @@ -199,7 +205,10 @@ private void setScale(int scale) { private void setViewModel() { mViewModel = new ViewModelProvider(this).get(LiveViewModel.class); mViewModel.channel.observe(this, result -> mPlayers.start(result)); - mViewModel.live.observe(this, this::setGroup); + mViewModel.live.observe(this, live -> { + hideProgress(); + setGroup(live); + }); } private void getLive() { @@ -217,12 +226,12 @@ private void setGroup(Live home) { mGroupAdapter.setItems(items, null); setPosition(LiveConfig.get().find(items)); mBinding.control.home.setText(home.getName()); - hideProgress(); } private void setPosition(int[] position) { if (position[0] == -1) return; if (mGroupAdapter.size() == 1) return; + if (position[0] >= mGroupAdapter.size()) return; mGroup = (Group) mGroupAdapter.get(position[0]); mBinding.group.setSelectedPosition(position[0]); if (mGroup.getChannel().isEmpty()) return; @@ -233,12 +242,12 @@ private void setPosition(int[] position) { private void setPosition() { if (mChannel == null) return; - Group group = mChannel.getGroup(); - int position = mGroupAdapter.indexOf(group); + mGroup = mChannel.getGroup(); + int position = mGroupAdapter.indexOf(mGroup); boolean change = mBinding.group.getSelectedPosition() != position; if (change) mBinding.group.setSelectedPosition(position); - if (change) mChannelAdapter.setItems(group.getChannel(), null); - mBinding.channel.setSelectedPosition(group.getPosition()); + if (change) mChannelAdapter.setItems(mGroup.getChannel(), null); + mBinding.channel.setSelectedPosition(mGroup.getPosition()); } private void onChildSelected(@Nullable RecyclerView.ViewHolder child, Group group) { @@ -256,16 +265,10 @@ private void setChannelActivated() { getUrl(); } - private void setTraffic() { - Traffic.setSpeed(mBinding.widget.traffic); - App.post(mR4, Constant.INTERVAL_TRAFFIC); - } - - private void onToggle() { - if (isVisible(mBinding.control.getRoot())) hideControl(); - else if (isVisible(mBinding.recycler)) hideUI(); - else showUI(); - hideInfo(); + private void onTrack(View view) { + int type = Integer.parseInt(view.getTag().toString()); + TrackDialog.create().player(mPlayers).type(type).show(getSupportFragmentManager(), null); + hideControl(); } private void onHome() { @@ -273,6 +276,10 @@ private void onHome() { hideControl(); } + private void onLine() { + nextLine(false); + } + private void onScale() { int index = Prefers.getLiveScale(); String[] array = ResUtil.getStringArray(R.array.select_scale); @@ -313,12 +320,6 @@ private void onDecode() { getUrl(); } - private void onTrack(View view) { - int type = Integer.parseInt(view.getTag().toString()); - TrackDialog.create().player(mPlayers).type(type).show(getSupportFragmentManager(), null); - hideControl(); - } - private void hideUI() { App.removeCallbacks(mR0); if (isGone(mBinding.recycler)) return; @@ -336,6 +337,7 @@ private void showUI() { private void showProgress() { mBinding.widget.progress.setVisibility(View.VISIBLE); App.post(mR4, 0); + hideError(); } private void hideProgress() { @@ -344,10 +346,22 @@ private void hideProgress() { Traffic.reset(); } + private void showError(String text) { + mBinding.widget.error.setVisibility(View.VISIBLE); + mBinding.widget.text.setText(text); + hideProgress(); + } + + private void hideError() { + mBinding.widget.error.setVisibility(View.GONE); + mBinding.widget.text.setText(""); + } + private void showControl(View view) { mBinding.control.getRoot().setVisibility(View.VISIBLE); view.requestFocus(); setR2Callback(); + hideInfo(); } private void hideControl() { @@ -355,6 +369,11 @@ private void hideControl() { App.removeCallbacks(mR2); } + private void hideCenter() { + mBinding.widget.action.setImageResource(R.drawable.ic_widget_play); + mBinding.widget.center.setVisibility(View.GONE); + } + private void showInfo() { mBinding.widget.info.setVisibility(View.VISIBLE); setR1Callback(); @@ -367,13 +386,12 @@ private void hideInfo() { } private void showEpg() { - mBinding.control.play.setText(mChannel.getData().getEpg()); mBinding.widget.play.setText(mChannel.getData().getEpg()); } - private void hideCenter() { - mBinding.widget.action.setImageResource(R.drawable.ic_widget_play); - mBinding.widget.center.setVisibility(View.GONE); + private void setTraffic() { + Traffic.setSpeed(mBinding.widget.traffic); + App.post(mR4, Constant.INTERVAL_TRAFFIC); } private void setR1Callback() { @@ -384,14 +402,21 @@ private void setR2Callback() { App.post(mR2, Constant.INTERVAL_HIDE); } + private void onToggle() { + if (isVisible(mBinding.control.getRoot())) hideControl(); + if (isVisible(mBinding.recycler)) hideUI(); + else showUI(); + hideInfo(); + } + private void resetPass() { this.count = 0; } @Override public void onItemClick(Group item) { - mChannelAdapter.setItems(mGroup.getChannel(), null); - mBinding.channel.setSelectedPosition(mGroup.getPosition()); + mChannelAdapter.setItems(item.getChannel(), null); + mBinding.channel.setSelectedPosition(item.getPosition()); if (!item.isKeep() || ++count < 5 || mHides.isEmpty()) return; App.removeCallbacks(mR0); PassDialog.show(this); @@ -431,21 +456,20 @@ private void delKeep(Channel item) { } private void setChannel(Channel item) { - LiveConfig.get().setKeep(mGroup, mChannel = item); App.post(mR3, 100); + mChannel = item; showInfo(); } private void setInfo() { mChannel.loadLogo(mBinding.widget.logo); - mBinding.control.name.setText(mChannel.getName()); - mBinding.control.line.setText(mChannel.getLineText()); - mBinding.control.number.setText(mChannel.getNumber()); mBinding.widget.name.setText(mChannel.getName()); mBinding.widget.line.setText(mChannel.getLineText()); mBinding.widget.number.setText(mChannel.getNumber()); - mBinding.control.line.setVisibility(mChannel.getLineVisible()); + mBinding.control.line.setText(mChannel.getLineText()); mBinding.widget.line.setVisibility(mChannel.getLineVisible()); + mBinding.control.line.setVisibility(mChannel.getLineVisible()); + showEpg(); checkEpg(); } @@ -453,8 +477,7 @@ private void checkEpg() { if (mChannel.getEpg().isEmpty()) return; String date = mFormatDate.format(new Date()); String epg = mChannel.getEpg().replace("{date}", date); - if (mChannel.getData().equal(date)) showEpg(); - else getEpg(epg, mChannel); + if (!mChannel.getData().equal(date)) getEpg(epg, mChannel); } private void getEpg(String epg, Channel channel) { @@ -468,10 +491,107 @@ public void onResponse(@NonNull Call call, @NonNull Response response) throws IO } private void getUrl() { + LiveConfig.get().setKeep(mChannel); mViewModel.getUrl(mChannel); showProgress(); } + @Override + public void setLive(Live item) { + LiveConfig.get().setHome(item); + mPlayers.stop(); + mHides.clear(); + hideControl(); + getLive(); + } + + @Override + public void setPass(String pass) { + boolean first = true; + int position = mGroupAdapter.size(); + Iterator iterator = mHides.iterator(); + while (iterator.hasNext()) { + Group item = iterator.next(); + if (!item.getPass().equals(pass)) continue; + mGroupAdapter.add(mGroupAdapter.size(), item); + if (first) mBinding.group.setSelectedPosition(position); + if (first) onItemClick(mGroup = item); + iterator.remove(); + first = false; + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onPlayerEvent(PlayerEvent event) { + switch (event.getState()) { + case 0: + setTrackVisible(false); + break; + case Player.STATE_IDLE: + break; + case Player.STATE_BUFFERING: + showProgress(); + break; + case Player.STATE_READY: + hideProgress(); + mPlayers.reset(); + setSpeedVisible(); + setTrackVisible(true); + break; + case Player.STATE_ENDED: + nextChannel(); + break; + } + } + + private void setSpeedVisible() { + mBinding.control.speed.setVisibility(mPlayers.isVod() ? View.VISIBLE : View.GONE); + } + + private void setTrackVisible(boolean visible) { + mBinding.control.text.setVisibility(visible && mPlayers.haveTrack(C.TRACK_TYPE_TEXT) ? View.VISIBLE : View.GONE); + mBinding.control.audio.setVisibility(visible && mPlayers.haveTrack(C.TRACK_TYPE_AUDIO) ? View.VISIBLE : View.GONE); + mBinding.control.video.setVisibility(visible && mPlayers.haveTrack(C.TRACK_TYPE_VIDEO) ? View.VISIBLE : View.GONE); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onErrorEvent(ErrorEvent event) { + if (!event.isRetry() || mPlayers.addRetry() > 3) onError(event); + else getUrl(); + } + + private void onError(ErrorEvent event) { + mPlayers.stop(); + startFlow(event); + } + + private void startFlow(ErrorEvent event) { + if (!mChannel.isLast()) { + nextLine(true); + } else if (isGone(mBinding.recycler)) { + mChannel.setLine(0); + nextChannel(); + } else { + showError(event.getMsg()); + } + } + + private void prevChannel() { + int position = mGroup.getPosition() - 1; + boolean limit = position < 0; + if (Prefers.isAcross() & limit) prevGroup(true); + else mGroup.setPosition(limit ? mChannelAdapter.size() - 1 : position); + setChannel(mGroup.current()); + } + + private void nextChannel() { + int position = mGroup.getPosition() + 1; + boolean limit = position > mChannelAdapter.size() - 1; + if (Prefers.isAcross() && limit) nextGroup(true); + else mGroup.setPosition(limit ? 0 : position); + setChannel(mGroup.current()); + } + private void prevLine() { mChannel.prevLine(); showInfo(); @@ -485,8 +605,22 @@ private void nextLine(boolean show) { getUrl(); } - private void notifyItemChanged(RecyclerView view, ArrayObjectAdapter adapter) { - if (!view.isComputingLayout()) adapter.notifyArrayItemRangeChanged(0, adapter.size()); + private void seekTo(int time) { + mPlayers.seekTo(time); + showProgress(); + hideCenter(); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + if (isVisible(mBinding.control.getRoot())) setR2Callback(); + if (mKeyDown.hasEvent(event)) mKeyDown.onKeyDown(event); + return super.dispatchKeyEvent(event); + } + + @Override + public void setUITimer() { + App.post(mR0, Constant.INTERVAL_HIDE); } @Override @@ -514,15 +648,10 @@ public boolean prevGroup(boolean skip) { } @Override - public boolean dispatchKeyEvent(KeyEvent event) { - if (isVisible(mBinding.control.getRoot())) setR2Callback(); - if (mKeyDown.hasEvent(event)) mKeyDown.onKeyDown(event); - return super.dispatchKeyEvent(event); - } - - @Override - public void setUITimer() { - App.post(mR0, Constant.INTERVAL_HIDE); + public boolean dispatch(boolean check) { + boolean condition1 = mGroup != null && mChannel != null; + boolean condition2 = isGone(mBinding.recycler) && isGone(mBinding.control.getRoot()); + return check ? condition1 && condition2 : condition1; } @Override @@ -547,29 +676,14 @@ public void onSeeking(int time) { hideProgress(); } - @Override - public boolean dispatch(boolean check) { - boolean condition1 = mGroup != null && mChannel != null; - boolean condition2 = isGone(mBinding.recycler) && isGone(mBinding.control.getRoot()); - return check ? condition1 && condition2 : condition1; - } - @Override public void onKeyUp() { - int position = mGroup.getPosition() - 1; - boolean limit = position < 0; - if (Prefers.isAcross() & limit) prevGroup(true); - else mGroup.setPosition(limit ? mChannelAdapter.size() - 1 : position); - setChannel(mGroup.current()); + prevChannel(); } @Override public void onKeyDown() { - int position = mGroup.getPosition() + 1; - boolean limit = position > mChannelAdapter.size() - 1; - if (Prefers.isAcross() && limit) nextGroup(true); - else mGroup.setPosition(limit ? 0 : position); - setChannel(mGroup.current()); + nextChannel(); } @Override @@ -586,12 +700,6 @@ public void onKeyRight(int time) { mKeyDown.resetTime(); } - private void seekTo(int time) { - mPlayers.seekTo(time); - showProgress(); - hideCenter(); - } - @Override public void onKeyCenter() { hideInfo(); @@ -600,11 +708,7 @@ public void onKeyCenter() { @Override public void onMenu() { - if (isVisible(mBinding.control.home)) showControl(mBinding.control.home); - else if (isVisible(mBinding.control.line)) showControl(mBinding.control.line); - else showControl(mBinding.control.player); - hideInfo(); - hideUI(); + showControl(mBinding.control.player); } @Override @@ -614,87 +718,11 @@ public void onSingleTap() { @Override public void onDoubleTap() { - if (isVisible(mBinding.control.getRoot())) hideControl(); + if (isVisible(mBinding.recycler)) hideUI(); + else if (isVisible(mBinding.control.getRoot())) hideControl(); else onMenu(); } - @Override - public void setPass(String pass) { - boolean first = true; - int position = mGroupAdapter.size(); - Iterator iterator = mHides.iterator(); - while (iterator.hasNext()) { - Group item = iterator.next(); - if (!item.getPass().equals(pass)) continue; - mGroupAdapter.add(mGroupAdapter.size(), item); - if (first) mBinding.group.setSelectedPosition(position); - if (first) onItemClick(mGroup = item); - iterator.remove(); - first = false; - } - } - - @Override - public void setLive(Live item) { - LiveConfig.get().setHome(item); - mHides.clear(); - hideControl(); - getLive(); - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onPlayerEvent(PlayerEvent event) { - switch (event.getState()) { - case 0: - setTrackVisible(false); - break; - case Player.STATE_IDLE: - break; - case Player.STATE_BUFFERING: - showProgress(); - break; - case Player.STATE_READY: - hideProgress(); - mPlayers.reset(); - setSpeedVisible(); - setTrackVisible(true); - break; - case Player.STATE_ENDED: - onKeyDown(); - break; - } - } - - private void setSpeedVisible() { - mBinding.control.speed.setVisibility(mPlayers.isVod() ? View.VISIBLE : View.GONE); - } - - private void setTrackVisible(boolean visible) { - mBinding.control.text.setVisibility(visible && mPlayers.haveTrack(C.TRACK_TYPE_TEXT) ? View.VISIBLE : View.GONE); - mBinding.control.audio.setVisibility(visible && mPlayers.haveTrack(C.TRACK_TYPE_AUDIO) ? View.VISIBLE : View.GONE); - mBinding.control.video.setVisibility(visible && mPlayers.haveTrack(C.TRACK_TYPE_VIDEO) ? View.VISIBLE : View.GONE); - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onErrorEvent(ErrorEvent event) { - if (!event.isRetry() || mPlayers.addRetry() > 3) onError(); - else getUrl(); - } - - private void onError() { - mPlayers.stop(); - checkNext(); - } - - private void checkNext() { - if (mChannel.isLast()) { - if (isGone(mBinding.recycler)) onKeyDown(); - } else { - nextLine(true); - getUrl(); - } - } - @Override protected void onResume() { super.onResume(); @@ -705,8 +733,8 @@ protected void onResume() { @Override protected void onPause() { super.onPause(); - Clock.get().release(); mPlayers.pause(); + Clock.stop(); } @Override diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/PushActivity.java b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/PushActivity.java index 85e7bd588e..052b6c9050 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/PushActivity.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/PushActivity.java @@ -8,6 +8,7 @@ import androidx.viewbinding.ViewBinding; import com.fongmi.android.tv.R; +import com.fongmi.android.tv.api.ApiConfig; import com.fongmi.android.tv.databinding.ActivityPushBinding; import com.fongmi.android.tv.server.Server; import com.fongmi.android.tv.ui.base.BaseActivity; @@ -29,7 +30,7 @@ protected ViewBinding getBinding() { @Override protected void initView() { - String address = Server.get().getAddress(false); + String address = Server.get().getAddress(); mBinding.code.setImageBitmap(QRCode.getBitmap(address, 250, 1)); mBinding.info.setText(ResUtil.getString(R.string.push_info, address)); mBinding.clip.setOnClickListener(this::onClip); @@ -37,6 +38,6 @@ protected void initView() { private void onClip(View view) { CharSequence text = ((ClipboardManager) getSystemService(CLIPBOARD_SERVICE)).getText(); - if (text != null) DetailActivity.start(this, "push_agent", text.toString(), ""); + if (text != null && ApiConfig.hasPush()) DetailActivity.push(this, text.toString(), false); } } diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/SearchActivity.java b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/SearchActivity.java index 7de8f3e41e..ef89553e50 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/SearchActivity.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/SearchActivity.java @@ -25,6 +25,7 @@ import com.fongmi.android.tv.ui.custom.CustomTextListener; import com.fongmi.android.tv.ui.custom.SpaceItemDecoration; import com.fongmi.android.tv.ui.custom.dialog.SiteDialog; +import com.fongmi.android.tv.utils.Prefers; import com.fongmi.android.tv.utils.Utils; import java.io.IOException; @@ -71,6 +72,7 @@ public void afterTextChanged(Editable s) { mBinding.mic.setListener(this, new CustomTextListener() { @Override public void onEndOfSpeech() { + mBinding.keyword.requestFocus(); mBinding.mic.stop(); } @@ -93,10 +95,12 @@ private void setRecyclerView() { private void getHot() { mBinding.hint.setText(R.string.search_hot); + mWordAdapter.addAll(Hot.get(Prefers.getHot())); OkHttp.newCall("https://api.web.360kan.com/v1/rank?cat=1").enqueue(new Callback() { @Override public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException { List items = Hot.get(response.body().string()); + if (mWordAdapter.getItemCount() > 0) return; App.post(() -> mWordAdapter.addAll(items)); } }); @@ -126,7 +130,6 @@ public void onDataChanged(int size) { @Override public void onSearch() { - mBinding.mic.setFocusable(false); String keyword = mBinding.keyword.getText().toString().trim(); mBinding.keyword.setSelection(mBinding.keyword.length()); Utils.hideKeyboard(mBinding.keyword); @@ -155,6 +158,5 @@ public void onRemote() { protected void onResume() { super.onResume(); mBinding.keyword.requestFocus(); - mBinding.mic.setFocusable(true); } } diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/VodActivity.java b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/VodActivity.java index 8d0ea76816..8a91cdb4ba 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/VodActivity.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/VodActivity.java @@ -28,6 +28,7 @@ import com.fongmi.android.tv.ui.fragment.VodFragment; import com.fongmi.android.tv.ui.presenter.TypePresenter; import com.fongmi.android.tv.utils.ResUtil; +import com.fongmi.android.tv.utils.Trans; import com.fongmi.android.tv.utils.Utils; import com.google.gson.Gson; @@ -101,7 +102,7 @@ private void setRecyclerView() { private List getTypes(Result result) { List types = new ArrayList<>(); - for (String cate : getSite().getCategories()) for (Class type : result.getTypes()) if (cate.equals(type.getTypeName())) types.add(type); + for (String cate : getSite().getCategories()) for (Class type : result.getTypes()) if (Trans.s2t(cate).equals(type.getTypeName())) types.add(type); return types; } diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/base/BaseActivity.java b/app/src/leanback/java/com/fongmi/android/tv/ui/base/BaseActivity.java index a05897f7ec..6f336e5868 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/base/BaseActivity.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/base/BaseActivity.java @@ -9,6 +9,8 @@ import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; +import androidx.leanback.widget.ArrayObjectAdapter; +import androidx.recyclerview.widget.RecyclerView; import androidx.viewbinding.ViewBinding; import com.fongmi.android.tv.R; @@ -46,6 +48,10 @@ protected Activity getActivity() { return this; } + protected boolean customWall() { + return true; + } + protected void initView() { } @@ -60,8 +66,13 @@ protected boolean isGone(View view) { return view.getVisibility() == View.GONE; } + protected void notifyItemChanged(RecyclerView view, ArrayObjectAdapter adapter) { + if (!view.isComputingLayout()) adapter.notifyArrayItemRangeChanged(0, adapter.size()); + } + private void setWall() { try { + if (!customWall()) return; File file = FileUtil.getWall(Prefers.getWall()); if (file.exists() && file.length() > 0) getWindow().setBackgroundDrawable(WallConfig.drawable(Drawable.createFromPath(file.getAbsolutePath()))); else getWindow().setBackgroundDrawableResource(ResUtil.getDrawable(file.getName())); diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/custom/CustomKeyDownLive.java b/app/src/leanback/java/com/fongmi/android/tv/ui/custom/CustomKeyDownLive.java index 24b72be43b..70c3b23e66 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/custom/CustomKeyDownLive.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/custom/CustomKeyDownLive.java @@ -10,6 +10,7 @@ import com.fongmi.android.tv.App; import com.fongmi.android.tv.Constant; import com.fongmi.android.tv.utils.Prefers; +import com.fongmi.android.tv.utils.ResUtil; import com.fongmi.android.tv.utils.Utils; public class CustomKeyDownLive extends GestureDetector.SimpleOnGestureListener { @@ -58,9 +59,11 @@ private void check(KeyEvent event) { } else if (event.getAction() == KeyEvent.ACTION_DOWN && Utils.isRightKey(event)) { listener.onSeeking(addTime()); } else if (event.getAction() == KeyEvent.ACTION_DOWN && Utils.isUpKey(event)) { - if (Prefers.isInvert()) listener.onKeyDown(); else listener.onKeyUp(); + if (Prefers.isInvert()) listener.onKeyDown(); + else listener.onKeyUp(); } else if (event.getAction() == KeyEvent.ACTION_DOWN && Utils.isDownKey(event)) { - if (Prefers.isInvert()) listener.onKeyUp(); else listener.onKeyDown(); + if (Prefers.isInvert()) listener.onKeyUp(); + else listener.onKeyDown(); } else if (event.getAction() == KeyEvent.ACTION_UP && Utils.isLeftKey(event)) { listener.onKeyLeft(holdTime); } else if (event.getAction() == KeyEvent.ACTION_UP && Utils.isRightKey(event)) { @@ -89,21 +92,24 @@ public boolean onDoubleTap(@NonNull MotionEvent e) { @Override public boolean onSingleTapConfirmed(@NonNull MotionEvent e) { - if (listener.dispatch(false)) listener.onSingleTap(); + if (!listener.dispatch(false)) return true; + int half = ResUtil.getScreenWidthNav() / 2; + if (e.getX() > half) listener.onDoubleTap(); + else listener.onSingleTap(); return true; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - if (listener.dispatch(true)) check(e1, e2, velocityX, velocityY); + if (listener.dispatch(true)) checkFunc(e1, e2, velocityX, velocityY); return true; } - private void check(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + private void checkFunc(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { if (e1.getX() - e2.getX() > DISTANCE && Math.abs(velocityX) > VELOCITY) { - listener.onKeyLeft(30 * 1000); + listener.onKeyLeft(Constant.INTERVAL_SEEK * 3); } else if (e2.getX() - e1.getX() > DISTANCE && Math.abs(velocityX) > VELOCITY) { - listener.onKeyRight(30 * 1000); + listener.onKeyRight(Constant.INTERVAL_SEEK * 3); } else if (e1.getY() - e2.getY() > DISTANCE && Math.abs(velocityY) > VELOCITY) { listener.onKeyUp(); } else if (e2.getY() - e1.getY() > DISTANCE && Math.abs(velocityY) > VELOCITY) { diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/custom/CustomKeyDownVod.java b/app/src/leanback/java/com/fongmi/android/tv/ui/custom/CustomKeyDownVod.java index 5260afc0db..df408e9663 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/custom/CustomKeyDownVod.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/custom/CustomKeyDownVod.java @@ -131,11 +131,8 @@ private void checkFunc(float distanceX, float distanceY, MotionEvent e2) { private void checkSide(MotionEvent e2) { int half = ResUtil.getScreenWidthNav() / 2; - if (e2.getX() > half) { - changeVolume = true; - } else { - changeBright = true; - } + if (e2.getX() > half) changeVolume = true; + else changeBright = true; } private void setBright(float deltaY) { diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/custom/dialog/ConfigDialog.java b/app/src/leanback/java/com/fongmi/android/tv/ui/custom/dialog/ConfigDialog.java index 808fcd4a46..42ec85eff8 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/custom/dialog/ConfigDialog.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/custom/dialog/ConfigDialog.java @@ -2,6 +2,7 @@ import android.Manifest; import android.content.DialogInterface; +import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; @@ -70,9 +71,9 @@ private void initDialog() { } private void initView() { - String address = Server.get().getAddress(false); + String address = Server.get().getAddress(); binding.text.setText(url = getUrl()); - binding.text.setSelection(url.length()); + binding.text.setSelection(TextUtils.isEmpty(url) ? 0 : url.length()); binding.code.setImageBitmap(QRCode.getBitmap(address, 200, 0)); binding.storage.setVisibility(Utils.hasPermission(activity) ? View.GONE : View.VISIBLE); binding.info.setText(ResUtil.getString(R.string.push_info, address).replace(",", "\n")); diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/fragment/VodFragment.java b/app/src/leanback/java/com/fongmi/android/tv/ui/fragment/VodFragment.java index 5dc86fa74f..c89874c65c 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/fragment/VodFragment.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/fragment/VodFragment.java @@ -16,6 +16,7 @@ import androidx.lifecycle.ViewModelProvider; import androidx.viewbinding.ViewBinding; +import com.fongmi.android.tv.App; import com.fongmi.android.tv.Product; import com.fongmi.android.tv.R; import com.fongmi.android.tv.bean.Filter; @@ -86,7 +87,6 @@ protected void initView() { mTypeIds = new ArrayList<>(); mExtends = new HashMap<>(); mFilters = Filter.arrayFrom(getFilter()); - mBinding.progress.getRoot().setVisibility(View.VISIBLE); setRecyclerView(); setViewModel(); } @@ -111,10 +111,10 @@ private void setViewModel() { mViewModel = new ViewModelProvider(this).get(SiteViewModel.class); mViewModel.result.observe(getViewLifecycleOwner(), result -> { int size = result.getList().size(); - mBinding.progress.getRoot().setVisibility(View.GONE); mScroller.endLoading(size == 0); addVideo(result.getList()); checkPage(size); + hideProgress(); }); } @@ -137,11 +137,13 @@ private void checkPage(int count) { } private void getVideo(String typeId, String page) { - if (page.equals("1")) mLast = null; + boolean first = page.equals("1"); + if (first) mLast = null; + if (first) showProgress(); if (isFolder()) mTypeIds.add(typeId); if (isFolder() && !mOpen) mBinding.recycler.moveToTop(); int filterSize = mOpen ? mFilters.size() : 0; - boolean clear = page.equals("1") && mAdapter.size() > filterSize; + boolean clear = first && mAdapter.size() > filterSize; if (clear) mAdapter.removeItems(filterSize, mAdapter.size() - filterSize); mViewModel.categoryContent(getKey(), typeId, page, true, mExtends); } @@ -175,11 +177,20 @@ private ListRow getRow(Filter filter) { return new ListRow(adapter); } + private void showProgress() { + if (!mOpen) mBinding.progress.getRoot().setVisibility(View.VISIBLE); + } + + private void hideProgress() { + mBinding.progress.getRoot().setVisibility(View.GONE); + } + private void showFilter() { List rows = new ArrayList<>(); for (Filter filter : mFilters) rows.add(getRow(filter)); + App.post(() -> mBinding.recycler.smoothScrollToPosition(0), 48); mAdapter.addAll(0, rows); - mBinding.recycler.postDelayed(() -> mBinding.recycler.smoothScrollToPosition(0), 50); + hideProgress(); } private void hideFilter() { diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/presenter/HistoryPresenter.java b/app/src/leanback/java/com/fongmi/android/tv/ui/presenter/HistoryPresenter.java index b7b37cad2e..271f9df965 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/presenter/HistoryPresenter.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/presenter/HistoryPresenter.java @@ -64,11 +64,11 @@ public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object object) { ViewHolder holder = (ViewHolder) viewHolder; setClickListener(holder.view, item); holder.binding.name.setText(item.getVodName()); - holder.binding.site.setVisibility(View.VISIBLE); - holder.binding.site.setText(ApiConfig.getSiteName(item.getSiteKey())); - holder.binding.remark.setText(ResUtil.getString(R.string.vod_last, item.getVodRemarks())); + holder.binding.site.setText(item.getSiteName()); + holder.binding.site.setVisibility(item.getSiteVisible()); holder.binding.remark.setVisibility(delete ? View.GONE : View.VISIBLE); holder.binding.delete.setVisibility(!delete ? View.GONE : View.VISIBLE); + holder.binding.remark.setText(ResUtil.getString(R.string.vod_last, item.getVodRemarks())); ImgUtil.loadHistory(item.getVodPic(), holder.binding.image); } diff --git a/app/src/main/res/color/channel.xml b/app/src/leanback/res/color/channel.xml similarity index 100% rename from app/src/main/res/color/channel.xml rename to app/src/leanback/res/color/channel.xml diff --git a/app/src/main/res/color/group.xml b/app/src/leanback/res/color/group.xml similarity index 100% rename from app/src/main/res/color/group.xml rename to app/src/leanback/res/color/group.xml diff --git a/app/src/leanback/res/drawable/shape_bottom_sheet.xml b/app/src/leanback/res/drawable/shape_bottom_sheet.xml index 9e7396996e..f95f9d3224 100644 --- a/app/src/leanback/res/drawable/shape_bottom_sheet.xml +++ b/app/src/leanback/res/drawable/shape_bottom_sheet.xml @@ -8,8 +8,4 @@ android:topLeftRadius="12dp" android:topRightRadius="12dp" /> - - \ No newline at end of file diff --git a/app/src/leanback/res/drawable/shape_info_live.xml b/app/src/leanback/res/drawable/shape_live_info.xml similarity index 100% rename from app/src/leanback/res/drawable/shape_info_live.xml rename to app/src/leanback/res/drawable/shape_live_info.xml diff --git a/app/src/leanback/res/layout/activity_live.xml b/app/src/leanback/res/layout/activity_live.xml index ef3909d58d..c6e9cee7fd 100644 --- a/app/src/leanback/res/layout/activity_live.xml +++ b/app/src/leanback/res/layout/activity_live.xml @@ -4,13 +4,13 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/black" android:keepScreenOn="true"> diff --git a/app/src/leanback/res/layout/adapter_part.xml b/app/src/leanback/res/layout/adapter_part.xml index 4cce77a368..1965336888 100644 --- a/app/src/leanback/res/layout/adapter_part.xml +++ b/app/src/leanback/res/layout/adapter_part.xml @@ -10,6 +10,7 @@ android:focusableInTouchMode="true" android:gravity="center" android:nextFocusUp="@id/array" + android:singleLine="true" android:textColor="@color/white" android:textSize="16sp" tools:text="分詞" /> \ No newline at end of file diff --git a/app/src/leanback/res/layout/dialog_pass.xml b/app/src/leanback/res/layout/dialog_pass.xml index c6340e08c3..522aa19d03 100644 --- a/app/src/leanback/res/layout/dialog_pass.xml +++ b/app/src/leanback/res/layout/dialog_pass.xml @@ -2,18 +2,15 @@ + android:padding="16dp"> \ No newline at end of file + app:maxHeight="220dp" /> \ No newline at end of file diff --git a/app/src/leanback/res/layout/view_control_live.xml b/app/src/leanback/res/layout/view_control_live.xml index 1294932fe4..40efa841d0 100644 --- a/app/src/leanback/res/layout/view_control_live.xml +++ b/app/src/leanback/res/layout/view_control_live.xml @@ -7,197 +7,184 @@ android:background="@drawable/shape_controller" android:orientation="vertical" android:paddingStart="16dp" - android:paddingTop="16dp" + android:paddingTop="18dp" android:paddingEnd="16dp" android:paddingBottom="8dp"> - + android:fillViewport="true" + android:scrollbars="none"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:gravity="center_vertical" + android:orientation="horizontal"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -41,7 +41,6 @@ android:focusable="true" android:focusableInTouchMode="true" android:nextFocusLeft="@id/next" - android:nextFocusDown="@id/parse" android:text="@string/play_next" android:textColor="@color/white" android:textSize="14sp" /> @@ -54,7 +53,6 @@ android:background="@drawable/selector_text" android:focusable="true" android:focusableInTouchMode="true" - android:nextFocusDown="@id/parse" android:text="@string/play_prev" android:textColor="@color/white" android:textSize="14sp" /> @@ -67,7 +65,6 @@ android:background="@drawable/selector_text" android:focusable="true" android:focusableInTouchMode="true" - android:nextFocusDown="@id/parse" android:textColor="@color/white" android:textSize="14sp" tools:text="刷新" /> @@ -80,7 +77,6 @@ android:background="@drawable/selector_text" android:focusable="true" android:focusableInTouchMode="true" - android:nextFocusDown="@id/parse" android:textColor="@color/white" android:textSize="14sp" tools:text="EXO" /> @@ -93,7 +89,6 @@ android:background="@drawable/selector_text" android:focusable="true" android:focusableInTouchMode="true" - android:nextFocusDown="@id/parse" android:textColor="@color/white" android:textSize="14sp" tools:text="硬解" /> @@ -106,7 +101,6 @@ android:background="@drawable/selector_text" android:focusable="true" android:focusableInTouchMode="true" - android:nextFocusDown="@id/parse" android:textColor="@color/white" android:textSize="14sp" tools:text="1.00" /> @@ -119,7 +113,6 @@ android:background="@drawable/selector_text" android:focusable="true" android:focusableInTouchMode="true" - android:nextFocusDown="@id/parse" android:textColor="@color/white" android:textSize="14sp" tools:text="預設" /> @@ -132,7 +125,6 @@ android:background="@drawable/selector_text" android:focusable="true" android:focusableInTouchMode="true" - android:nextFocusDown="@id/parse" android:tag="3" android:text="@string/play_track_text" android:textColor="@color/white" @@ -148,7 +140,6 @@ android:background="@drawable/selector_text" android:focusable="true" android:focusableInTouchMode="true" - android:nextFocusDown="@id/parse" android:tag="1" android:text="@string/play_track_audio" android:textColor="@color/white" @@ -164,7 +155,6 @@ android:background="@drawable/selector_text" android:focusable="true" android:focusableInTouchMode="true" - android:nextFocusDown="@id/parse" android:tag="2" android:text="@string/play_track_video" android:textColor="@color/white" @@ -180,7 +170,6 @@ android:background="@drawable/selector_text" android:focusable="true" android:focusableInTouchMode="true" - android:nextFocusDown="@id/parse" android:textColor="@color/white" android:textSize="14sp" tools:text="00:00" /> @@ -189,12 +178,10 @@ android:id="@+id/ending" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginEnd="12dp" android:background="@drawable/selector_text" android:focusable="true" android:focusableInTouchMode="true" android:nextFocusRight="@id/ending" - android:nextFocusDown="@id/parse" android:textColor="@color/white" android:textSize="14sp" tools:text="00:00" /> diff --git a/app/src/leanback/res/layout/view_widget_live.xml b/app/src/leanback/res/layout/view_widget_live.xml index 18d10b7be9..11d106da99 100644 --- a/app/src/leanback/res/layout/view_widget_live.xml +++ b/app/src/leanback/res/layout/view_widget_live.xml @@ -26,6 +26,35 @@ + + + + + + + + 搜索 收藏 推送 - 设定 + 设置 最近观看 更新推荐 + + 设置 + %s”的搜索结果 diff --git a/app/src/leanback/res/values-zh-rTW/strings.xml b/app/src/leanback/res/values-zh-rTW/strings.xml index 403a145b26..2ec9064c91 100644 --- a/app/src/leanback/res/values-zh-rTW/strings.xml +++ b/app/src/leanback/res/values-zh-rTW/strings.xml @@ -10,6 +10,9 @@ 最近觀看 更新推薦 + + 設定 + %s」的搜尋結果 diff --git a/app/src/leanback/res/values/strings.xml b/app/src/leanback/res/values/strings.xml index b091a85d35..7f7cda826c 100644 --- a/app/src/leanback/res/values/strings.xml +++ b/app/src/leanback/res/values/strings.xml @@ -10,6 +10,9 @@ History Recommend + + Setting + Search results for %s diff --git a/app/src/main/java/com/fongmi/android/tv/Github.java b/app/src/main/java/com/fongmi/android/tv/Github.java index 945f98c0e1..0cacf5510f 100644 --- a/app/src/main/java/com/fongmi/android/tv/Github.java +++ b/app/src/main/java/com/fongmi/android/tv/Github.java @@ -6,7 +6,7 @@ import java.io.IOException; -import okhttp3.Request; +import okhttp3.OkHttpClient; public class Github { @@ -17,6 +17,7 @@ public class Github { public static final String RELEASE = "release"; public static final String DEV = "dev"; + private OkHttpClient client; private String proxy; private static class Loader { @@ -28,6 +29,7 @@ public static Github get() { } public Github() { + client = OkHttp.client(Constant.TIMEOUT_GITHUB); check(A); check(B); check(C); @@ -36,7 +38,7 @@ public Github() { private void check(String url) { try { if (getProxy().length() > 0) return; - int code = OkHttp.client(Constant.TIMEOUT_GITHUB).newCall(new Request.Builder().url(url).build()).execute().code(); + int code = OkHttp.newCall(client, url).execute().code(); if (code == 200) setProxy(url); } catch (IOException ignored) { } diff --git a/app/src/main/java/com/fongmi/android/tv/api/ApiConfig.java b/app/src/main/java/com/fongmi/android/tv/api/ApiConfig.java index e0b7c4dd2b..d664a49ff2 100644 --- a/app/src/main/java/com/fongmi/android/tv/api/ApiConfig.java +++ b/app/src/main/java/com/fongmi/android/tv/api/ApiConfig.java @@ -125,7 +125,7 @@ private void loadConfig(Callback callback) { try { checkJson(JsonParser.parseString(Decoder.getJson(config.getUrl())).getAsJsonObject(), callback); } catch (Exception e) { - if (config.getUrl().isEmpty()) App.post(() -> callback.error(0)); + if (TextUtils.isEmpty(config.getUrl())) App.post(() -> callback.error(0)); else loadCache(callback); LiveConfig.get().load(); e.printStackTrace(); diff --git a/app/src/main/java/com/fongmi/android/tv/api/LiveConfig.java b/app/src/main/java/com/fongmi/android/tv/api/LiveConfig.java index 3726f96858..8371ca7e41 100644 --- a/app/src/main/java/com/fongmi/android/tv/api/LiveConfig.java +++ b/app/src/main/java/com/fongmi/android/tv/api/LiveConfig.java @@ -1,5 +1,7 @@ package com.fongmi.android.tv.api; +import android.text.TextUtils; + import com.fongmi.android.tv.App; import com.fongmi.android.tv.Product; import com.fongmi.android.tv.R; @@ -55,6 +57,10 @@ public static boolean isEmpty() { return get().getHome() == null; } + public static boolean hasUrl() { + return getUrl().length() > 0; + } + public LiveConfig init() { this.home = null; this.config = Config.live(); @@ -87,7 +93,7 @@ private void loadConfig(Callback callback) { parseConfig(Decoder.getJson(config.getUrl()), callback); } catch (Exception e) { e.printStackTrace(); - App.post(() -> callback.error(config.getUrl().isEmpty() ? 0 : R.string.error_config_get)); + App.post(() -> callback.error(TextUtils.isEmpty(config.getUrl()) ? 0 : R.string.error_config_get)); } } @@ -162,14 +168,16 @@ private int[] getKeep(List items) { Group group = items.get(i); if (group.getName().equals(splits[1])) { int j = group.find(splits[2]); + if (j != -1 && splits.length == 4) group.getChannel().get(j).setLine(splits[3]); if (j != -1) return new int[]{i, j}; } } return new int[]{1, 0}; } - public void setKeep(Group group, Channel channel) { - if (!group.isHidden() && home != null) Prefers.putKeep(home.getName() + AppDatabase.SYMBOL + group.getName() + AppDatabase.SYMBOL + channel.getName()); + public void setKeep(Channel channel) { + if (channel.getGroup().isHidden() || home == null) return; + Prefers.putKeep(home.getName() + AppDatabase.SYMBOL + channel.getGroup().getName() + AppDatabase.SYMBOL + channel.getName() + AppDatabase.SYMBOL + channel.getCurrent()); } public int[] find(List items) { @@ -186,7 +194,7 @@ public int[] find(String number, List items) { } public boolean isSame(String url) { - return same || config.getUrl().isEmpty() || url.equals(config.getUrl()); + return same || TextUtils.isEmpty(config.getUrl()) || url.equals(config.getUrl()); } public List getLives() { diff --git a/app/src/main/java/com/fongmi/android/tv/api/WallConfig.java b/app/src/main/java/com/fongmi/android/tv/api/WallConfig.java index 67ef26b79e..c2475bdce3 100644 --- a/app/src/main/java/com/fongmi/android/tv/api/WallConfig.java +++ b/app/src/main/java/com/fongmi/android/tv/api/WallConfig.java @@ -1,6 +1,7 @@ package com.fongmi.android.tv.api; import android.graphics.drawable.Drawable; +import android.text.TextUtils; import com.fongmi.android.tv.App; import com.fongmi.android.tv.Product; @@ -97,7 +98,7 @@ private File write(File file) throws IOException { } public boolean isSame(String url) { - return same || config.getUrl().isEmpty() || url.equals(config.getUrl()); + return same || TextUtils.isEmpty(config.getUrl()) || url.equals(config.getUrl()); } public static void refresh(int index) { diff --git a/app/src/main/java/com/fongmi/android/tv/bean/Channel.java b/app/src/main/java/com/fongmi/android/tv/bean/Channel.java index 1179ae6ba1..920b0deccb 100644 --- a/app/src/main/java/com/fongmi/android/tv/bean/Channel.java +++ b/app/src/main/java/com/fongmi/android/tv/bean/Channel.java @@ -141,13 +141,17 @@ public int getLine() { } public void setLine(int line) { - this.line = line; + this.line = Math.max(line, 0); } public boolean isSelected() { return selected; } + public void setSelected(boolean selected) { + this.selected = selected; + } + public void setSelected(Channel item) { this.selected = item.equals(this); } @@ -206,6 +210,10 @@ public void live(Live live) { if (!getLogo().startsWith("http")) setLogo(live.getLogo().replace("{name}", getName()).replace("{logo}", getLogo())); } + public void setLine(String line) { + setLine(getUrls().indexOf(line)); + } + public String getScheme() { return Uri.parse(getCurrent()).getScheme().toLowerCase(); } diff --git a/app/src/main/java/com/fongmi/android/tv/bean/Class.java b/app/src/main/java/com/fongmi/android/tv/bean/Class.java index 47cc3c97f4..ba8498876c 100644 --- a/app/src/main/java/com/fongmi/android/tv/bean/Class.java +++ b/app/src/main/java/com/fongmi/android/tv/bean/Class.java @@ -2,6 +2,7 @@ import android.text.TextUtils; +import com.fongmi.android.tv.utils.Trans; import com.google.gson.annotations.SerializedName; import org.simpleframework.xml.Attribute; @@ -88,6 +89,11 @@ public boolean isHome() { return getTypeId().equals("home"); } + public void trans() { + if (Trans.pass()) return; + this.typeName = Trans.s2t(typeName); + } + @Override public boolean equals(Object obj) { if (this == obj) return true; diff --git a/app/src/main/java/com/fongmi/android/tv/bean/Config.java b/app/src/main/java/com/fongmi/android/tv/bean/Config.java index b0d5634324..26215fe171 100644 --- a/app/src/main/java/com/fongmi/android/tv/bean/Config.java +++ b/app/src/main/java/com/fongmi/android/tv/bean/Config.java @@ -24,22 +24,15 @@ public class Config { private String parse; public static Config create(int type) { - return create("", type); + return new Config().type(type); } - public static Config create(String url, int type) { - return new Config(url, "", type); + public static Config create(int type, String url) { + return new Config().type(type).url(url).insert(); } - public static Config create(String url, String name, int type) { - return new Config(url, name, type); - } - - public Config(String url, String name, int type) { - this.url = url; - this.name = name; - this.type = type; - this.id = (int) insert(); + public static Config create(int type, String url, String name) { + return new Config().type(type).url(url).name(name).insert(); } public int getId() { @@ -111,6 +104,11 @@ public Config type(int type) { return this; } + public Config url(String url) { + setUrl(url); + return this; + } + public Config name(String name) { setName(name); return this; @@ -170,26 +168,27 @@ public static Config find(int id) { public static Config find(String url, int type) { Config item = AppDatabase.get().getConfigDao().find(url, type); - return item == null ? create(url, type) : item.type(type); + return item == null ? create(type, url) : item.type(type); } public static Config find(String url, String name, int type) { Config item = AppDatabase.get().getConfigDao().find(url, type); - return item == null ? create(url, name, type) : item.type(type).name(name); + return item == null ? create(type, url, name) : item.type(type).name(name); } public static Config find(Config config, int type) { Config item = AppDatabase.get().getConfigDao().find(config.getUrl(), type); - return item == null ? create(config.getUrl(), config.getName(), type) : item.type(type).name(config.getName()); + return item == null ? create(type, config.getUrl(), config.getName()) : item.type(type).name(config.getName()); } public static Config find(Depot depot, int type) { Config item = AppDatabase.get().getConfigDao().find(depot.getUrl(), type); - return item == null ? create(depot.getUrl(), depot.getName(), type) : item.type(type).name(depot.getName()); + return item == null ? create(type, depot.getUrl(), depot.getName()) : item.type(type).name(depot.getName()); } - public long insert() { - return getUrl().isEmpty() ? -1 : AppDatabase.get().getConfigDao().insert(this); + public Config insert() { + setId(Math.toIntExact(AppDatabase.get().getConfigDao().insert(this))); + return this; } public Config update() { diff --git a/app/src/main/java/com/fongmi/android/tv/bean/Device.java b/app/src/main/java/com/fongmi/android/tv/bean/Device.java new file mode 100644 index 0000000000..3c9fb8a811 --- /dev/null +++ b/app/src/main/java/com/fongmi/android/tv/bean/Device.java @@ -0,0 +1,129 @@ +package com.fongmi.android.tv.bean; + +import android.net.Uri; +import android.text.TextUtils; + +import androidx.annotation.NonNull; +import androidx.room.Entity; +import androidx.room.Index; +import androidx.room.PrimaryKey; + +import com.fongmi.android.tv.Product; +import com.fongmi.android.tv.db.AppDatabase; +import com.fongmi.android.tv.server.Server; +import com.fongmi.android.tv.utils.Utils; +import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; + +import java.util.List; + +@Entity(indices = @Index(value = {"uuid", "name"}, unique = true)) +public class Device { + + @PrimaryKey(autoGenerate = true) + @SerializedName("id") + private Integer id; + @SerializedName("uuid") + private String uuid; + @SerializedName("name") + private String name; + @SerializedName("ip") + private String ip; + @SerializedName("type") + private int type; + + public static Device get() { + Device device = new Device(); + device.setUuid(Utils.getDeviceId()); + device.setName(Utils.getDeviceName()); + device.setIp(Server.get().getAddress()); + device.setType(Product.getDeviceType()); + return device; + } + + public static Device objectFrom(String str) { + return new Gson().fromJson(str, Device.class); + } + + public Device() { + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getUuid() { + return TextUtils.isEmpty(uuid) ? "" : uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getName() { + return TextUtils.isEmpty(name) ? "" : name; + } + + public void setName(String name) { + this.name = name; + } + + public String getIp() { + return TextUtils.isEmpty(ip) ? "" : ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } + + public boolean isMobile() { + return getType() == 1; + } + + public boolean isDLNA() { + return getType() == 2; + } + + public String getHost() { + return isDLNA() ? getUuid() : Uri.parse(getIp()).getHost(); + } + + public Device save() { + AppDatabase.get().getDeviceDao().insertOrUpdate(this); + return this; + } + + public static List getAll() { + return AppDatabase.get().getDeviceDao().findAll(); + } + + public static void delete() { + AppDatabase.get().getDeviceDao().delete(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Device)) return false; + Device it = (Device) obj; + return getUuid().equals(it.getUuid()) && getName().equals(it.getName()); + } + + @NonNull + @Override + public String toString() { + return new Gson().toJson(this); + } +} diff --git a/app/src/main/java/com/fongmi/android/tv/bean/Epg.java b/app/src/main/java/com/fongmi/android/tv/bean/Epg.java index 411a98731a..fad87f6bd1 100644 --- a/app/src/main/java/com/fongmi/android/tv/bean/Epg.java +++ b/app/src/main/java/com/fongmi/android/tv/bean/Epg.java @@ -4,6 +4,7 @@ import com.fongmi.android.tv.R; import com.fongmi.android.tv.utils.ResUtil; +import com.fongmi.android.tv.utils.Trans; import com.fongmi.android.tv.utils.Utils; import com.google.gson.Gson; import com.google.gson.annotations.SerializedName; @@ -50,6 +51,10 @@ public String getTitle() { return TextUtils.isEmpty(title) ? "" : title; } + public void setTitle(String title) { + this.title = title; + } + public String getStart() { return TextUtils.isEmpty(start) ? "" : start; } @@ -82,6 +87,7 @@ private void setTime(SimpleDateFormat format) { for (Epg item : getList()) { item.setStartTime(Utils.format(format, getDate().concat(item.getStart()))); item.setEndTime(Utils.format(format, getDate().concat(item.getEnd()))); + item.setTitle(Trans.s2t(item.getTitle())); } } diff --git a/app/src/main/java/com/fongmi/android/tv/bean/Filter.java b/app/src/main/java/com/fongmi/android/tv/bean/Filter.java index b2bc71f639..8eaf93e7eb 100644 --- a/app/src/main/java/com/fongmi/android/tv/bean/Filter.java +++ b/app/src/main/java/com/fongmi/android/tv/bean/Filter.java @@ -1,5 +1,8 @@ package com.fongmi.android.tv.bean; +import android.text.TextUtils; + +import com.fongmi.android.tv.utils.Trans; import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.annotations.SerializedName; @@ -33,11 +36,17 @@ public String getKey() { } public String getName() { - return name; + return TextUtils.isEmpty(name) ? "" : name; } public List getValue() { - return value; + return value == null ? Collections.emptyList() : value; + } + + public Filter trans() { + if (Trans.pass()) return this; + for (Value value : getValue()) value.trans(); + return this; } public static class Value { @@ -50,11 +59,11 @@ public static class Value { private boolean activated; public String getN() { - return n; + return TextUtils.isEmpty(n) ? "" : n; } public String getV() { - return v; + return TextUtils.isEmpty(v) ? "" : v; } public boolean isActivated() { @@ -65,6 +74,10 @@ public void setActivated(Value item) { this.activated = item.equals(this); } + public void trans() { + this.n = Trans.s2t(n); + } + @Override public boolean equals(Object obj) { if (this == obj) return true; diff --git a/app/src/main/java/com/fongmi/android/tv/bean/Group.java b/app/src/main/java/com/fongmi/android/tv/bean/Group.java index 3e2dbe76c5..baeaf938d5 100644 --- a/app/src/main/java/com/fongmi/android/tv/bean/Group.java +++ b/app/src/main/java/com/fongmi/android/tv/bean/Group.java @@ -28,6 +28,7 @@ public class Group { @SerializedName("pass") private String pass; + private boolean selected; private int position; public static List arrayFrom(String str) { @@ -83,6 +84,14 @@ public void setPass(String pass) { this.pass = pass; } + public boolean isSelected() { + return selected; + } + + public void setSelected(boolean selected) { + this.selected = selected; + } + public int getPosition() { return position; } @@ -99,12 +108,8 @@ public boolean isKeep() { return getName().equals(ResUtil.getString(R.string.keep)); } - public boolean isSetting() { - return getName().equals(ResUtil.getString(R.string.live_setting)); - } - public boolean skip() { - return isKeep() || isSetting(); + return isKeep(); } public void loadLogo(ImageView view) { diff --git a/app/src/main/java/com/fongmi/android/tv/bean/History.java b/app/src/main/java/com/fongmi/android/tv/bean/History.java index 06a5a4b7b6..b061c5122b 100644 --- a/app/src/main/java/com/fongmi/android/tv/bean/History.java +++ b/app/src/main/java/com/fongmi/android/tv/bean/History.java @@ -1,5 +1,8 @@ package com.fongmi.android.tv.bean; +import android.text.TextUtils; +import android.view.View; + import androidx.annotation.NonNull; import androidx.room.Entity; import androidx.room.PrimaryKey; @@ -7,6 +10,8 @@ import com.fongmi.android.tv.R; import com.fongmi.android.tv.api.ApiConfig; import com.fongmi.android.tv.db.AppDatabase; +import com.google.gson.Gson; +import com.google.gson.annotations.SerializedName; import java.util.List; @@ -15,24 +20,45 @@ public class History { @NonNull @PrimaryKey + @SerializedName("key") private String key; + @SerializedName("vodPic") private String vodPic; + @SerializedName("vodName") private String vodName; + @SerializedName("vodFlag") private String vodFlag; + @SerializedName("vodRemarks") private String vodRemarks; + @SerializedName("episodeUrl") private String episodeUrl; + @SerializedName("revSort") private boolean revSort; + @SerializedName("revPlay") private boolean revPlay; + @SerializedName("createTime") private long createTime; + @SerializedName("opening") private long opening; + @SerializedName("ending") private long ending; + @SerializedName("position") private long position; + @SerializedName("duration") private long duration; + @SerializedName("speed") private float speed; + @SerializedName("player") private int player; + @SerializedName("scale") private int scale; + @SerializedName("cid") private int cid; + public static History objectFrom(String str) { + return new Gson().fromJson(str, History.class); + } + public History() { this.speed = 1; this.scale = -1; @@ -176,12 +202,16 @@ public void setCid(int cid) { this.cid = cid; } + public String getSiteName() { + return ApiConfig.getSiteName(getSiteKey()); + } + public String getSiteKey() { - return getKey().substring(0, getKey().lastIndexOf(AppDatabase.SYMBOL)); + return getKey().split(AppDatabase.SYMBOL)[0]; } public String getVodId() { - return getKey().substring(getKey().lastIndexOf(AppDatabase.SYMBOL) + AppDatabase.SYMBOL.length()); + return getKey().split(AppDatabase.SYMBOL)[1]; } public Vod.Flag getFlag() { @@ -192,6 +222,10 @@ public Vod.Flag.Episode getEpisode() { return new Vod.Flag.Episode(getVodRemarks(), getEpisodeUrl()); } + public int getSiteVisible() { + return TextUtils.isEmpty(getSiteName()) ? View.GONE : View.VISIBLE; + } + public int getRevPlayText() { return isRevPlay() ? R.string.play_backward : R.string.play_forward; } @@ -228,8 +262,19 @@ private void checkMerge(List items) { public void update(long position, long duration) { setPosition(position); setDuration(duration); + update(); + } + + public History update(int cid) { + setCid(cid); + update(); + return this; + } + + public History update() { checkMerge(AppDatabase.get().getHistoryDao().findByName(ApiConfig.getCid(), getVodName())); AppDatabase.get().getHistoryDao().insertOrUpdate(this); + return this; } public History delete() { @@ -253,4 +298,10 @@ public void findEpisode(List flags) { } } } + + @NonNull + @Override + public String toString() { + return new Gson().toJson(this); + } } diff --git a/app/src/main/java/com/fongmi/android/tv/bean/Keep.java b/app/src/main/java/com/fongmi/android/tv/bean/Keep.java index 6ebc9253b8..4f2ebb5861 100644 --- a/app/src/main/java/com/fongmi/android/tv/bean/Keep.java +++ b/app/src/main/java/com/fongmi/android/tv/bean/Keep.java @@ -80,11 +80,11 @@ public void setCid(int cid) { } public String getSiteKey() { - return getKey().substring(0, getKey().lastIndexOf(AppDatabase.SYMBOL)); + return getKey().split(AppDatabase.SYMBOL)[0]; } public String getVodId() { - return getKey().substring(getKey().lastIndexOf(AppDatabase.SYMBOL) + AppDatabase.SYMBOL.length()); + return getKey().split(AppDatabase.SYMBOL)[1]; } public static Keep find(String key) { diff --git a/app/src/main/java/com/fongmi/android/tv/bean/Result.java b/app/src/main/java/com/fongmi/android/tv/bean/Result.java index ad25f424a2..2b41e61d2d 100644 --- a/app/src/main/java/com/fongmi/android/tv/bean/Result.java +++ b/app/src/main/java/com/fongmi/android/tv/bean/Result.java @@ -6,6 +6,7 @@ import com.fongmi.android.tv.gson.FilterAdapter; import com.fongmi.android.tv.utils.Json; +import com.fongmi.android.tv.utils.Trans; import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.annotations.SerializedName; @@ -59,7 +60,7 @@ public class Result { public static Result fromJson(String str) { try { Result result = FilterAdapter.gson().fromJson(str, Result.class); - return result == null ? empty() : result; + return result == null ? empty() : result.trans(); } catch (Exception e) { return empty(); } @@ -67,7 +68,7 @@ public static Result fromJson(String str) { public static Result fromXml(String str) { try { - return new Persister().read(Result.class, str); + return new Persister().read(Result.class, str).trans(); } catch (Exception e) { return empty(); } @@ -189,6 +190,10 @@ public List getSubs() { return subs == null ? Collections.emptyList() : subs; } + public String getRealUrl() { + return getPlayUrl() + getUrl(); + } + public Map getHeaders() { return Json.toMap(getHeader()); } @@ -198,6 +203,14 @@ public Result clear() { return this; } + public Result trans() { + if (Trans.pass()) return this; + for (Class type : getTypes()) type.trans(); + for (Vod vod : getList()) vod.trans(); + for (Sub sub : getSubs()) sub.trans(); + return this; + } + @NonNull @Override public String toString() { diff --git a/app/src/main/java/com/fongmi/android/tv/bean/Sub.java b/app/src/main/java/com/fongmi/android/tv/bean/Sub.java index 9ea302c8d7..04f9408ef5 100644 --- a/app/src/main/java/com/fongmi/android/tv/bean/Sub.java +++ b/app/src/main/java/com/fongmi/android/tv/bean/Sub.java @@ -5,6 +5,7 @@ import androidx.media3.common.MediaItem; +import com.fongmi.android.tv.utils.Trans; import com.google.gson.annotations.SerializedName; public class Sub { @@ -34,6 +35,11 @@ public String getFormat() { return TextUtils.isEmpty(format) ? "" : format; } + public void trans() { + if (Trans.pass()) return; + this.name = Trans.s2t(name); + } + public MediaItem.SubtitleConfiguration getExo() { return new MediaItem.SubtitleConfiguration.Builder(Uri.parse(getUrl())).setLabel(getName()).setMimeType(getFormat()).setLanguage(getLang()).build(); } diff --git a/app/src/main/java/com/fongmi/android/tv/bean/Vod.java b/app/src/main/java/com/fongmi/android/tv/bean/Vod.java index a453e5d82c..171dab426b 100644 --- a/app/src/main/java/com/fongmi/android/tv/bean/Vod.java +++ b/app/src/main/java/com/fongmi/android/tv/bean/Vod.java @@ -5,6 +5,7 @@ import androidx.annotation.NonNull; +import com.fongmi.android.tv.utils.Trans; import com.fongmi.android.tv.utils.Utils; import com.google.gson.Gson; import com.google.gson.annotations.SerializedName; @@ -175,6 +176,17 @@ public boolean isFolder() { return getVodTag().equals("folder"); } + public void trans() { + if (Trans.pass()) return; + this.vodName = Trans.s2t(vodName); + this.vodArea = Trans.s2t(vodArea); + this.typeName = Trans.s2t(typeName); + this.vodActor = Trans.s2t(vodActor); + this.vodRemarks = Trans.s2t(vodRemarks); + this.vodContent = Trans.s2t(vodContent); + this.vodDirector = Trans.s2t(vodDirector); + } + public void setVodFlags() { String[] playFlags = getVodPlayFrom().split("\\$\\$\\$"); String[] playUrls = getVodPlayUrl().split("\\$\\$\\$"); @@ -296,7 +308,7 @@ public static List arrayFrom(String str) { public Episode(String name, String url) { this.number = Utils.getDigit(name); - this.name = name; + this.name = Trans.s2t(name); this.url = url; } diff --git a/app/src/main/java/com/fongmi/android/tv/db/AppDatabase.java b/app/src/main/java/com/fongmi/android/tv/db/AppDatabase.java index 4bc62befd8..c3b5dd43f3 100644 --- a/app/src/main/java/com/fongmi/android/tv/db/AppDatabase.java +++ b/app/src/main/java/com/fongmi/android/tv/db/AppDatabase.java @@ -11,20 +11,22 @@ import com.fongmi.android.tv.App; import com.fongmi.android.tv.bean.Config; +import com.fongmi.android.tv.bean.Device; import com.fongmi.android.tv.bean.History; import com.fongmi.android.tv.bean.Keep; import com.fongmi.android.tv.bean.Site; import com.fongmi.android.tv.bean.Track; import com.fongmi.android.tv.db.dao.ConfigDao; +import com.fongmi.android.tv.db.dao.DeviceDao; import com.fongmi.android.tv.db.dao.HistoryDao; import com.fongmi.android.tv.db.dao.KeepDao; import com.fongmi.android.tv.db.dao.SiteDao; import com.fongmi.android.tv.db.dao.TrackDao; -@Database(entities = {Keep.class, Site.class, Track.class, Config.class, History.class}, version = AppDatabase.VERSION) +@Database(entities = {Keep.class, Site.class, Track.class, Config.class, Device.class, History.class}, version = AppDatabase.VERSION) public abstract class AppDatabase extends RoomDatabase { - public static final int VERSION = 20; + public static final int VERSION = 22; public static final String SYMBOL = "@@@"; private static volatile AppDatabase instance; @@ -45,6 +47,8 @@ private static AppDatabase create(Context context) { .addMigrations(MIGRATION_17_18) .addMigrations(MIGRATION_18_19) .addMigrations(MIGRATION_19_20) + .addMigrations(MIGRATION_20_21) + .addMigrations(MIGRATION_21_22) .allowMainThreadQueries() .fallbackToDestructiveMigration() .build(); @@ -58,6 +62,8 @@ private static AppDatabase create(Context context) { public abstract ConfigDao getConfigDao(); + public abstract DeviceDao getDeviceDao(); + public abstract HistoryDao getHistoryDao(); static final Migration MIGRATION_11_12 = new Migration(11, 12) { @@ -126,4 +132,19 @@ public void migrate(@NonNull SupportSQLiteDatabase database) { database.execSQL("ALTER TABLE Config ADD COLUMN name TEXT DEFAULT NULL"); } }; + + static final Migration MIGRATION_20_21 = new Migration(20, 21) { + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + database.execSQL("CREATE TABLE IF NOT EXISTS `Device` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `uuid` TEXT, `name` TEXT, `ip` TEXT)"); + database.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_Device_uuid_name` ON `Device` (`uuid`, `name`)"); + } + }; + + static final Migration MIGRATION_21_22 = new Migration(21, 22) { + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + database.execSQL("ALTER TABLE Device ADD COLUMN type INTEGER DEFAULT 0 NOT NULL"); + } + }; } diff --git a/app/src/main/java/com/fongmi/android/tv/db/dao/DeviceDao.java b/app/src/main/java/com/fongmi/android/tv/db/dao/DeviceDao.java new file mode 100644 index 0000000000..0cf09e241f --- /dev/null +++ b/app/src/main/java/com/fongmi/android/tv/db/dao/DeviceDao.java @@ -0,0 +1,18 @@ +package com.fongmi.android.tv.db.dao; + +import androidx.room.Dao; +import androidx.room.Query; + +import com.fongmi.android.tv.bean.Device; + +import java.util.List; + +@Dao +public abstract class DeviceDao extends BaseDao { + + @Query("SELECT * FROM Device") + public abstract List findAll(); + + @Query("DELETE FROM Device") + public abstract void delete(); +} diff --git a/app/src/main/java/com/fongmi/android/tv/event/CastEvent.java b/app/src/main/java/com/fongmi/android/tv/event/CastEvent.java new file mode 100644 index 0000000000..29a629243c --- /dev/null +++ b/app/src/main/java/com/fongmi/android/tv/event/CastEvent.java @@ -0,0 +1,47 @@ +package com.fongmi.android.tv.event; + +import com.fongmi.android.tv.bean.Device; +import com.fongmi.android.tv.bean.History; +import com.fongmi.android.tv.utils.FileUtil; + +import org.greenrobot.eventbus.EventBus; + +public class CastEvent { + + private final History history; + private final Device device; + private String config; + + public static void post(String device, String config, String history) { + EventBus.getDefault().post(new CastEvent(device, config, history)); + } + + public CastEvent(String device, String config, String history) { + this.history = History.objectFrom(history); + this.device = Device.objectFrom(device); + this.config = config; + checkConfig(); + } + + public History getHistory() { + return history; + } + + public Device getDevice() { + return device; + } + + public String getConfig() { + return config; + } + + public void setConfig(String config) { + this.config = config; + } + + private void checkConfig() { + if (!config.startsWith("file")) return; + if (FileUtil.getLocal(config).exists()) return; + setConfig(device.getIp() + "/" + config); + } +} diff --git a/app/src/main/java/com/fongmi/android/tv/event/PlayerEvent.java b/app/src/main/java/com/fongmi/android/tv/event/PlayerEvent.java index eed8d1ca4a..593b4ced49 100644 --- a/app/src/main/java/com/fongmi/android/tv/event/PlayerEvent.java +++ b/app/src/main/java/com/fongmi/android/tv/event/PlayerEvent.java @@ -7,6 +7,7 @@ public class PlayerEvent { private final int state; + private final String url; public static void ready() { EventBus.getDefault().post(new PlayerEvent(Player.STATE_READY)); @@ -16,11 +17,25 @@ public static void state(int state) { EventBus.getDefault().post(new PlayerEvent(state)); } + public static void url(String url) { + EventBus.getDefault().post(new PlayerEvent(url)); + } + private PlayerEvent(int state) { this.state = state; + this.url = ""; + } + + public PlayerEvent(String url) { + this.state = 0; + this.url = url; } public int getState() { return state; } + + public String getUrl() { + return url; + } } diff --git a/app/src/main/java/com/fongmi/android/tv/event/RefreshEvent.java b/app/src/main/java/com/fongmi/android/tv/event/RefreshEvent.java index ef2cd8d0ff..aa6b73c17e 100644 --- a/app/src/main/java/com/fongmi/android/tv/event/RefreshEvent.java +++ b/app/src/main/java/com/fongmi/android/tv/event/RefreshEvent.java @@ -6,6 +6,14 @@ public class RefreshEvent { private final Type type; + public static void empty() { + EventBus.getDefault().post(new RefreshEvent(Type.EMPTY)); + } + + public static void config() { + EventBus.getDefault().post(new RefreshEvent(Type.CONFIG)); + } + public static void image() { EventBus.getDefault().post(new RefreshEvent(Type.IMAGE)); } @@ -39,6 +47,6 @@ public Type getType() { } public enum Type { - IMAGE, VIDEO, HISTORY, KEEP, SIZE, WALL + EMPTY, CONFIG, IMAGE, VIDEO, HISTORY, KEEP, SIZE, WALL } } diff --git a/app/src/main/java/com/fongmi/android/tv/gson/FilterAdapter.java b/app/src/main/java/com/fongmi/android/tv/gson/FilterAdapter.java index d56a7def0a..18eb7c60c6 100644 --- a/app/src/main/java/com/fongmi/android/tv/gson/FilterAdapter.java +++ b/app/src/main/java/com/fongmi/android/tv/gson/FilterAdapter.java @@ -30,8 +30,8 @@ public LinkedHashMap> deserialize(JsonElement json, Type ty for (String key : filters.keySet()) { List items = new ArrayList<>(); JsonElement element = filters.get(key); - if (element.isJsonObject()) items.add(Filter.objectFrom(element)); - else for (JsonElement item : element.getAsJsonArray()) items.add(Filter.objectFrom(item)); + if (element.isJsonObject()) items.add(Filter.objectFrom(element).trans()); + else for (JsonElement item : element.getAsJsonArray()) items.add(Filter.objectFrom(item).trans()); filterMap.put(key, items); } return filterMap; diff --git a/app/src/main/java/com/fongmi/android/tv/model/LiveViewModel.java b/app/src/main/java/com/fongmi/android/tv/model/LiveViewModel.java index c8bcb753c3..b21e025659 100644 --- a/app/src/main/java/com/fongmi/android/tv/model/LiveViewModel.java +++ b/app/src/main/java/com/fongmi/android/tv/model/LiveViewModel.java @@ -58,13 +58,14 @@ private void execute(int type, Callable callable) { executor = Executors.newFixedThreadPool(2); executor.execute(() -> { try { - if (!Thread.interrupted() && type == LIVE) live.postValue((Live) executor.submit(callable).get(Constant.TIMEOUT_HTTP, TimeUnit.MILLISECONDS)); - if (!Thread.interrupted() && type == CHANNEL) channel.postValue((Channel) executor.submit(callable).get(Constant.TIMEOUT_LIVE, TimeUnit.MILLISECONDS)); + if (Thread.interrupted()) return; + if (type == LIVE) live.postValue((Live) executor.submit(callable).get(Constant.TIMEOUT_HTTP, TimeUnit.MILLISECONDS)); + if (type == CHANNEL) channel.postValue((Channel) executor.submit(callable).get(Constant.TIMEOUT_LIVE, TimeUnit.MILLISECONDS)); } catch (Throwable e) { + if (e instanceof InterruptedException || Thread.interrupted()) return; + if (type == LIVE) live.postValue(new Live()); + if (type == CHANNEL) channel.postValue(new Channel()); e.printStackTrace(); - if (e instanceof InterruptedException) return; - if (!Thread.interrupted() && type == LIVE) live.postValue(new Live()); - if (!Thread.interrupted() && type == CHANNEL) channel.postValue(new Channel()); } }); } diff --git a/app/src/main/java/com/fongmi/android/tv/model/SiteViewModel.java b/app/src/main/java/com/fongmi/android/tv/model/SiteViewModel.java index 8bed315fd9..5f2dbad3ff 100644 --- a/app/src/main/java/com/fongmi/android/tv/model/SiteViewModel.java +++ b/app/src/main/java/com/fongmi/android/tv/model/SiteViewModel.java @@ -13,6 +13,7 @@ import com.fongmi.android.tv.bean.Site; import com.fongmi.android.tv.bean.Vod; import com.fongmi.android.tv.net.OkHttp; +import com.fongmi.android.tv.utils.Trans; import com.fongmi.android.tv.utils.Utils; import com.github.catvod.crawler.Spider; import com.github.catvod.crawler.SpiderDebug; @@ -169,12 +170,12 @@ public void playerContent(String key, String flag, String id) { public void searchContent(Site site, String keyword) throws Throwable { if (site.getType() == 3) { Spider spider = ApiConfig.get().getCSP(site); - String searchContent = spider.searchContent(keyword, false); + String searchContent = spider.searchContent(Trans.t2s(keyword), false); SpiderDebug.log(searchContent); post(site, Result.fromJson(searchContent)); } else { ArrayMap params = new ArrayMap<>(); - params.put("wd", keyword); + params.put("wd", Trans.t2s(keyword)); if (site.getType() != 0) params.put("ac", "detail"); String body = OkHttp.newCall(site.getApi(), params).execute().body().string(); SpiderDebug.log(site.getName() + "," + body); @@ -194,11 +195,12 @@ private void execute(MutableLiveData result, Callable callable) executor = Executors.newFixedThreadPool(2); executor.execute(() -> { try { - if (!Thread.interrupted()) result.postValue(executor.submit(callable).get(Constant.TIMEOUT_VOD, TimeUnit.MILLISECONDS)); + if (Thread.interrupted()) return; + result.postValue(executor.submit(callable).get(Constant.TIMEOUT_VOD, TimeUnit.MILLISECONDS)); } catch (Throwable e) { + if (e instanceof InterruptedException || Thread.interrupted()) return; + result.postValue(Result.empty()); e.printStackTrace(); - if (e instanceof InterruptedException) return; - if (!Thread.interrupted()) result.postValue(Result.empty()); } }); } diff --git a/app/src/main/java/com/fongmi/android/tv/net/OkHttp.java b/app/src/main/java/com/fongmi/android/tv/net/OkHttp.java index ff8e230660..dd5da3c3ba 100644 --- a/app/src/main/java/com/fongmi/android/tv/net/OkHttp.java +++ b/app/src/main/java/com/fongmi/android/tv/net/OkHttp.java @@ -13,6 +13,7 @@ import okhttp3.HttpUrl; import okhttp3.OkHttpClient; import okhttp3.Request; +import okhttp3.RequestBody; public class OkHttp { @@ -39,6 +40,10 @@ public static Call newCall(String url) { return client().newCall(new Request.Builder().url(url).build()); } + public static Call newCall(OkHttpClient client, String url) { + return client.newCall(new Request.Builder().url(url).build()); + } + public static Call newCall(String url, Headers headers) { return client().newCall(new Request.Builder().url(url).headers(headers).build()); } @@ -47,6 +52,14 @@ public static Call newCall(String url, ArrayMap params) { return client().newCall(new Request.Builder().url(buildUrl(url, params)).build()); } + public static Call newCall(OkHttpClient client, String url, ArrayMap params) { + return client.newCall(new Request.Builder().url(buildUrl(url, params)).build()); + } + + public static Call newCall(OkHttpClient client, String url, RequestBody body) { + return client.newCall(new Request.Builder().url(url).post(body).build()); + } + private static HttpUrl buildUrl(String url, ArrayMap params) { HttpUrl.Builder builder = Objects.requireNonNull(HttpUrl.parse(url)).newBuilder(); for (Map.Entry entry : params.entrySet()) builder.addQueryParameter(entry.getKey(), entry.getValue()); diff --git a/app/src/main/java/com/fongmi/android/tv/player/ExoUtil.java b/app/src/main/java/com/fongmi/android/tv/player/ExoUtil.java index ca09bfc514..59b4923d27 100644 --- a/app/src/main/java/com/fongmi/android/tv/player/ExoUtil.java +++ b/app/src/main/java/com/fongmi/android/tv/player/ExoUtil.java @@ -7,7 +7,6 @@ import androidx.media3.common.MimeTypes; import androidx.media3.common.PlaybackException; import androidx.media3.common.Tracks; -import androidx.media3.common.util.Util; import androidx.media3.database.DatabaseProvider; import androidx.media3.database.StandaloneDatabaseProvider; import androidx.media3.datasource.DataSource; @@ -35,6 +34,7 @@ import com.fongmi.android.tv.bean.Sub; import com.fongmi.android.tv.utils.FileUtil; import com.fongmi.android.tv.utils.Prefers; +import com.fongmi.android.tv.utils.Sniffer; import com.google.common.net.HttpHeaders; import java.util.ArrayList; @@ -71,7 +71,7 @@ public static boolean haveTrack(Tracks tracks, int type) { } public static MediaSource getSource(Result result, int errorCode) { - return getSource(result.getHeaders(), result.getPlayUrl() + result.getUrl(), result.getSubs(), errorCode); + return getSource(result.getHeaders(), result.getRealUrl(), result.getSubs(), errorCode); } public static MediaSource getSource(Map headers, String url, int errorCode) { @@ -106,7 +106,7 @@ private static synchronized HttpDataSource.Factory getHttpDataSourceFactory() { } private static synchronized DataSource.Factory getDataSourceFactory(Map headers) { - if (!headers.containsKey(HttpHeaders.USER_AGENT)) headers.put(HttpHeaders.USER_AGENT, Util.getUserAgent(App.get(), App.get().getPackageName())); + if (!headers.containsKey(HttpHeaders.USER_AGENT)) headers.put(HttpHeaders.USER_AGENT, Sniffer.CHROME); if (dataSourceFactory == null) dataSourceFactory = buildReadOnlyCacheDataSource(new DefaultDataSource.Factory(App.get(), getHttpDataSourceFactory()), getCache()); httpDataSourceFactory.setDefaultRequestProperties(headers); return dataSourceFactory; diff --git a/app/src/main/java/com/fongmi/android/tv/player/Players.java b/app/src/main/java/com/fongmi/android/tv/player/Players.java index a3824c0adb..0961d623bc 100644 --- a/app/src/main/java/com/fongmi/android/tv/player/Players.java +++ b/app/src/main/java/com/fongmi/android/tv/player/Players.java @@ -332,11 +332,11 @@ private void stopParse() { } private void setMediaSource(Result result) { - SpiderDebug.log(errorCode + "," + result.getUrl() + "," + result.getHeaders()); - if (isIjk()) ijkPlayer.setMediaSource(result.getPlayUrl() + result.getUrl(), result.getHeaders()); + SpiderDebug.log(errorCode + "," + result.getRealUrl() + "," + result.getHeaders()); + if (isIjk()) ijkPlayer.setMediaSource(result.getRealUrl(), result.getHeaders()); if (isExo()) exoPlayer.setMediaSource(ExoUtil.getSource(result, errorCode)); if (isExo()) exoPlayer.prepare(); - setTimeoutCheck(); + setTimeoutCheck(result.getRealUrl()); } private void setMediaSource(Map headers, String url) { @@ -344,12 +344,12 @@ private void setMediaSource(Map headers, String url) { if (isIjk()) ijkPlayer.setMediaSource(url, headers); if (isExo()) exoPlayer.setMediaSource(ExoUtil.getSource(headers, url, errorCode)); if (isExo()) exoPlayer.prepare(); - setTimeoutCheck(); + setTimeoutCheck(url); } - private void setTimeoutCheck() { + private void setTimeoutCheck(String url) { App.post(runnable, timeout); - PlayerEvent.state(0); + PlayerEvent.url(url); } private void removeTimeoutCheck() { diff --git a/app/src/main/java/com/fongmi/android/tv/ui/custom/TrackNameProvider.java b/app/src/main/java/com/fongmi/android/tv/player/TrackNameProvider.java similarity index 99% rename from app/src/main/java/com/fongmi/android/tv/ui/custom/TrackNameProvider.java rename to app/src/main/java/com/fongmi/android/tv/player/TrackNameProvider.java index 0ec4d501f6..dc02f215c0 100644 --- a/app/src/main/java/com/fongmi/android/tv/ui/custom/TrackNameProvider.java +++ b/app/src/main/java/com/fongmi/android/tv/player/TrackNameProvider.java @@ -1,4 +1,4 @@ -package com.fongmi.android.tv.ui.custom; +package com.fongmi.android.tv.player; import android.content.res.Resources; import android.text.TextUtils; diff --git a/app/src/main/java/com/fongmi/android/tv/server/Nano.java b/app/src/main/java/com/fongmi/android/tv/server/Nano.java index cc279aa24b..f4367e49c8 100644 --- a/app/src/main/java/com/fongmi/android/tv/server/Nano.java +++ b/app/src/main/java/com/fongmi/android/tv/server/Nano.java @@ -2,6 +2,7 @@ import com.fongmi.android.tv.R; import com.fongmi.android.tv.api.ApiConfig; +import com.fongmi.android.tv.bean.Device; import com.fongmi.android.tv.server.process.InputRequestProcess; import com.fongmi.android.tv.server.process.RawRequestProcess; import com.fongmi.android.tv.server.process.RequestProcess; @@ -71,6 +72,7 @@ public Response serve(IHTTPSession session) { case GET: if (url.startsWith("/file")) return doFile(url); else if (url.startsWith("/proxy")) return doProxy(session.getParms()); + else if (url.startsWith("/device")) return createPlainTextResponse(NanoHTTPD.Response.Status.OK, Device.get().toString()); break; case POST: if (url.startsWith("/upload")) return doUpload(session.getParms(), files); @@ -99,9 +101,9 @@ private Response doFile(String url) { String path = url.substring(6); File file = FileUtil.getRootFile(path); if (file.isFile()) return newChunkedResponse(Response.Status.OK, "application/octet-stream", new FileInputStream(file)); - else return newFixedLengthResponse(Response.Status.OK, MIME_PLAINTEXT, listFiles(file)); + else return createPlainTextResponse(Response.Status.OK, listFiles(file)); } catch (Exception e) { - return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, e.getMessage()); + return createPlainTextResponse(Response.Status.INTERNAL_ERROR, e.getMessage()); } } @@ -110,7 +112,7 @@ private Response doProxy(Map params) { Object[] rs = ApiConfig.get().proxyLocal(params); return newChunkedResponse(Response.Status.lookup((Integer) rs[0]), (String) rs[1], (InputStream) rs[2]); } catch (Exception e) { - return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "500"); + return createPlainTextResponse(Response.Status.INTERNAL_ERROR, "500"); } } @@ -122,20 +124,20 @@ private Response doUpload(Map params, Map files) if (fn.toLowerCase().endsWith(".zip")) FileUtil.unzip(temp, FileUtil.getRootPath() + File.separator + path); else FileUtil.copy(temp, FileUtil.getRootFile(path + File.separator + fn)); } - return newFixedLengthResponse(Response.Status.OK, MIME_PLAINTEXT, "OK"); + return createPlainTextResponse(Response.Status.OK, "OK"); } private Response doNewFolder(Map params) { String path = params.get("path"); String name = params.get("name"); FileUtil.getRootFile(path + File.separator + name).mkdirs(); - return newFixedLengthResponse(Response.Status.OK, MIME_PLAINTEXT, "OK"); + return createPlainTextResponse(Response.Status.OK, "OK"); } private Response doDelFolder(Map params) { String path = params.get("path"); FileUtil.clearDir(FileUtil.getRootFile(path)); - return newFixedLengthResponse(Response.Status.OK, MIME_PLAINTEXT, "OK"); + return createPlainTextResponse(Response.Status.OK, "OK"); } private String getParent(File root) { @@ -175,10 +177,12 @@ public static Response createPlainTextResponse(Response.IStatus status, String t public interface Listener { - void onSearch(String text); + void onSearch(String word); - void onPush(String text); + void onPush(String url); - void onApi(String text); + void onApi(String url); + + void onCast(String device, String config, String history); } } diff --git a/app/src/main/java/com/fongmi/android/tv/server/Server.java b/app/src/main/java/com/fongmi/android/tv/server/Server.java index 00494deaaf..40e3813b7c 100644 --- a/app/src/main/java/com/fongmi/android/tv/server/Server.java +++ b/app/src/main/java/com/fongmi/android/tv/server/Server.java @@ -5,6 +5,7 @@ import android.text.format.Formatter; import com.fongmi.android.tv.App; +import com.fongmi.android.tv.event.CastEvent; import com.fongmi.android.tv.event.ServerEvent; import java.net.Inet4Address; @@ -30,14 +31,18 @@ public Server() { this.port = 9978; } - public String getAddress(boolean local) { - return "http://" + (local ? "127.0.0.1" : getIP()) + ":" + port; + public String getAddress() { + return getAddress(false); } public String getAddress(String path) { return getAddress(true) + "/" + path; } + public String getAddress(boolean local) { + return "http://" + (local ? "127.0.0.1" : getIP()) + ":" + port; + } + public void start() { if (nano != null) return; do { @@ -86,17 +91,22 @@ private String getHostAddress() throws SocketException { } @Override - public void onSearch(String text) { - if (text.length() > 0) ServerEvent.search(text); + public void onSearch(String word) { + if (word.length() > 0) ServerEvent.search(word); + } + + @Override + public void onPush(String url) { + if (url.length() > 0) ServerEvent.push(url); } @Override - public void onPush(String text) { - if (text.length() > 0) ServerEvent.push(text); + public void onApi(String url) { + if (url.length() > 0) ServerEvent.api(url); } @Override - public void onApi(String text) { - if (text.length() > 0) ServerEvent.api(text); + public void onCast(String device, String config, String history) { + CastEvent.post(device, config, history); } } diff --git a/app/src/main/java/com/fongmi/android/tv/server/process/InputRequestProcess.java b/app/src/main/java/com/fongmi/android/tv/server/process/InputRequestProcess.java index ad0f4c01a4..6b0305999d 100644 --- a/app/src/main/java/com/fongmi/android/tv/server/process/InputRequestProcess.java +++ b/app/src/main/java/com/fongmi/android/tv/server/process/InputRequestProcess.java @@ -33,6 +33,9 @@ public NanoHTTPD.Response doResponse(NanoHTTPD.IHTTPSession session, String path case "api": nano.getListener().onApi(params.get("url").trim()); break; + case "cast": + nano.getListener().onCast(params.get("device").trim(), params.get("config").trim(), params.get("history").trim()); + break; } return Nano.createPlainTextResponse(NanoHTTPD.Response.Status.OK, "ok"); } diff --git a/app/src/main/java/com/fongmi/android/tv/ui/custom/dialog/TrackDialog.java b/app/src/main/java/com/fongmi/android/tv/ui/custom/dialog/TrackDialog.java index a4b21f6065..d2591d7053 100644 --- a/app/src/main/java/com/fongmi/android/tv/ui/custom/dialog/TrackDialog.java +++ b/app/src/main/java/com/fongmi/android/tv/ui/custom/dialog/TrackDialog.java @@ -13,7 +13,7 @@ import com.fongmi.android.tv.player.Players; import com.fongmi.android.tv.ui.adapter.TrackAdapter; import com.fongmi.android.tv.ui.custom.SpaceItemDecoration; -import com.fongmi.android.tv.ui.custom.TrackNameProvider; +import com.fongmi.android.tv.player.TrackNameProvider; import java.util.ArrayList; import java.util.List; diff --git a/app/src/main/java/com/fongmi/android/tv/utils/ImgUtil.java b/app/src/main/java/com/fongmi/android/tv/utils/ImgUtil.java index 697ff273d8..12332aa074 100644 --- a/app/src/main/java/com/fongmi/android/tv/utils/ImgUtil.java +++ b/app/src/main/java/com/fongmi/android/tv/utils/ImgUtil.java @@ -27,17 +27,19 @@ public static void load(String url, ImageView view) { public static void load(String url, ImageView view, ImageView.ScaleType scaleType) { view.setScaleType(scaleType); if (TextUtils.isEmpty(url)) view.setImageResource(R.drawable.ic_img_error); - else Glide.with(App.get()).asBitmap().load(getUrl(Utils.checkProxy(url))).skipMemoryCache(true).dontAnimate().sizeMultiplier(Prefers.getThumbnail()).signature(new ObjectKey(url + "_" + Prefers.getQuality())).placeholder(R.drawable.ic_img_loading).listener(getListener(view, scaleType)).into(view); + else Glide.with(App.get()).asBitmap().load(getUrl(url)).skipMemoryCache(true).dontAnimate().sizeMultiplier(Prefers.getThumbnail()).signature(new ObjectKey(url + "_" + Prefers.getQuality())).placeholder(R.drawable.ic_img_loading).listener(getListener(view, scaleType)).into(view); } public static void loadKeep(String url, ImageView view) { view.setScaleType(ImageView.ScaleType.CENTER); - Glide.with(App.get()).asBitmap().load(Utils.checkProxy(url)).error(R.drawable.ic_img_error).placeholder(R.drawable.ic_img_loading).listener(getListener(view)).into(view); + if (TextUtils.isEmpty(url)) view.setImageResource(R.drawable.ic_img_error); + else Glide.with(App.get()).asBitmap().load(getUrl(url)).error(R.drawable.ic_img_error).placeholder(R.drawable.ic_img_loading).listener(getListener(view)).into(view); } public static void loadHistory(String url, ImageView view) { view.setScaleType(ImageView.ScaleType.CENTER); - Glide.with(App.get()).asBitmap().load(Utils.checkProxy(url)).error(R.drawable.ic_img_error).placeholder(R.drawable.ic_img_loading).listener(getListener(view)).into(view); + if (TextUtils.isEmpty(url)) view.setImageResource(R.drawable.ic_img_error); + else Glide.with(App.get()).asBitmap().load(getUrl(url)).error(R.drawable.ic_img_error).placeholder(R.drawable.ic_img_loading).listener(getListener(view)).into(view); } public static void loadLive(String url, ImageView view) { @@ -46,8 +48,10 @@ public static void loadLive(String url, ImageView view) { else Glide.with(App.get()).asBitmap().load(url).skipMemoryCache(true).dontAnimate().signature(new ObjectKey(url)).error(R.drawable.ic_img_empty).into(view); } - public static GlideUrl getUrl(String url) { + public static Object getUrl(String url) { String param = null; + url = Utils.checkProxy(url); + if (url.startsWith("data:")) return url; LazyHeaders.Builder builder = new LazyHeaders.Builder(); if (url.contains("@Cookie=")) builder.addHeader("Cookie", param = url.split("@Cookie=")[1].split("@")[0]); if (url.contains("@Referer=")) builder.addHeader("Referer", param = url.split("@Referer=")[1].split("@")[0]); diff --git a/app/src/main/java/com/fongmi/android/tv/utils/Notify.java b/app/src/main/java/com/fongmi/android/tv/utils/Notify.java index 6d5bd47b20..d62129708a 100644 --- a/app/src/main/java/com/fongmi/android/tv/utils/Notify.java +++ b/app/src/main/java/com/fongmi/android/tv/utils/Notify.java @@ -33,11 +33,7 @@ public static void show(String text) { } public static void progress(Context context) { - progress(context, false); - } - - public static void progress(Context context, boolean dim) { - dismiss(); get().create(context, dim); + dismiss(); get().create(context); } public static void dismiss() { @@ -47,11 +43,10 @@ public static void dismiss() { } } - private void create(Context context, boolean dim) { + private void create(Context context) { ViewProgressBinding binding = ViewProgressBinding.inflate(LayoutInflater.from(context)); mDialog = new MaterialAlertDialogBuilder(context).setView(binding.getRoot()).create(); mDialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent); - mDialog.getWindow().setDimAmount(dim ? 0.5f : 0); mDialog.show(); } diff --git a/app/src/main/java/com/fongmi/android/tv/utils/Prefers.java b/app/src/main/java/com/fongmi/android/tv/utils/Prefers.java index 69945979cb..759b85b453 100644 --- a/app/src/main/java/com/fongmi/android/tv/utils/Prefers.java +++ b/app/src/main/java/com/fongmi/android/tv/utils/Prefers.java @@ -28,6 +28,14 @@ public static int getInt(String key) { return getInt(key, 0); } + public static long getLong(String key, long defaultValue) { + return getPrefers().getLong(key, defaultValue); + } + + public static long getLong(String key) { + return getLong(key, 0); + } + public static boolean getBoolean(String key, boolean defaultValue) { return getPrefers().getBoolean(key, defaultValue); } diff --git a/app/src/main/java/com/fongmi/android/tv/utils/Sniffer.java b/app/src/main/java/com/fongmi/android/tv/utils/Sniffer.java index fdec818781..47caf5dc81 100644 --- a/app/src/main/java/com/fongmi/android/tv/utils/Sniffer.java +++ b/app/src/main/java/com/fongmi/android/tv/utils/Sniffer.java @@ -4,7 +4,7 @@ public class Sniffer { - public static final String CHROME = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36"; + public static final String CHROME = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36"; public static final Pattern RULE = Pattern.compile( "http((?!http).){12,}?\\.(m3u8|mp4|flv|avi|mkv|rm|wmv|mpg|m4a|mp3)\\?.*|" + diff --git a/app/src/main/java/com/fongmi/android/tv/utils/Trans.java b/app/src/main/java/com/fongmi/android/tv/utils/Trans.java new file mode 100644 index 0000000000..bf6d4f51f7 --- /dev/null +++ b/app/src/main/java/com/fongmi/android/tv/utils/Trans.java @@ -0,0 +1,60 @@ +package com.fongmi.android.tv.utils; + +import android.text.TextUtils; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +public class Trans { + + private final Map s2t; + private final Map t2s; + private final boolean trans; + + private static class Loader { + static volatile Trans INSTANCE = new Trans(); + } + + private static Trans get() { + return Loader.INSTANCE; + } + + private Trans() { + s2t = new HashMap<>(); + t2s = new HashMap<>(); + trans = Locale.getDefault().getCountry().equals("TW"); + if (trans) init(); + } + + private void init() { + char[] UTF8T = "萬與醜專業叢東絲丟兩嚴喪個爿豐臨為麗舉麼義烏樂喬習鄉書買亂爭於虧雲亙亞產畝親褻嚲億僅從侖倉儀們價眾優夥會傴傘偉傳傷倀倫傖偽佇體餘傭僉俠侶僥偵側僑儈儕儂俁儔儼倆儷儉債傾傯僂僨償儻儐儲儺兒兌兗黨蘭關興茲養獸囅內岡冊寫軍農塚馮衝決況凍淨淒涼淩減湊凜幾鳳鳧憑凱擊氹鑿芻劃劉則剛創刪別剗剄劊劌剴劑剮劍剝劇勸辦務勱動勵勁勞勢勳猛勩勻匭匱區醫華協單賣盧鹵臥衛卻巹廠廳歷厲壓厭厙廁廂厴廈廚廄廝縣參靉靆雙發變敘疊葉號歎嘰籲後嚇呂嗎唚噸聽啟吳嘸囈嘔嚦唄員咼嗆嗚詠哢嚨嚀噝吒噅鹹呱響啞噠嘵嗶噦嘩噲嚌噥喲嘜嗊嘮啢嗩唕喚呼嘖嗇囀齧囉嘽嘯噴嘍嚳囁嗬噯噓嚶囑嚕劈囂謔團園囪圍圇國圖圓聖壙場阪壞塊堅壇壢壩塢墳墜壟壟壚壘墾坰堊墊埡墶壋塏堖塒塤堝墊垵塹墮壪牆壯聲殼壺壼處備復夠頭誇夾奪奩奐奮獎奧妝婦媽嫵嫗媯姍婁婭嬈嬌孌娛媧嫻嫿嬰嬋嬸媼嬡嬪嬙嬤孫學孿寧寶實寵審憲宮寬賓寢對尋導壽將爾塵堯尷屍盡層屭屜屆屬屢屨嶼歲豈嶇崗峴嶴嵐島嶺崠巋嶨嶧峽嶢嶠崢巒嶗崍嶮嶄嶸嶔崳嶁脊巔鞏巰幣帥師幃帳簾幟帶幀幫幬幘幗冪襆幹並廣莊慶廬廡庫應廟龐廢廎廩開異棄張彌弳彎彈強歸當錄彠彥徹徑徠禦憶懺憂愾懷態慫憮慪悵愴憐總懟懌戀懇惡慟懨愷惻惱惲悅愨懸慳憫驚懼慘懲憊愜慚憚慣湣慍憤憒願懾憖怵懣懶懍戇戔戲戧戰戩戶紮撲扡執擴捫掃揚擾撫拋摶摳掄搶護報擔擬攏揀擁攔擰撥擇掛摯攣掗撾撻挾撓擋撟掙擠揮撏撈損撿換搗據撚擄摑擲撣摻摜摣攬撳攙擱摟攪攜攝攄擺搖擯攤攖撐攆擷擼攛擻攢敵斂數齋斕斗斬斷無舊時曠暘曇晝曨顯晉曬曉曄暈暉暫曖劄術樸機殺雜權條來楊榪傑極構樅樞棗櫪梘棖槍楓梟櫃檸檉梔柵標棧櫛櫳棟櫨櫟欄樹棲樣欒棬椏橈楨檔榿橋樺檜槳樁夢檮棶檢欞槨櫝槧欏橢樓欖櫬櫚櫸檟檻檳櫧橫檣櫻櫫櫥櫓櫞簷檁歡歟歐殲歿殤殘殞殮殫殯毆毀轂畢斃氈毿氌氣氫氬氳彙漢汙湯洶遝溝沒灃漚瀝淪滄渢溈滬濔濘淚澩瀧瀘濼瀉潑澤涇潔灑窪浹淺漿澆湞溮濁測澮濟瀏滻渾滸濃潯濜塗湧濤澇淶漣潿渦溳渙滌潤澗漲澀澱淵淥漬瀆漸澠漁瀋滲溫遊灣濕潰濺漵漊潷滾滯灩灄滿瀅濾濫灤濱灘澦瀠瀟瀲濰潛瀦瀾瀨瀕灝滅燈靈災燦煬爐燉煒熗點煉熾爍爛烴燭煙煩燒燁燴燙燼熱煥燜燾煆糊溜愛爺牘犛牽犧犢強狀獷獁猶狽麅獮獰獨狹獅獪猙獄猻獫獵獼玀豬貓蝟獻獺璣璵瑒瑪瑋環現瑲璽瑉玨琺瓏璫琿璡璉瑣瓊瑤璦璿瓔瓚甕甌電畫暢佘疇癤療瘧癘瘍鬁瘡瘋皰屙癰痙癢瘂癆瘓癇癡癉瘮瘞瘺癟癱癮癭癩癬癲臒皚皺皸盞鹽監蓋盜盤瞘眥矓著睜睞瞼瞞矚矯磯礬礦碭碼磚硨硯碸礪礱礫礎硜矽碩硤磽磑礄確鹼礙磧磣堿镟滾禮禕禰禎禱禍稟祿禪離禿稈種積稱穢穠穭稅穌穩穡窮竊竅窯竄窩窺竇窶豎競篤筍筆筧箋籠籩築篳篩簹箏籌簽簡籙簀篋籜籮簞簫簣簍籃籬籪籟糴類秈糶糲粵糞糧糝餱緊縶糸糾紆紅紂纖紇約級紈纊紀紉緯紜紘純紕紗綱納紝縱綸紛紙紋紡紵紖紐紓線紺絏紱練組紳細織終縐絆紼絀紹繹經紿綁絨結絝繞絰絎繪給絢絳絡絕絞統綆綃絹繡綌綏絛繼綈績緒綾緓續綺緋綽緔緄繩維綿綬繃綢綯綹綣綜綻綰綠綴緇緙緗緘緬纜緹緲緝縕繢緦綞緞緶線緱縋緩締縷編緡緣縉縛縟縝縫縗縞纏縭縊縑繽縹縵縲纓縮繆繅纈繚繕繒韁繾繰繯繳纘罌網羅罰罷羆羈羥羨翹翽翬耮耬聳恥聶聾職聹聯聵聰肅腸膚膁腎腫脹脅膽勝朧腖臚脛膠脈膾髒臍腦膿臠腳脫腡臉臘醃膕齶膩靦膃騰臏臢輿艤艦艙艫艱豔艸藝節羋薌蕪蘆蓯葦藶莧萇蒼苧蘇檾蘋莖蘢蔦塋煢繭荊薦薘莢蕘蓽蕎薈薺蕩榮葷滎犖熒蕁藎蓀蔭蕒葒葤藥蒞蓧萊蓮蒔萵薟獲蕕瑩鶯蓴蘀蘿螢營縈蕭薩蔥蕆蕢蔣蔞藍薊蘺蕷鎣驀薔蘞藺藹蘄蘊藪槁蘚虜慮虛蟲虯蟣雖蝦蠆蝕蟻螞蠶蠔蜆蠱蠣蟶蠻蟄蛺蟯螄蠐蛻蝸蠟蠅蟈蟬蠍螻蠑螿蟎蠨釁銜補襯袞襖嫋褘襪襲襏裝襠褌褳襝褲襇褸襤繈襴見觀覎規覓視覘覽覺覬覡覿覥覦覯覲覷觴觸觶讋譽謄訁計訂訃認譏訐訌討讓訕訖訓議訊記訒講諱謳詎訝訥許訛論訩訟諷設訪訣證詁訶評詛識詗詐訴診詆謅詞詘詔詖譯詒誆誄試詿詩詰詼誠誅詵話誕詬詮詭詢詣諍該詳詫諢詡譸誡誣語誚誤誥誘誨誑說誦誒請諸諏諾讀諑誹課諉諛誰諗調諂諒諄誶談誼謀諶諜謊諫諧謔謁謂諤諭諼讒諮諳諺諦謎諞諝謨讜謖謝謠謗諡謙謐謹謾謫譾謬譚譖譙讕譜譎讞譴譫讖穀豶貝貞負貟貢財責賢敗賬貨質販貪貧貶購貯貫貳賤賁貰貼貴貺貸貿費賀貽賊贄賈賄貲賃賂贓資賅贐賕賑賚賒賦賭齎贖賞賜贔賙賡賠賧賴賵贅賻賺賽賾贗讚贇贈贍贏贛赬趙趕趨趲躉躍蹌蹠躒踐躂蹺蹕躚躋踴躊蹤躓躑躡蹣躕躥躪躦軀車軋軌軒軑軔轉軛輪軟轟軲軻轤軸軹軼軤軫轢軺輕軾載輊轎輈輇輅較輒輔輛輦輩輝輥輞輬輟輜輳輻輯轀輸轡轅轄輾轆轍轔辭辯辮邊遼達遷過邁運還這進遠違連遲邇逕跡適選遜遞邐邏遺遙鄧鄺鄔郵鄒鄴鄰鬱郤郟鄶鄭鄆酈鄖鄲醞醱醬釅釃釀釋里钜鑒鑾鏨釓釔針釘釗釙釕釷釺釧釤鈒釩釣鍆釹鍚釵鈃鈣鈈鈦鈍鈔鍾鈉鋇鋼鈑鈐鑰欽鈞鎢鉤鈧鈁鈥鈄鈕鈀鈺錢鉦鉗鈷缽鈳鉕鈽鈸鉞鑽鉬鉭鉀鈿鈾鐵鉑鈴鑠鉛鉚鈰鉉鉈鉍鈹鐸鉶銬銠鉺銪鋏鋣鐃銍鐺銅鋁銱銦鎧鍘銖銑鋌銩銛鏵銓鉿銚鉻銘錚銫鉸銥鏟銃鐋銨銀銣鑄鐒鋪鋙錸鋱鏈鏗銷鎖鋰鋥鋤鍋鋯鋨鏽銼鋝鋒鋅鋶鐦鐧銳銻鋃鋟鋦錒錆鍺錯錨錡錁錕錩錫錮鑼錘錐錦鍁錈錇錟錠鍵鋸錳錙鍥鍈鍇鏘鍶鍔鍤鍬鍾鍛鎪鍠鍰鎄鍍鎂鏤鎡鏌鎮鎛鎘鑷鐫鎳鎿鎦鎬鎊鎰鎔鏢鏜鏍鏰鏞鏡鏑鏃鏇鏐鐔钁鐐鏷鑥鐓鑭鐠鑹鏹鐙鑊鐳鐶鐲鐮鐿鑔鑣鑞鑲長門閂閃閆閈閉問闖閏闈閑閎間閔閌悶閘鬧閨聞闥閩閭闓閥閣閡閫鬮閱閬闍閾閹閶鬩閿閽閻閼闡闌闃闠闊闋闔闐闒闕闞闤隊陽陰陣階際陸隴陳陘陝隉隕險隨隱隸雋難雛讎靂霧霽黴靄靚靜靨韃鞽韉韝韋韌韍韓韙韞韜韻頁頂頃頇項順須頊頑顧頓頎頒頌頏預顱領頗頸頡頰頲頜潁熲頦頤頻頮頹頷頴穎顆題顒顎顓顏額顳顢顛顙顥纇顫顬顰顴風颺颭颮颯颶颸颼颻飀飄飆飆飛饗饜飣饑飥餳飩餼飪飫飭飯飲餞飾飽飼飿飴餌饒餉餄餎餃餏餅餑餖餓餘餒餕餜餛餡館餷饋餶餿饞饁饃餺餾饈饉饅饊饌饢馬馭馱馴馳驅馹駁驢駔駛駟駙駒騶駐駝駑駕驛駘驍罵駰驕驊駱駭駢驫驪騁驗騂駸駿騏騎騍騅騌驌驂騙騭騤騷騖驁騮騫騸驃騾驄驏驟驥驦驤髏髖髕鬢魘魎魚魛魢魷魨魯魴魺鮁鮃鯰鱸鮋鮓鮒鮊鮑鱟鮍鮐鮭鮚鮳鮪鮞鮦鰂鮜鱠鱭鮫鮮鮺鯗鱘鯁鱺鰱鰹鯉鰣鰷鯀鯊鯇鮶鯽鯒鯖鯪鯕鯫鯡鯤鯧鯝鯢鯰鯛鯨鯵鯴鯔鱝鰈鰏鱨鯷鰮鰃鰓鱷鰍鰒鰉鰁鱂鯿鰠鼇鰭鰨鰥鰩鰟鰜鰳鰾鱈鱉鰻鰵鱅鰼鱖鱔鱗鱒鱯鱤鱧鱣鳥鳩雞鳶鳴鳲鷗鴉鶬鴇鴆鴣鶇鸕鴨鴞鴦鴒鴟鴝鴛鴬鴕鷥鷙鴯鴰鵂鴴鵃鴿鸞鴻鵐鵓鸝鵑鵠鵝鵒鷳鵜鵡鵲鶓鵪鶤鵯鵬鵮鶉鶊鵷鷫鶘鶡鶚鶻鶿鶥鶩鷊鷂鶲鶹鶺鷁鶼鶴鷖鸚鷓鷚鷯鷦鷲鷸鷺鸇鷹鸌鸏鸛鸘鹺麥麩黃黌黶黷黲黽黿鼂鼉鞀鼴齇齊齏齒齔齕齗齟齡齙齠齜齦齬齪齲齷龍龔龕龜誌制谘範鬆冇嚐嘗鬨準鐘彆閒乾儘臟拚作".toCharArray(); + char[] UTF8S = "万与丑专业丛东丝丢两严丧个丬丰临为丽举么义乌乐乔习乡书买乱争于亏云亘亚产亩亲亵亸亿仅从仑仓仪们价众优伙会伛伞伟传伤伥伦伧伪伫体余佣佥侠侣侥侦侧侨侩侪侬俣俦俨俩俪俭债倾偬偻偾偿傥傧储傩儿兑兖党兰关兴兹养兽冁内冈册写军农冢冯冲决况冻净凄凉凌减凑凛几凤凫凭凯击凼凿刍划刘则刚创删别刬刭刽刿剀剂剐剑剥剧劝办务劢动励劲劳势勋勐勚匀匦匮区医华协单卖卢卤卧卫却卺厂厅历厉压厌厍厕厢厣厦厨厩厮县参叆叇双发变叙叠叶号叹叽吁后吓吕吗吣吨听启吴呒呓呕呖呗员呙呛呜咏咔咙咛咝咤咴咸哌响哑哒哓哔哕哗哙哜哝哟唛唝唠唡唢唣唤唿啧啬啭啮啰啴啸喷喽喾嗫呵嗳嘘嘤嘱噜噼嚣嚯团园囱围囵国图圆圣圹场坂坏块坚坛坜坝坞坟坠垄垅垆垒垦垧垩垫垭垯垱垲垴埘埙埚埝埯堑堕塆墙壮声壳壶壸处备复够头夸夹夺奁奂奋奖奥妆妇妈妩妪妫姗娄娅娆娇娈娱娲娴婳婴婵婶媪嫒嫔嫱嬷孙学孪宁宝实宠审宪宫宽宾寝对寻导寿将尔尘尧尴尸尽层屃屉届属屡屦屿岁岂岖岗岘岙岚岛岭岽岿峃峄峡峣峤峥峦崂崃崄崭嵘嵚嵛嵝嵴巅巩巯币帅师帏帐帘帜带帧帮帱帻帼幂幞干并广庄庆庐庑库应庙庞废庼廪开异弃张弥弪弯弹强归当录彟彦彻径徕御忆忏忧忾怀态怂怃怄怅怆怜总怼怿恋恳恶恸恹恺恻恼恽悦悫悬悭悯惊惧惨惩惫惬惭惮惯愍愠愤愦愿慑慭憷懑懒懔戆戋戏戗战戬户扎扑扦执扩扪扫扬扰抚抛抟抠抡抢护报担拟拢拣拥拦拧拨择挂挚挛挜挝挞挟挠挡挢挣挤挥挦捞损捡换捣据捻掳掴掷掸掺掼揸揽揿搀搁搂搅携摄摅摆摇摈摊撄撑撵撷撸撺擞攒敌敛数斋斓斗斩断无旧时旷旸昙昼昽显晋晒晓晔晕晖暂暧札术朴机杀杂权条来杨杩杰极构枞枢枣枥枧枨枪枫枭柜柠柽栀栅标栈栉栊栋栌栎栏树栖样栾桊桠桡桢档桤桥桦桧桨桩梦梼梾检棂椁椟椠椤椭楼榄榇榈榉槚槛槟槠横樯樱橥橱橹橼檐檩欢欤欧歼殁殇残殒殓殚殡殴毁毂毕毙毡毵氇气氢氩氲汇汉污汤汹沓沟没沣沤沥沦沧沨沩沪沵泞泪泶泷泸泺泻泼泽泾洁洒洼浃浅浆浇浈浉浊测浍济浏浐浑浒浓浔浕涂涌涛涝涞涟涠涡涢涣涤润涧涨涩淀渊渌渍渎渐渑渔渖渗温游湾湿溃溅溆溇滗滚滞滟滠满滢滤滥滦滨滩滪潆潇潋潍潜潴澜濑濒灏灭灯灵灾灿炀炉炖炜炝点炼炽烁烂烃烛烟烦烧烨烩烫烬热焕焖焘煅煳熘爱爷牍牦牵牺犊强状犷犸犹狈狍狝狞独狭狮狯狰狱狲猃猎猕猡猪猫猬献獭玑玙玚玛玮环现玱玺珉珏珐珑珰珲琎琏琐琼瑶瑷璇璎瓒瓮瓯电画畅畲畴疖疗疟疠疡疬疮疯疱疴痈痉痒痖痨痪痫痴瘅瘆瘗瘘瘪瘫瘾瘿癞癣癫癯皑皱皲盏盐监盖盗盘眍眦眬着睁睐睑瞒瞩矫矶矾矿砀码砖砗砚砜砺砻砾础硁硅硕硖硗硙硚确硷碍碛碜碱碹磙礼祎祢祯祷祸禀禄禅离秃秆种积称秽秾稆税稣稳穑穷窃窍窑窜窝窥窦窭竖竞笃笋笔笕笺笼笾筑筚筛筜筝筹签简箓箦箧箨箩箪箫篑篓篮篱簖籁籴类籼粜粝粤粪粮糁糇紧絷纟纠纡红纣纤纥约级纨纩纪纫纬纭纮纯纰纱纲纳纴纵纶纷纸纹纺纻纼纽纾线绀绁绂练组绅细织终绉绊绋绌绍绎经绐绑绒结绔绕绖绗绘给绚绛络绝绞统绠绡绢绣绤绥绦继绨绩绪绫绬续绮绯绰绱绲绳维绵绶绷绸绹绺绻综绽绾绿缀缁缂缃缄缅缆缇缈缉缊缋缌缍缎缏缐缑缒缓缔缕编缗缘缙缚缛缜缝缞缟缠缡缢缣缤缥缦缧缨缩缪缫缬缭缮缯缰缱缲缳缴缵罂网罗罚罢罴羁羟羡翘翙翚耢耧耸耻聂聋职聍联聩聪肃肠肤肷肾肿胀胁胆胜胧胨胪胫胶脉脍脏脐脑脓脔脚脱脶脸腊腌腘腭腻腼腽腾膑臜舆舣舰舱舻艰艳艹艺节芈芗芜芦苁苇苈苋苌苍苎苏苘苹茎茏茑茔茕茧荆荐荙荚荛荜荞荟荠荡荣荤荥荦荧荨荩荪荫荬荭荮药莅莜莱莲莳莴莶获莸莹莺莼萚萝萤营萦萧萨葱蒇蒉蒋蒌蓝蓟蓠蓣蓥蓦蔷蔹蔺蔼蕲蕴薮藁藓虏虑虚虫虬虮虽虾虿蚀蚁蚂蚕蚝蚬蛊蛎蛏蛮蛰蛱蛲蛳蛴蜕蜗蜡蝇蝈蝉蝎蝼蝾螀螨蟏衅衔补衬衮袄袅袆袜袭袯装裆裈裢裣裤裥褛褴襁襕见观觃规觅视觇览觉觊觋觌觍觎觏觐觑觞触觯詟誉誊讠计订讣认讥讦讧讨让讪讫训议讯记讱讲讳讴讵讶讷许讹论讻讼讽设访诀证诂诃评诅识诇诈诉诊诋诌词诎诏诐译诒诓诔试诖诗诘诙诚诛诜话诞诟诠诡询诣诤该详诧诨诩诪诫诬语诮误诰诱诲诳说诵诶请诸诹诺读诼诽课诿谀谁谂调谄谅谆谇谈谊谋谌谍谎谏谐谑谒谓谔谕谖谗谘谙谚谛谜谝谞谟谠谡谢谣谤谥谦谧谨谩谪谫谬谭谮谯谰谱谲谳谴谵谶谷豮贝贞负贠贡财责贤败账货质贩贪贫贬购贮贯贰贱贲贳贴贵贶贷贸费贺贻贼贽贾贿赀赁赂赃资赅赆赇赈赉赊赋赌赍赎赏赐赑赒赓赔赕赖赗赘赙赚赛赜赝赞赟赠赡赢赣赪赵赶趋趱趸跃跄跖跞践跶跷跸跹跻踊踌踪踬踯蹑蹒蹰蹿躏躜躯车轧轨轩轪轫转轭轮软轰轱轲轳轴轵轶轷轸轹轺轻轼载轾轿辀辁辂较辄辅辆辇辈辉辊辋辌辍辎辏辐辑辒输辔辕辖辗辘辙辚辞辩辫边辽达迁过迈运还这进远违连迟迩迳迹适选逊递逦逻遗遥邓邝邬邮邹邺邻郁郄郏郐郑郓郦郧郸酝酦酱酽酾酿释里鉅鉴銮錾钆钇针钉钊钋钌钍钎钏钐钑钒钓钔钕钖钗钘钙钚钛钝钞钟钠钡钢钣钤钥钦钧钨钩钪钫钬钭钮钯钰钱钲钳钴钵钶钷钸钹钺钻钼钽钾钿铀铁铂铃铄铅铆铈铉铊铋铍铎铏铐铑铒铕铗铘铙铚铛铜铝铞铟铠铡铢铣铤铥铦铧铨铪铫铬铭铮铯铰铱铲铳铴铵银铷铸铹铺铻铼铽链铿销锁锂锃锄锅锆锇锈锉锊锋锌锍锎锏锐锑锒锓锔锕锖锗错锚锜锞锟锠锡锢锣锤锥锦锨锩锫锬锭键锯锰锱锲锳锴锵锶锷锸锹锺锻锼锽锾锿镀镁镂镃镆镇镈镉镊镌镍镎镏镐镑镒镕镖镗镙镚镛镜镝镞镟镠镡镢镣镤镥镦镧镨镩镪镫镬镭镮镯镰镱镲镳镴镶长门闩闪闫闬闭问闯闰闱闲闳间闵闶闷闸闹闺闻闼闽闾闿阀阁阂阃阄阅阆阇阈阉阊阋阌阍阎阏阐阑阒阓阔阕阖阗阘阙阚阛队阳阴阵阶际陆陇陈陉陕陧陨险随隐隶隽难雏雠雳雾霁霉霭靓静靥鞑鞒鞯鞴韦韧韨韩韪韫韬韵页顶顷顸项顺须顼顽顾顿颀颁颂颃预颅领颇颈颉颊颋颌颍颎颏颐频颒颓颔颕颖颗题颙颚颛颜额颞颟颠颡颢颣颤颥颦颧风飏飐飑飒飓飔飕飖飗飘飙飚飞飨餍饤饥饦饧饨饩饪饫饬饭饮饯饰饱饲饳饴饵饶饷饸饹饺饻饼饽饾饿馀馁馂馃馄馅馆馇馈馉馊馋馌馍馎馏馐馑馒馓馔馕马驭驮驯驰驱驲驳驴驵驶驷驸驹驺驻驼驽驾驿骀骁骂骃骄骅骆骇骈骉骊骋验骍骎骏骐骑骒骓骔骕骖骗骘骙骚骛骜骝骞骟骠骡骢骣骤骥骦骧髅髋髌鬓魇魉鱼鱽鱾鱿鲀鲁鲂鲄鲅鲆鲇鲈鲉鲊鲋鲌鲍鲎鲏鲐鲑鲒鲓鲔鲕鲖鲗鲘鲙鲚鲛鲜鲝鲞鲟鲠鲡鲢鲣鲤鲥鲦鲧鲨鲩鲪鲫鲬鲭鲮鲯鲰鲱鲲鲳鲴鲵鲶鲷鲸鲹鲺鲻鲼鲽鲾鲿鳀鳁鳂鳃鳄鳅鳆鳇鳈鳉鳊鳋鳌鳍鳎鳏鳐鳑鳒鳓鳔鳕鳖鳗鳘鳙鳛鳜鳝鳞鳟鳠鳡鳢鳣鸟鸠鸡鸢鸣鸤鸥鸦鸧鸨鸩鸪鸫鸬鸭鸮鸯鸰鸱鸲鸳鸴鸵鸶鸷鸸鸹鸺鸻鸼鸽鸾鸿鹀鹁鹂鹃鹄鹅鹆鹇鹈鹉鹊鹋鹌鹍鹎鹏鹐鹑鹒鹓鹔鹕鹖鹗鹘鹚鹛鹜鹝鹞鹟鹠鹡鹢鹣鹤鹥鹦鹧鹨鹩鹪鹫鹬鹭鹯鹰鹱鹲鹳鹴鹾麦麸黄黉黡黩黪黾鼋鼌鼍鼗鼹齄齐齑齿龀龁龂龃龄龅龆龇龈龉龊龋龌龙龚龛龟志制咨范松冇尝尝闹准钟彆闲干尽脏拼作".toCharArray(); + for (int i = 0, n = UTF8T.length; i < n; ++i) { + s2t.put(UTF8S[i], UTF8T[i]); + t2s.put(UTF8T[i], UTF8S[i]); + } + } + + private String get(String text, Map map) { + if (TextUtils.isEmpty(text)) return text; + char[] chars = text.toCharArray(); + for (int i = 0; i < chars.length; ++i) { + Character found = map.get(chars[i]); + if (found != null) chars[i] = found; + } + return String.valueOf(chars); + } + + public static boolean pass() { + return !get().trans; + } + + public static String s2t(String text) { + return pass() ? text : get().get(text, get().s2t); + } + + public static String t2s(String text) { + return pass() ? text : get().get(text, get().t2s); + } +} diff --git a/app/src/main/java/com/fongmi/android/tv/utils/Utils.java b/app/src/main/java/com/fongmi/android/tv/utils/Utils.java index 92655b0cf1..d4ca0cc5db 100644 --- a/app/src/main/java/com/fongmi/android/tv/utils/Utils.java +++ b/app/src/main/java/com/fongmi/android/tv/utils/Utils.java @@ -92,7 +92,7 @@ public static boolean isVideoFormat(String url) { public static boolean isVideoFormat(String url, Map headers) { if (Sniffer.CUSTOM.matcher(url).find()) return true; if (headers.containsKey("Accept") && headers.get("Accept").startsWith("image")) return false; - if (url.contains("url=http") || url.contains("v=http") || url.contains(".js") || url.contains(".css") || url.contains(".html")) return false; + if (url.contains("url=http") || url.contains("v=http") || url.contains(".css") || url.contains(".html")) return false; return Sniffer.RULE.matcher(url).find(); } @@ -144,10 +144,16 @@ public static String getMd5(String src) { } } - public static String getUUID() { + public static String getDeviceId() { return Settings.Secure.getString(App.get().getContentResolver(), Settings.Secure.ANDROID_ID); } + public static String getDeviceName() { + String model = Build.MODEL; + String manufacturer = Build.MANUFACTURER; + return model.startsWith(manufacturer) ? model : manufacturer + " " + model; + } + public static String getBase64(String ext) { return Base64.encodeToString(ext.getBytes(), Base64.DEFAULT | Base64.NO_WRAP); } diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index cbcf07139e..577c4ad1e6 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -10,7 +10,6 @@ 密码 未分类 - 设定 正在播放:%s 线路 %s diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index f95d8cdefa..3e030edb49 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -10,7 +10,6 @@ 密碼 未分類 - 設定 正在播放:%s 來源 %s diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8171148d16..12ef297b7b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2,6 +2,7 @@ TV + C0868879 Press back again to exit @@ -10,7 +11,6 @@ Pass Group - Setting Playing: %s Line %s diff --git a/app/src/mobile/AndroidManifest.xml b/app/src/mobile/AndroidManifest.xml index d23547b237..96c398fedf 100644 --- a/app/src/mobile/AndroidManifest.xml +++ b/app/src/mobile/AndroidManifest.xml @@ -2,21 +2,16 @@ - - - + + - + @@ -49,18 +44,36 @@ + android:configChanges="screenSize|smallestScreenSize|screenLayout" + android:screenOrientation="fullUser" /> + android:configChanges="screenSize|smallestScreenSize|screenLayout" + android:screenOrientation="fullUser" /> + + + android:excludeFromRecents="true" + android:launchMode="singleTask" + android:resizeableActivity="true" + android:screenOrientation="sensorLandscape" + android:supportsPictureInPicture="true" /> + + + + \ No newline at end of file diff --git a/app/src/mobile/java/com/fongmi/android/tv/Product.java b/app/src/mobile/java/com/fongmi/android/tv/Product.java index 0861719972..b71eb270c0 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/Product.java +++ b/app/src/mobile/java/com/fongmi/android/tv/Product.java @@ -12,6 +12,10 @@ public class Product { + public static int getDeviceType() { + return 1; + } + public static int getColumn() { return Math.abs(Prefers.getSize() - 5); } diff --git a/app/src/mobile/java/com/fongmi/android/tv/Updater.java b/app/src/mobile/java/com/fongmi/android/tv/Updater.java index 4870514c08..4c1af2db65 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/Updater.java +++ b/app/src/mobile/java/com/fongmi/android/tv/Updater.java @@ -1,4 +1,4 @@ -package com.fongmi.android.tv.api; +package com.fongmi.android.tv; import android.app.Activity; import android.content.DialogInterface; @@ -7,10 +7,6 @@ import androidx.appcompat.app.AlertDialog; -import com.fongmi.android.tv.App; -import com.fongmi.android.tv.BuildConfig; -import com.fongmi.android.tv.Github; -import com.fongmi.android.tv.R; import com.fongmi.android.tv.databinding.DialogUpdateBinding; import com.fongmi.android.tv.net.Download; import com.fongmi.android.tv.net.OkHttp; diff --git a/app/src/mobile/java/com/fongmi/android/tv/cast/CastDevice.java b/app/src/mobile/java/com/fongmi/android/tv/cast/CastDevice.java new file mode 100644 index 0000000000..19f2849fe1 --- /dev/null +++ b/app/src/mobile/java/com/fongmi/android/tv/cast/CastDevice.java @@ -0,0 +1,57 @@ +package com.fongmi.android.tv.cast; + +import org.fourthline.cling.model.meta.Device; + +import java.util.ArrayList; +import java.util.List; + +public class CastDevice { + + private final List> devices; + + private static class Loader { + static volatile CastDevice INSTANCE = new CastDevice(); + } + + public static CastDevice get() { + return Loader.INSTANCE; + } + + public CastDevice() { + this.devices = new ArrayList<>(); + } + + public boolean isEmpty() { + return devices.isEmpty(); + } + + private com.fongmi.android.tv.bean.Device create(Device item) { + com.fongmi.android.tv.bean.Device device = new com.fongmi.android.tv.bean.Device(); + device.setUuid(item.getIdentity().getUdn().getIdentifierString()); + device.setName(item.getDetails().getFriendlyName()); + device.setType(2); + return device; + } + + public List getAll() { + List items = new ArrayList<>(); + for (Device device : devices) items.add(create(device)); + return items; + } + + public List add(Device device) { + devices.remove(device); + devices.add(device); + return getAll(); + } + + public com.fongmi.android.tv.bean.Device remove(Device device) { + devices.remove(device); + return create(device); + } + + public Device find(com.fongmi.android.tv.bean.Device item) { + for (Device device : devices) if (device.getIdentity().getUdn().getIdentifierString().equals(item.getUuid())) return device; + return null; + } +} diff --git a/app/src/mobile/java/com/fongmi/android/tv/cast/CastVideo.java b/app/src/mobile/java/com/fongmi/android/tv/cast/CastVideo.java new file mode 100644 index 0000000000..626a937ade --- /dev/null +++ b/app/src/mobile/java/com/fongmi/android/tv/cast/CastVideo.java @@ -0,0 +1,43 @@ +package com.fongmi.android.tv.cast; + +import androidx.annotation.NonNull; + +import com.android.cast.dlna.core.ICast; +import com.fongmi.android.tv.server.Server; +import com.fongmi.android.tv.utils.FileUtil; + +import java.util.UUID; + +public class CastVideo implements ICast { + + private final String name; + private final String url; + + public static CastVideo get(String name, String url) { + return new CastVideo(name, url); + } + + private CastVideo(String name, String url) { + if (url.startsWith("file")) url = Server.get().getAddress() + "/" + url.replace(FileUtil.getRootPath(), ""); + this.name = name; + this.url = url; + } + + @NonNull + @Override + public String getId() { + return UUID.randomUUID().toString(); + } + + @NonNull + @Override + public String getUri() { + return url; + } + + @NonNull + @Override + public String getName() { + return name; + } +} \ No newline at end of file diff --git a/app/src/mobile/java/com/fongmi/android/tv/cast/ScanEvent.java b/app/src/mobile/java/com/fongmi/android/tv/cast/ScanEvent.java new file mode 100644 index 0000000000..3a16ead017 --- /dev/null +++ b/app/src/mobile/java/com/fongmi/android/tv/cast/ScanEvent.java @@ -0,0 +1,20 @@ +package com.fongmi.android.tv.cast; + +import org.greenrobot.eventbus.EventBus; + +public class ScanEvent { + + private final String address; + + public static void post(String address) { + EventBus.getDefault().post(new ScanEvent(address)); + } + + public ScanEvent(String address) { + this.address = address; + } + + public String getAddress() { + return address; + } +} diff --git a/app/src/mobile/java/com/fongmi/android/tv/cast/ScanTask.java b/app/src/mobile/java/com/fongmi/android/tv/cast/ScanTask.java new file mode 100644 index 0000000000..b913613288 --- /dev/null +++ b/app/src/mobile/java/com/fongmi/android/tv/cast/ScanTask.java @@ -0,0 +1,80 @@ +package com.fongmi.android.tv.cast; + +import com.fongmi.android.tv.App; +import com.fongmi.android.tv.bean.Device; +import com.fongmi.android.tv.net.OkHttp; +import com.fongmi.android.tv.server.Server; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.concurrent.CountDownLatch; + +import okhttp3.OkHttpClient; + +public class ScanTask { + + private final Listener listener; + private final OkHttpClient client; + private final List devices; + + public static ScanTask create(Listener listener) { + return new ScanTask(listener); + } + + public ScanTask(Listener listener) { + this.listener = listener; + this.client = OkHttp.client(1000); + this.devices = new ArrayList<>(); + } + + public void start(List ips) { + App.execute(() -> run(getUrl(ips))); + } + + public void start(String url) { + App.execute(() -> run(List.of(url))); + } + + private void run(List items) { + try { + getDevice(items); + } catch (Exception e) { + e.printStackTrace(); + } finally { + App.post(() -> listener.onFind(devices)); + } + } + + private void getDevice(List urls) throws Exception { + CountDownLatch cd = new CountDownLatch(urls.size()); + for (String url : urls) new Thread(() -> findDevice(cd, url)).start(); + cd.await(); + } + + private List getUrl(List ips) { + LinkedHashSet urls = new LinkedHashSet<>(ips); + String local = Server.get().getAddress(); + String base = local.substring(0, local.lastIndexOf(".") + 1); + for (int i = 1; i < 256; i++) urls.add(base + i + ":9978"); + return new ArrayList<>(urls); + } + + private void findDevice(CountDownLatch cd, String url) { + try { + if (url.contains(Server.get().getAddress())) return; + String result = OkHttp.newCall(client, url.concat("/device")).execute().body().string(); + Device device = Device.objectFrom(result); + if (device == null) return; + devices.add(device.save()); + } catch (Exception ignored) { + } finally { + cd.countDown(); + } + } + + public interface Listener { + + void onFind(List devices); + } +} diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/custom/PiP.java b/app/src/mobile/java/com/fongmi/android/tv/pip/PiP.java similarity index 98% rename from app/src/mobile/java/com/fongmi/android/tv/ui/custom/PiP.java rename to app/src/mobile/java/com/fongmi/android/tv/pip/PiP.java index 17f9bd8ae6..18366ed628 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/custom/PiP.java +++ b/app/src/mobile/java/com/fongmi/android/tv/pip/PiP.java @@ -1,4 +1,4 @@ -package com.fongmi.android.tv.ui.custom; +package com.fongmi.android.tv.pip; import android.app.Activity; import android.app.PendingIntent; diff --git a/app/src/mobile/java/com/fongmi/android/tv/receiver/PiPReceiver.java b/app/src/mobile/java/com/fongmi/android/tv/pip/Receiver.java similarity index 66% rename from app/src/mobile/java/com/fongmi/android/tv/receiver/PiPReceiver.java rename to app/src/mobile/java/com/fongmi/android/tv/pip/Receiver.java index a65bd16e45..529dc921ca 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/receiver/PiPReceiver.java +++ b/app/src/mobile/java/com/fongmi/android/tv/pip/Receiver.java @@ -1,4 +1,4 @@ -package com.fongmi.android.tv.receiver; +package com.fongmi.android.tv.pip; import android.app.Activity; import android.content.BroadcastReceiver; @@ -6,15 +6,12 @@ import android.content.Intent; import android.content.IntentFilter; -import com.fongmi.android.tv.databinding.ActivityDetailBinding; -import com.fongmi.android.tv.ui.custom.PiP; +public class Receiver extends BroadcastReceiver { -public class PiPReceiver extends BroadcastReceiver { + private final Listener listener; - private final ActivityDetailBinding binding; - - public PiPReceiver(ActivityDetailBinding binding) { - this.binding = binding; + public Receiver(Listener listener) { + this.listener = listener; } public void register(Activity activity) { @@ -35,14 +32,23 @@ public void onReceive(Context context, Intent intent) { switch (controlType) { case PiP.CONTROL_TYPE_PLAY: case PiP.CONTROL_TYPE_PAUSE: - binding.control.play.performClick(); + listener.onControlPlay(); break; case PiP.CONTROL_TYPE_NEXT: - binding.control.next.performClick(); + listener.onControlNext(); break; case PiP.CONTROL_TYPE_PREV: - binding.control.prev.performClick(); + listener.onControlPrev(); break; } } + + public interface Listener { + + void onControlPlay(); + + void onControlNext(); + + void onControlPrev(); + } } diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/CollectActivity.java b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/CollectActivity.java index dbd7109973..376ad2d99d 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/CollectActivity.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/CollectActivity.java @@ -174,7 +174,7 @@ private void search() { mBinding.view.setVisibility(View.VISIBLE); mBinding.result.setVisibility(View.VISIBLE); if (mExecutor != null) mExecutor.shutdownNow(); - mExecutor = new PauseThreadPoolExecutor(Constant.THREAD_POOL, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); + mExecutor = new PauseThreadPoolExecutor(Constant.THREAD_POOL * 2, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); String keyword = mBinding.keyword.getText().toString().trim(); for (Site site : mSites) mExecutor.execute(() -> search(site, keyword)); App.post(() -> mRecordAdapter.add(keyword), 250); diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/DetailActivity.java b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/DetailActivity.java index 333f5fbc0e..f4170c3dbb 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/DetailActivity.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/DetailActivity.java @@ -3,6 +3,7 @@ import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; +import android.app.Dialog; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.res.Configuration; @@ -42,9 +43,10 @@ import com.fongmi.android.tv.event.PlayerEvent; import com.fongmi.android.tv.event.RefreshEvent; import com.fongmi.android.tv.model.SiteViewModel; +import com.fongmi.android.tv.pip.PiP; +import com.fongmi.android.tv.pip.Receiver; import com.fongmi.android.tv.player.ExoUtil; import com.fongmi.android.tv.player.Players; -import com.fongmi.android.tv.receiver.PiPReceiver; import com.fongmi.android.tv.ui.adapter.EpisodeAdapter; import com.fongmi.android.tv.ui.adapter.FlagAdapter; import com.fongmi.android.tv.ui.adapter.ParseAdapter; @@ -54,11 +56,11 @@ import com.fongmi.android.tv.ui.custom.SpaceItemDecoration; import com.fongmi.android.tv.ui.custom.ViewType; import com.fongmi.android.tv.ui.custom.dialog.ControlDialog; -import com.fongmi.android.tv.ui.custom.dialog.EpisodeDialog; +import com.fongmi.android.tv.ui.custom.dialog.EpisodeGridDialog; +import com.fongmi.android.tv.ui.custom.dialog.EpisodeListDialog; import com.fongmi.android.tv.ui.custom.dialog.TrackDialog; import com.fongmi.android.tv.utils.Clock; import com.fongmi.android.tv.utils.Notify; -import com.fongmi.android.tv.ui.custom.PiP; import com.fongmi.android.tv.utils.Prefers; import com.fongmi.android.tv.utils.ResUtil; import com.fongmi.android.tv.utils.Traffic; @@ -70,6 +72,7 @@ import org.greenrobot.eventbus.ThreadMode; import java.io.File; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.ExecutorService; @@ -77,7 +80,7 @@ import tv.danmaku.ijk.media.player.ui.IjkVideoView; -public class DetailActivity extends BaseActivity implements CustomKeyDownVod.Listener, TrackDialog.Listener, ControlDialog.Listener, Clock.Callback, FlagAdapter.OnClickListener, EpisodeAdapter.OnClickListener, ParseAdapter.OnClickListener { +public class DetailActivity extends BaseActivity implements Clock.Callback, CustomKeyDownVod.Listener, Receiver.Listener, TrackDialog.Listener, ControlDialog.Listener, FlagAdapter.OnClickListener, EpisodeAdapter.OnClickListener, ParseAdapter.OnClickListener { private ViewGroup.LayoutParams mFrameParams; private ActivityDetailBinding mBinding; @@ -89,7 +92,8 @@ public class DetailActivity extends BaseActivity implements CustomKeyDownVod.Lis private ExecutorService mExecutor; private SiteViewModel mViewModel; private FlagAdapter mFlagAdapter; - private PiPReceiver mReceiver; + private List mDialogs; + private Receiver mReceiver; private History mHistory; private Players mPlayers; private boolean fullscreen; @@ -104,6 +108,7 @@ public class DetailActivity extends BaseActivity implements CustomKeyDownVod.Lis private Runnable mR2; private Runnable mR3; private String mKey; + private String url; private PiP mPiP; public static void file(FragmentActivity activity, String url) { @@ -112,6 +117,10 @@ public static void file(FragmentActivity activity, String url) { else PermissionX.init(activity).permissions(Manifest.permission.WRITE_EXTERNAL_STORAGE).request((allGranted, grantedList, deniedList) -> start(activity, "push_agent", "file://" + url, name)); } + public static void cast(Activity activity, History history) { + start(activity, history.getSiteKey(), history.getVodId(), history.getVodName()); + } + public static void push(Activity activity, String url) { start(activity, "push_agent", url, url); } @@ -141,7 +150,7 @@ private String getId() { } private String getHistoryKey() { - return getKey().concat(AppDatabase.SYMBOL).concat(getId()); + return getKey().concat(AppDatabase.SYMBOL).concat(getId()).concat(AppDatabase.SYMBOL) + ApiConfig.getCid(); } private Site getSite() { @@ -196,8 +205,9 @@ protected void initView(Bundle savedInstanceState) { mKeyDown = CustomKeyDownVod.create(this, mBinding.video); mFrameParams = mBinding.video.getLayoutParams(); mBinding.progressLayout.showProgress(); - mReceiver = new PiPReceiver(mBinding); mPlayers = new Players().init(); + mReceiver = new Receiver(this); + mDialogs = new ArrayList<>(); mR1 = this::hideControl; mR2 = this::setTraffic; mR3 = this::setOrient; @@ -237,6 +247,7 @@ protected void initEvent() { mBinding.control.action.decode.setOnClickListener(view -> onDecode()); mBinding.control.action.ending.setOnClickListener(view -> onEnding()); mBinding.control.action.opening.setOnClickListener(view -> onOpening()); + mBinding.control.action.episodes.setOnClickListener(view -> onEpisodes()); mBinding.control.action.speed.setOnLongClickListener(view -> onSpeedLong()); mBinding.control.action.ending.setOnLongClickListener(view -> onEndingReset()); mBinding.control.action.opening.setOnLongClickListener(view -> onOpeningReset()); @@ -328,6 +339,7 @@ private void getPlayer(Vod.Flag flag, Vod.Flag.Episode episode, boolean replay) mViewModel.playerContent(getKey(), flag.getFlag(), episode.getUrl()); updateHistory(episode, replay); showProgress(); + setUrl(null); } private void setEmpty() { @@ -349,6 +361,7 @@ private void setDetail(Vod item) { setText(mBinding.actor, R.string.detail_actor, Html.fromHtml(item.getVodActor()).toString()); setText(mBinding.content, 0, Html.fromHtml(item.getVodContent()).toString()); setText(mBinding.director, R.string.detail_director, Html.fromHtml(item.getVodDirector()).toString()); + mBinding.contentLayout.setVisibility(mBinding.content.getVisibility()); mFlagAdapter.addAll(item.getVodFlags()); setOther(mBinding.other, item); checkFlag(item); @@ -371,12 +384,12 @@ private void setOther(TextView view, Vod item) { } @Override - public void onItemClick(Vod.Flag item) { + public void onItemClick(Vod.Flag item, boolean force) { if (item.isActivated()) return; mFlagAdapter.setActivated(item); mBinding.flag.scrollToPosition(mFlagAdapter.getPosition()); setEpisodeAdapter(item.getEpisodes()); - seamless(item); + seamless(item, force); } @Override @@ -396,6 +409,7 @@ public void onItemClick(Parse item) { } private void setEpisodeAdapter(List items) { + mBinding.control.action.episodes.setVisibility(items.size() < 2 ? View.GONE : View.VISIBLE); mBinding.control.nextRoot.setVisibility(items.size() < 2 ? View.GONE : View.VISIBLE); mBinding.control.prevRoot.setVisibility(items.size() < 2 ? View.GONE : View.VISIBLE); mBinding.episode.setVisibility(items.isEmpty() ? View.GONE : View.VISIBLE); @@ -404,7 +418,8 @@ private void setEpisodeAdapter(List items) { mEpisodeAdapter.addAll(items); } - private void seamless(Vod.Flag flag) { + private void seamless(Vod.Flag flag, boolean force) { + if (!force && !getSite().isChangeable()) return; Vod.Flag.Episode episode = flag.find(mHistory.getVodRemarks()); if (episode == null || episode.isActivated()) return; mHistory.setVodRemarks(episode.getName()); @@ -425,7 +440,7 @@ private void onName() { private void onMore() { for (Fragment fragment : getSupportFragmentManager().getFragments()) if (fragment instanceof BottomSheetDialogFragment) return; - EpisodeDialog.create().reverse(mHistory.isRevSort()).episodes(mEpisodeAdapter.getItems()).show(getSupportFragmentManager(), null); + EpisodeGridDialog.create().reverse(mHistory.isRevSort()).episodes(mEpisodeAdapter.getItems()).show(getSupportFragmentManager(), null); } private void onActor() { @@ -464,7 +479,7 @@ private void onLock() { } private void onShare() { - new ShareCompat.IntentBuilder(this).setType("text/plain").setText(getId()).startChooser(); + new ShareCompat.IntentBuilder(this).setType("text/plain").setText(getUrl()).startChooser(); } private void checkPlay() { @@ -525,9 +540,9 @@ private void onScale() { } private void onSpeed() { - setR1Callback(); mBinding.control.action.speed.setText(mPlayers.addSpeed()); mHistory.setSpeed(mPlayers.getSpeed()); + setR1Callback(); } private boolean onSpeedLong() { @@ -593,6 +608,10 @@ private boolean onOpeningReset() { return true; } + private void onEpisodes() { + mDialogs.add(EpisodeListDialog.create(this).episodes(mEpisodeAdapter.getItems()).show()); + } + private boolean onActionTouch(View v, MotionEvent e) { setR1Callback(); return false; @@ -653,23 +672,24 @@ private void hideProgress() { } private void showError(String text) { - mBinding.widget.text.setText(text); mBinding.widget.error.setVisibility(View.VISIBLE); + mBinding.widget.text.setText(text); + hideProgress(); } private void hideError() { - mBinding.widget.text.setText(""); mBinding.widget.error.setVisibility(View.GONE); + mBinding.widget.text.setText(""); } private void showControl() { + mBinding.control.share.setVisibility(getUrl() == null || isFullscreen() ? View.GONE : View.VISIBLE); + mBinding.control.keep.setVisibility(mHistory == null || isFullscreen() ? View.GONE : View.VISIBLE); mBinding.control.parse.setVisibility(isFullscreen() && isUseParse() ? View.VISIBLE : View.GONE); mBinding.control.rotate.setVisibility(isFullscreen() && !isLock() ? View.VISIBLE : View.GONE); mBinding.control.back.setVisibility(isFullscreen() && !isLock() ? View.VISIBLE : View.GONE); mBinding.control.action.getRoot().setVisibility(isFullscreen() ? View.VISIBLE : View.GONE); mBinding.control.setting.setVisibility(isFullscreen() ? View.GONE : View.VISIBLE); - mBinding.control.share.setVisibility(isFullscreen() ? View.GONE : View.VISIBLE); - mBinding.control.keep.setVisibility(isFullscreen() ? View.GONE : View.VISIBLE); mBinding.control.lock.setVisibility(isFullscreen() ? View.VISIBLE : View.GONE); mBinding.control.center.setVisibility(isLock() ? View.GONE : View.VISIBLE); mBinding.control.bottom.setVisibility(isLock() ? View.GONE : View.VISIBLE); @@ -685,7 +705,9 @@ private void hideControl() { } private void hideSheet() { + for (Dialog dialog : mDialogs) dialog.dismiss(); for (Fragment fragment : getSupportFragmentManager().getFragments()) if (fragment instanceof BottomSheetDialogFragment) ((BottomSheetDialogFragment) fragment).dismiss(); + mDialogs.clear(); } private void setTraffic() { @@ -710,7 +732,7 @@ private void checkFlag(Vod item) { private void checkHistory(Vod item) { mHistory = History.find(getHistoryKey()); mHistory = mHistory == null ? createHistory(item) : mHistory; - onItemClick(mHistory.getFlag()); + onItemClick(mHistory.getFlag(), true); if (mHistory.isRevSort()) reverseEpisode(true); mBinding.control.action.opening.setText(mHistory.getOpening() == 0 ? getString(R.string.play_op) : mPlayers.stringToTime(mHistory.getOpening())); mBinding.control.action.ending.setText(mHistory.getEnding() == 0 ? getString(R.string.play_ed) : mPlayers.stringToTime(mHistory.getEnding())); @@ -787,6 +809,7 @@ public void onPlayerEvent(PlayerEvent event) { switch (event.getState()) { case 0: checkPosition(); + setUrl(event.getUrl()); setTrackVisible(false); break; case Player.STATE_IDLE: @@ -847,7 +870,6 @@ private void onError(ErrorEvent event) { Clock.get().setCallback(null); showError(event.getMsg()); mPlayers.stop(); - hideProgress(); startFlow(); } @@ -939,7 +961,7 @@ private void nextParse(int position) { private void nextFlag(int position) { Vod.Flag flag = mFlagAdapter.get(position + 1); Notify.show(getString(R.string.play_switch_flag, flag.getFlag())); - onItemClick(flag); + onItemClick(flag, true); } private void nextSite() { @@ -1016,6 +1038,14 @@ public void setLock(boolean lock) { this.lock = lock; } + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + private void notifyItemChanged(RecyclerView.Adapter adapter) { adapter.notifyItemRangeChanged(0, adapter.getItemCount()); } @@ -1098,7 +1128,7 @@ public void onSingleTap() { @Override public void onDoubleTap() { if (!isFullscreen()) { - enterFullscreen(); + App.post(this::enterFullscreen, 250); } else if (mPlayers.isPlaying()) { mPlayers.pause(); showControl(); @@ -1108,6 +1138,21 @@ public void onDoubleTap() { } } + @Override + public void onControlPlay() { + mBinding.control.play.performClick(); + } + + @Override + public void onControlNext() { + mBinding.control.next.performClick(); + } + + @Override + public void onControlPrev() { + mBinding.control.prev.performClick(); + } + @Override protected void onUserLeaveHint() { super.onUserLeaveHint(); diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/KeepActivity.java b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/KeepActivity.java index 144c21b5e3..3e00dae715 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/KeepActivity.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/KeepActivity.java @@ -18,6 +18,7 @@ import com.fongmi.android.tv.net.Callback; import com.fongmi.android.tv.ui.adapter.KeepAdapter; import com.fongmi.android.tv.ui.base.BaseActivity; +import com.fongmi.android.tv.utils.Notify; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import org.greenrobot.eventbus.Subscribe; @@ -76,12 +77,13 @@ private void loadConfig(Config config, Keep item) { @Override public void success() { DetailActivity.start(getActivity(), item.getSiteKey(), item.getVodId(), item.getVodName()); + RefreshEvent.config(); RefreshEvent.video(); } @Override public void error(int resId) { - CollectActivity.start(getActivity(), item.getVodName()); + Notify.show(resId); } }); } diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/MainActivity.java b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/MainActivity.java index 01d4a86a2d..c202414a1a 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/MainActivity.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/MainActivity.java @@ -1,6 +1,7 @@ package com.fongmi.android.tv.ui.activity; import android.content.Intent; +import android.content.res.Configuration; import android.os.Bundle; import android.view.MenuItem; @@ -10,19 +11,19 @@ import com.fongmi.android.tv.App; import com.fongmi.android.tv.R; +import com.fongmi.android.tv.Updater; import com.fongmi.android.tv.api.ApiConfig; import com.fongmi.android.tv.api.LiveConfig; -import com.fongmi.android.tv.api.Updater; import com.fongmi.android.tv.api.WallConfig; import com.fongmi.android.tv.databinding.ActivityMainBinding; import com.fongmi.android.tv.event.RefreshEvent; import com.fongmi.android.tv.net.Callback; import com.fongmi.android.tv.server.Server; import com.fongmi.android.tv.ui.base.BaseActivity; +import com.fongmi.android.tv.ui.custom.FileChooser; import com.fongmi.android.tv.ui.custom.FragmentStateManager; import com.fongmi.android.tv.ui.fragment.SettingFragment; import com.fongmi.android.tv.ui.fragment.VodFragment; -import com.fongmi.android.tv.ui.custom.FileChooser; import com.fongmi.android.tv.utils.Notify; import com.google.android.material.navigation.NavigationBarView; @@ -46,7 +47,6 @@ protected void onNewIntent(Intent intent) { @Override protected void initView(Bundle savedInstanceState) { initFragment(savedInstanceState); - Notify.progress(this); Updater.get().start(); Server.get().start(); initConfig(); @@ -87,13 +87,15 @@ private Callback getCallback() { @Override public void success() { checkAction(getIntent()); + RefreshEvent.config(); RefreshEvent.video(); } @Override public void error(int resId) { + RefreshEvent.config(); + RefreshEvent.empty(); Notify.show(resId); - Notify.dismiss(); } }; } @@ -104,6 +106,14 @@ private void setConfirm() { App.post(() -> confirm = false, 2000); } + @Override + public void onRefreshEvent(RefreshEvent event) { + super.onRefreshEvent(event); + if (!event.getType().equals(RefreshEvent.Type.CONFIG)) return; + mBinding.navigation.getMenu().findItem(R.id.vod).setVisible(true); + mBinding.navigation.getMenu().findItem(R.id.setting).setVisible(true); + } + @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { if (mBinding.navigation.getSelectedItemId() == item.getItemId()) return false; @@ -112,6 +122,12 @@ public boolean onNavigationItemSelected(@NonNull MenuItem item) { return false; } + @Override + public void onConfigurationChanged(@NonNull Configuration newConfig) { + super.onConfigurationChanged(newConfig); + RefreshEvent.video(); + } + @Override public void onBackPressed() { if (mManager.isVisible(1)) { diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/ScanActivity.java b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/ScanActivity.java new file mode 100644 index 0000000000..af30916dd6 --- /dev/null +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/ScanActivity.java @@ -0,0 +1,97 @@ +package com.fongmi.android.tv.ui.activity; + +import android.app.Activity; +import android.content.Intent; +import android.content.res.Configuration; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.viewbinding.ViewBinding; + +import com.fongmi.android.tv.cast.ScanEvent; +import com.fongmi.android.tv.databinding.ActivityScanBinding; +import com.fongmi.android.tv.ui.base.BaseActivity; +import com.fongmi.android.tv.utils.Utils; +import com.google.zxing.BarcodeFormat; +import com.google.zxing.ResultPoint; +import com.journeyapps.barcodescanner.BarcodeCallback; +import com.journeyapps.barcodescanner.BarcodeResult; +import com.journeyapps.barcodescanner.CaptureManager; +import com.journeyapps.barcodescanner.DefaultDecoderFactory; + +import java.util.List; + +public class ScanActivity extends BaseActivity implements BarcodeCallback { + + private ActivityScanBinding mBinding; + private CaptureManager mCapture; + + public static void start(Activity activity) { + activity.startActivity(new Intent(activity, ScanActivity.class)); + } + + @Override + protected ViewBinding getBinding() { + return mBinding = ActivityScanBinding.inflate(getLayoutInflater()); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Utils.hideSystemUI(this); + } + + @Override + protected void initView(Bundle savedInstanceState) { + mCapture = new CaptureManager(this, mBinding.scanner); + mBinding.scanner.getBarcodeView().setDecoderFactory(new DefaultDecoderFactory(List.of(BarcodeFormat.QR_CODE))); + } + + @Override + public void possibleResultPoints(List resultPoints) { + } + + @Override + public void barcodeResult(BarcodeResult result) { + if (!result.getText().startsWith("http")) return; + ScanEvent.post(result.getText()); + finish(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + mCapture.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + + @Override + public void onConfigurationChanged(@NonNull Configuration newConfig) { + super.onConfigurationChanged(newConfig); + Utils.hideSystemUI(this); + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + if (hasFocus) Utils.hideSystemUI(this); + } + + @Override + protected void onResume() { + super.onResume(); + mCapture.onResume(); + mBinding.scanner.decodeSingle(this); + } + + @Override + protected void onPause() { + super.onPause(); + mCapture.onPause(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mCapture.onDestroy(); + } +} diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/ChannelAdapter.java b/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/ChannelAdapter.java new file mode 100644 index 0000000000..a74a365bf2 --- /dev/null +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/ChannelAdapter.java @@ -0,0 +1,92 @@ +package com.fongmi.android.tv.ui.adapter; + +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.fongmi.android.tv.bean.Channel; +import com.fongmi.android.tv.databinding.AdapterChannelBinding; + +import java.util.ArrayList; +import java.util.List; + +public class ChannelAdapter extends RecyclerView.Adapter { + + private final OnClickListener mListener; + private final List mItems; + + public ChannelAdapter(OnClickListener listener) { + this.mListener = listener; + this.mItems = new ArrayList<>(); + } + + public interface OnClickListener { + + void onItemClick(Channel item); + + boolean onLongClick(Channel item); + } + + public void addAll(List items) { + mItems.clear(); + mItems.addAll(items); + notifyDataSetChanged(); + } + + public void remove(Channel item) { + int position = mItems.indexOf(item); + if (position == -1) return; + mItems.remove(position); + notifyItemRemoved(position); + } + + public int getPosition() { + for (int i = 0; i < mItems.size(); i++) if (mItems.get(i).isSelected()) return i; + return 0; + } + + public void setSelected(int position) { + for (int i = 0; i < mItems.size(); i++) mItems.get(i).setSelected(i == position); + notifyItemRangeChanged(0, getItemCount()); + } + + public int setSelected(Channel channel) { + int position = mItems.indexOf(channel); + setSelected(position); + return position; + } + + @Override + public int getItemCount() { + return mItems.size(); + } + + @NonNull + @Override + public ChannelAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(AdapterChannelBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull ChannelAdapter.ViewHolder holder, int position) { + Channel item = mItems.get(position); + item.loadLogo(holder.binding.logo); + holder.binding.name.setText(item.getName()); + holder.binding.number.setText(item.getNumber()); + holder.binding.getRoot().setSelected(item.isSelected()); + holder.binding.getRoot().setOnClickListener(view -> mListener.onItemClick(item)); + holder.binding.getRoot().setOnLongClickListener(view -> mListener.onLongClick(item)); + } + + static class ViewHolder extends RecyclerView.ViewHolder { + + private final AdapterChannelBinding binding; + + ViewHolder(@NonNull AdapterChannelBinding binding) { + super(binding.getRoot()); + this.binding = binding; + } + } +} \ No newline at end of file diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/DeviceAdapter.java b/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/DeviceAdapter.java new file mode 100644 index 0000000000..63d00e7425 --- /dev/null +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/DeviceAdapter.java @@ -0,0 +1,85 @@ +package com.fongmi.android.tv.ui.adapter; + +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.fongmi.android.tv.R; +import com.fongmi.android.tv.bean.Device; +import com.fongmi.android.tv.databinding.AdapterDeviceBinding; + +import java.util.ArrayList; +import java.util.List; + +public class DeviceAdapter extends RecyclerView.Adapter { + + private final OnClickListener mListener; + private final List mItems; + + public DeviceAdapter(OnClickListener listener) { + this.mItems = new ArrayList<>(); + this.mListener = listener; + } + + public interface OnClickListener { + + void onItemClick(Device item); + } + + public void addAll(List items) { + if (items == null) return; + mItems.removeAll(items); + mItems.addAll(items); + notifyDataSetChanged(); + } + + public void remove(Device item) { + if (item == null) return; + mItems.remove(item); + notifyDataSetChanged(); + } + + public void clear() { + mItems.clear(); + Device.delete(); + notifyDataSetChanged(); + } + + public List getIps() { + List ips = new ArrayList<>(); + for (Device item : mItems) if (!item.isDLNA()) ips.add(item.getIp()); + return ips; + } + + @Override + public int getItemCount() { + return mItems.size(); + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(AdapterDeviceBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + Device item = mItems.get(position); + holder.binding.name.setText(item.getName()); + holder.binding.host.setText(item.getHost()); + holder.binding.getRoot().setOnClickListener(v -> mListener.onItemClick(item)); + holder.binding.type.setImageResource(item.isMobile() ? R.drawable.ic_cast_mobile : R.drawable.ic_cast_tv); + } + + static class ViewHolder extends RecyclerView.ViewHolder { + + private final AdapterDeviceBinding binding; + + ViewHolder(@NonNull AdapterDeviceBinding binding) { + super(binding.getRoot()); + this.binding = binding; + } + } +} diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/FlagAdapter.java b/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/FlagAdapter.java index f74fb64bf4..4467c13719 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/FlagAdapter.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/FlagAdapter.java @@ -25,7 +25,7 @@ public FlagAdapter(OnClickListener listener) { public interface OnClickListener { - void onItemClick(Vod.Flag item); + void onItemClick(Vod.Flag item, boolean force); } public void addAll(List items) { @@ -76,7 +76,7 @@ public void onBindViewHolder(@NonNull ViewHolder holder, int position) { Vod.Flag item = mItems.get(position); holder.binding.text.setText(item.getFlag()); holder.binding.text.setActivated(item.isActivated()); - holder.binding.text.setOnClickListener(v -> mListener.onItemClick(item)); + holder.binding.text.setOnClickListener(v -> mListener.onItemClick(item, false)); } static class ViewHolder extends RecyclerView.ViewHolder { diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/GroupAdapter.java b/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/GroupAdapter.java new file mode 100644 index 0000000000..98a6c4adcc --- /dev/null +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/GroupAdapter.java @@ -0,0 +1,93 @@ +package com.fongmi.android.tv.ui.adapter; + +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.fongmi.android.tv.bean.Group; +import com.fongmi.android.tv.databinding.AdapterGroupBinding; + +import java.util.ArrayList; +import java.util.List; + +public class GroupAdapter extends RecyclerView.Adapter { + + private final OnClickListener mListener; + private final List mItems; + + public GroupAdapter(OnClickListener listener) { + this.mListener = listener; + this.mItems = new ArrayList<>(); + } + + public interface OnClickListener { + + void onItemClick(Group item); + } + + public void addAll(List items) { + mItems.clear(); + mItems.addAll(items); + notifyDataSetChanged(); + } + + public void add(Group item) { + mItems.add(item); + notifyItemInserted(getItemCount() - 1); + } + + public Group get(int position) { + return mItems.get(position); + } + + public int getPosition() { + for (int i = 0; i < mItems.size(); i++) if (mItems.get(i).isSelected()) return i; + return 0; + } + + public int indexOf(Group group) { + return mItems.indexOf(group); + } + + public void setSelected(int position) { + for (int i = 0; i < mItems.size(); i++) mItems.get(i).setSelected(i == position); + notifyItemRangeChanged(0, getItemCount()); + } + + public void setSelected(Group group) { + int position = mItems.indexOf(group); + setSelected(position); + } + + @Override + public int getItemCount() { + return mItems.size(); + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(AdapterGroupBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + Group item = mItems.get(position); + item.loadLogo(holder.binding.logo); + holder.binding.name.setText(item.getName()); + holder.binding.getRoot().setSelected(item.isSelected()); + holder.binding.getRoot().setOnClickListener(view -> mListener.onItemClick(item)); + } + + static class ViewHolder extends RecyclerView.ViewHolder { + + private final AdapterGroupBinding binding; + + ViewHolder(@NonNull AdapterGroupBinding binding) { + super(binding.getRoot()); + this.binding = binding; + } + } +} diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/HistoryAdapter.java b/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/HistoryAdapter.java index 42ba884c24..8a0a3ec931 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/HistoryAdapter.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/HistoryAdapter.java @@ -66,10 +66,10 @@ public void clear() { } public void remove(History item) { - int position = mItems.indexOf(item); - if (position == -1) return; - mItems.remove(position); - notifyItemRemoved(position); + int index = mItems.indexOf(item); + if (index == -1) return; + mItems.remove(index); + notifyItemRemoved(index); } @Override @@ -90,11 +90,11 @@ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { public void onBindViewHolder(@NonNull ViewHolder holder, int position) { History item = mItems.get(position); holder.binding.name.setText(item.getVodName()); - holder.binding.site.setVisibility(View.VISIBLE); - holder.binding.site.setText(ApiConfig.getSiteName(item.getSiteKey())); - holder.binding.remark.setText(ResUtil.getString(R.string.vod_last, item.getVodRemarks())); + holder.binding.site.setText(item.getSiteName()); + holder.binding.site.setVisibility(item.getSiteVisible()); holder.binding.remark.setVisibility(delete ? View.GONE : View.VISIBLE); holder.binding.delete.setVisibility(!delete ? View.GONE : View.VISIBLE); + holder.binding.remark.setText(ResUtil.getString(R.string.vod_last, item.getVodRemarks())); ImgUtil.loadHistory(item.getVodPic(), holder.binding.image); setClickListener(holder.binding.getRoot(), item); } diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/base/BaseActivity.java b/app/src/mobile/java/com/fongmi/android/tv/ui/base/BaseActivity.java index 0f5c92e635..6d50a337de 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/base/BaseActivity.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/base/BaseActivity.java @@ -39,6 +39,10 @@ protected Activity getActivity() { return this; } + protected boolean customWall() { + return true; + } + protected void initView(Bundle savedInstanceState) { } @@ -55,6 +59,7 @@ protected boolean isGone(View view) { private void setWall() { try { + if (!customWall()) return; File file = FileUtil.getWall(Prefers.getWall()); if (file.exists() && file.length() > 0) getWindow().setBackgroundDrawable(WallConfig.drawable(Drawable.createFromPath(file.getAbsolutePath()))); else getWindow().setBackgroundDrawableResource(ResUtil.getDrawable(file.getName())); diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/custom/CustomKeyDownLive.java b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/CustomKeyDownLive.java new file mode 100644 index 0000000000..560d7389bb --- /dev/null +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/CustomKeyDownLive.java @@ -0,0 +1,174 @@ +package com.fongmi.android.tv.ui.custom; + +import android.app.Activity; +import android.content.Context; +import android.media.AudioManager; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; +import android.view.WindowManager; + +import androidx.annotation.NonNull; + +import com.fongmi.android.tv.App; +import com.fongmi.android.tv.utils.ResUtil; + +public class CustomKeyDownLive extends GestureDetector.SimpleOnGestureListener { + + private static final int DISTANCE = 100; + private static final int VELOCITY = 10; + + private final GestureDetector detector; + private final AudioManager manager; + private final Listener listener; + private final Activity activity; + private final View videoView; + private boolean changeBright; + private boolean changeVolume; + private boolean center; + private boolean touch; + private boolean lock; + private float bright; + private float volume; + + public static CustomKeyDownLive create(Activity activity, View videoView) { + return new CustomKeyDownLive(activity, videoView); + } + + private CustomKeyDownLive(Activity activity, View videoView) { + this.manager = (AudioManager) App.get().getSystemService(Context.AUDIO_SERVICE); + this.detector = new GestureDetector(activity, this); + this.listener = (Listener) activity; + this.videoView = videoView; + this.activity = activity; + } + + public boolean onTouchEvent(MotionEvent e) { + if (changeBright && e.getAction() == MotionEvent.ACTION_UP) listener.onBrightEnd(); + if (changeVolume && e.getAction() == MotionEvent.ACTION_UP) listener.onVolumeEnd(); + return detector.onTouchEvent(e); + } + + public void setLock(boolean lock) { + this.lock = lock; + } + + private boolean isEdge(MotionEvent e) { + return ResUtil.isEdge(e, ResUtil.dp2px(16)); + } + + @Override + public boolean onDown(@NonNull MotionEvent e) { + if (isEdge(e) || lock) return true; + volume = manager.getStreamVolume(AudioManager.STREAM_MUSIC); + bright = activity.getWindow().getAttributes().screenBrightness; + changeBright = false; + changeVolume = false; + center = false; + touch = true; + return true; + } + + @Override + public boolean onScroll(@NonNull MotionEvent e1, @NonNull MotionEvent e2, float distanceX, float distanceY) { + if (isEdge(e1) || lock) return true; + float deltaY = e1.getY() - e2.getY(); + if (touch) checkFunc(distanceX, distanceY, e2); + if (changeBright) setBright(deltaY); + if (changeVolume) setVolume(deltaY); + return true; + } + + @Override + public boolean onDoubleTap(@NonNull MotionEvent e) { + listener.onDoubleTap(); + return true; + } + + @Override + public boolean onSingleTapConfirmed(@NonNull MotionEvent e) { + if (lock) return true; + int half = ResUtil.getScreenWidthNav() / 2; + if (e.getX() > half) listener.onDoubleTap(); + else listener.onSingleTap(); + return true; + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + if (isEdge(e1) || !center) return true; + checkFunc(e1, e2, velocityX, velocityY); + return true; + } + + private void checkFunc(float distanceX, float distanceY, MotionEvent e2) { + int four = ResUtil.getScreenWidthNav() / 4; + if (e2.getX() > four && e2.getX() < four * 3) center = true; + else if (Math.abs(distanceX) < Math.abs(distanceY)) checkSide(e2); + touch = false; + } + + private void checkFunc(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + if (e1.getX() - e2.getX() > DISTANCE && Math.abs(velocityX) > VELOCITY) { + listener.onFlingLeft(); + } else if (e2.getX() - e1.getX() > DISTANCE && Math.abs(velocityX) > VELOCITY) { + listener.onFlingRight(); + } else if (e1.getY() - e2.getY() > DISTANCE && Math.abs(velocityY) > VELOCITY) { + listener.onFlingUp(); + } else if (e2.getY() - e1.getY() > DISTANCE && Math.abs(velocityY) > VELOCITY) { + listener.onFlingDown(); + } + } + + private void checkSide(MotionEvent e2) { + int half = ResUtil.getScreenWidthNav() / 2; + if (e2.getX() > half) changeVolume = true; + else changeBright = true; + } + + private void setBright(float deltaY) { + int height = videoView.getMeasuredHeight(); + if (bright == -1.0f) bright = 0.5f; + float brightness = deltaY * 2 / height + bright; + if (brightness < 0) brightness = 0f; + if (brightness > 1.0f) brightness = 1.0f; + WindowManager.LayoutParams attributes = activity.getWindow().getAttributes(); + attributes.screenBrightness = brightness; + activity.getWindow().setAttributes(attributes); + listener.onBright((int) (brightness * 100)); + } + + private void setVolume(float deltaY) { + int height = videoView.getMeasuredHeight(); + int maxVolume = manager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + float deltaV = deltaY * 2 / height * maxVolume; + float index = volume + deltaV; + if (index > maxVolume) index = maxVolume; + if (index < 0) index = 0; + manager.setStreamVolume(AudioManager.STREAM_MUSIC, (int) index, 0); + listener.onVolume((int) (index / maxVolume * 100)); + } + + public interface Listener { + + void onBright(int progress); + + void onBrightEnd(); + + void onVolume(int progress); + + void onVolumeEnd(); + + void onFlingUp(); + + void onFlingDown(); + + void onFlingLeft(); + + void onFlingRight(); + + void onSingleTap(); + + void onDoubleTap(); + } +} diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/custom/CustomKeyDownVod.java b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/CustomKeyDownVod.java index b41d7363e2..a9c2d8c0b2 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/custom/CustomKeyDownVod.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/CustomKeyDownVod.java @@ -11,7 +11,6 @@ import androidx.annotation.NonNull; import com.fongmi.android.tv.App; -import com.fongmi.android.tv.Constant; import com.fongmi.android.tv.utils.ResUtil; public class CustomKeyDownVod extends GestureDetector.SimpleOnGestureListener { @@ -19,7 +18,6 @@ public class CustomKeyDownVod extends GestureDetector.SimpleOnGestureListener { private final GestureDetector detector; private final AudioManager manager; private final Listener listener; - private final Runnable runnable; private final Activity activity; private final View videoView; private boolean changeBright; @@ -40,7 +38,6 @@ private CustomKeyDownVod(Activity activity, View videoView) { this.manager = (AudioManager) App.get().getSystemService(Context.AUDIO_SERVICE); this.detector = new GestureDetector(activity, this); this.listener = (Listener) activity; - this.runnable = this::subTime; this.videoView = videoView; this.activity = activity; } @@ -105,20 +102,7 @@ public boolean onSingleTapConfirmed(@NonNull MotionEvent e) { return true; } - private void subTime() { - listener.onSeek(time = time - Constant.INTERVAL_SEEK); - App.post(runnable, getDelay()); - } - - private int getDelay() { - int count = Math.abs(time) / Constant.INTERVAL_SEEK; - if (count < 5) return 250; - else if (count < 15) return 100; - else return 50; - } - private void onSeekEnd() { - App.removeCallbacks(runnable); listener.onSeekEnd(time); changeTime = false; time = 0; @@ -132,11 +116,8 @@ private void checkFunc(float distanceX, float distanceY, MotionEvent e2) { private void checkSide(MotionEvent e2) { int half = ResUtil.getScreenWidthNav() / 2; - if (e2.getX() > half) { - changeVolume = true; - } else { - changeBright = true; - } + if (e2.getX() > half) changeVolume = true; + else changeBright = true; } private void setBright(float deltaY) { diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/custom/FileChooser.java b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/FileChooser.java index c2027816d2..4077d31ff0 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/custom/FileChooser.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/FileChooser.java @@ -1,11 +1,24 @@ package com.fongmi.android.tv.ui.custom; +import android.content.ContentResolver; +import android.content.ContentUris; import android.content.Context; import android.content.Intent; +import android.database.Cursor; import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.provider.DocumentsContract; +import android.provider.MediaStore; import androidx.fragment.app.Fragment; +import com.fongmi.android.tv.utils.FileUtil; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; + public class FileChooser { public static final int REQUEST_PICK_FILE = 9999; @@ -40,6 +53,131 @@ public void show(String mimeType, int code) { } public static String getPathFromUri(Context context, Uri uri) { - return uri.toString(); + if (uri == null) return null; + String path = null; + if (DocumentsContract.isDocumentUri(context, uri)) path = getPathFromDocumentUri(context, uri); + else if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) path = getDataColumn(context, uri); + else if (ContentResolver.SCHEME_FILE.equalsIgnoreCase(uri.getScheme())) path = uri.getPath(); + return path != null ? path : createFileFromUri(context, uri).getAbsolutePath(); + } + + private static String getPathFromDocumentUri(Context context, Uri uri) { + String docId = DocumentsContract.getDocumentId(uri); + String[] split = docId.split(":"); + if (isExternalStorageDocument(uri)) return getPath(docId, split); + else if (isDownloadsDocument(uri)) return getPath(context, uri, docId); + else if (isMediaDocument(uri)) return getPath(context, split); + else return null; + } + + private static String getPath(String docId, String[] split) { + if ("primary".equalsIgnoreCase(split[0])) { + return split.length > 1 ? Environment.getExternalStorageDirectory() + "/" + split[1] : Environment.getExternalStorageDirectory() + "/"; + } else { + return "/storage/" + docId.replace(":", "/"); + } + } + + private static String getPath(Context context, Uri uri, String docId) { + String fileName = getNameColumn(context, uri); + if (docId.startsWith("raw:")) { + return docId.replaceFirst("raw:", ""); + } else if (fileName != null) { + return Environment.getExternalStorageDirectory() + "/Download/" + fileName; + } else { + return getDataColumn(context, ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.parseLong(docId))); + } + } + + private static String getPath(Context context, String[] split) { + switch (split[0]) { + case "image": + return getDataColumn(context, ContentUris.withAppendedId(getImageUri(), Long.parseLong(split[1]))); + case "video": + return getDataColumn(context, ContentUris.withAppendedId(getVideoUri(), Long.parseLong(split[1]))); + case "audio": + return getDataColumn(context, ContentUris.withAppendedId(getAudioUri(), Long.parseLong(split[1]))); + default: + return getDataColumn(context, ContentUris.withAppendedId(MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL), Long.parseLong(split[1]))); + } + } + + private static File createFileFromUri(Context context, Uri uri) { + String[] projection = {MediaStore.MediaColumns.DISPLAY_NAME}; + Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null); + try (cursor) { + if (cursor == null || !cursor.moveToFirst()) return null; + InputStream is = context.getContentResolver().openInputStream(uri); + if (is == null) return null; + int count; + byte[] buffer = new byte[4096]; + int column = cursor.getColumnIndexOrThrow(projection[0]); + File file = new File(FileUtil.getCachePath(), cursor.getString(column)); + FileOutputStream os = new FileOutputStream(file); + while ((count = is.read(buffer)) != -1) os.write(buffer, 0, count); + os.close(); + is.close(); + return file; + } catch (Exception e) { + return null; + } + } + + private static String getDataColumn(Context context, Uri uri) { + String[] projection = {MediaStore.MediaColumns.DATA}; + Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null); + try (cursor) { + if (cursor == null || !cursor.moveToFirst()) return null; + return cursor.getString(cursor.getColumnIndexOrThrow(projection[0])); + } catch (Exception e) { + return null; + } + } + + private static String getNameColumn(Context context, Uri uri) { + String[] projection = {MediaStore.MediaColumns.DISPLAY_NAME}; + Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null); + try (cursor) { + if (cursor == null || !cursor.moveToFirst()) return null; + return cursor.getString(cursor.getColumnIndexOrThrow(projection[0])); + } catch (Exception e) { + return null; + } + } + + private static Uri getImageUri() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + return MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL); + } else { + return MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + } + } + + private static Uri getVideoUri() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + return MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL); + } else { + return MediaStore.Video.Media.EXTERNAL_CONTENT_URI; + } + } + + private static Uri getAudioUri() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + return MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL); + } else { + return MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; + } + } + + private static boolean isExternalStorageDocument(Uri uri) { + return "com.android.externalstorage.documents".equals(uri.getAuthority()); + } + + private static boolean isDownloadsDocument(Uri uri) { + return "com.android.providers.downloads.documents".equals(uri.getAuthority()); + } + + private static boolean isMediaDocument(Uri uri) { + return "com.android.providers.media.documents".equals(uri.getAuthority()); } -} +} \ No newline at end of file diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/ConfigDialog.java b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/ConfigDialog.java index 6e3a2c46d6..b33d706d2a 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/ConfigDialog.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/ConfigDialog.java @@ -57,7 +57,6 @@ private void initDialog() { private void initView() { binding.text.setText(url = getUrl()); - binding.text.setSelection(url.length()); binding.input.setEndIconOnClickListener(this::onChoose); } diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/EpisodeDialog.java b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/EpisodeGridDialog.java similarity index 85% rename from app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/EpisodeDialog.java rename to app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/EpisodeGridDialog.java index a072a67510..2daccb840a 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/EpisodeDialog.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/EpisodeGridDialog.java @@ -11,44 +11,44 @@ import androidx.viewpager2.adapter.FragmentStateAdapter; import com.fongmi.android.tv.bean.Vod; -import com.fongmi.android.tv.databinding.DialogEpisodeBinding; +import com.fongmi.android.tv.databinding.DialogEpisodeGridBinding; import com.fongmi.android.tv.ui.fragment.EpisodeFragment; import com.google.android.material.tabs.TabLayoutMediator; import java.util.ArrayList; import java.util.List; -public class EpisodeDialog extends BaseDialog { +public class EpisodeGridDialog extends BaseDialog { - private List episodes; - private DialogEpisodeBinding binding; private final List titles; + private DialogEpisodeGridBinding binding; + private List episodes; private boolean reverse; private int spanCount; private int itemCount; - public static EpisodeDialog create() { - return new EpisodeDialog(); + public static EpisodeGridDialog create() { + return new EpisodeGridDialog(); } - public EpisodeDialog() { + public EpisodeGridDialog() { this.titles = new ArrayList<>(); this.spanCount = 5; } - public EpisodeDialog reverse(boolean reverse) { + public EpisodeGridDialog reverse(boolean reverse) { this.reverse = reverse; return this; } - public EpisodeDialog episodes(List episodes) { + public EpisodeGridDialog episodes(List episodes) { this.episodes = episodes; return this; } @Override protected ViewBinding getBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { - return binding = DialogEpisodeBinding.inflate(inflater, container, false); + return binding = DialogEpisodeGridBinding.inflate(inflater, container, false); } @Override diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/EpisodeListDialog.java b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/EpisodeListDialog.java new file mode 100644 index 0000000000..d347db8b97 --- /dev/null +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/EpisodeListDialog.java @@ -0,0 +1,79 @@ +package com.fongmi.android.tv.ui.custom.dialog; + +import android.view.LayoutInflater; + +import androidx.fragment.app.FragmentActivity; +import androidx.lifecycle.ViewModelProvider; + +import com.fongmi.android.tv.bean.Vod; +import com.fongmi.android.tv.databinding.DialogEpisodeListBinding; +import com.fongmi.android.tv.model.SiteViewModel; +import com.fongmi.android.tv.ui.adapter.EpisodeAdapter; +import com.fongmi.android.tv.ui.custom.ViewType; +import com.fongmi.android.tv.utils.Utils; +import com.google.android.material.sidesheet.SideSheetDialog; + +import java.util.List; + +public class EpisodeListDialog implements EpisodeAdapter.OnClickListener { + + private final FragmentActivity activity; + private DialogEpisodeListBinding binding; + private List episodes; + private SiteViewModel viewModel; + private EpisodeAdapter adapter; + private SideSheetDialog dialog; + + public static EpisodeListDialog create(FragmentActivity activity) { + return new EpisodeListDialog(activity); + } + + public EpisodeListDialog(FragmentActivity activity) { + this.activity = activity; + } + + public EpisodeListDialog episodes(List episodes) { + this.episodes = episodes; + return this; + } + + public SideSheetDialog show() { + initDialog(); + initView(); + return dialog; + } + + private void initDialog() { + binding = DialogEpisodeListBinding.inflate(LayoutInflater.from(activity)); + dialog = new SideSheetDialog(activity); + dialog.setContentView(binding.getRoot()); + dialog.show(); + } + + private void initView() { + Utils.hideSystemUI(dialog.getWindow()); + setRecyclerView(); + setViewModel(); + setEpisode(); + } + + private void setRecyclerView() { + binding.recycler.setHasFixedSize(true); + binding.recycler.setItemAnimator(null); + binding.recycler.setAdapter(adapter = new EpisodeAdapter(this, ViewType.GRID)); + } + + private void setViewModel() { + viewModel = new ViewModelProvider(activity).get(SiteViewModel.class); + } + + private void setEpisode() { + adapter.addAll(episodes); + binding.recycler.scrollToPosition(adapter.getPosition()); + } + + @Override + public void onItemClick(Vod.Flag.Episode item) { + viewModel.setEpisode(item); + } +} diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/LiveDialog.java b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/LiveDialog.java index 05e73e0a99..0fa6ddf8b2 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/LiveDialog.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/LiveDialog.java @@ -1,5 +1,6 @@ package com.fongmi.android.tv.ui.custom.dialog; +import android.app.Activity; import android.view.LayoutInflater; import androidx.appcompat.app.AlertDialog; @@ -15,10 +16,14 @@ public class LiveDialog implements LiveAdapter.OnClickListener { - private final DialogLiveBinding binding; private final LiveCallback callback; - private final AlertDialog dialog; - private final LiveAdapter adapter; + private DialogLiveBinding binding; + private LiveAdapter adapter; + private AlertDialog dialog; + + public static LiveDialog create(Activity activity) { + return new LiveDialog(activity); + } public static LiveDialog create(Fragment fragment) { return new LiveDialog(fragment); @@ -26,8 +31,17 @@ public static LiveDialog create(Fragment fragment) { public LiveDialog(Fragment fragment) { this.callback = (LiveCallback) fragment; - this.binding = DialogLiveBinding.inflate(LayoutInflater.from(fragment.getContext())); - this.dialog = new MaterialAlertDialogBuilder(fragment.getActivity()).setView(binding.getRoot()).create(); + init(fragment.getActivity()); + } + + public LiveDialog(Activity activity) { + this.callback = (LiveCallback) activity; + init(activity); + } + + private void init(Activity activity) { + this.binding = DialogLiveBinding.inflate(LayoutInflater.from(activity)); + this.dialog = new MaterialAlertDialogBuilder(activity).setView(binding.getRoot()).create(); this.adapter = new LiveAdapter(this); } diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/PassDialog.java b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/PassDialog.java new file mode 100644 index 0000000000..906df100f6 --- /dev/null +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/PassDialog.java @@ -0,0 +1,60 @@ +package com.fongmi.android.tv.ui.custom.dialog; + +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.view.inputmethod.EditorInfo; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; +import androidx.viewbinding.ViewBinding; + +import com.fongmi.android.tv.databinding.DialogPassBinding; +import com.fongmi.android.tv.impl.PassCallback; +import com.fongmi.android.tv.utils.ResUtil; +import com.google.android.material.bottomsheet.BottomSheetDialogFragment; + +public class PassDialog extends BaseDialog { + + private final PassCallback callback; + private DialogPassBinding binding; + + public static void show(FragmentActivity activity) { + for (Fragment fragment : activity.getSupportFragmentManager().getFragments()) if (fragment instanceof BottomSheetDialogFragment) return; + new PassDialog(activity).show(activity.getSupportFragmentManager(), null); + } + + private PassDialog(FragmentActivity activity) { + this.callback = (PassCallback) activity; + } + + @Override + protected ViewBinding getBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return binding = DialogPassBinding.inflate(inflater, container, false); + } + + @Override + protected void initEvent() { + binding.pass.setOnEditorActionListener(this::onDone); + } + + private void onPass() { + String pass = binding.pass.getText().toString().trim(); + if (pass.length() > 0) callback.setPass(pass); + dismiss(); + } + + private boolean onDone(TextView view, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_DONE) onPass(); + return true; + } + + @Override + public void onResume() { + super.onResume(); + getDialog().getWindow().setLayout(ResUtil.dp2px(250), -1); + } +} diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/ReceiveDialog.java b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/ReceiveDialog.java new file mode 100644 index 0000000000..a4e882c493 --- /dev/null +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/ReceiveDialog.java @@ -0,0 +1,93 @@ +package com.fongmi.android.tv.ui.custom.dialog; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.viewbinding.ViewBinding; + +import com.fongmi.android.tv.api.ApiConfig; +import com.fongmi.android.tv.bean.Config; +import com.fongmi.android.tv.bean.History; +import com.fongmi.android.tv.databinding.DialogReceiveBinding; +import com.fongmi.android.tv.event.CastEvent; +import com.fongmi.android.tv.event.RefreshEvent; +import com.fongmi.android.tv.net.Callback; +import com.fongmi.android.tv.ui.activity.DetailActivity; +import com.fongmi.android.tv.utils.ImgUtil; +import com.fongmi.android.tv.utils.Notify; + +public class ReceiveDialog extends BaseDialog { + + private DialogReceiveBinding binding; + private CastEvent event; + + public static ReceiveDialog create() { + return new ReceiveDialog(); + } + + public ReceiveDialog event(CastEvent event) { + this.event = event; + return this; + } + + @Override + protected ViewBinding getBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return binding = DialogReceiveBinding.inflate(inflater, container, false); + } + + @Override + protected void initView() { + History item = event.getHistory(); + binding.name.setText(item.getVodName()); + binding.from.setText(event.getDevice().getName()); + ImgUtil.loadHistory(item.getVodPic(), binding.image); + } + + @Override + protected void initEvent() { + binding.frame.setOnClickListener(v -> onReceiveCast()); + } + + private void showProgress() { + binding.frame.setEnabled(false); + binding.play.setVisibility(View.GONE); + binding.progress.getRoot().setVisibility(View.VISIBLE); + } + + private void hideProgress() { + binding.frame.setEnabled(true); + binding.play.setVisibility(View.VISIBLE); + binding.progress.getRoot().setVisibility(View.GONE); + } + + private void onReceiveCast() { + if (ApiConfig.getUrl().equals(event.getConfig())) { + DetailActivity.cast(getActivity(), event.getHistory().update(ApiConfig.getCid())); + dismiss(); + } else { + showProgress(); + ApiConfig.get().clear().config(Config.find(event.getConfig(), 0)).load(getCallback()); + } + } + + private Callback getCallback() { + return new Callback() { + @Override + public void success() { + RefreshEvent.config(); + RefreshEvent.video(); + onReceiveCast(); + hideProgress(); + } + + @Override + public void error(int resId) { + Notify.show(resId); + hideProgress(); + } + }; + } +} diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/SettingFragment.java b/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/SettingFragment.java index f70e4a6026..0e92218436 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/SettingFragment.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/SettingFragment.java @@ -12,9 +12,9 @@ import com.fongmi.android.tv.BuildConfig; import com.fongmi.android.tv.R; +import com.fongmi.android.tv.Updater; import com.fongmi.android.tv.api.ApiConfig; import com.fongmi.android.tv.api.LiveConfig; -import com.fongmi.android.tv.api.Updater; import com.fongmi.android.tv.api.WallConfig; import com.fongmi.android.tv.bean.Config; import com.fongmi.android.tv.bean.Live; @@ -26,11 +26,11 @@ import com.fongmi.android.tv.impl.SiteCallback; import com.fongmi.android.tv.net.Callback; import com.fongmi.android.tv.ui.base.BaseFragment; +import com.fongmi.android.tv.ui.custom.FileChooser; import com.fongmi.android.tv.ui.custom.dialog.ConfigDialog; import com.fongmi.android.tv.ui.custom.dialog.HistoryDialog; import com.fongmi.android.tv.ui.custom.dialog.LiveDialog; import com.fongmi.android.tv.ui.custom.dialog.SiteDialog; -import com.fongmi.android.tv.ui.custom.FileChooser; import com.fongmi.android.tv.utils.FileUtil; import com.fongmi.android.tv.utils.Notify; import com.fongmi.android.tv.utils.Prefers; @@ -103,17 +103,17 @@ public void setConfig(Config config) { private void load(Config config) { switch (config.getType()) { case 0: - Notify.progress(getActivity(), true); + Notify.progress(getActivity()); mBinding.vodUrl.setText(config.getDesc()); ApiConfig.get().clear().config(config).load(getCallback(config)); break; case 1: - Notify.progress(getActivity(), true); + Notify.progress(getActivity()); mBinding.liveUrl.setText(config.getDesc()); LiveConfig.get().clear().config(config).load(getCallback(config)); break; case 2: - Notify.progress(getActivity(), true); + Notify.progress(getActivity()); mBinding.wallUrl.setText(config.getDesc()); WallConfig.get().clear().config(config).load(getCallback(config)); break; @@ -141,12 +141,14 @@ private void setConfig() { case 0: Notify.dismiss(); RefreshEvent.video(); + RefreshEvent.config(); mBinding.vodUrl.setText(ApiConfig.getDesc()); mBinding.liveUrl.setText(LiveConfig.getDesc()); mBinding.wallUrl.setText(WallConfig.getDesc()); break; case 1: Notify.dismiss(); + RefreshEvent.config(); mBinding.liveUrl.setText(LiveConfig.getDesc()); break; case 2: @@ -222,6 +224,9 @@ private void setWallRefresh() { private void updateText() { if (player == null || decode == null) return; + mBinding.vodUrl.setText(ApiConfig.getDesc()); + mBinding.liveUrl.setText(LiveConfig.getDesc()); + mBinding.wallUrl.setText(WallConfig.getDesc()); mBinding.playerText.setText(player[Prefers.getPlayer()]); mBinding.decodeText.setText(decode[Prefers.getDecode()]); } diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/TypeFragment.java b/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/TypeFragment.java index c7c2561df4..be6ef0e22b 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/TypeFragment.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/TypeFragment.java @@ -38,15 +38,6 @@ public class TypeFragment extends BaseFragment implements CustomScroller.Callbac private List mTypeIds; private VodAdapter mAdapter; - public static TypeFragment newInstance(Result result) { - Bundle args = new Bundle(); - args.putString("typeId", "home"); - args.putString("result", result.toString()); - TypeFragment fragment = new TypeFragment(); - fragment.setArguments(args); - return fragment; - } - public static TypeFragment newInstance(String typeId, boolean folder) { Bundle args = new Bundle(); args.putString("typeId", typeId); @@ -56,10 +47,6 @@ public static TypeFragment newInstance(String typeId, boolean folder) { return fragment; } - private String getResult() { - return getArguments().getString("result"); - } - private String getTypeId() { return getArguments().getString("typeId"); } @@ -72,6 +59,10 @@ private boolean isHome() { return getTypeId().equals("home"); } + private VodFragment getParent() { + return (VodFragment) getParentFragment(); + } + @Override protected ViewBinding getBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { return mBinding = FragmentTypeBinding.inflate(inflater, container, false); @@ -79,16 +70,16 @@ protected ViewBinding getBinding(@NonNull LayoutInflater inflater, @Nullable Vie @Override protected void initView() { + mScroller = new CustomScroller(this); mTypeIds = new ArrayList<>(); mExtends = new HashMap<>(); - mScroller = new CustomScroller(this); - mBinding.swipeLayout.setEnabled(!isHome()); setRecyclerView(); setViewModel(); } @Override protected void initEvent() { + mBinding.swipeLayout.setEnabled(!isHome()); mBinding.swipeLayout.setOnRefreshListener(this); mBinding.recycler.addOnScrollListener(mScroller = new CustomScroller(this)); } @@ -118,6 +109,15 @@ private void getVideo() { getVideo(getTypeId(), "1"); } + private void getVideo(String typeId, String page) { + if (isFolder()) mTypeIds.add(typeId); + if (isFolder()) mBinding.recycler.scrollToPosition(0); + if (page.equals("1")) mAdapter.clear(); + if (page.equals("1") && !mBinding.swipeLayout.isRefreshing()) mBinding.progressLayout.showProgress(); + if (isHome() && page.equals("1")) setAdapter(getParent().getResult()); + else mViewModel.categoryContent(ApiConfig.get().getHome().getKey(), typeId, page, true, mExtends); + } + private void setAdapter(Result result) { int size = result.getList().size(); mBinding.progressLayout.showContent(isFolder(), size); @@ -132,21 +132,17 @@ private void checkPage(int count) { getVideo(getTypeId(), String.valueOf(mScroller.addPage())); } - private void getVideo(String typeId, String page) { - if (isFolder()) mTypeIds.add(typeId); - if (isFolder()) mBinding.recycler.scrollToPosition(0); - if (page.equals("1")) mAdapter.clear(); - if (page.equals("1") && !mBinding.swipeLayout.isRefreshing()) mBinding.progressLayout.showProgress(); - if (!isHome()) mViewModel.categoryContent(ApiConfig.get().getHome().getKey(), typeId, page, true, mExtends); - else setAdapter(Result.fromJson(getResult())); - } - private void refresh(int num) { String typeId = mTypeIds.get(mTypeIds.size() - num); mTypeIds = mTypeIds.subList(0, mTypeIds.size() - num); getVideo(typeId, "1"); } + public void setFilter(String key, String value) { + mExtends.put(key, value); + onRefresh(); + } + @Override public void onRefresh() { if (isFolder()) refresh(1); @@ -160,12 +156,6 @@ public void onLoadMore(String page) { getVideo(getTypeId(), page); } - public void setFilter(String key, String value) { - mExtends.put(key, value); - if (isFolder()) refresh(1); - else getVideo(); - } - @Override public void onItemClick(Vod item) { if (item.isFolder()) getVideo(item.getVodId(), "1"); diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/VodFragment.java b/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/VodFragment.java index 613cf0fc4c..686425c0be 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/VodFragment.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/fragment/VodFragment.java @@ -22,6 +22,7 @@ import com.fongmi.android.tv.bean.Result; import com.fongmi.android.tv.bean.Site; import com.fongmi.android.tv.databinding.FragmentVodBinding; +import com.fongmi.android.tv.event.CastEvent; import com.fongmi.android.tv.event.RefreshEvent; import com.fongmi.android.tv.impl.FilterCallback; import com.fongmi.android.tv.impl.SiteCallback; @@ -37,9 +38,10 @@ import com.fongmi.android.tv.ui.custom.FileChooser; import com.fongmi.android.tv.ui.custom.dialog.FilterDialog; import com.fongmi.android.tv.ui.custom.dialog.LinkDialog; +import com.fongmi.android.tv.ui.custom.dialog.ReceiveDialog; import com.fongmi.android.tv.ui.custom.dialog.SiteDialog; -import com.fongmi.android.tv.utils.Notify; import com.fongmi.android.tv.utils.Prefers; +import com.fongmi.android.tv.utils.Trans; import com.google.android.material.bottomsheet.BottomSheetDialogFragment; import org.greenrobot.eventbus.EventBus; @@ -61,7 +63,7 @@ public class VodFragment extends BaseFragment implements SiteCallback, FilterCal private TypeAdapter mAdapter; private Runnable mRunnable; private List mHots; - private Result result; + private Result mResult; public static VodFragment newInstance() { return new VodFragment(); @@ -85,6 +87,7 @@ protected void initView() { EventBus.getDefault().register(this); setRecyclerView(); setViewModel(); + showProgress(); initHot(); getHot(); } @@ -95,6 +98,7 @@ protected void initEvent() { mBinding.link.setOnClickListener(this::onLink); mBinding.logo.setOnClickListener(this::onLogo); mBinding.keep.setOnClickListener(this::onKeep); + mBinding.retry.setOnClickListener(this::onRetry); mBinding.filter.setOnClickListener(this::onFilter); mBinding.search.setOnClickListener(this::onSearch); mBinding.history.setOnClickListener(this::onHistory); @@ -117,7 +121,7 @@ private void setRecyclerView() { private void setViewModel() { mViewModel = new ViewModelProvider(this).get(SiteViewModel.class); - mViewModel.result.observe(getViewLifecycleOwner(), this::setAdapter); + mViewModel.result.observe(getViewLifecycleOwner(), result -> setAdapter(mResult = result)); } private void initHot() { @@ -140,34 +144,39 @@ private void updateHot() { mBinding.hot.setText(mHots.get(new Random().nextInt(11))); } - private Result handle() { + private Result handle(Result result) { List types = new ArrayList<>(); - for (String cate : getSite().getCategories()) for (Class type : result.getTypes()) if (cate.equals(type.getTypeName())) types.add(type); + for (String cate : getSite().getCategories()) for (Class type : result.getTypes()) if (Trans.s2t(cate).equals(type.getTypeName())) types.add(type); result.setTypes(types); return result; } private void setAdapter(Result result) { - this.result = result; - mAdapter.addAll(handle()); - for (Class item : mAdapter.getTypes()) if (result.getFilters().containsKey(item.getTypeId())) item.setFilters(result.getFilters().get(item.getTypeId())); + mAdapter.addAll(handle(result)); mBinding.pager.getAdapter().notifyDataSetChanged(); - Notify.dismiss(); + for (Class item : mAdapter.getTypes()) if (result.getFilters().containsKey(item.getTypeId())) item.setFilters(result.getFilters().get(item.getTypeId())); + setFabVisible(0); + hideProgress(); + checkRetry(); } private void setFabVisible(int position) { - if (position == 0) { - mBinding.link.show(); + if (mAdapter.getItemCount() == 0) { + mBinding.link.setVisibility(View.GONE); mBinding.filter.setVisibility(View.GONE); } else if (mAdapter.get(position).getFilters().size() > 0) { mBinding.link.setVisibility(View.GONE); mBinding.filter.show(); - } else { - mBinding.link.setVisibility(View.GONE); + } else if (position == 0) { + mBinding.link.show(); mBinding.filter.setVisibility(View.GONE); } } + private void checkRetry() { + mBinding.retry.setVisibility(mAdapter.getItemCount() == 0 ? View.VISIBLE : View.GONE); + } + private void onLink(View view) { if (ApiConfig.hasPush()) LinkDialog.create(this).show(); else mBinding.link.hide(); @@ -181,6 +190,10 @@ private void onKeep(View view) { KeepActivity.start(getActivity()); } + private void onRetry(View view) { + homeContent(); + } + private void onFilter(View view) { for (Fragment fragment : getChildFragmentManager().getFragments()) if (fragment instanceof BottomSheetDialogFragment) return; FilterDialog.create(this).filter(mAdapter.get(mBinding.pager.getCurrentItem()).getFilters()).show(getChildFragmentManager(), null); @@ -198,9 +211,33 @@ private void onHistory(View view) { HistoryActivity.start(getActivity()); } + private void showProgress() { + mBinding.retry.setVisibility(View.GONE); + mBinding.progress.getRoot().setVisibility(View.VISIBLE); + } + + private void hideProgress() { + mBinding.progress.getRoot().setVisibility(View.GONE); + } + + private void homeContent() { + showProgress(); + setFabVisible(0); + mAdapter.clear(); + mViewModel.homeContent(); + mBinding.pager.setAdapter(new PageAdapter(getChildFragmentManager())); + } + + public Result getResult() { + return mResult == null ? new Result() : mResult; + } + @Subscribe(threadMode = ThreadMode.MAIN) public void onRefreshEvent(RefreshEvent event) { switch (event.getType()) { + case EMPTY: + hideProgress(); + break; case VIDEO: case SIZE: homeContent(); @@ -208,17 +245,15 @@ public void onRefreshEvent(RefreshEvent event) { } } - private void homeContent() { - setFabVisible(0); - mAdapter.clear(); - mViewModel.homeContent(); - mBinding.pager.setAdapter(new PageAdapter(getChildFragmentManager())); + @Subscribe(threadMode = ThreadMode.MAIN) + public void onCastEvent(CastEvent event) { + for (Fragment fragment : getChildFragmentManager().getFragments()) if (fragment instanceof BottomSheetDialogFragment) return; + ReceiveDialog.create().event(event).show(getChildFragmentManager(), null); } @Override public void setSite(Site item) { ApiConfig.get().setHome(item); - Notify.progress(getActivity()); homeContent(); } @@ -268,7 +303,7 @@ public PageAdapter(@NonNull FragmentManager fm) { @Override public Fragment getItem(int position) { Class type = mAdapter.get(position); - return type.isHome() ? TypeFragment.newInstance(Result.list(result.getList())) : TypeFragment.newInstance(type.getTypeId(), type.getTypeFlag().equals("1")); + return TypeFragment.newInstance(type.getTypeId(), type.getTypeFlag().equals("1")); } @Override diff --git a/app/src/mobile/res/color/channel.xml b/app/src/mobile/res/color/channel.xml new file mode 100644 index 0000000000..1d23cc3b80 --- /dev/null +++ b/app/src/mobile/res/color/channel.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/color/group.xml b/app/src/mobile/res/color/group.xml new file mode 100644 index 0000000000..6be396f579 --- /dev/null +++ b/app/src/mobile/res/color/group.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/drawable/ic_cast_mobile.xml b/app/src/mobile/res/drawable/ic_cast_mobile.xml new file mode 100644 index 0000000000..4c7690700f --- /dev/null +++ b/app/src/mobile/res/drawable/ic_cast_mobile.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/mobile/res/drawable/ic_cast_refresh.xml b/app/src/mobile/res/drawable/ic_cast_refresh.xml new file mode 100644 index 0000000000..0951177a62 --- /dev/null +++ b/app/src/mobile/res/drawable/ic_cast_refresh.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/mobile/res/drawable/ic_cast_scan.xml b/app/src/mobile/res/drawable/ic_cast_scan.xml new file mode 100644 index 0000000000..2488fbfc2a --- /dev/null +++ b/app/src/mobile/res/drawable/ic_cast_scan.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + diff --git a/app/src/mobile/res/drawable/ic_cast_tv.xml b/app/src/mobile/res/drawable/ic_cast_tv.xml new file mode 100644 index 0000000000..38214101e5 --- /dev/null +++ b/app/src/mobile/res/drawable/ic_cast_tv.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/mobile/res/drawable/ic_control_cast.xml b/app/src/mobile/res/drawable/ic_control_cast.xml new file mode 100644 index 0000000000..7a6c2c7621 --- /dev/null +++ b/app/src/mobile/res/drawable/ic_control_cast.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/mobile/res/drawable/ic_nav_live.xml b/app/src/mobile/res/drawable/ic_nav_live.xml new file mode 100644 index 0000000000..507f8b20e5 --- /dev/null +++ b/app/src/mobile/res/drawable/ic_nav_live.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/mobile/res/drawable/ic_nav_setting.xml b/app/src/mobile/res/drawable/ic_nav_setting.xml index 1fc14c7a89..aed85331f1 100644 --- a/app/src/mobile/res/drawable/ic_nav_setting.xml +++ b/app/src/mobile/res/drawable/ic_nav_setting.xml @@ -2,9 +2,9 @@ android:width="48dp" android:height="48dp" android:tint="?attr/colorControlNormal" - android:viewportWidth="48" - android:viewportHeight="48"> + android:viewportWidth="960" + android:viewportHeight="960"> + android:pathData="M546,880L414,880Q403,880 394.5,873Q386,866 384,855L368,754Q349,747 328,735Q307,723 291,710L198,753Q187,758 176,754.5Q165,751 159,740L93,623Q87,613 90,602Q93,591 102,584L188,521Q186,512 185.5,500.5Q185,489 185,480Q185,471 185.5,459.5Q186,448 188,439L102,376Q93,369 90,358Q87,347 93,337L159,220Q165,209 176,205.5Q187,202 198,207L291,250Q307,237 328,225Q349,213 368,207L384,105Q386,94 394.5,87Q403,80 414,80L546,80Q557,80 565.5,87Q574,94 576,105L592,206Q611,213 632.5,224.5Q654,236 669,250L762,207Q773,202 784,205.5Q795,209 801,220L867,336Q873,346 870.5,357.5Q868,369 858,376L772,437Q774,447 774.5,458.5Q775,470 775,480Q775,490 774.5,501Q774,512 772,522L858,584Q867,591 870,602Q873,613 867,623L801,740Q795,751 784,754.5Q773,758 762,753L669,710Q653,723 632.5,735.5Q612,748 592,754L576,855Q574,866 565.5,873Q557,880 546,880ZM480,610Q534,610 572,572Q610,534 610,480Q610,426 572,388Q534,350 480,350Q426,350 388,388Q350,426 350,480Q350,534 388,572Q426,610 480,610ZM480,550Q451,550 430.5,529.5Q410,509 410,480Q410,451 430.5,430.5Q451,410 480,410Q509,410 529.5,430.5Q550,451 550,480Q550,509 529.5,529.5Q509,550 480,550ZM480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480ZM436,820L524,820L538,708Q571,700 600.5,683Q630,666 654,642L760,688L800,616L706,547Q710,530 712.5,513.5Q715,497 715,480Q715,463 713,446.5Q711,430 706,413L800,344L760,272L654,318Q631,292 602,274.5Q573,257 538,252L524,140L436,140L422,252Q388,259 358.5,276Q329,293 306,318L200,272L160,344L254,413Q250,430 247.5,446.5Q245,463 245,480Q245,497 247.5,513.5Q250,530 254,547L160,616L200,688L306,642Q330,666 359.5,683Q389,700 422,708L436,820Z" /> diff --git a/app/src/mobile/res/drawable/ic_nav_vod.xml b/app/src/mobile/res/drawable/ic_nav_vod.xml index 6d443e599f..d23ce1cc5a 100644 --- a/app/src/mobile/res/drawable/ic_nav_vod.xml +++ b/app/src/mobile/res/drawable/ic_nav_vod.xml @@ -2,9 +2,9 @@ android:width="48dp" android:height="48dp" android:tint="?attr/colorControlNormal" - android:viewportWidth="48" - android:viewportHeight="48"> + android:viewportWidth="960" + android:viewportHeight="960"> + android:pathData="M140,160L214,312L344,312L270,160L359,160L433,312L563,312L489,160L578,160L652,312L782,312L708,160L820,160Q844,160 862,178Q880,196 880,220L880,740Q880,764 862,782Q844,800 820,800L140,800Q116,800 98,782Q80,764 80,740L80,220Q80,196 98,178Q116,160 140,160L140,160ZM140,372L140,740Q140,740 140,740Q140,740 140,740L820,740Q820,740 820,740Q820,740 820,740L820,372L140,372ZM140,372L140,372L140,740Q140,740 140,740Q140,740 140,740L140,740Q140,740 140,740Q140,740 140,740L140,372Z" /> diff --git a/app/src/mobile/res/drawable/ic_vod_retry.xml b/app/src/mobile/res/drawable/ic_vod_retry.xml new file mode 100644 index 0000000000..b524e45069 --- /dev/null +++ b/app/src/mobile/res/drawable/ic_vod_retry.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/mobile/res/drawable/shape_channel.xml b/app/src/mobile/res/drawable/shape_channel.xml new file mode 100644 index 0000000000..04ec9b5a90 --- /dev/null +++ b/app/src/mobile/res/drawable/shape_channel.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/drawable/shape_group.xml b/app/src/mobile/res/drawable/shape_group.xml new file mode 100644 index 0000000000..0bbb932a53 --- /dev/null +++ b/app/src/mobile/res/drawable/shape_group.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/leanback/res/drawable/shape_info_vod.xml b/app/src/mobile/res/drawable/shape_live_info.xml similarity index 53% rename from app/src/leanback/res/drawable/shape_info_vod.xml rename to app/src/mobile/res/drawable/shape_live_info.xml index 4480b195c8..dc4b2e126f 100644 --- a/app/src/leanback/res/drawable/shape_info_vod.xml +++ b/app/src/mobile/res/drawable/shape_live_info.xml @@ -3,10 +3,9 @@ android:shape="rectangle"> \ No newline at end of file diff --git a/app/src/mobile/res/drawable/shape_live_list.xml b/app/src/mobile/res/drawable/shape_live_list.xml new file mode 100644 index 0000000000..a2664c9fd1 --- /dev/null +++ b/app/src/mobile/res/drawable/shape_live_list.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/layout/activity_detail.xml b/app/src/mobile/res/layout/activity_detail.xml index 97e0563d8e..81064a8934 100644 --- a/app/src/mobile/res/layout/activity_detail.xml +++ b/app/src/mobile/res/layout/activity_detail.xml @@ -237,31 +237,39 @@ android:paddingEnd="16dp" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" /> - - - + android:orientation="vertical"> + + + + + + diff --git a/app/src/mobile/res/layout/activity_keep.xml b/app/src/mobile/res/layout/activity_keep.xml index 763d38fb99..1803eac2ac 100644 --- a/app/src/mobile/res/layout/activity_keep.xml +++ b/app/src/mobile/res/layout/activity_keep.xml @@ -23,7 +23,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" - android:text="@string/keep" + android:text="@string/app_keep" android:textColor="@color/white" android:textSize="20sp" android:textStyle="bold" /> diff --git a/app/src/mobile/res/layout/activity_live.xml b/app/src/mobile/res/layout/activity_live.xml new file mode 100644 index 0000000000..0d1834d1d6 --- /dev/null +++ b/app/src/mobile/res/layout/activity_live.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/mobile/res/layout/activity_main.xml b/app/src/mobile/res/layout/activity_main.xml index d4298c5c92..91426a8953 100644 --- a/app/src/mobile/res/layout/activity_main.xml +++ b/app/src/mobile/res/layout/activity_main.xml @@ -16,7 +16,7 @@ android:layout_width="match_parent" android:layout_height="56dp" android:layout_alignParentBottom="true" - android:background="@color/white_10" + android:background="@color/transparent" app:elevation="0dp" app:itemActiveIndicatorStyle="@style/Indicator" app:itemIconTint="@color/nav" diff --git a/app/src/mobile/res/layout/activity_scan.xml b/app/src/mobile/res/layout/activity_scan.xml new file mode 100644 index 0000000000..a3841f7bb5 --- /dev/null +++ b/app/src/mobile/res/layout/activity_scan.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/layout/adapter_channel.xml b/app/src/mobile/res/layout/adapter_channel.xml new file mode 100644 index 0000000000..d46dd8ddc6 --- /dev/null +++ b/app/src/mobile/res/layout/adapter_channel.xml @@ -0,0 +1,45 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/layout/adapter_device.xml b/app/src/mobile/res/layout/adapter_device.xml new file mode 100644 index 0000000000..1e9b94772b --- /dev/null +++ b/app/src/mobile/res/layout/adapter_device.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/layout/adapter_group.xml b/app/src/mobile/res/layout/adapter_group.xml new file mode 100644 index 0000000000..d54ddbe094 --- /dev/null +++ b/app/src/mobile/res/layout/adapter_group.xml @@ -0,0 +1,35 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/layout/dialog_cast.xml b/app/src/mobile/res/layout/dialog_cast.xml new file mode 100644 index 0000000000..d9df809cf0 --- /dev/null +++ b/app/src/mobile/res/layout/dialog_cast.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/layout/dialog_control.xml b/app/src/mobile/res/layout/dialog_control.xml index 28efea9b77..ea7efe4958 100644 --- a/app/src/mobile/res/layout/dialog_control.xml +++ b/app/src/mobile/res/layout/dialog_control.xml @@ -5,7 +5,29 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - android:padding="16dp"> + android:padding="16dp" + tools:background="@color/white"> + + + + - - - - + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/layout/dialog_live.xml b/app/src/mobile/res/layout/dialog_live.xml index 0aa7da9589..c839913bb7 100644 --- a/app/src/mobile/res/layout/dialog_live.xml +++ b/app/src/mobile/res/layout/dialog_live.xml @@ -6,4 +6,4 @@ android:layout_height="wrap_content" android:padding="16dp" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" - app:maxHeight="286dp" /> \ No newline at end of file + app:maxHeight="240dp" /> \ No newline at end of file diff --git a/app/src/mobile/res/layout/dialog_pass.xml b/app/src/mobile/res/layout/dialog_pass.xml new file mode 100644 index 0000000000..f6e92c45c5 --- /dev/null +++ b/app/src/mobile/res/layout/dialog_pass.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/layout/dialog_receive.xml b/app/src/mobile/res/layout/dialog_receive.xml new file mode 100644 index 0000000000..14e02dcc42 --- /dev/null +++ b/app/src/mobile/res/layout/dialog_receive.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/layout/dialog_track.xml b/app/src/mobile/res/layout/dialog_track.xml index 0665662030..d5b105654b 100644 --- a/app/src/mobile/res/layout/dialog_track.xml +++ b/app/src/mobile/res/layout/dialog_track.xml @@ -4,11 +4,6 @@ android:id="@+id/recycler" android:layout_width="match_parent" android:layout_height="wrap_content" - android:clipChildren="false" - android:clipToPadding="false" - android:paddingStart="12dp" - android:paddingTop="16dp" - android:paddingEnd="12dp" - android:paddingBottom="16dp" + android:padding="16dp" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" - app:maxHeight="212dp" /> \ No newline at end of file + app:maxHeight="228dp" /> \ No newline at end of file diff --git a/app/src/mobile/res/layout/fragment_setting.xml b/app/src/mobile/res/layout/fragment_setting.xml index 295839dde4..e553829cf0 100644 --- a/app/src/mobile/res/layout/fragment_setting.xml +++ b/app/src/mobile/res/layout/fragment_setting.xml @@ -1,29 +1,43 @@ - + android:layout_height="match_parent"> - + android:background="@color/transparent"> + + + + + android:fillViewport="true" + app:layout_behavior="@string/appbar_scrolling_view_behavior"> + android:orientation="vertical" + android:paddingStart="16dp" + android:paddingTop="16dp" + android:paddingEnd="16dp" + android:paddingBottom="16dp"> - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/mobile/res/layout/fragment_vod.xml b/app/src/mobile/res/layout/fragment_vod.xml index 2f1e4c7838..43797f7df6 100644 --- a/app/src/mobile/res/layout/fragment_vod.xml +++ b/app/src/mobile/res/layout/fragment_vod.xml @@ -17,7 +17,11 @@ android:layout_height="wrap_content" android:gravity="center_vertical" android:orientation="horizontal" - android:padding="16dp"> + android:paddingStart="16dp" + android:paddingTop="16dp" + android:paddingEnd="16dp" + android:paddingBottom="4dp" + app:layout_scrollFlags="scroll|enterAlways"> + + + + @@ -116,7 +136,7 @@ app:backgroundTint="@color/blue_500" app:layout_anchor="@id/pager" app:layout_anchorGravity="end|bottom" - app:layout_behavior=".ui.custom.CustomFabBehavior" + app:layout_behavior="com.fongmi.android.tv.ui.custom.CustomFabBehavior" app:srcCompat="@drawable/ic_fab_link" app:tint="@color/white" tools:visibility="visible" /> diff --git a/app/src/mobile/res/layout/view_control_live.xml b/app/src/mobile/res/layout/view_control_live.xml new file mode 100644 index 0000000000..f76ce65c82 --- /dev/null +++ b/app/src/mobile/res/layout/view_control_live.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/layout/view_control_live_action.xml b/app/src/mobile/res/layout/view_control_live_action.xml new file mode 100644 index 0000000000..7108a9e30b --- /dev/null +++ b/app/src/mobile/res/layout/view_control_live_action.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/layout/view_control_vod.xml b/app/src/mobile/res/layout/view_control_vod.xml index 4cd9cede3c..73695fb91b 100644 --- a/app/src/mobile/res/layout/view_control_vod.xml +++ b/app/src/mobile/res/layout/view_control_vod.xml @@ -4,7 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/black_10"> + android:background="@color/black_20"> - + android:gravity="center_vertical" + android:orientation="horizontal"> - + - + + + + + + + + + + + + + + + diff --git a/app/src/mobile/res/layout/view_widget_live.xml b/app/src/mobile/res/layout/view_widget_live.xml new file mode 100644 index 0000000000..e074a1703f --- /dev/null +++ b/app/src/mobile/res/layout/view_widget_live.xml @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/menu/menu_nav.xml b/app/src/mobile/res/menu/menu_nav.xml index 86d8793e32..bd52140780 100644 --- a/app/src/mobile/res/menu/menu_nav.xml +++ b/app/src/mobile/res/menu/menu_nav.xml @@ -4,11 +4,19 @@ + android:title="@string/nav_vod" + android:visible="false" /> + + + android:title="@string/nav_setting" + android:visible="false" /> \ No newline at end of file diff --git a/app/src/mobile/res/values-zh-rCN/strings.xml b/app/src/mobile/res/values-zh-rCN/strings.xml index df6ace70bb..0f3e0ab607 100644 --- a/app/src/mobile/res/values-zh-rCN/strings.xml +++ b/app/src/mobile/res/values-zh-rCN/strings.xml @@ -1,18 +1,17 @@ + + 我的收藏 + 最近观看 + 点播 + 直播 设定 推荐 - - 最近观看 - - - 我的收藏 - 线路 更多 @@ -20,6 +19,12 @@ 简介 快搜“%s + + 选择装置 + 找不到装置 + 装置已离线 + 请扫描影视 QRCode 进行绑定 + 倍速 缩放 diff --git a/app/src/mobile/res/values-zh-rTW/strings.xml b/app/src/mobile/res/values-zh-rTW/strings.xml index 3f8c7f3a83..1ee42ce332 100644 --- a/app/src/mobile/res/values-zh-rTW/strings.xml +++ b/app/src/mobile/res/values-zh-rTW/strings.xml @@ -1,18 +1,17 @@ + + 我的收藏 + 最近觀看 + 點播 + 直播 設定 推薦 - - 最近觀看 - - - 我的收藏 - 線路 更多 @@ -20,6 +19,12 @@ 簡介 快搜「%s + + 選擇裝置 + 找不到裝置 + 裝置已離線 + 請掃描影視 QRCode 進行綁定 + 倍速 縮放 diff --git a/app/src/mobile/res/values/strings.xml b/app/src/mobile/res/values/strings.xml index 4c61cde2dd..5076efad87 100644 --- a/app/src/mobile/res/values/strings.xml +++ b/app/src/mobile/res/values/strings.xml @@ -1,18 +1,17 @@ + + Keep + History + Vod + Live Setting Home - - History - - - Keep - Flag More @@ -20,6 +19,12 @@ Summary Searching %s + + Select devices + Could\'t find a device to cast + Device is offline + Please scan QR Code to bind + Speed Scale diff --git a/app/src/mobile/res/values/styles.xml b/app/src/mobile/res/values/styles.xml index f36cfe290d..08ee094b85 100644 --- a/app/src/mobile/res/values/styles.xml +++ b/app/src/mobile/res/values/styles.xml @@ -15,7 +15,7 @@ @color/accent @null @null - @color/white_10 + @color/transparent @style/ThemeOverlay.App.BottomSheetDialog diff --git a/drpy/build.gradle b/drpy/build.gradle index ffeeec02ba..aed7939f09 100644 --- a/drpy/build.gradle +++ b/drpy/build.gradle @@ -18,5 +18,5 @@ dependencies { implementation 'com.google.code.gson:gson:2.10.1' implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.10' implementation 'org.jsoup:jsoup:1.15.3' - implementation 'wang.harlon.quickjs:wrapper-android:0.18.7' + implementation 'wang.harlon.quickjs:wrapper-android:0.18.8' } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 3daa69e15b..4e59a36c4f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx8192m -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 43b8189947..cb95de8bcc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Wed Mar 29 12:54:35 CST 2023 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/ui/IjkVideoView.java b/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/ui/IjkVideoView.java index 8795f7f83a..df4f4e46bc 100644 --- a/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/ui/IjkVideoView.java +++ b/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/ui/IjkVideoView.java @@ -176,7 +176,7 @@ private void openVideo() { } private void fixUserAgent() { - if (!mHeaders.containsKey(Utils.USER_AGENT)) mHeaders.put(Utils.USER_AGENT, Utils.getUserAgent(mAppContext)); + if (!mHeaders.containsKey(Utils.USER_AGENT)) mHeaders.put(Utils.USER_AGENT, Utils.CHROME); mIjkPlayer.setOption(format, "user_agent", mHeaders.get(Utils.USER_AGENT)); mHeaders.remove(Utils.USER_AGENT); } diff --git a/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/ui/Utils.java b/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/ui/Utils.java index da5235001d..22d7a72338 100644 --- a/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/ui/Utils.java +++ b/ijkplayer/src/main/java/tv/danmaku/ijk/media/player/ui/Utils.java @@ -9,6 +9,7 @@ public class Utils { public static final String USER_AGENT = "User-Agent"; + public static final String CHROME = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36"; public static float dp2px(Context context, float dpValue) { return Math.round((dpValue * context.getResources().getDisplayMetrics().densityDpi) / DisplayMetrics.DENSITY_DEFAULT); diff --git a/release/leanback-dev.json b/release/leanback-dev.json index e0a0ba570b..e215acbc4a 100644 --- a/release/leanback-dev.json +++ b/release/leanback-dev.json @@ -1,5 +1,5 @@ { - "code": 72, - "name": "1.7.2", - "desc": "# [IJK]\n* 支持 rtmp 格式\n# [EXO]\n* 升級至 Media3\n# [點播]\n* 設置顯示線路名稱\n* 修正分類空白問題\n* 修正暫停沒消失問題\n* 修正詳情沒換行問題" + "code": 73, + "name": "1.7.3", + "desc": "# [直播]\n* 修正重音問題\n* 記憶當下頻道線路\n# [點播]\n* 緩存熱搜\n* 支持 WebDAV\n* 支持 Base64 圖\n* 支持投屏(影視->影視)\n* 修正歷史紀錄不同配置互相取代\n* 換源開關連動換線路是否自動選集" } \ No newline at end of file diff --git a/release/leanback-java.apk b/release/leanback-java.apk index 8ed0fa0515..4a5da4e6b9 100644 Binary files a/release/leanback-java.apk and b/release/leanback-java.apk differ diff --git a/release/leanback-python.apk b/release/leanback-python.apk index afb615b8ca..6284e865db 100644 Binary files a/release/leanback-python.apk and b/release/leanback-python.apk differ diff --git a/release/leanback-release.json b/release/leanback-release.json index e0a0ba570b..e215acbc4a 100644 --- a/release/leanback-release.json +++ b/release/leanback-release.json @@ -1,5 +1,5 @@ { - "code": 72, - "name": "1.7.2", - "desc": "# [IJK]\n* 支持 rtmp 格式\n# [EXO]\n* 升級至 Media3\n# [點播]\n* 設置顯示線路名稱\n* 修正分類空白問題\n* 修正暫停沒消失問題\n* 修正詳情沒換行問題" + "code": 73, + "name": "1.7.3", + "desc": "# [直播]\n* 修正重音問題\n* 記憶當下頻道線路\n# [點播]\n* 緩存熱搜\n* 支持 WebDAV\n* 支持 Base64 圖\n* 支持投屏(影視->影視)\n* 修正歷史紀錄不同配置互相取代\n* 換源開關連動換線路是否自動選集" } \ No newline at end of file diff --git a/release/mobile-dev.json b/release/mobile-dev.json index d5a1aa5778..c9c5418bfc 100644 --- a/release/mobile-dev.json +++ b/release/mobile-dev.json @@ -1,5 +1,5 @@ { - "code": 10, - "name": "1.1.0", - "desc": "* 設置顯示線路名稱\n* 支持開啟本地視頻\n* 修正詳情沒換行問題\n* 修正暗黑模式切換問題\n* 升級 EXO 至 Media3" + "code": 11, + "name": "1.1.1", + "desc": "# [直播]\n* 千呼萬喚始出來\n# [點播]\n* 首頁支持橫屏\n* 支持橫屏選集\n* 支持 WebDAV\n* 支持 Base64 圖\n* 調整搜索線程為10\n* 首頁新增重試按鈕\n* 修正首頁推薦閃退\n* 支持接收鄰近裝置投放\n* 支持投屏(影視->影視)\n* 支持投屏(影視->DLNA)\n* 修正歷史紀錄不同配置互相取代\n* 換源開關連動換線路是否自動選集" } \ No newline at end of file diff --git a/release/mobile-java.apk b/release/mobile-java.apk index cc1eecd3f7..9752ae0ae2 100644 Binary files a/release/mobile-java.apk and b/release/mobile-java.apk differ diff --git a/release/mobile-python.apk b/release/mobile-python.apk index ca87528103..d02f8c8f2f 100644 Binary files a/release/mobile-python.apk and b/release/mobile-python.apk differ diff --git a/release/mobile-release.json b/release/mobile-release.json index d5a1aa5778..c9c5418bfc 100644 --- a/release/mobile-release.json +++ b/release/mobile-release.json @@ -1,5 +1,5 @@ { - "code": 10, - "name": "1.1.0", - "desc": "* 設置顯示線路名稱\n* 支持開啟本地視頻\n* 修正詳情沒換行問題\n* 修正暗黑模式切換問題\n* 升級 EXO 至 Media3" + "code": 11, + "name": "1.1.1", + "desc": "# [直播]\n* 千呼萬喚始出來\n# [點播]\n* 首頁支持橫屏\n* 支持橫屏選集\n* 支持 WebDAV\n* 支持 Base64 圖\n* 調整搜索線程為10\n* 首頁新增重試按鈕\n* 修正首頁推薦閃退\n* 支持接收鄰近裝置投放\n* 支持投屏(影視->影視)\n* 支持投屏(影視->DLNA)\n* 修正歷史紀錄不同配置互相取代\n* 換源開關連動換線路是否自動選集" } \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 2af0b2b968..4be87c0b50 100644 --- a/settings.gradle +++ b/settings.gradle @@ -12,6 +12,11 @@ dependencyResolutionManagement { mavenCentral() jcenter() google() + maven { url 'https://jitpack.io' } + maven { + url 'http://4thline.org/m2' + allowInsecureProtocol = true + } } } include ':app'