From a1a45aeacd6e2d75d1b507e0b4ba43df930613e7 Mon Sep 17 00:00:00 2001 From: Tosencen Date: Tue, 8 Jul 2025 16:57:46 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E8=BF=9BUI:=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E9=A1=B5=E9=9D=A2=E5=9B=BE=E6=A0=87=EF=BC=8C?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=85=B3=E4=BA=8E=E5=BC=B9=E7=AA=97=EF=BC=8C?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=95=9C=E5=83=8F=E6=9B=B4=E6=96=B0=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 43 +++- .vscode/settings.json | 3 + .../main/java/com/fongmi/android/tv/App.java | 23 ++ .../java/com/fongmi/android/tv/Setting.java | 8 + .../com/fongmi/android/tv/bean/Config.java | 13 + .../fongmi/android/tv/utils/CacheCleaner.java | 92 +++++++ app/src/main/res/values-zh-rCN/strings.xml | 5 + app/src/main/res/values-zh-rTW/strings.xml | 14 +- app/src/main/res/values/strings.xml | 19 +- .../java/com/fongmi/android/tv/Updater.java | 59 ++++- .../android/tv/ui/activity/HomeActivity.java | 8 +- .../android/tv/ui/activity/VideoActivity.java | 81 +++++- .../android/tv/ui/adapter/ConfigAdapter.java | 25 +- .../android/tv/ui/adapter/TypeAdapter.java | 1 - .../android/tv/ui/dialog/ConfigDialog.java | 12 +- .../android/tv/ui/dialog/HistoryDialog.java | 9 +- .../tv/ui/fragment/SettingFragment.java | 63 ++++- app/src/mobile/res/color-night/nav.xml | 2 +- app/src/mobile/res/color/nav.xml | 2 +- .../mobile/res/drawable/bg_nav_selected.xml | 6 + app/src/mobile/res/drawable/ic_nav_live.xml | 18 +- .../res/drawable/ic_nav_live_selected.xml | 16 ++ app/src/mobile/res/drawable/ic_nav_vod.xml | 1 - .../res/drawable/ic_nav_vod_selected.xml | 10 + .../mobile/res/drawable/ic_setting_copy.xml | 11 + .../mobile/res/drawable/ic_setting_doh.xml | 10 + .../res/drawable/ic_setting_incognito.xml | 10 + .../mobile/res/drawable/ic_setting_size.xml | 10 + .../mobile/res/drawable/selector_nav_live.xml | 5 + .../res/drawable/selector_nav_setting.xml | 5 + .../mobile/res/drawable/selector_nav_vod.xml | 5 + .../res/drawable/shape_about_button.xml | 15 ++ .../res/drawable/shape_about_dialog.xml | 6 + .../res/drawable/shape_dialog_button.xml | 3 - .../mobile/res/drawable/shape_site_dialog.xml | 8 + .../res/layout-land/view_control_vod.xml | 235 +++++++++++++++++ app/src/mobile/res/layout/activity_home.xml | 4 +- app/src/mobile/res/layout/adapter_config.xml | 38 ++- app/src/mobile/res/layout/dialog_about.xml | 61 +++++ .../mobile/res/layout/fragment_setting.xml | 237 +++++++++++++----- .../res/layout/fragment_setting_player.xml | 5 +- .../mobile/res/layout/view_control_vod.xml | 12 + app/src/mobile/res/layout/view_no_live.xml | 23 ++ app/src/mobile/res/values/styles.xml | 9 +- .../java/com/github/catvod/utils/Github.java | 106 +++++++- 45 files changed, 1217 insertions(+), 134 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 app/src/main/java/com/fongmi/android/tv/utils/CacheCleaner.java create mode 100644 app/src/mobile/res/drawable/bg_nav_selected.xml create mode 100644 app/src/mobile/res/drawable/ic_nav_live_selected.xml create mode 100644 app/src/mobile/res/drawable/ic_nav_vod_selected.xml create mode 100644 app/src/mobile/res/drawable/ic_setting_copy.xml create mode 100644 app/src/mobile/res/drawable/ic_setting_doh.xml create mode 100644 app/src/mobile/res/drawable/ic_setting_incognito.xml create mode 100644 app/src/mobile/res/drawable/ic_setting_size.xml create mode 100644 app/src/mobile/res/drawable/selector_nav_live.xml create mode 100644 app/src/mobile/res/drawable/selector_nav_setting.xml create mode 100644 app/src/mobile/res/drawable/selector_nav_vod.xml create mode 100644 app/src/mobile/res/drawable/shape_about_button.xml create mode 100644 app/src/mobile/res/drawable/shape_about_dialog.xml create mode 100644 app/src/mobile/res/drawable/shape_site_dialog.xml create mode 100644 app/src/mobile/res/layout-land/view_control_vod.xml create mode 100644 app/src/mobile/res/layout/dialog_about.xml create mode 100644 app/src/mobile/res/layout/view_no_live.xml diff --git a/.gitignore b/.gitignore index c7b1543e..5fa56d22 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,37 @@ -.idea -.gradle +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Log/OS Files +*.log + +# Android Studio generated files and folders +captures/ +.externalNativeBuild/ +.cxx/ +*.apk +*.aab +output.json + +# IntelliJ +*.iml +.idea/ +misc.xml +deploymentTargetDropDown.xml +render.experimental.xml + +# Keystore files *.jks -lib-*.aar -*build -/media* -/Release -/local.properties \ No newline at end of file +*.keystore + +# Google Services (e.g. APIs or Firebase) +google-services.json + +# Android Profiling +*.hprof + +# APK files +apk/release/*.apk \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..e0f15db2 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic" +} \ No newline at end of file diff --git a/app/src/main/java/com/fongmi/android/tv/App.java b/app/src/main/java/com/fongmi/android/tv/App.java index 5d88da44..9e8af84d 100644 --- a/app/src/main/java/com/fongmi/android/tv/App.java +++ b/app/src/main/java/com/fongmi/android/tv/App.java @@ -14,6 +14,7 @@ import androidx.core.os.HandlerCompat; import com.fongmi.android.tv.event.EventIndex; import com.fongmi.android.tv.ui.activity.CrashActivity; +import com.fongmi.android.tv.utils.CacheCleaner; import com.fongmi.android.tv.utils.Notify; import com.fongmi.hook.Hook; import com.github.catvod.Init; @@ -41,6 +42,7 @@ public class App extends Application { private final Gson gson; private final long time; private Hook hook; + private final Runnable cleanTask; public App() { instance = this; @@ -48,6 +50,7 @@ public class App extends Application { handler = HandlerCompat.createAsync(Looper.getMainLooper()); time = System.currentTimeMillis(); gson = new Gson(); + cleanTask = this::checkCacheClean; } public static App get() { @@ -119,6 +122,10 @@ public class App extends Application { OkHttp.get().setDoh(Doh.objectFrom(Setting.getDoh())); EventBus.builder().addIndex(new EventIndex()).installDefaultEventBus(); CaocConfig.Builder.create().backgroundMode(CaocConfig.BACKGROUND_MODE_SILENT).errorActivity(CrashActivity.class).apply(); + + // 初始化自动缓存清理 + initCacheCleaner(); + registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { @Override public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) { @@ -133,6 +140,8 @@ public class App extends Application { @Override public void onActivityResumed(@NonNull Activity activity) { if (activity != activity()) setActivity(activity); + // 应用回到前台时检查缓存 + checkCacheClean(); } @Override @@ -156,6 +165,20 @@ public class App extends Application { }); } + private void initCacheCleaner() { + CacheCleaner cleaner = CacheCleaner.get(); + cleaner.setCacheThreshold(200 * 1024 * 1024); // 固定使用200MB阈值 + + // 定期检查缓存 (每30分钟) + post(cleanTask, 30 * 60 * 1000); + } + + private void checkCacheClean() { + CacheCleaner.get().checkAndClean(); + // 每30分钟定期检查缓存 + post(cleanTask, 30 * 60 * 1000); + } + @Override public PackageManager getPackageManager() { return hook != null ? hook : getBaseContext().getPackageManager(); diff --git a/app/src/main/java/com/fongmi/android/tv/Setting.java b/app/src/main/java/com/fongmi/android/tv/Setting.java index 84b4eee7..97ced020 100644 --- a/app/src/main/java/com/fongmi/android/tv/Setting.java +++ b/app/src/main/java/com/fongmi/android/tv/Setting.java @@ -201,6 +201,14 @@ public class Setting { Prefers.put("update", update); } + public static boolean getUseCnMirror() { + return Prefers.getBoolean("use_cn_mirror", false); + } + + public static void putUseCnMirror(boolean useCnMirror) { + Prefers.put("use_cn_mirror", useCnMirror); + } + public static boolean isCaption() { return Prefers.getBoolean("caption"); } 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 2c2e2dfb..c4e1e96c 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 @@ -267,6 +267,19 @@ public class Config { Keep.delete(getId()); } + public Config copy() { + Config copy = new Config(); + copy.setType(type); + copy.setUrl(url); + copy.setJson(json); + copy.setName(TextUtils.isEmpty(name) ? url + " 副本" : name + " 副本"); + copy.setLogo(logo); + copy.setHome(home); + copy.setParse(parse); + copy.setTime(System.currentTimeMillis()); + return copy; + } + @NonNull @Override public String toString() { diff --git a/app/src/main/java/com/fongmi/android/tv/utils/CacheCleaner.java b/app/src/main/java/com/fongmi/android/tv/utils/CacheCleaner.java new file mode 100644 index 00000000..aa8b0b5a --- /dev/null +++ b/app/src/main/java/com/fongmi/android/tv/utils/CacheCleaner.java @@ -0,0 +1,92 @@ +package com.fongmi.android.tv.utils; + +import android.os.StatFs; + +import com.fongmi.android.tv.App; +import com.fongmi.android.tv.impl.Callback; +import com.github.catvod.utils.Path; + +/** + * 缓存自动清理管理器 + */ +public class CacheCleaner { + + // 默认缓存清理阈值 200MB + private static final long DEFAULT_CACHE_THRESHOLD = 200 * 1024 * 1024; + // 最小保留空间 500MB + private static final long MIN_FREE_SPACE = 500 * 1024 * 1024; + // 单例实例 + private static CacheCleaner instance; + // 缓存清理阈值 + private long cacheThreshold; + + private CacheCleaner() { + this.cacheThreshold = DEFAULT_CACHE_THRESHOLD; + } + + public static CacheCleaner get() { + if (instance == null) { + synchronized (CacheCleaner.class) { + if (instance == null) { + instance = new CacheCleaner(); + } + } + } + return instance; + } + + /** + * 设置缓存阈值 + * @param threshold 阈值大小(字节) + */ + public void setCacheThreshold(long threshold) { + this.cacheThreshold = threshold; + } + + /** + * 检查缓存,如果超过阈值则清理 + */ + public void checkAndClean() { + App.execute(() -> { + try { + // 获取当前缓存大小 + long cacheSize = FileUtil.getDirectorySize(Path.cache()); + // 获取剩余存储空间 + long freeSpace = getAvailableStorageSpace(); + + // 如果缓存超过阈值或可用空间低于最小要求,清理缓存 + if (cacheSize > cacheThreshold || freeSpace < MIN_FREE_SPACE) { + cleanCache(); + } + } catch (Exception e) { + e.printStackTrace(); + } + }); + } + + /** + * 清理缓存 + */ + private void cleanCache() { + FileUtil.clearCache(new Callback() { + @Override + public void success() { + // 缓存清理成功 + } + }); + } + + /** + * 获取设备可用存储空间 + * @return 可用空间(字节) + */ + private long getAvailableStorageSpace() { + try { + StatFs stat = new StatFs(Path.cache().getPath()); + return stat.getAvailableBlocksLong() * stat.getBlockSizeLong(); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } +} \ No newline at end of file diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index a405ab0b..30c081ea 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -78,6 +78,8 @@ DoH Proxy 缓存 + 自动清理缓存 + 清理阈值 备份 恢复 版本 @@ -88,6 +90,8 @@ 应用设置 网络设置 数据管理 + v3.0.3 + 在GitHub上查看 选择备份 @@ -143,6 +147,7 @@ 不支持的文件格式 设备授权数已达上限 该订阅无直播内容 + 当前源没有直播内容 发现新版本 %s diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 2a2684ef..a569911f 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -76,7 +76,9 @@ 圖片尺寸 DoH Proxy - 暫存 + 緩存 + 自動清理緩存 + 清理閾值 備份 還原 版本 @@ -87,6 +89,8 @@ 應用設置 網絡設置 數據管理 + v3.0.3 + 在GitHub上查看 選擇備份 @@ -138,6 +142,10 @@ 連線逾時 暫無播放資料 找不到資料 + 不支持的檔案格式 + 設備授權數已達上限 + 該訂閱無直播內容 + 當前源沒有直播內容 發現新版本 %s @@ -204,4 +212,8 @@ 選擇字幕 + 已設為當前點播源 + + 點我添加源 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 081e1b13..c966ebdd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -82,13 +82,17 @@ Image size DoH Proxy - Cache - Backup - Restore - Version + 缓存 + Auto Clean Cache + Cache Threshold + 备份 + 恢复 + 版本 Choose Off On + v3.0.3 + View on GitHub Select backup @@ -146,6 +150,7 @@ Unsupported file format Device authorization limit reached This subscription has no live content + Current source has no live content Not found No play data No flag data @@ -224,4 +229,10 @@ 已设为当前点播源 + Remember settings + Target size + Scan result + + 点我添加源 + \ No newline at end of file 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 4f80248d..bd9385d7 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/Updater.java +++ b/app/src/mobile/java/com/fongmi/android/tv/Updater.java @@ -16,7 +16,9 @@ import com.github.catvod.net.OkHttp; import com.github.catvod.utils.Github; import com.github.catvod.utils.Path; import com.google.android.material.dialog.MaterialAlertDialogBuilder; +import com.orhanobut.logger.Logger; +import org.json.JSONArray; import org.json.JSONObject; import java.io.File; @@ -25,20 +27,25 @@ import java.util.Locale; public class Updater implements Download.Callback { private DialogUpdateBinding binding; - private final Download download; + private Download download; private AlertDialog dialog; private boolean dev; + private String downloadUrl; private File getFile() { return Path.cache("update.apk"); } - private String getJson() { - return Github.getJson(dev, BuildConfig.FLAVOR_mode); + private String getApkName() { + return "mobile-" + BuildConfig.FLAVOR_abi + ".apk"; } - private String getApk() { - return Github.getApk(dev, BuildConfig.FLAVOR_mode + "-" + BuildConfig.FLAVOR_abi); + private String getJson() { + String url = Github.getReleaseApi(); + boolean usingCnMirror = Github.useCnMirror(); + Logger.d("Using CN Mirror: " + usingCnMirror); + Logger.d("Update check URL: " + url); + return url; } public static Updater create() { @@ -46,7 +53,7 @@ public class Updater implements Download.Callback { } public Updater() { - this.download = Download.create(getApk(), getFile(), this); + this.download = Download.create("", getFile(), this); } public Updater force() { @@ -74,18 +81,43 @@ public class Updater implements Download.Callback { App.execute(() -> doInBackground(activity)); } - private boolean need(int code, String name) { - return Setting.getUpdate() && (dev ? !name.equals(BuildConfig.VERSION_NAME) && code >= BuildConfig.VERSION_CODE : code > BuildConfig.VERSION_CODE); + private boolean need(String tagName) { + Logger.d("Current version: " + BuildConfig.VERSION_NAME); + Logger.d("Latest version: " + tagName); + if (tagName.startsWith("v")) tagName = tagName.substring(1); + return Setting.getUpdate() && !tagName.equals(BuildConfig.VERSION_NAME); } private void doInBackground(Activity activity) { try { - JSONObject object = new JSONObject(OkHttp.string(getJson())); - String name = object.optString("name"); - String desc = object.optString("desc"); - int code = object.optInt("code"); - if (need(code, name)) App.post(() -> show(activity, name, desc)); + String jsonUrl = getJson(); + Logger.d("Fetching update info from: " + jsonUrl); + String response = OkHttp.string(jsonUrl); + Logger.d("Update check response: " + response); + + JSONObject release = new JSONObject(response); + String tagName = release.getString("tag_name"); + String body = release.getString("body"); + JSONArray assets = release.getJSONArray("assets"); + + // Find the correct APK asset + String apkName = getApkName(); + for (int i = 0; i < assets.length(); i++) { + JSONObject asset = assets.getJSONObject(i); + if (asset.getString("name").equals(apkName)) { + downloadUrl = asset.getString("browser_download_url"); + break; + } + } + + if (downloadUrl != null && need(tagName)) { + download = Download.create(downloadUrl, getFile(), this); + App.post(() -> show(activity, tagName, body)); + } else { + Logger.d("No update needed or APK not found"); + } } catch (Exception e) { + Logger.e("Update check failed", e); e.printStackTrace(); } } @@ -127,6 +159,7 @@ public class Updater implements Download.Callback { @Override public void error(String msg) { + Logger.e("Download error: " + msg); Notify.show(msg); dismiss(); } diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/HomeActivity.java b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/HomeActivity.java index 117cbaca..d120a759 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/HomeActivity.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/HomeActivity.java @@ -179,7 +179,13 @@ public class HomeActivity extends BaseActivity implements NavigationBarView.OnIt if (mBinding.navigation.getSelectedItemId() == item.getItemId()) return false; if (item.getItemId() == R.id.setting) return mManager.change(1); if (item.getItemId() == R.id.vod) return mManager.change(0); - if (item.getItemId() == R.id.live) return openLive(); + if (item.getItemId() == R.id.live) { + if (LiveConfig.isEmpty()) { + Notify.showCenter(R.string.error_no_live); + return false; + } + return openLive(); + } return false; } diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/VideoActivity.java b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/VideoActivity.java index 2e2765ec..f077fccb 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/activity/VideoActivity.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/activity/VideoActivity.java @@ -4,17 +4,24 @@ import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; import android.app.Dialog; +import android.content.BroadcastReceiver; +import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.BatteryManager; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; import android.provider.Settings; import android.text.Html; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextUtils; +import android.text.format.DateFormat; import android.text.style.ClickableSpan; import android.view.MotionEvent; import android.view.View; @@ -22,6 +29,7 @@ import android.view.ViewGroup; import android.view.WindowManager; import android.widget.RelativeLayout; import android.widget.TextView; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -63,6 +71,7 @@ import com.fongmi.android.tv.event.RefreshEvent; import com.fongmi.android.tv.model.SiteViewModel; import com.fongmi.android.tv.player.Players; import com.fongmi.android.tv.player.exo.ExoUtil; +import com.fongmi.android.tv.player.Source; import com.fongmi.android.tv.service.PlaybackService; import com.fongmi.android.tv.ui.adapter.EpisodeAdapter; import com.fongmi.android.tv.ui.adapter.FlagAdapter; @@ -98,6 +107,7 @@ import com.github.catvod.utils.Trans; import com.google.android.material.bottomsheet.BottomSheetDialogFragment; import com.permissionx.guolindev.PermissionX; +import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; @@ -148,6 +158,10 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo private Clock mClock; private String tag; private PiP mPiP; + private Handler mHandler; + private Runnable mTimeUpdateRunnable; + private BroadcastReceiver mBatteryReceiver; + private int mBatteryLevel = -1; public static void push(FragmentActivity activity, String text) { if (FileChooser.isValid(activity, Uri.parse(text))) file(activity, FileChooser.getPathFromUri(activity, Uri.parse(text))); @@ -302,6 +316,64 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo showProgress(); showDanmaku(); checkId(); + mHandler = new Handler(Looper.getMainLooper()); + initTimeBatteryUpdate(); + } + + private void initTimeBatteryUpdate() { + mBatteryReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { + int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); + int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); + if (level != -1 && scale != -1) { + mBatteryLevel = (int) ((level / (float) scale) * 100); + updateTimeBattery(); + } + } + } + }; + + mTimeUpdateRunnable = new Runnable() { + @Override + public void run() { + updateTimeBattery(); + mHandler.postDelayed(this, 30000); + } + }; + } + + private void updateTimeBattery() { + TextView timeBattery = mBinding.getRoot().findViewById(R.id.time_battery); + if (timeBattery == null) return; + + // 只在横屏模式下显示 + if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { + String time = DateFormat.getTimeFormat(this).format(System.currentTimeMillis()); + String battery = mBatteryLevel >= 0 ? mBatteryLevel + "%" : ""; + String text = time + (battery.isEmpty() ? "" : " | " + battery); + timeBattery.setText(text); + timeBattery.setVisibility(View.VISIBLE); + } else { + timeBattery.setVisibility(View.GONE); + } + } + + private void startTimeBatteryUpdates() { + registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + updateTimeBattery(); + mHandler.post(mTimeUpdateRunnable); + } + + private void stopTimeBatteryUpdates() { + try { + if (mBatteryReceiver != null) { + unregisterReceiver(mBatteryReceiver); + } + } catch (Exception e) { + } + mHandler.removeCallbacks(mTimeUpdateRunnable); } @Override @@ -960,6 +1032,7 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo mBinding.control.bottom.setVisibility(isLock() ? View.GONE : View.VISIBLE); mBinding.control.top.setVisibility(isLock() ? View.GONE : View.VISIBLE); mBinding.control.getRoot().setVisibility(View.VISIBLE); + updateTimeBattery(); setR1Callback(); checkPlayImg(); } @@ -1555,6 +1628,7 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo if (isAutoRotate() && isPort() && newConfig.orientation == Configuration.ORIENTATION_PORTRAIT && !isRotate()) exitFullscreen(); if (isAutoRotate() && isPort() && newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) enterFullscreen(); if (isFullscreen()) Util.hideSystemUI(this); + updateTimeBattery(); } @Override @@ -1574,6 +1648,7 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo @Override protected void onResume() { super.onResume(); + startTimeBatteryUpdates(); if (isRedirect()) onPlay(); setRedirect(false); } @@ -1581,6 +1656,7 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo @Override protected void onPause() { super.onPause(); + stopTimeBatteryUpdates(); if (isRedirect()) onPaused(); } @@ -1608,14 +1684,17 @@ public class VideoActivity extends BaseActivity implements Clock.Callback, Custo protected void onDestroy() { super.onDestroy(); stopSearch(); - mClock.release(); mPlayers.release(); + mClock.release(); Timer.get().reset(); RefreshEvent.history(); PlaybackService.stop(); + mHandler.removeCallbacksAndMessages(null); App.removeCallbacks(mR1, mR2, mR3, mR4); + EventBus.getDefault().unregister(this); mViewModel.result.removeObserver(mObserveDetail); mViewModel.player.removeObserver(mObservePlayer); mViewModel.search.removeObserver(mObserveSearch); + stopTimeBatteryUpdates(); } } diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/ConfigAdapter.java b/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/ConfigAdapter.java index 63ffad8d..03f115b7 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/ConfigAdapter.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/ConfigAdapter.java @@ -11,6 +11,7 @@ import com.fongmi.android.tv.api.config.VodConfig; import com.fongmi.android.tv.bean.Config; import com.fongmi.android.tv.databinding.AdapterConfigBinding; +import java.util.ArrayList; import java.util.List; public class ConfigAdapter extends RecyclerView.Adapter { @@ -26,19 +27,36 @@ public class ConfigAdapter extends RecyclerView.Adapter(); + List configs = Config.getAll(type); + Config currentConfig = type == 0 ? VodConfig.get().getConfig() : LiveConfig.get().getConfig(); + + for (Config config : configs) { + if (config.equals(currentConfig) || config.isEmpty()) continue; + mItems.add(config); + } + return this; } + public void addItem(Config item) { + if (item.isEmpty()) return; + + mItems.add(0, item); + notifyItemInserted(0); + } + public int remove(Config item) { + int position = mItems.indexOf(item); item.delete(); mItems.remove(item); - notifyDataSetChanged(); + notifyItemRemoved(position); return getItemCount(); } @@ -58,6 +76,7 @@ public class ConfigAdapter extends RecyclerView.Adapter mListener.onTextClick(item)); + holder.binding.copy.setOnClickListener(v -> mListener.onCopyClick(item)); holder.binding.delete.setOnClickListener(v -> mListener.onDeleteClick(item)); } diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/TypeAdapter.java b/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/TypeAdapter.java index fd90a30c..482db8f2 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/TypeAdapter.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/adapter/TypeAdapter.java @@ -44,7 +44,6 @@ public class TypeAdapter extends RecyclerView.Adapter { public void addAll(Result result) { mItems.addAll(result.getTypes()); - if (!result.getList().isEmpty()) mItems.add(0, home()); if (!mItems.isEmpty()) mItems.get(0).setActivated(true); notifyDataSetChanged(); } diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/dialog/ConfigDialog.java b/app/src/mobile/java/com/fongmi/android/tv/ui/dialog/ConfigDialog.java index e61daa8d..16776a2f 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/dialog/ConfigDialog.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/dialog/ConfigDialog.java @@ -144,8 +144,18 @@ public class ConfigDialog { private void onPositive(DialogInterface dialog, int which) { String url = binding.url.getText().toString().trim(); String name = binding.name.getText().toString().trim(); + + // 如果是编辑模式,更新现有配置 if (edit) Config.find(ori, type).url(url).name(name).update(); - if (url.isEmpty()) Config.delete(ori, type); + + // 如果URL为空,删除配置 + if (url.isEmpty()) { + Config.delete(ori, type); + dialog.dismiss(); + return; + } + + // 只有URL不为空时,才设置配置 callback.setConfig(Config.find(url, type)); dialog.dismiss(); } diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/dialog/HistoryDialog.java b/app/src/mobile/java/com/fongmi/android/tv/ui/dialog/HistoryDialog.java index dbed6096..25f3339c 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/dialog/HistoryDialog.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/dialog/HistoryDialog.java @@ -73,6 +73,13 @@ public class HistoryDialog implements ConfigAdapter.OnClickListener { @Override public void onDeleteClick(Config item) { - if (adapter.remove(item) == 0) dialog.dismiss(); + int count = adapter.remove(item); + if (count == 0) { + dialog.dismiss(); + } else { + // 强制重新测量布局高度 + binding.recycler.requestLayout(); + dialog.getWindow().setLayout(dialog.getWindow().getAttributes().width, android.view.ViewGroup.LayoutParams.WRAP_CONTENT); + } } } 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 b582d6d7..1481b488 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 @@ -3,9 +3,15 @@ package com.fongmi.android.tv.ui.fragment; import android.Manifest; import android.app.Activity; import android.content.Intent; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.TextUtils; +import android.text.style.ForegroundColorSpan; +import android.text.style.RelativeSizeSpan; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -32,6 +38,7 @@ import com.fongmi.android.tv.impl.SiteCallback; import com.fongmi.android.tv.player.Source; import com.fongmi.android.tv.ui.activity.HomeActivity; import com.fongmi.android.tv.ui.base.BaseFragment; +import com.fongmi.android.tv.ui.dialog.AboutDialog; import com.fongmi.android.tv.ui.dialog.ConfigDialog; import com.fongmi.android.tv.ui.dialog.HistoryDialog; import com.fongmi.android.tv.ui.dialog.LiveDialog; @@ -91,9 +98,9 @@ public class SettingFragment extends BaseFragment implements ConfigCallback, Sit @Override protected void initView() { - mBinding.vodUrl.setText(VodConfig.getDesc()); - mBinding.liveUrl.setText(LiveConfig.getDesc()); - mBinding.wallUrl.setText(WallConfig.getDesc()); + setSourceHintText(mBinding.vodUrl, VodConfig.getDesc()); + setSourceHintText(mBinding.liveUrl, LiveConfig.getDesc()); + setSourceHintText(mBinding.wallUrl, WallConfig.getDesc()); mBinding.versionText.setText(BuildConfig.VERSION_NAME); // 设置开关的颜色为黄色 @@ -145,6 +152,7 @@ public class SettingFragment extends BaseFragment implements ConfigCallback, Sit mBinding.player.setOnClickListener(this::onPlayer); mBinding.restore.setOnClickListener(this::onRestore); mBinding.version.setOnClickListener(this::onVersion); + mBinding.about.setOnClickListener(this::onAbout); mBinding.vod.setOnLongClickListener(this::onVodEdit); mBinding.vodHome.setOnClickListener(this::onVodHome); mBinding.live.setOnLongClickListener(this::onLiveEdit); @@ -162,6 +170,9 @@ public class SettingFragment extends BaseFragment implements ConfigCallback, Sit @Override public void setConfig(Config config) { + // 如果URL为空,不进行任何操作 + if (config.isEmpty()) return; + if (config.getUrl().startsWith("file") && !PermissionX.isGranted(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) { PermissionX.init(this).permissions(Manifest.permission.WRITE_EXTERNAL_STORAGE).request((allGranted, grantedList, deniedList) -> load(config)); } else { @@ -204,7 +215,18 @@ public class SettingFragment extends BaseFragment implements ConfigCallback, Sit @Override public void error(String msg) { Notify.show(msg); - setConfig(type); + Notify.dismiss(); + switch (type) { + case 0: + setSourceHintText(mBinding.vodUrl, VodConfig.getDesc()); + break; + case 1: + setSourceHintText(mBinding.liveUrl, LiveConfig.getDesc()); + break; + case 2: + setSourceHintText(mBinding.wallUrl, WallConfig.getDesc()); + break; + } } }; } @@ -216,24 +238,37 @@ public class SettingFragment extends BaseFragment implements ConfigCallback, Sit Notify.dismiss(); RefreshEvent.video(); RefreshEvent.config(); - mBinding.vodUrl.setText(VodConfig.getDesc()); - mBinding.liveUrl.setText(LiveConfig.getDesc()); - mBinding.wallUrl.setText(WallConfig.getDesc()); + setSourceHintText(mBinding.vodUrl, VodConfig.getDesc()); + setSourceHintText(mBinding.liveUrl, LiveConfig.getDesc()); + setSourceHintText(mBinding.wallUrl, WallConfig.getDesc()); break; case 1: setCacheText(); Notify.dismiss(); RefreshEvent.config(); - mBinding.liveUrl.setText(LiveConfig.getDesc()); + setSourceHintText(mBinding.liveUrl, LiveConfig.getDesc()); break; case 2: setCacheText(); Notify.dismiss(); - mBinding.wallUrl.setText(WallConfig.getDesc()); + setSourceHintText(mBinding.wallUrl, WallConfig.getDesc()); break; } } + private void setSourceHintText(TextView textView, String desc) { + if (TextUtils.isEmpty(desc)) { + SpannableString spannable = new SpannableString(getString(R.string.source_hint)); + spannable.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.white)), 0, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + spannable.setSpan(new RelativeSizeSpan(0.8f), 0, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + int alpha = (int)(255 * 0.5f); + spannable.setSpan(new ForegroundColorSpan(android.graphics.Color.argb(alpha, 255, 255, 255)), 0, spannable.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + textView.setText(spannable); + } else { + textView.setText(desc); + } + } + @Override public void setSite(Site item) { VodConfig.get().setHome(item); @@ -299,6 +334,10 @@ public class SettingFragment extends BaseFragment implements ConfigCallback, Sit private void onVersion(View view) { Updater.create().force().release().start(getActivity()); } + + private void onAbout(View view) { + AboutDialog.show(this); + } private boolean onVersionDev(View view) { Updater.create().force().dev().start(getActivity()); @@ -415,9 +454,9 @@ public class SettingFragment extends BaseFragment implements ConfigCallback, Sit @Override public void onHiddenChanged(boolean hidden) { if (hidden) return; - mBinding.vodUrl.setText(VodConfig.getDesc()); - mBinding.liveUrl.setText(LiveConfig.getDesc()); - mBinding.wallUrl.setText(WallConfig.getDesc()); + setSourceHintText(mBinding.vodUrl, VodConfig.getDesc()); + setSourceHintText(mBinding.liveUrl, LiveConfig.getDesc()); + setSourceHintText(mBinding.wallUrl, WallConfig.getDesc()); setCacheText(); } diff --git a/app/src/mobile/res/color-night/nav.xml b/app/src/mobile/res/color-night/nav.xml index 1d6ea80e..b3981e62 100644 --- a/app/src/mobile/res/color-night/nav.xml +++ b/app/src/mobile/res/color-night/nav.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/app/src/mobile/res/color/nav.xml b/app/src/mobile/res/color/nav.xml index 1d6ea80e..b3981e62 100644 --- a/app/src/mobile/res/color/nav.xml +++ b/app/src/mobile/res/color/nav.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/app/src/mobile/res/drawable/bg_nav_selected.xml b/app/src/mobile/res/drawable/bg_nav_selected.xml new file mode 100644 index 00000000..ea085c16 --- /dev/null +++ b/app/src/mobile/res/drawable/bg_nav_selected.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/drawable/ic_nav_live.xml b/app/src/mobile/res/drawable/ic_nav_live.xml index ffcecf33..549989c9 100644 --- a/app/src/mobile/res/drawable/ic_nav_live.xml +++ b/app/src/mobile/res/drawable/ic_nav_live.xml @@ -1,10 +1,18 @@ + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:fillAlpha="0" + android:strokeWidth="1.5" + android:strokeColor="#FF000000" + android:pathData="M3,6C3,4.89543 3.89543,4 5,4H19C20.1046,4 21,4.89543 21,6V18C21,19.1046 20.1046,20 19,20H5C3.89543,20 3,19.1046 3,18V6Z"/> + diff --git a/app/src/mobile/res/drawable/ic_nav_live_selected.xml b/app/src/mobile/res/drawable/ic_nav_live_selected.xml new file mode 100644 index 00000000..0f7e5f99 --- /dev/null +++ b/app/src/mobile/res/drawable/ic_nav_live_selected.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/app/src/mobile/res/drawable/ic_nav_vod.xml b/app/src/mobile/res/drawable/ic_nav_vod.xml index 2a9172ca..8bc75346 100644 --- a/app/src/mobile/res/drawable/ic_nav_vod.xml +++ b/app/src/mobile/res/drawable/ic_nav_vod.xml @@ -1,7 +1,6 @@ + + \ No newline at end of file diff --git a/app/src/mobile/res/drawable/ic_setting_copy.xml b/app/src/mobile/res/drawable/ic_setting_copy.xml new file mode 100644 index 00000000..1419ed55 --- /dev/null +++ b/app/src/mobile/res/drawable/ic_setting_copy.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/app/src/mobile/res/drawable/ic_setting_doh.xml b/app/src/mobile/res/drawable/ic_setting_doh.xml new file mode 100644 index 00000000..fdad7480 --- /dev/null +++ b/app/src/mobile/res/drawable/ic_setting_doh.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/mobile/res/drawable/ic_setting_incognito.xml b/app/src/mobile/res/drawable/ic_setting_incognito.xml new file mode 100644 index 00000000..c08f9829 --- /dev/null +++ b/app/src/mobile/res/drawable/ic_setting_incognito.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/mobile/res/drawable/ic_setting_size.xml b/app/src/mobile/res/drawable/ic_setting_size.xml new file mode 100644 index 00000000..33c3c6db --- /dev/null +++ b/app/src/mobile/res/drawable/ic_setting_size.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/mobile/res/drawable/selector_nav_live.xml b/app/src/mobile/res/drawable/selector_nav_live.xml new file mode 100644 index 00000000..a98570b5 --- /dev/null +++ b/app/src/mobile/res/drawable/selector_nav_live.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/drawable/selector_nav_setting.xml b/app/src/mobile/res/drawable/selector_nav_setting.xml new file mode 100644 index 00000000..c65f4ec8 --- /dev/null +++ b/app/src/mobile/res/drawable/selector_nav_setting.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/drawable/selector_nav_vod.xml b/app/src/mobile/res/drawable/selector_nav_vod.xml new file mode 100644 index 00000000..94ab5a41 --- /dev/null +++ b/app/src/mobile/res/drawable/selector_nav_vod.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/drawable/shape_about_button.xml b/app/src/mobile/res/drawable/shape_about_button.xml new file mode 100644 index 00000000..795bf3ee --- /dev/null +++ b/app/src/mobile/res/drawable/shape_about_button.xml @@ -0,0 +1,15 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/drawable/shape_about_dialog.xml b/app/src/mobile/res/drawable/shape_about_dialog.xml new file mode 100644 index 00000000..d65639d6 --- /dev/null +++ b/app/src/mobile/res/drawable/shape_about_dialog.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/drawable/shape_dialog_button.xml b/app/src/mobile/res/drawable/shape_dialog_button.xml index 91207197..2a52f166 100644 --- a/app/src/mobile/res/drawable/shape_dialog_button.xml +++ b/app/src/mobile/res/drawable/shape_dialog_button.xml @@ -4,9 +4,6 @@ - + + + + \ No newline at end of file diff --git a/app/src/mobile/res/layout-land/view_control_vod.xml b/app/src/mobile/res/layout-land/view_control_vod.xml new file mode 100644 index 00000000..91f8108d --- /dev/null +++ b/app/src/mobile/res/layout-land/view_control_vod.xml @@ -0,0 +1,235 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/mobile/res/layout/activity_home.xml b/app/src/mobile/res/layout/activity_home.xml index 277a91d6..16810611 100644 --- a/app/src/mobile/res/layout/activity_home.xml +++ b/app/src/mobile/res/layout/activity_home.xml @@ -14,14 +14,14 @@ diff --git a/app/src/mobile/res/layout/adapter_config.xml b/app/src/mobile/res/layout/adapter_config.xml index 4ddeb35e..5265dcd6 100644 --- a/app/src/mobile/res/layout/adapter_config.xml +++ b/app/src/mobile/res/layout/adapter_config.xml @@ -6,17 +6,37 @@ android:gravity="center_vertical" android:orientation="horizontal"> - + android:layout_weight="1"> + + + + + + + + + + + + + + + + + + +