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 ba1fbca7f..93bef9e26 100644 --- a/app/src/main/java/com/fongmi/android/tv/App.java +++ b/app/src/main/java/com/fongmi/android/tv/App.java @@ -106,6 +106,7 @@ public class App extends Application { super.onCreate(); Notify.createChannel(); Logger.addLogAdapter(getLogAdapter()); + OkHttp.get().setProxy(Setting.getProxy()); OkHttp.get().setDoh(Doh.objectFrom(Setting.getDoh())); CaocConfig.Builder.create().backgroundMode(CaocConfig.BACKGROUND_MODE_SILENT).errorActivity(CrashActivity.class).apply(); registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { 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 e0e572d62..8bfe3c2ef 100644 --- a/app/src/main/java/com/fongmi/android/tv/Setting.java +++ b/app/src/main/java/com/fongmi/android/tv/Setting.java @@ -14,6 +14,14 @@ public class Setting { Prefers.put("doh", doh); } + public static String getProxy() { + return Prefers.getString("proxy"); + } + + public static void putProxy(String proxy) { + Prefers.put("proxy", proxy); + } + public static String getKeep() { return Prefers.getString("keep"); } diff --git a/app/src/main/java/com/fongmi/android/tv/impl/ProxyCallback.java b/app/src/main/java/com/fongmi/android/tv/impl/ProxyCallback.java new file mode 100644 index 000000000..b9993a642 --- /dev/null +++ b/app/src/main/java/com/fongmi/android/tv/impl/ProxyCallback.java @@ -0,0 +1,6 @@ +package com.fongmi.android.tv.impl; + +public interface ProxyCallback { + + void setProxy(String proxy); +} 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 93da6a8bc..37b03f017 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 @@ -170,7 +170,7 @@ public class ExoUtil { } private static synchronized HttpDataSource.Factory getHttpDataSourceFactory() { - if (httpDataSourceFactory == null) httpDataSourceFactory = Setting.getHttp() == 0 ? new DefaultHttpDataSource.Factory().setAllowCrossProtocolRedirects(true) : new OkHttpDataSource.Factory((Call.Factory) OkHttp.client()); + if (httpDataSourceFactory == null) httpDataSourceFactory = Setting.getHttp() == 0 ? new DefaultHttpDataSource.Factory().setAllowCrossProtocolRedirects(true).setProxy(Setting.getProxy()) : new OkHttpDataSource.Factory((Call.Factory) OkHttp.client()); return httpDataSourceFactory; } diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 2c5b0cf93..054cbb3be 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -78,6 +78,7 @@ 图片品质 图片尺寸 DoH + Proxy 缓存 版本 权限 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 6c2f8ad45..6d4a4063a 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -78,6 +78,7 @@ 圖片品質 圖片尺寸 DoH + Proxy 暫存 版本 權限 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 766f66adf..0485c7a63 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -78,6 +78,7 @@ Image quality Image size DoH + Proxy Cache Version Permission diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/ProxyDialog.java b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/ProxyDialog.java new file mode 100644 index 000000000..4daa1cbac --- /dev/null +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/ProxyDialog.java @@ -0,0 +1,91 @@ +package com.fongmi.android.tv.ui.custom.dialog; + +import android.content.DialogInterface; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.inputmethod.EditorInfo; + +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.Fragment; + +import com.fongmi.android.tv.R; +import com.fongmi.android.tv.Setting; +import com.fongmi.android.tv.databinding.DialogProxyBinding; +import com.fongmi.android.tv.impl.ProxyCallback; +import com.fongmi.android.tv.ui.custom.CustomTextListener; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + +public class ProxyDialog { + + private final DialogProxyBinding binding; + private final ProxyCallback callback; + private AlertDialog dialog; + private boolean append; + + public static ProxyDialog create(Fragment fragment) { + return new ProxyDialog(fragment); + } + + public ProxyDialog(Fragment fragment) { + this.callback = (ProxyCallback) fragment; + this.binding = DialogProxyBinding.inflate(LayoutInflater.from(fragment.getContext())); + this.append = true; + } + + public void show() { + initDialog(); + initView(); + initEvent(); + } + + private void initDialog() { + dialog = new MaterialAlertDialogBuilder(binding.getRoot().getContext()).setTitle(R.string.setting_proxy).setView(binding.getRoot()).setPositiveButton(R.string.dialog_positive, this::onPositive).setNegativeButton(R.string.dialog_negative, this::onNegative).create(); + dialog.getWindow().setDimAmount(0); + dialog.show(); + } + + private void initView() { + String text = Setting.getProxy(); + binding.text.setText(text); + binding.text.setSelection(TextUtils.isEmpty(text) ? 0 : text.length()); + } + + private void initEvent() { + binding.text.addTextChangedListener(new CustomTextListener() { + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + detect(s.toString()); + } + }); + binding.text.setOnEditorActionListener((textView, actionId, event) -> { + if (actionId == EditorInfo.IME_ACTION_DONE) dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick(); + return true; + }); + } + + private void detect(String s) { + if (append && s.equals("h")) { + append = false; + binding.text.append("ttp://"); + } else if (append && s.equals("s")) { + append = false; + binding.text.append("ocks5://"); + } else if (append && s.length() == 1) { + append = false; + binding.text.getText().insert(0, "socks5://"); + } else if (s.length() > 1) { + append = false; + } else if (s.length() == 0) { + append = true; + } + } + + private void onPositive(DialogInterface dialog, int which) { + callback.setProxy(binding.text.getText().toString().trim()); + dialog.dismiss(); + } + + private void onNegative(DialogInterface dialog, int which) { + dialog.dismiss(); + } +} diff --git a/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/UaDialog.java b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/UaDialog.java index 029f1b88b..6285057d4 100644 --- a/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/UaDialog.java +++ b/app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/UaDialog.java @@ -42,9 +42,9 @@ public class UaDialog { } private void initView() { - String ua = Setting.getUa(); - binding.text.setText(ua); - binding.text.setSelection(TextUtils.isEmpty(ua) ? 0 : ua.length()); + String text = Setting.getUa(); + binding.text.setText(text); + binding.text.setSelection(TextUtils.isEmpty(text) ? 0 : text.length()); } private void initEvent() { 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 be41cbe07..aa874d851 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 @@ -26,12 +26,14 @@ import com.fongmi.android.tv.event.RefreshEvent; import com.fongmi.android.tv.impl.Callback; import com.fongmi.android.tv.impl.ConfigCallback; import com.fongmi.android.tv.impl.LiveCallback; +import com.fongmi.android.tv.impl.ProxyCallback; import com.fongmi.android.tv.impl.SiteCallback; import com.fongmi.android.tv.ui.activity.MainActivity; import com.fongmi.android.tv.ui.base.BaseFragment; 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.ProxyDialog; import com.fongmi.android.tv.ui.custom.dialog.SiteDialog; import com.fongmi.android.tv.utils.FileChooser; import com.fongmi.android.tv.utils.FileUtil; @@ -41,13 +43,14 @@ import com.fongmi.android.tv.utils.Utils; import com.github.catvod.bean.Doh; import com.github.catvod.net.OkHttp; import com.github.catvod.utils.Path; +import com.github.catvod.utils.Util; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.permissionx.guolindev.PermissionX; import java.util.ArrayList; import java.util.List; -public class SettingFragment extends BaseFragment implements ConfigCallback, SiteCallback, LiveCallback { +public class SettingFragment extends BaseFragment implements ConfigCallback, SiteCallback, LiveCallback, ProxyCallback { private FragmentSettingBinding mBinding; private String[] render; @@ -87,6 +90,7 @@ public class SettingFragment extends BaseFragment implements ConfigCallback, Sit mBinding.wallUrl.setText(WallConfig.getDesc()); mBinding.dohText.setText(getDohList()[getDohIndex()]); mBinding.versionText.setText(BuildConfig.VERSION_NAME); + mBinding.proxyText.setText(Util.scheme(Setting.getProxy())); mBinding.sizeText.setText((size = ResUtil.getStringArray(R.array.select_size))[Setting.getSize()]); mBinding.scaleText.setText((scale = ResUtil.getStringArray(R.array.select_scale))[Setting.getScale()]); mBinding.playerText.setText((player = ResUtil.getStringArray(R.array.select_player))[Setting.getPlayer()]); @@ -109,6 +113,7 @@ public class SettingFragment extends BaseFragment implements ConfigCallback, Sit mBinding.vod.setOnClickListener(this::onVod); mBinding.live.setOnClickListener(this::onLive); mBinding.wall.setOnClickListener(this::onWall); + mBinding.proxy.setOnClickListener(this::onProxy); mBinding.cache.setOnClickListener(this::onCache); mBinding.version.setOnClickListener(this::onVersion); mBinding.vodHome.setOnClickListener(this::onVodHome); @@ -318,6 +323,19 @@ public class SettingFragment extends BaseFragment implements ConfigCallback, Sit ApiConfig.load(Config.vod(), getCallback()); } + private void onProxy(View view) { + ProxyDialog.create(this).show(); + } + + @Override + public void setProxy(String proxy) { + Setting.putProxy(proxy); + OkHttp.get().setProxy(proxy); + Notify.progress(getActivity()); + ApiConfig.load(Config.vod(), getCallback()); + mBinding.proxyText.setText(Util.scheme(proxy)); + } + private void onCache(View view) { FileUtil.clearCache(new Callback() { @Override diff --git a/app/src/mobile/res/layout/dialog_proxy.xml b/app/src/mobile/res/layout/dialog_proxy.xml new file mode 100644 index 000000000..f69ec08f5 --- /dev/null +++ b/app/src/mobile/res/layout/dialog_proxy.xml @@ -0,0 +1,25 @@ + + + + + + + + + \ 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 06f082a64..b4515cb8f 100644 --- a/app/src/mobile/res/layout/fragment_setting.xml +++ b/app/src/mobile/res/layout/fragment_setting.xml @@ -372,6 +372,33 @@ + + + + + + + + params) { + FormBody.Builder body = new FormBody.Builder(); + for (Map.Entry entry : params.entrySet()) body.add(entry.getKey(), entry.getValue()); + return 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()); return builder.build(); } - public static FormBody toBody(ArrayMap params) { - FormBody.Builder body = new FormBody.Builder(); - for (Map.Entry entry : params.entrySet()) body.add(entry.getKey(), entry.getValue()); - return body.build(); + private static void setProxy(OkHttpClient.Builder builder) { + Uri uri = Uri.parse(proxy()); + if (uri.getHost() == null) return; + String userInfo = uri.getUserInfo(); + if (Util.scheme(uri).startsWith("socks")) { + builder.proxy(new Proxy(Proxy.Type.SOCKS, InetSocketAddress.createUnresolved(uri.getHost(), uri.getPort()))); + } + if (Util.scheme(uri).startsWith("http")) { + builder.proxy(new Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(uri.getHost(), uri.getPort()))); + if (userInfo != null && userInfo.contains(":")) builder.proxyAuthenticator((route, response) -> { + String credential = Credentials.basic(userInfo.split(":")[0], userInfo.split(":")[1]); + return response.request().newBuilder().header("Proxy-Authorization", credential).build(); + }); + } + if (userInfo != null && userInfo.contains(":")) Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(userInfo.split(":")[0], userInfo.split(":")[1].toCharArray()); + } + }); } }