mirror of https://github.com/NekoX-Dev/NekoX.git
This commit is contained in:
parent
6c7fbedf04
commit
f92cf1265d
|
@ -130,7 +130,7 @@ public class SharedConfig {
|
|||
loadConfig();
|
||||
}
|
||||
|
||||
public static class ProxyInfo {
|
||||
public static class ProxyInfo implements Comparable<ProxyInfo> {
|
||||
|
||||
public int group;
|
||||
|
||||
|
@ -146,6 +146,11 @@ public class SharedConfig {
|
|||
public boolean available;
|
||||
public long availableCheckTime;
|
||||
|
||||
@Override
|
||||
public int compareTo(ProxyInfo info) {
|
||||
return (int) (info.ping - ping);
|
||||
}
|
||||
|
||||
public boolean isInternal = false;
|
||||
public String descripton;
|
||||
|
||||
|
@ -1534,6 +1539,18 @@ public class SharedConfig {
|
|||
saveProxyList();
|
||||
}
|
||||
|
||||
public static void deleteAllProxy() {
|
||||
|
||||
proxyListLoaded = false;
|
||||
|
||||
proxyList.clear();
|
||||
|
||||
saveProxyList();
|
||||
|
||||
loadProxyList();
|
||||
|
||||
}
|
||||
|
||||
public static void checkSaveToGalleryFiles() {
|
||||
try {
|
||||
File telegramPath;
|
||||
|
|
|
@ -153,6 +153,12 @@ public class DocumentSelectActivity extends BaseFragment {
|
|||
private boolean canSelectOnlyImageFiles;
|
||||
private boolean allowMusic;
|
||||
|
||||
public void setAllowPhoto(boolean allowPhoto) {
|
||||
this.allowPhoto = allowPhoto;
|
||||
}
|
||||
|
||||
private boolean allowPhoto = true;
|
||||
|
||||
private boolean searching;
|
||||
private boolean searchWas;
|
||||
|
||||
|
@ -1328,12 +1334,16 @@ public class DocumentSelectActivity extends BaseFragment {
|
|||
FileLog.e(e);
|
||||
}
|
||||
|
||||
fs = new ListItem();
|
||||
fs.title = LocaleController.getString("Gallery", R.string.Gallery);
|
||||
fs.subtitle = LocaleController.getString("GalleryInfo", R.string.GalleryInfo);
|
||||
fs.icon = R.drawable.files_gallery;
|
||||
fs.file = null;
|
||||
items.add(fs);
|
||||
if (allowPhoto) {
|
||||
|
||||
fs = new ListItem();
|
||||
fs.title = LocaleController.getString("Gallery", R.string.Gallery);
|
||||
fs.subtitle = LocaleController.getString("GalleryInfo", R.string.GalleryInfo);
|
||||
fs.icon = R.drawable.files_gallery;
|
||||
fs.file = null;
|
||||
items.add(fs);
|
||||
|
||||
}
|
||||
|
||||
if (allowMusic) {
|
||||
fs = new ListItem();
|
||||
|
|
|
@ -12,16 +12,18 @@ import android.Manifest;
|
|||
import android.annotation.SuppressLint;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemClock;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
|
@ -30,16 +32,23 @@ import android.widget.FrameLayout;
|
|||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.FileProvider;
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.telegram.messenger.AndroidUtilities;
|
||||
import org.telegram.messenger.ApplicationLoader;
|
||||
import org.telegram.messenger.DownloadController;
|
||||
import org.telegram.messenger.LocaleController;
|
||||
import org.telegram.messenger.MessagesController;
|
||||
import org.telegram.messenger.NotificationCenter;
|
||||
import org.telegram.messenger.R;
|
||||
import org.telegram.messenger.BuildConfig;
|
||||
import org.telegram.messenger.SendMessagesHelper;
|
||||
import org.telegram.messenger.SharedConfig;
|
||||
import org.telegram.messenger.browser.Browser;
|
||||
import org.telegram.tgnet.ConnectionsManager;
|
||||
|
@ -58,12 +67,21 @@ import org.telegram.ui.Cells.TextSettingsCell;
|
|||
import org.telegram.ui.Components.LayoutHelper;
|
||||
import org.telegram.ui.Components.RecyclerListView;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import okhttp3.HttpUrl;
|
||||
import tw.nekomimi.nekogram.ShadowsocksRSettingsActivity;
|
||||
import tw.nekomimi.nekogram.ShadowsocksSettingsActivity;
|
||||
import tw.nekomimi.nekogram.VmessSettingsActivity;
|
||||
import tw.nekomimi.nekogram.utils.AlertUtil;
|
||||
import tw.nekomimi.nekogram.utils.FileUtil;
|
||||
import tw.nekomimi.nekogram.utils.ProxyUtil;
|
||||
|
||||
public class ProxyListActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate {
|
||||
|
@ -253,6 +271,120 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente
|
|||
private int menu_add_scan_qr = 6;
|
||||
private int menu_other = 9;
|
||||
private int menu_retest_ping = 10;
|
||||
private int menu_reorder_by_ping = 11;
|
||||
private int menu_export_json = 12;
|
||||
private int menu_import_json = 13;
|
||||
private int menu_delete_all = 14;
|
||||
|
||||
private void processProxyList(ArrayList<String> files) {
|
||||
|
||||
for (String proxyListFilePath : files) {
|
||||
|
||||
File proxyListFile = new File(proxyListFilePath);
|
||||
|
||||
try {
|
||||
|
||||
if (proxyListFile.length() > 2 * 1024 * 1024L) {
|
||||
|
||||
throw new IllegalArgumentException("file too large.");
|
||||
|
||||
}
|
||||
|
||||
JSONObject proxyRootObject = new JSONObject(FileUtil.readUtf8String(proxyListFile));
|
||||
|
||||
if (proxyRootObject.isNull("nekox_proxy_list_verion")) {
|
||||
|
||||
throw new IllegalArgumentException("not a nekox proxy list file.");
|
||||
|
||||
}
|
||||
|
||||
int version = proxyRootObject.getInt("nekox_proxy_list_verion");
|
||||
|
||||
if (version == 1) {
|
||||
|
||||
if (proxyRootObject.isNull("proxies")) {
|
||||
|
||||
throw new IllegalArgumentException("proxies array not found.");
|
||||
|
||||
}
|
||||
|
||||
JSONArray proxyArray = proxyRootObject.getJSONArray("proxies");
|
||||
|
||||
if (proxyArray.length() == 0) {
|
||||
|
||||
throw new IllegalArgumentException("Empty proxy list.");
|
||||
|
||||
}
|
||||
|
||||
for (int index = 0; index < proxyArray.length(); index++) {
|
||||
|
||||
String proxyUrl = proxyArray.getString(index);
|
||||
|
||||
LinkedList<String> imported = new LinkedList<>();
|
||||
LinkedHashMap<String, String> errors = new LinkedHashMap<>();
|
||||
|
||||
try {
|
||||
|
||||
imported.add(ProxyUtil.importInBackground(proxyUrl).getTitle());
|
||||
|
||||
} catch (Exception ex) {
|
||||
|
||||
errors.put(proxyUrl, ex.getMessage());
|
||||
|
||||
}
|
||||
|
||||
StringBuilder status = new StringBuilder();
|
||||
|
||||
if (!imported.isEmpty()) {
|
||||
|
||||
status.append(LocaleController.getString("ImportedProxies", R.string.ImportedProxies));
|
||||
|
||||
for (String success : imported) {
|
||||
|
||||
status.append("\n").append(success);
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (!errors.isEmpty()) {
|
||||
|
||||
status.append("\n\n");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!errors.isEmpty()) {
|
||||
|
||||
status.append(LocaleController.getString("", R.string.ErrorsInImport));
|
||||
|
||||
for (Map.Entry<String, String> error : errors.entrySet()) {
|
||||
|
||||
status.append("\n").append(error.getKey()).append(": ").append(error.getValue());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AlertUtil.showSimpleAlert(getParentActivity(), status.toString());
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
throw new IllegalArgumentException("invalid proxy list version " + version + ".");
|
||||
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
AlertUtil.showSimpleAlert(getParentActivity(), LocaleController.getString("InvalidProxyFile", R.string.InvalidProxyFile) + proxyListFilePath + "\n\n" + e.getMessage());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public View createView(Context context) {
|
||||
|
@ -269,6 +401,88 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente
|
|||
finishFragment();
|
||||
} else if (id == menu_retest_ping) {
|
||||
checkProxyList(true);
|
||||
} else if (id == menu_reorder_by_ping) {
|
||||
SharedConfig.proxyList = new LinkedList<>(new TreeSet<>(SharedConfig.proxyList));
|
||||
SharedConfig.saveProxyList();
|
||||
updateRows(true);
|
||||
} else if (id == menu_export_json) {
|
||||
if (SharedConfig.proxyList.isEmpty()) {
|
||||
AlertUtil.showSimpleAlert(getParentActivity(), LocaleController.getString("NoProxy", R.string.NoProxy));
|
||||
return;
|
||||
}
|
||||
File cacheFile = new File(ApplicationLoader.applicationContext.getExternalCacheDir(), "NekoX-Proxy-List-" + new Date().toLocaleString() + ".json");
|
||||
|
||||
try {
|
||||
|
||||
JSONObject listRoot = new JSONObject();
|
||||
|
||||
listRoot.put("nekox_proxy_list_verion", 1);
|
||||
|
||||
JSONArray proxyArray = new JSONArray();
|
||||
|
||||
for (SharedConfig.ProxyInfo info : SharedConfig.proxyList) {
|
||||
|
||||
if (info.isInternal) continue;
|
||||
|
||||
proxyArray.put(info.toUrl());
|
||||
|
||||
}
|
||||
|
||||
listRoot.put("proxies", proxyArray);
|
||||
|
||||
FileUtil.writeUtf8String(listRoot.toString(4), cacheFile);
|
||||
} catch (JSONException e) {
|
||||
}
|
||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
intent.setType("*/*");
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
try {
|
||||
intent.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(getParentActivity(), BuildConfig.APPLICATION_ID + ".provider", cacheFile));
|
||||
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
} catch (Exception ignore) {
|
||||
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(cacheFile));
|
||||
}
|
||||
} else {
|
||||
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(cacheFile));
|
||||
}
|
||||
getParentActivity().startActivityForResult(Intent.createChooser(intent, LocaleController.getString("ShareFile", R.string.ShareFile)), 500);
|
||||
} else if (id == menu_import_json) {
|
||||
if (Build.VERSION.SDK_INT >= 23 && getParentActivity().checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
try {
|
||||
getParentActivity().requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 4);
|
||||
} catch (Throwable ignore) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
DocumentSelectActivity fragment = new DocumentSelectActivity(false);
|
||||
fragment.setMaxSelectedFiles(-1);
|
||||
fragment.setAllowPhoto(false);
|
||||
fragment.setDelegate(new DocumentSelectActivity.DocumentSelectActivityDelegate() {
|
||||
|
||||
@Override
|
||||
public void didSelectFiles(DocumentSelectActivity activity, ArrayList<String> files, String caption, boolean notify, int scheduleDate) {
|
||||
activity.finishFragment();
|
||||
processProxyList(files);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didSelectPhotos(ArrayList<SendMessagesHelper.SendingMediaInfo> photos, boolean notify, int scheduleDate) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startDocumentSelectActivity() {
|
||||
}
|
||||
});
|
||||
presentFragment(fragment);
|
||||
} else if (id == menu_delete_all) {
|
||||
AlertUtil.showComfirm(getParentActivity(),
|
||||
LocaleController.getString("DeleteAllProxies", R.string.DeleteAllProxies),
|
||||
LocaleController.getString("DeleteAllProxies", R.string.DeleteAllProxiesAlert),
|
||||
LocaleController.getString("Delete", R.string.Delete),
|
||||
true, (d, v) -> {
|
||||
SharedConfig.deleteAllProxy();
|
||||
updateRows(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -363,6 +577,10 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente
|
|||
otherItem = menu.addItem(menu_other, R.drawable.ic_ab_other);
|
||||
otherItem.setContentDescription(LocaleController.getString("AccDescrMoreOptions", R.string.AccDescrMoreOptions));
|
||||
otherItem.addSubItem(menu_retest_ping, LocaleController.getString("RetestPing", R.string.RetestPing));
|
||||
otherItem.addSubItem(menu_reorder_by_ping, LocaleController.getString("ReorderByPing", R.string.ReorderByPing));
|
||||
otherItem.addSubItem(menu_export_json, LocaleController.getString("ExportProxies", R.string.ExportProxies));
|
||||
otherItem.addSubItem(menu_import_json, LocaleController.getString("ImportProxies", R.string.ImportProxies));
|
||||
otherItem.addSubItem(menu_delete_all, LocaleController.getString("DeleteAllProxies", R.string.DeleteAllProxies));
|
||||
|
||||
listAdapter = new ListAdapter(context);
|
||||
|
||||
|
@ -697,7 +915,6 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente
|
|||
continue;
|
||||
}
|
||||
proxyInfo.checking = true;
|
||||
|
||||
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.proxyCheckDone, proxyInfo);
|
||||
|
||||
if (proxyInfo instanceof SharedConfig.ExternalSocks5Proxy && proxyInfo != SharedConfig.currentProxy) {
|
||||
|
|
|
@ -1,13 +1,62 @@
|
|||
package tw.nekomimi.nekogram.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.text.TextUtils
|
||||
import android.util.TypedValue
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import com.google.android.exoplayer2.drm.DecryptionResource
|
||||
import org.telegram.messenger.ApplicationLoader
|
||||
import org.telegram.messenger.*
|
||||
import org.telegram.ui.ActionBar.AlertDialog
|
||||
import org.telegram.ui.ActionBar.Theme
|
||||
import org.telegram.ui.ChatActivity
|
||||
import org.telegram.ui.Components.AvatarDrawable
|
||||
import org.telegram.ui.Components.BackupImageView
|
||||
import org.telegram.ui.Components.LayoutHelper
|
||||
import tw.nekomimi.nekogram.MessageHelper
|
||||
|
||||
object AlertUtil {
|
||||
|
||||
@JvmStatic
|
||||
fun showToast(text: String) = Toast.makeText(ApplicationLoader.applicationContext, text.takeIf { it.isNotBlank() } ?: "喵 !", Toast.LENGTH_LONG).show()
|
||||
fun showToast(text: String) = Toast.makeText(ApplicationLoader.applicationContext, text.takeIf { it.isNotBlank() }
|
||||
?: "喵 !", Toast.LENGTH_LONG).show()
|
||||
|
||||
@JvmStatic
|
||||
fun showSimpleAlert(ctx: Context, text: String) {
|
||||
|
||||
val builder = AlertDialog.Builder(ctx)
|
||||
|
||||
builder.setTitle(LocaleController.getString("NekoX", R.string.NekoX))
|
||||
builder.setMessage(text)
|
||||
|
||||
builder.setPositiveButton(LocaleController.getString("Ok", R.string.OK), null);
|
||||
|
||||
builder.show()
|
||||
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun showComfirm(ctx: Context, title: String, text: String, button: String,red: Boolean = false, listener: DialogInterface.OnClickListener) {
|
||||
|
||||
val builder = AlertDialog.Builder(ctx)
|
||||
|
||||
builder.setTitle(title)
|
||||
|
||||
builder.setMessage(AndroidUtilities.replaceTags(text))
|
||||
builder.setPositiveButton(button, listener)
|
||||
|
||||
builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null)
|
||||
val alertDialog = builder.show()
|
||||
|
||||
if (red) {
|
||||
|
||||
(alertDialog.getButton(DialogInterface.BUTTON_POSITIVE) as TextView?)?.setTextColor(Theme.getColor(Theme.key_dialogTextRed2))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -220,6 +220,52 @@ object ProxyUtil {
|
|||
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun importInBackground(link: String): SharedConfig.ProxyInfo {
|
||||
|
||||
val info = runCatching {
|
||||
|
||||
if (link.startsWith(VMESS_PROTOCOL) || link.startsWith(VMESS1_PROTOCOL)) {
|
||||
|
||||
SharedConfig.VmessProxy(link)
|
||||
|
||||
} else if (link.startsWith(SS_PROTOCOL)) {
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
|
||||
error(LocaleController.getString("MinApi21Required", R.string.MinApi21Required))
|
||||
|
||||
}
|
||||
|
||||
SharedConfig.ShadowsocksProxy(link)
|
||||
|
||||
} else if (link.startsWith(SSR_PROTOCOL)) {
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
|
||||
error(LocaleController.getString("MinApi21Required", R.string.MinApi21Required))
|
||||
|
||||
}
|
||||
|
||||
SharedConfig.ShadowsocksRProxy(link)
|
||||
|
||||
} else {
|
||||
|
||||
SharedConfig.ProxyInfo.fromUrl(link)
|
||||
|
||||
}
|
||||
|
||||
}.getOrThrow()
|
||||
|
||||
if (SharedConfig.addProxy(info) != info) {
|
||||
|
||||
error("proxy already exists")
|
||||
|
||||
}
|
||||
|
||||
return info
|
||||
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun shareProxy(ctx: Activity, info: SharedConfig.ProxyInfo, type: Int) {
|
||||
|
|
|
@ -99,6 +99,17 @@
|
|||
|
||||
<string name="ProxyRemarks">Remarks</string>
|
||||
<string name="RetestPing">Retest ping for all servers</string>
|
||||
<string name="ReorderByPing">Reorder servers by ping</string>
|
||||
<string name="ExportProxies">Export servers to file</string>
|
||||
<string name="ImportProxies">Import servers from file</string>
|
||||
<string name="SendProxyListTo">Do you want to send proxy list to **%1$s**?</string>
|
||||
|
||||
<string name="InvalidProxyFile">Invalid proxy list file: </string>
|
||||
<string name="ImportedProxies">Imported proxy servers: </string>
|
||||
<string name="ErrorsInImport">Import errors: </string>
|
||||
<string name="NoProxy">You haven\'t added any proxy servers.</string>
|
||||
<string name="DeleteAllProxies">Delete all servers</string>
|
||||
<string name="DeleteAllProxiesAlert">Warning! This will **delete all proxies**.</string>
|
||||
|
||||
<string name="MinApi21Required">Sorry, you need at least Android 5 (API21).</string>
|
||||
|
||||
|
|
Loading…
Reference in New Issue