diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 9cfbced..48a9ee9 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -6,25 +6,27 @@ jobs: build: runs-on: ubuntu-latest steps: - - name: Code Checkout - uses: actions/checkout@v1 + - name: Checkout + uses: actions/checkout@v3 - name: Extract Signature run: echo ${{secrets.KEY_FILE}} | base64 -d > $HOME/.android.jks - - name: set up JDK 1.8 - uses: actions/setup-java@v1 + - name: set up JDK 11 + uses: actions/setup-java@v3 with: - java-version: 1.8 + java-version: '11' + distribution: 'temurin' + cache: gradle - name: Build with Gradle run: ./gradlew assembleRelease env: KEY_PASS: ${{secrets.KEY_PASS}} - name: Upload artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3.1.1 with: name: ShadowsocksGostPlugin.apk path: app/build/outputs/apk/release/app-release.apk - name: Release Build - uses: meeDamian/github-release@1.0 + uses: meeDamian/github-release@2.0 with: token: ${{secrets.GITHUB_TOKEN}} draft: true diff --git a/README.md b/README.md index a4c1c84..dc20894 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +
@@ -29,14 +31,14 @@ Download prebuilt APK here [Release](https://github.com/xausky/ShadowsocksGostPl
## ❗ 注意 Notices
-* 使用#SS_HOST参数会先对填写的主机名进行DNS解析后才传递
-* 如果是与主机名相关的远程协议比如ws协议必须直接在参数里配置域名
-* 在参数里面配置的域名会忽略手机系统的DNS配置固定使用 Public DNS+
+* ~使用#SS_HOST参数会先对填写的主机名进行DNS解析后才传递~ 这应该是旧版Shadowsocks-Android才会存在的问题
+* ~如果是与主机名相关的远程协议比如ws协议必须直接在参数里配置域名~ 同上
+* 在参数里面配置的域名会忽略手机系统的DNS配置,默认使用 Public DNS+解析
* ~如果插件参数里面使用 `-F=` 形式的参数传递则后续参数不能含有 `=` 号,推荐使用 `-F ` 形式代替~ 使用新版配置格式(CFGBLOB)即可避开这个问题
-* Host specified by #SS_HOST will be firstly resolved with DNS before being passed on
-* If the hostname is tied to the protocol, like WebSocket (ws), you must directly use domain name in configuration parameters
-* The domain name(s) appeared in configuration parameters is/are hard-coded to be resolved with Public DNS+, in other words, ignoring the OS's DNS configurations
+* ~Host specified by #SS_HOST will be firstly resolved with DNS before being passed on~ Should be a problem specific to older versions of Shadowsocks-Android
+* ~If the hostname is tied to the protocol, like WebSocket (ws), you must directly use domain name in configuration parameters~ Same as above
+* The domain name(s) appeared in configuration parameters is/are resolved with Public DNS+ by default, in other words, ignoring the OS's DNS configurations
* ~If you configure a parameter in the form of `-F=`, then the subsequent parameters can no longer contain `=`, so it's recommended to use the form of `-F ` instead~ This issue can be avoided simply by using new config format (CFGBLOB)
## ❤ 关注我 Follow me
diff --git a/app/build.gradle b/app/build.gradle
index aed0908..25b72b2 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -7,7 +7,7 @@ android {
minSdkVersion 21
targetSdkVersion 29
versionCode 2111
- versionName "2.11.1"
+ versionName "3.0.0-rc.0"
}
signingConfigs {
releaseConfig {
diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png
new file mode 100644
index 0000000..f92a4e3
Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ
diff --git a/app/src/main/java/com/github/shadowsocks/plugin/gost/ConfigActivity.java b/app/src/main/java/com/github/shadowsocks/plugin/gost/ConfigActivity.java
index ff3f66f..daac8e2 100644
--- a/app/src/main/java/com/github/shadowsocks/plugin/gost/ConfigActivity.java
+++ b/app/src/main/java/com/github/shadowsocks/plugin/gost/ConfigActivity.java
@@ -2,11 +2,14 @@
import android.annotation.SuppressLint;
import android.app.AlertDialog;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.DialogInterface;
+import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.Color;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.text.Editable;
@@ -23,6 +26,7 @@
import android.widget.Toast;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.github.shadowsocks.plugin.ConfigurationActivity;
import com.github.shadowsocks.plugin.PluginOptions;
@@ -31,15 +35,19 @@
import org.json.JSONException;
import org.json.JSONObject;
+import java.io.BufferedReader;
import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
-import java.util.regex.Matcher;
public class ConfigActivity extends ConfigurationActivity {
private LinearLayout linearlayout_cmdargs;
@@ -349,6 +357,7 @@ private void addFileEntry(final String fileName, final String fileData, String h
@SuppressLint("InflateParams") final View child = inflater.inflate(R.layout.fileentry, null);
TextView fileNameLabel = child.findViewById(R.id.text_file_name);
+ Button button_load = child.findViewById(R.id.button_load);
Button button_del_file = child.findViewById(R.id.button_del_file);
EditText fileDataEditText = child.findViewById(R.id.editText_file_data);
@@ -364,6 +373,13 @@ private void addFileEntry(final String fileName, final String fileData, String h
fileDataMap.put(fileName, fileDataEditText.getText());
+ button_load.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ readFromFile(fileName);
+ }
+ });
+
button_del_file.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -374,8 +390,74 @@ public void onClick(View v) {
parent.addView(child);
}
+ private static final int READ_FROM_FILE = 1;
+ private String openingFileName = "";
+ private void readFromFile(String openingFileName) {
+ if (readFileThread != null && readFileThread.isAlive()) {
+ showToast(R.string.still_reading);
+ return;
+ }
+ this.openingFileName = openingFileName;
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ String mimeType = "*/*";
+ if (openingFileName.endsWith(".json"))
+ mimeType = "application/json";
+ else if (openingFileName.endsWith(".pem"))
+ mimeType = "application/x-pem-file";
+ else if (openingFileName.endsWith(".txt"))
+ mimeType = "text/plain";
+ intent.setType(mimeType);
+ startActivityForResult(intent, READ_FROM_FILE);
+ }
+
+ private Thread readFileThread;
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+ if (requestCode == READ_FROM_FILE) {
+ if (data != null) {
+ final Uri uri = data.getData();
+ final ContentResolver resolver = this.getContentResolver();
+ if (readFileThread == null || !readFileThread.isAlive()) {
+ readFileThread = new Thread() {
+ @Override
+ public void run() {
+ try {
+ StringBuilder stringBuilder = new StringBuilder();
+ InputStream inputStream = resolver.openInputStream(uri);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(Objects.requireNonNull(inputStream)));
+ char[] buf = new char[4096];
+ for (int r, t = 0; (r = reader.read(buf)) != -1; t += r) {
+ if (t > 1024 * 1024) {
+ showToast(R.string.err_file_too_large);
+ return;
+ }
+ stringBuilder.append(buf, 0, r);
+ }
+ final String result = stringBuilder.toString();
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Editable editable = fileDataMap.get(openingFileName);
+ if (editable != null) {
+ editable.clear();
+ editable.append(result);
+ }
+ }
+ });
+ } catch (IOException ignored) {}
+ }
+ };
+ readFileThread.start();
+ } else Log.e("ConfigActivity", "readFileThread is unexpectedly alive");
+ }
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
private final String[] fileNameList = {
- "config.json",
+ "config.yaml",
"cacert.pem",
"clientcert.pem",
"clientcertkey.pem",
@@ -420,6 +502,18 @@ private void saveUI() throws NullPointerException, JSONException {
}
this.decodedPluginOptions.put("Files", files);
+ // save DNS server
+ EditText editText_dns_server = findViewById(R.id.editText_dns_server);
+ Editable editable_dns_server = editText_dns_server.getText();
+ String dnsServer = "";
+ if (editable_dns_server != null) {
+ dnsServer = editable_dns_server.toString();
+ }
+ if (dnsServer.length() == 0) {
+ dnsServer = getString(R.string.example_dns_server);
+ }
+ this.decodedPluginOptions.put("DNSServer", dnsServer);
+
// save legacyCfg, if there's one
String legacyCfg = "";
EditText editText_legacyCfg = findViewById(R.id.editText_legacyCfg);
@@ -496,6 +590,17 @@ private void populateUI() throws JSONException {
addFileEntry(fileName, jsonObject.getString(fileName), "", true);
}
+ // populate dns server
+ String dnsServer = "";
+ try {
+ dnsServer = this.decodedPluginOptions.getString("DNSServer");
+ } catch (JSONException ignored) {}
+ if (dnsServer.length() == 0) {
+ dnsServer = getString(R.string.example_dns_server);
+ }
+ EditText editText_dns_server = findViewById(R.id.editText_dns_server);
+ editText_dns_server.setText(dnsServer);
+
// populate legacyCfg, if there's one
String legacyCfg = "";
try {
diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
deleted file mode 100644
index 1f6bb29..0000000
--- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-