[mobile] support proxy

pull/142/head
FongMi 3 years ago
parent 021c9d04d8
commit cab70f4eee
  1. 1
      app/src/main/java/com/fongmi/android/tv/App.java
  2. 8
      app/src/main/java/com/fongmi/android/tv/Setting.java
  3. 6
      app/src/main/java/com/fongmi/android/tv/impl/ProxyCallback.java
  4. 2
      app/src/main/java/com/fongmi/android/tv/player/ExoUtil.java
  5. 1
      app/src/main/res/values-zh-rCN/strings.xml
  6. 1
      app/src/main/res/values-zh-rTW/strings.xml
  7. 1
      app/src/main/res/values/strings.xml
  8. 91
      app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/ProxyDialog.java
  9. 6
      app/src/mobile/java/com/fongmi/android/tv/ui/custom/dialog/UaDialog.java
  10. 20
      app/src/mobile/java/com/fongmi/android/tv/ui/fragment/SettingFragment.java
  11. 25
      app/src/mobile/res/layout/dialog_proxy.xml
  12. 27
      app/src/mobile/res/layout/fragment_setting.xml
  13. 4
      catvod/src/main/java/com/github/catvod/crawler/Spider.java
  14. 52
      catvod/src/main/java/com/github/catvod/net/OkHttp.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() {

@ -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");
}

@ -0,0 +1,6 @@
package com.fongmi.android.tv.impl;
public interface ProxyCallback {
void setProxy(String proxy);
}

@ -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;
}

@ -78,6 +78,7 @@
<string name="setting_quality">图片品质</string>
<string name="setting_size">图片尺寸</string>
<string name="setting_doh">DoH</string>
<string name="setting_proxy">Proxy</string>
<string name="setting_cache">缓存</string>
<string name="setting_version">版本</string>
<string name="setting_storage">权限</string>

@ -78,6 +78,7 @@
<string name="setting_quality">圖片品質</string>
<string name="setting_size">圖片尺寸</string>
<string name="setting_doh">DoH</string>
<string name="setting_proxy">Proxy</string>
<string name="setting_cache">暫存</string>
<string name="setting_version">版本</string>
<string name="setting_storage">權限</string>

@ -78,6 +78,7 @@
<string name="setting_quality">Image quality</string>
<string name="setting_size">Image size</string>
<string name="setting_doh">DoH</string>
<string name="setting_proxy">Proxy</string>
<string name="setting_cache">Cache</string>
<string name="setting_version">Version</string>
<string name="setting_storage">Permission</string>

@ -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();
}
}

@ -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() {

@ -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

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="24dp"
android:paddingTop="16dp"
android:paddingEnd="24dp">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/input"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="actionDone"
android:importantForAutofill="no"
android:inputType="text"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>

@ -372,6 +372,33 @@
</LinearLayout>
<LinearLayout
android:id="@+id/proxy"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="@drawable/shape_item"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:text="@string/setting_proxy"
android:textColor="@color/white"
android:textSize="16sp" />
<TextView
android:id="@+id/proxyText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end"
android:textColor="@color/white"
android:textSize="16sp"
tools:text="http" />
</LinearLayout>
<LinearLayout
android:id="@+id/cache"
android:layout_width="match_parent"

@ -65,4 +65,8 @@ public abstract class Spider {
public static Dns safeDns() {
return OkHttp.dns();
}
public static String proxy() {
return OkHttp.proxy();
}
}

@ -1,6 +1,7 @@
package com.github.catvod.net;
import android.net.Uri;
import android.text.TextUtils;
import androidx.collection.ArrayMap;
@ -9,12 +10,17 @@ import com.github.catvod.utils.Path;
import com.github.catvod.utils.Util;
import com.google.common.net.HttpHeaders;
import java.net.Authenticator;
import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import okhttp3.Cache;
import okhttp3.Call;
import okhttp3.Credentials;
import okhttp3.Dns;
import okhttp3.FormBody;
import okhttp3.Headers;
@ -29,6 +35,7 @@ public class OkHttp {
private static final int TIMEOUT = 30 * 1000;
private static final int CACHE = 100 * 1024 * 1024;
private String proxy;
private DnsOverHttps dns;
private OkHttpClient client;
private OkHttpClient noRedirect;
@ -44,8 +51,15 @@ public class OkHttp {
public void setDoh(Doh doh) {
OkHttpClient dohClient = new OkHttpClient.Builder().cache(new Cache(Path.doh(), CACHE)).hostnameVerifier(SSLCompat.VERIFIER).sslSocketFactory(new SSLCompat(), SSLCompat.TM).build();
dns = doh.getUrl().isEmpty() ? null : new DnsOverHttps.Builder().client(dohClient).url(HttpUrl.get(doh.getUrl())).bootstrapDnsHosts(doh.getHosts()).build();
noRedirect = null;
client = null;
}
public void setProxy(String proxy) {
Authenticator.setDefault(null);
this.proxy = proxy;
noRedirect = null;
client = null;
}
public static OkHttpClient client() {
@ -62,8 +76,14 @@ public class OkHttp {
return get().dns != null ? get().dns : Dns.SYSTEM;
}
public static String proxy() {
return get().proxy;
}
public static OkHttpClient client(int timeout) {
return new OkHttpClient.Builder().addInterceptor(new DeflateInterceptor()).connectTimeout(timeout, TimeUnit.MILLISECONDS).readTimeout(timeout, TimeUnit.MILLISECONDS).writeTimeout(timeout, TimeUnit.MILLISECONDS).dns(dns()).hostnameVerifier(SSLCompat.VERIFIER).sslSocketFactory(new SSLCompat(), SSLCompat.TM).build();
OkHttpClient.Builder builder = new OkHttpClient.Builder().addInterceptor(new DeflateInterceptor()).connectTimeout(timeout, TimeUnit.MILLISECONDS).readTimeout(timeout, TimeUnit.MILLISECONDS).writeTimeout(timeout, TimeUnit.MILLISECONDS).dns(dns()).hostnameVerifier(SSLCompat.VERIFIER).sslSocketFactory(new SSLCompat(), SSLCompat.TM);
if (!TextUtils.isEmpty(proxy())) setProxy(builder);
return builder.build();
}
public static Call newCall(String url) {
@ -96,15 +116,37 @@ public class OkHttp {
return client.newCall(new Request.Builder().url(url).post(body).build());
}
public static FormBody toBody(ArrayMap<String, String> params) {
FormBody.Builder body = new FormBody.Builder();
for (Map.Entry<String, String> entry : params.entrySet()) body.add(entry.getKey(), entry.getValue());
return body.build();
}
private static HttpUrl buildUrl(String url, ArrayMap<String, String> params) {
HttpUrl.Builder builder = Objects.requireNonNull(HttpUrl.parse(url)).newBuilder();
for (Map.Entry<String, String> entry : params.entrySet()) builder.addQueryParameter(entry.getKey(), entry.getValue());
return builder.build();
}
public static FormBody toBody(ArrayMap<String, String> params) {
FormBody.Builder body = new FormBody.Builder();
for (Map.Entry<String, String> 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());
}
});
}
}

Loading…
Cancel
Save