Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 26 additions & 16 deletions app/src/main/java/com/fongmi/android/tv/Updater.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import com.fongmi.android.tv.utils.Notify;
import com.fongmi.android.tv.utils.ResUtil;
import com.fongmi.android.tv.utils.Task;
import com.fongmi.android.tv.utils.UpdateApkSource;
import com.fongmi.android.tv.utils.UpdateDownloadQueue;
import com.github.catvod.net.OkHttp;
import com.github.catvod.utils.Path;

Expand Down Expand Up @@ -66,7 +68,7 @@ public class Updater implements Download.Callback, UpdateListener {
private long lastTotal;
private long lastSpeed;
private long lastElapsed;
private boolean fallbackAttempted;
private UpdateDownloadQueue downloadQueue;

private Updater() {
}
Expand Down Expand Up @@ -331,33 +333,46 @@ public void onConfirm(View view) {
view.setEnabled(false);
downloading = true;
canceled = false;
fallbackAttempted = false;
downloadQueue = new UpdateDownloadQueue(UpdateApkSource.buildCandidates(selected.apkUrl, selected.fallbackApkUrl, Setting.getUpdateApkSource(), Setting.getUpdateApkCustomPrefix()));
resetProgress();
Path.clear(getFile());
setDialogProgress(0, 0, selected.size, 0, 0);
startDownload(selected.apkUrl);
if (!startNextDownload()) finishDownloadError(ResUtil.getString(R.string.update_download_invalid));
}

private void startDownload(String url) {
download = Download.create(url, getFile()).tag(url);
download.start(this);
}

private boolean retryFallback() {
if (canceled || selected == null || fallbackAttempted || TextUtils.isEmpty(selected.fallbackApkUrl)) return false;
fallbackAttempted = true;
private boolean retryDownload() {
if (canceled || selected == null || downloadQueue == null) return false;
Path.clear(getFile());
resetProgress();
setDialogProgress(0, 0, selected.size, 0, 0);
startDownload(selected.fallbackApkUrl);
return startNextDownload();
}

private boolean startNextDownload() {
String url = downloadQueue == null ? null : downloadQueue.next();
if (TextUtils.isEmpty(url)) return false;
startDownload(url);
return true;
}

private void finishDownloadError(String message) {
downloading = false;
resetProgress();
Notify.show(message);
dismiss();
}

@Override
public void onCancel(View view) {
if (downloading) {
canceled = true;
downloading = false;
if (downloadQueue != null) downloadQueue.cancel();
if (download != null) download.cancel();
download = null;
resetProgress();
Expand Down Expand Up @@ -418,11 +433,8 @@ private void setDialogProgress(int progress, long bytes, long total, long speed,
public void error(String msg) {
if (canceled) return;
download = null;
if (retryFallback()) return;
downloading = false;
resetProgress();
Notify.show(msg);
dismiss();
if (retryDownload()) return;
finishDownloadError(msg);
}

@Override
Expand All @@ -439,10 +451,8 @@ public void success(File file) {
if (!TextUtils.isEmpty(error)) {
Path.clear(file);
downloading = true;
if (retryFallback()) return;
downloading = false;
Notify.show(error);
dismiss();
if (retryDownload()) return;
finishDownloadError(error);
return;
}
FileUtil.openFile(file);
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/com/fongmi/android/tv/bean/Backup.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class Backup {
public static final String PREF_WEB_HOME_EXTENSION = "web_home_extension";
public static final String PREF_WEB_HOME_EXTENSION_SOURCES = "web_home_extension_user_sources";

private static final Set<String> APP_PREFS = Set.of("doh", "ua", "wall", "wall_type", "reset", "site_mode", "site_block_keys", "search_column", "sync_mode", "sync_paths", "incognito", "drive_check", "drive_check_cache", "compact_episode_title", "web_home_fullscreen", "viewing_record_sync_enabled", "viewing_record_sync_local_write", "playback_remote_sync_config", "playback_webhook_config", "playback_webhook_privacy_accepted", "shell_proxy", "shell_proxy_rules", "shell_proxy_url", "shell_proxy_hosts", "update", "adblock", "zhuyin", "theme_color", "wall_color", "crash", "render", "pad_live_mode", "size", "scale", "buffer", "buffer_bytes", "back_buffer", "play_cache", "preload", "preload_threads", "preload_size", "preload_time", "player_auto_change", "background", "speed", "play_speed", "caption", "tunnel", "exo_4k_compat", "playback_performance_profile", "playback_performance_initialized", "perf_codec_async_queueing", "perf_dynamic_scheduling", "perf_video_duration_progress", "perf_late_drop_input", "perf_track_limit", "perf_adaptive_downgrade", "perf_load_only_selected_tracks", "perf_surface_fixed_size", "perf_decoder_fallback", "perf_soft_video_tune", "perf_high_buffer", "perf_bandwidth_meter", "player_button_order", "player_button_hidden", "audio_prefer", "video_prefer", "prefer_aac", "subtitle_text_size", "subtitle_position", "player_osd_title", "player_osd_resolution", "player_osd_time", "player_osd_progress", "player_osd_traffic", "player_osd_mini", "player_osd_diagnostics", "boot_live", "across", "change", "invert", "scale_live", "live_epg_url", "live_epg_history");
private static final Set<String> APP_PREFS = Set.of("doh", "ua", "wall", "wall_type", "reset", "site_mode", "site_block_keys", "search_column", "sync_mode", "sync_paths", "incognito", "drive_check", "drive_check_cache", "compact_episode_title", "web_home_fullscreen", "viewing_record_sync_enabled", "viewing_record_sync_local_write", "playback_remote_sync_config", "playback_webhook_config", "playback_webhook_privacy_accepted", "shell_proxy", "shell_proxy_rules", "shell_proxy_url", "shell_proxy_hosts", "update", "update_apk_source", "update_apk_custom_prefix", "adblock", "zhuyin", "theme_color", "wall_color", "crash", "render", "pad_live_mode", "size", "scale", "buffer", "buffer_bytes", "back_buffer", "play_cache", "preload", "preload_threads", "preload_size", "preload_time", "player_auto_change", "background", "speed", "play_speed", "caption", "tunnel", "exo_4k_compat", "playback_performance_profile", "playback_performance_initialized", "perf_codec_async_queueing", "perf_dynamic_scheduling", "perf_video_duration_progress", "perf_late_drop_input", "perf_track_limit", "perf_adaptive_downgrade", "perf_load_only_selected_tracks", "perf_surface_fixed_size", "perf_decoder_fallback", "perf_soft_video_tune", "perf_high_buffer", "perf_bandwidth_meter", "player_button_order", "player_button_hidden", "audio_prefer", "video_prefer", "prefer_aac", "subtitle_text_size", "subtitle_position", "player_osd_title", "player_osd_resolution", "player_osd_time", "player_osd_progress", "player_osd_traffic", "player_osd_mini", "player_osd_diagnostics", "boot_live", "across", "change", "invert", "scale_live", "live_epg_url", "live_epg_history");

@SerializedName("site")
private List<Site> site;
Expand Down
17 changes: 17 additions & 0 deletions app/src/main/java/com/fongmi/android/tv/setting/Setting.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.fongmi.android.tv.App;
import com.fongmi.android.tv.BuildConfig;
import com.fongmi.android.tv.bean.Update;
import com.fongmi.android.tv.utils.UpdateApkSource;
import com.fongmi.android.tv.utils.WebViewUtil;
import com.github.catvod.crawler.DebugLogStore;
import com.github.catvod.crawler.SpiderDebug;
Expand Down Expand Up @@ -661,6 +662,22 @@ public static void putUpdateChannel(String channel) {
Prefers.put("update_channel", Update.CHANNEL_BETA.equals(channel) ? Update.CHANNEL_BETA : Update.CHANNEL_STABLE);
}

public static String getUpdateApkSource() {
return UpdateApkSource.normalizeSource(Prefers.getString("update_apk_source", UpdateApkSource.GH_PROXY));
}

public static void putUpdateApkSource(String source) {
Prefers.put("update_apk_source", UpdateApkSource.normalizeSource(source));
}

public static String getUpdateApkCustomPrefix() {
return UpdateApkSource.normalizeCustomPrefix(Prefers.getString("update_apk_custom_prefix"));
}

public static void putUpdateApkCustomPrefix(String prefix) {
Prefers.put("update_apk_custom_prefix", UpdateApkSource.normalizeCustomPrefix(prefix));
}

public static boolean isAdblock() {
return Prefers.getBoolean("adblock", true);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public static void show(FragmentActivity activity, Runnable updateAction) {
configureContentHeight(activity, binding);

Dialog dialog = LightDialog.create(activity, null, binding.getRoot());
binding.updateSource.setText(UpdateApkSourceDialog.summary(activity));
binding.updateSource.setOnClickListener(v -> UpdateApkSourceDialog.show(activity, () -> binding.updateSource.setText(UpdateApkSourceDialog.summary(activity))));
binding.confirm.setOnClickListener(v -> dialog.dismiss());
binding.checkUpdate.setOnClickListener(v -> {
dialog.dismiss();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.fongmi.android.tv.ui.dialog;

import android.app.Dialog;
import android.content.Context;
import android.text.InputType;
import android.view.LayoutInflater;

import androidx.fragment.app.FragmentActivity;

import com.fongmi.android.tv.R;
import com.fongmi.android.tv.databinding.DialogUpdateApkSourceCustomBinding;
import com.fongmi.android.tv.setting.Setting;
import com.fongmi.android.tv.utils.UpdateApkSource;

public final class UpdateApkSourceDialog {

private static final String[] SOURCES = {
UpdateApkSource.GH_PROXY,
UpdateApkSource.GHFAST,
UpdateApkSource.DIRECT,
UpdateApkSource.CUSTOM
};

private UpdateApkSourceDialog() {
}

public static void show(FragmentActivity activity, Runnable onChanged) {
CharSequence[] labels = {
activity.getString(R.string.update_apk_source_gh_proxy),
activity.getString(R.string.update_apk_source_ghfast),
activity.getString(R.string.update_apk_source_direct),
activity.getString(R.string.update_apk_source_custom)
};
ChoiceDialog.showSingle(activity, R.string.update_apk_source_title, labels, sourceIndex(Setting.getUpdateApkSource()), which -> {
if (which < 0 || which >= SOURCES.length) return;
if (UpdateApkSource.CUSTOM.equals(SOURCES[which])) {
showCustom(activity, onChanged);
return;
}
Setting.putUpdateApkSource(SOURCES[which]);
if (onChanged != null) onChanged.run();
});
}

public static String summary(Context context) {
int label = switch (Setting.getUpdateApkSource()) {
case UpdateApkSource.GHFAST -> R.string.update_apk_source_ghfast;
case UpdateApkSource.DIRECT -> R.string.update_apk_source_direct;
case UpdateApkSource.CUSTOM -> R.string.update_apk_source_custom;
default -> R.string.update_apk_source_gh_proxy;
};
return context.getString(R.string.about_update_apk_source_value, context.getString(label));
}

private static int sourceIndex(String source) {
for (int i = 0; i < SOURCES.length; i++) if (SOURCES[i].equals(source)) return i;
return 0;
}

private static void showCustom(FragmentActivity activity, Runnable onChanged) {
DialogUpdateApkSourceCustomBinding binding = DialogUpdateApkSourceCustomBinding.inflate(LayoutInflater.from(activity));
binding.input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
binding.input.setText(Setting.getUpdateApkCustomPrefix());
binding.input.setSelection(binding.input.length());

Dialog dialog = LightDialog.create(activity, activity.getString(R.string.update_apk_source_custom_title), binding.getRoot());
binding.cancel.setOnClickListener(view -> dialog.dismiss());
binding.save.setOnClickListener(view -> {
String value = binding.input.getText() == null ? "" : binding.input.getText().toString();
String prefix = UpdateApkSource.normalizeCustomPrefix(value);
if (prefix.isEmpty()) {
binding.inputLayout.setError(activity.getString(R.string.update_apk_source_custom_invalid));
binding.input.requestFocus();
return;
}
binding.inputLayout.setError(null);
Setting.putUpdateApkCustomPrefix(prefix);
Setting.putUpdateApkSource(UpdateApkSource.CUSTOM);
dialog.dismiss();
if (onChanged != null) onChanged.run();
});
dialog.setCanceledOnTouchOutside(false);
dialog.show();
binding.save.requestFocus();
}
}
106 changes: 106 additions & 0 deletions app/src/main/java/com/fongmi/android/tv/utils/UpdateApkSource.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package com.fongmi.android.tv.utils;

import java.net.URI;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

public final class UpdateApkSource {

public static final String GH_PROXY = "gh_proxy";
public static final String GHFAST = "ghfast";
public static final String DIRECT = "direct";
public static final String CUSTOM = "custom";

public static final String GH_PROXY_PREFIX = "https://gh-proxy.com/";
public static final String GHFAST_PREFIX = "https://ghfast.top/";

private UpdateApkSource() {
}

public static String normalizeSource(String source) {
if (GHFAST.equals(source) || DIRECT.equals(source) || CUSTOM.equals(source)) return source;
return GH_PROXY;
}

public static String normalizeCustomPrefix(String value) {
if (value == null) return "";
String prefix = value.trim();
if (!isValidCustomPrefix(prefix)) return "";
return prefix.replaceAll("/+$", "") + "/";
}

public static boolean isValidCustomPrefix(String value) {
if (value == null || value.isBlank()) return false;
try {
URI uri = URI.create(value.trim());
String scheme = uri.getScheme();
return ("http".equalsIgnoreCase(scheme) || "https".equalsIgnoreCase(scheme))
&& uri.getHost() != null
&& !uri.getHost().isBlank()
&& uri.getRawQuery() == null
&& uri.getRawFragment() == null;
} catch (Exception e) {
return false;
}
}

public static boolean isGithubUrl(String value) {
if (value == null || value.isBlank()) return false;
try {
URI uri = URI.create(value.trim());
return "github.com".equalsIgnoreCase(uri.getHost())
&& ("http".equalsIgnoreCase(uri.getScheme()) || "https".equalsIgnoreCase(uri.getScheme()));
} catch (Exception e) {
return false;
}
}

public static String applyPrefix(String prefix, String githubUrl) {
String normalized = normalizeCustomPrefix(prefix);
if (normalized.isEmpty() || !isGithubUrl(githubUrl)) return githubUrl;
return normalized + githubUrl.trim();
}

public static List<String> buildCandidates(String primaryUrl, String fallbackUrl, String source, String customPrefix) {
Set<String> github = new LinkedHashSet<>();
Set<String> other = new LinkedHashSet<>();
collect(primaryUrl, github, other);
collect(fallbackUrl, github, other);

Set<String> candidates = new LinkedHashSet<>();
for (String url : github) addGithubCandidates(candidates, url, normalizeSource(source), customPrefix);
candidates.addAll(other);
return new ArrayList<>(candidates);
}

private static void collect(String value, Set<String> github, Set<String> other) {
if (value == null || value.isBlank()) return;
String url = value.trim();
(isGithubUrl(url) ? github : other).add(url);
}

private static void addGithubCandidates(Set<String> candidates, String url, String source, String customPrefix) {
switch (source) {
case DIRECT -> candidates.add(url);
case GHFAST -> {
candidates.add(applyPrefix(GHFAST_PREFIX, url));
candidates.add(applyPrefix(GH_PROXY_PREFIX, url));
candidates.add(url);
}
case CUSTOM -> {
String custom = applyPrefix(customPrefix, url);
if (!url.equals(custom)) candidates.add(custom);
candidates.add(applyPrefix(GH_PROXY_PREFIX, url));
candidates.add(applyPrefix(GHFAST_PREFIX, url));
candidates.add(url);
}
default -> {
candidates.add(applyPrefix(GH_PROXY_PREFIX, url));
candidates.add(applyPrefix(GHFAST_PREFIX, url));
candidates.add(url);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.fongmi.android.tv.utils;

import java.util.ArrayList;
import java.util.List;

public final class UpdateDownloadQueue {

private final List<String> candidates;
private int index = -1;
private boolean canceled;

public UpdateDownloadQueue(List<String> candidates) {
this.candidates = candidates == null ? List.of() : new ArrayList<>(candidates);
}

public String next() {
if (canceled || index + 1 >= candidates.size()) return null;
return candidates.get(++index);
}

public void cancel() {
canceled = true;
}

public int size() {
return candidates.size();
}
}
18 changes: 17 additions & 1 deletion app/src/main/res/layout/dialog_about.xml
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,26 @@

</androidx.core.widget.NestedScrollView>

<com.google.android.material.button.MaterialButton
android:id="@+id/updateSource"
style="@style/Widget.Material3.Button.TonalButton"
android:layout_width="match_parent"
android:layout_height="44dp"
android:layout_marginTop="18dp"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:minHeight="44dp"
android:text="@string/about_update_apk_source"
android:textColor="@color/dialog_tonal_button_text"
android:textSize="14sp"
app:backgroundTint="@color/dialog_tonal_button_bg"
app:cornerRadius="8dp" />

<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="18dp"
android:layout_marginTop="10dp"
android:gravity="center_vertical"
android:orientation="horizontal">

Expand Down
Loading