Implement plugin entirely in Kotlin
This commit is contained in:
@@ -39,7 +39,6 @@ import android.support.v7.app.AppCompatActivity
|
||||
import android.support.v7.content.res.AppCompatResources
|
||||
import android.support.v7.preference.PreferenceDataStore
|
||||
import android.support.v7.widget.TooltipCompat
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
@@ -355,7 +354,7 @@ class MainActivity : AppCompatActivity(), ShadowsocksConnection.Interface, Drawe
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
if (TextUtils.isEmpty(sharedStr)) return
|
||||
if (sharedStr.isNullOrEmpty()) return
|
||||
val profiles = Profile.findAll(sharedStr).toList()
|
||||
if (profiles.isEmpty()) {
|
||||
Snackbar.make(findViewById(R.id.snackbar), R.string.profile_invalid_input, Snackbar.LENGTH_LONG).show()
|
||||
|
||||
@@ -143,7 +143,7 @@ class ProfileConfigFragment : PreferenceFragmentCompatDividers(), Toolbar.OnMenu
|
||||
override fun onPreferenceChange(preference: Preference?, newValue: Any?): Boolean = try {
|
||||
val selected = pluginConfiguration.selected
|
||||
pluginConfiguration = PluginConfiguration(pluginConfiguration.pluginsOptions +
|
||||
(pluginConfiguration.selected to PluginOptions(selected, newValue as String?)), selected)
|
||||
(pluginConfiguration.selected to PluginOptions(selected, newValue as? String?)), selected)
|
||||
DataStore.plugin = pluginConfiguration.toString()
|
||||
DataStore.dirty = true
|
||||
true
|
||||
|
||||
@@ -29,7 +29,6 @@ import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.os.RemoteCallbackList
|
||||
import android.support.v4.os.UserManagerCompat
|
||||
import android.text.TextUtils
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
@@ -218,7 +217,7 @@ object BaseService {
|
||||
fun onBind(intent: Intent): IBinder? = if (intent.action == Action.SERVICE) data.binder else null
|
||||
|
||||
fun checkProfile(profile: Profile): Boolean =
|
||||
if (TextUtils.isEmpty(profile.host) || TextUtils.isEmpty(profile.password)) {
|
||||
if (profile.host.isEmpty() || profile.password.isEmpty()) {
|
||||
stopRunner(true, (this as Context).getString(R.string.proxy_empty))
|
||||
false
|
||||
} else true
|
||||
|
||||
@@ -50,7 +50,7 @@ class PluginConfiguration(val pluginsOptions: Map<String, PluginOptions>, val se
|
||||
})
|
||||
|
||||
fun getOptions(id: String): PluginOptions = if (id.isEmpty()) PluginOptions() else
|
||||
pluginsOptions.get(id) ?: PluginOptions(id, PluginManager.fetchPlugins()[id]?.defaultConfig)
|
||||
pluginsOptions[id] ?: PluginOptions(id, PluginManager.fetchPlugins()[id]?.defaultConfig)
|
||||
val selectedOptions: PluginOptions get() = getOptions(selected)
|
||||
|
||||
override fun toString(): String {
|
||||
|
||||
+20
-22
@@ -18,35 +18,33 @@
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
package com.github.shadowsocks.plugin;
|
||||
package com.github.shadowsocks.plugin
|
||||
|
||||
/**
|
||||
* The contract between the plugin provider and host. Contains definitions for the supported actions, extras, etc.
|
||||
*
|
||||
* This class is written in Java to keep Java interoperability.
|
||||
*/
|
||||
public final class PluginContract {
|
||||
private PluginContract() { }
|
||||
|
||||
object PluginContract {
|
||||
/**
|
||||
* ContentProvider Action: Used for NativePluginProvider.
|
||||
*
|
||||
* Constant Value: "com.github.shadowsocks.plugin.ACTION_NATIVE_PLUGIN"
|
||||
*/
|
||||
public static final String ACTION_NATIVE_PLUGIN = "com.github.shadowsocks.plugin.ACTION_NATIVE_PLUGIN";
|
||||
const val ACTION_NATIVE_PLUGIN = "com.github.shadowsocks.plugin.ACTION_NATIVE_PLUGIN"
|
||||
|
||||
/**
|
||||
* Activity Action: Used for ConfigurationActivity.
|
||||
*
|
||||
* Constant Value: "com.github.shadowsocks.plugin.ACTION_CONFIGURE"
|
||||
*/
|
||||
public static final String ACTION_CONFIGURE = "com.github.shadowsocks.plugin.ACTION_CONFIGURE";
|
||||
const val ACTION_CONFIGURE = "com.github.shadowsocks.plugin.ACTION_CONFIGURE"
|
||||
/**
|
||||
* Activity Action: Used for HelpActivity or HelpCallback.
|
||||
*
|
||||
* Constant Value: "com.github.shadowsocks.plugin.ACTION_HELP"
|
||||
*/
|
||||
public static final String ACTION_HELP = "com.github.shadowsocks.plugin.ACTION_HELP";
|
||||
const val ACTION_HELP = "com.github.shadowsocks.plugin.ACTION_HELP"
|
||||
|
||||
/**
|
||||
* The lookup key for a string that provides the plugin entry binary.
|
||||
@@ -55,7 +53,7 @@ public final class PluginContract {
|
||||
*
|
||||
* Constant Value: "com.github.shadowsocks.plugin.EXTRA_ENTRY"
|
||||
*/
|
||||
public static final String EXTRA_ENTRY = "com.github.shadowsocks.plugin.EXTRA_ENTRY";
|
||||
const val EXTRA_ENTRY = "com.github.shadowsocks.plugin.EXTRA_ENTRY"
|
||||
/**
|
||||
* The lookup key for a string that provides the options as a string.
|
||||
*
|
||||
@@ -63,34 +61,34 @@ public final class PluginContract {
|
||||
*
|
||||
* Constant Value: "com.github.shadowsocks.plugin.EXTRA_OPTIONS"
|
||||
*/
|
||||
public static final String EXTRA_OPTIONS = "com.github.shadowsocks.plugin.EXTRA_OPTIONS";
|
||||
const val EXTRA_OPTIONS = "com.github.shadowsocks.plugin.EXTRA_OPTIONS"
|
||||
/**
|
||||
* The lookup key for a CharSequence that provides user relevant help message.
|
||||
*
|
||||
* Example: "obfs=<http|tls> Enable obfuscating: HTTP or TLS (Experimental).
|
||||
* obfs-host=<host_name> Hostname for obfuscating (Experimental)."
|
||||
* Example: "obfs=<http></http>|tls> Enable obfuscating: HTTP or TLS (Experimental).
|
||||
* obfs-host=<host_name> Hostname for obfuscating (Experimental)."
|
||||
*
|
||||
* Constant Value: "com.github.shadowsocks.plugin.EXTRA_HELP_MESSAGE"
|
||||
*/
|
||||
public static final String EXTRA_HELP_MESSAGE = "com.github.shadowsocks.plugin.EXTRA_HELP_MESSAGE";
|
||||
</host_name> */
|
||||
const val EXTRA_HELP_MESSAGE = "com.github.shadowsocks.plugin.EXTRA_HELP_MESSAGE"
|
||||
|
||||
/**
|
||||
* The metadata key to retrieve plugin id. Required for plugins.
|
||||
*
|
||||
* Constant Value: "com.github.shadowsocks.plugin.id"
|
||||
*/
|
||||
public static final String METADATA_KEY_ID = "com.github.shadowsocks.plugin.id";
|
||||
const val METADATA_KEY_ID = "com.github.shadowsocks.plugin.id"
|
||||
/**
|
||||
* The metadata key to retrieve default configuration. Default value is empty.
|
||||
*
|
||||
* Constant Value: "com.github.shadowsocks.plugin.default_config"
|
||||
*/
|
||||
public static final String METADATA_KEY_DEFAULT_CONFIG = "com.github.shadowsocks.plugin.default_config";
|
||||
const val METADATA_KEY_DEFAULT_CONFIG = "com.github.shadowsocks.plugin.default_config"
|
||||
|
||||
public static final String METHOD_GET_EXECUTABLE = "shadowsocks:getExecutable";
|
||||
const val METHOD_GET_EXECUTABLE = "shadowsocks:getExecutable"
|
||||
|
||||
/** ConfigurationActivity result: fallback to manual edit mode. */
|
||||
public static final int RESULT_FALLBACK = 1;
|
||||
/** ConfigurationActivity result: fallback to manual edit mode. */
|
||||
const val RESULT_FALLBACK = 1
|
||||
|
||||
/**
|
||||
* Relative to the file to be copied. This column is required.
|
||||
@@ -99,7 +97,7 @@ public final class PluginContract {
|
||||
*
|
||||
* Type: String
|
||||
*/
|
||||
public static final String COLUMN_PATH = "path";
|
||||
const val COLUMN_PATH = "path"
|
||||
/**
|
||||
* File mode bits. Default value is "644".
|
||||
*
|
||||
@@ -107,14 +105,14 @@ public final class PluginContract {
|
||||
*
|
||||
* Type: String
|
||||
*/
|
||||
public static final String COLUMN_MODE = "mode";
|
||||
const val COLUMN_MODE = "mode"
|
||||
|
||||
/**
|
||||
* The scheme for general plugin actions.
|
||||
*/
|
||||
public static final String SCHEME = "plugin";
|
||||
const val SCHEME = "plugin"
|
||||
/**
|
||||
* The authority for general plugin actions.
|
||||
*/
|
||||
public static final String AUTHORITY = "com.github.shadowsocks";
|
||||
const val AUTHORITY = "com.github.shadowsocks"
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> *
|
||||
* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
package com.github.shadowsocks.plugin;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Objects;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* Helper class for processing plugin options.
|
||||
*
|
||||
* Based on: https://github.com/apache/ant/blob/588ce1f/src/main/org/apache/tools/ant/types/Commandline.java
|
||||
*/
|
||||
public final class PluginOptions extends HashMap<String, String> {
|
||||
public PluginOptions() {
|
||||
super();
|
||||
}
|
||||
public PluginOptions(int initialCapacity) {
|
||||
super(initialCapacity);
|
||||
}
|
||||
public PluginOptions(int initialCapacity, float loadFactor) {
|
||||
super(initialCapacity, loadFactor);
|
||||
}
|
||||
|
||||
// TODO: this method is not needed since API 24
|
||||
public String getOrDefault(Object key, String defaultValue) {
|
||||
String v;
|
||||
return (((v = get(key)) != null) || containsKey(key))
|
||||
? v
|
||||
: defaultValue;
|
||||
}
|
||||
|
||||
private PluginOptions(String options, boolean parseId) {
|
||||
this();
|
||||
if (TextUtils.isEmpty(options)) return;
|
||||
final StringTokenizer tokenizer = new StringTokenizer(options + ';', "\\=;", true);
|
||||
final StringBuilder current = new StringBuilder();
|
||||
String key = null;
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
String nextToken = tokenizer.nextToken();
|
||||
if ("\\".equals(nextToken)) current.append(tokenizer.nextToken());
|
||||
else if ("=".equals(nextToken) && key == null) {
|
||||
key = current.toString();
|
||||
current.setLength(0);
|
||||
} else if (";".equals(nextToken)) {
|
||||
if (key != null) {
|
||||
put(key, current.toString());
|
||||
key = null;
|
||||
} else if (current.length() > 0)
|
||||
if (parseId) id = current.toString(); else put(current.toString(), null);
|
||||
current.setLength(0);
|
||||
parseId = false;
|
||||
} else current.append(nextToken);
|
||||
}
|
||||
}
|
||||
public PluginOptions(String options) {
|
||||
this(options, true);
|
||||
}
|
||||
public PluginOptions(String id, String options) {
|
||||
this(options, false);
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String id = "";
|
||||
|
||||
private static void append(StringBuilder result, String str) {
|
||||
for (int i = 0; i < str.length(); ++i) {
|
||||
char ch = str.charAt(i);
|
||||
switch (ch) {
|
||||
case '\\': case '=': case ';': result.append('\\'); // intentionally no break
|
||||
default: result.append(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
public String toString(boolean trimId) {
|
||||
final StringBuilder result = new StringBuilder();
|
||||
if (!trimId) if (TextUtils.isEmpty(id)) return ""; else append(result, id);
|
||||
for (Entry<String, String> entry : entrySet()) {
|
||||
if (result.length() > 0) result.append(';');
|
||||
append(result, entry.getKey());
|
||||
String value = entry.getValue();
|
||||
if (value != null) {
|
||||
result.append('=');
|
||||
append(result, value);
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return toString(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
if (!super.equals(o)) return false;
|
||||
PluginOptions that = (PluginOptions) o;
|
||||
return Objects.equals(id, that.id) && super.equals(that);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> *
|
||||
* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
package com.github.shadowsocks.plugin
|
||||
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Helper class for processing plugin options.
|
||||
*
|
||||
* Based on: https://github.com/apache/ant/blob/588ce1f/src/main/org/apache/tools/ant/types/Commandline.java
|
||||
*/
|
||||
class PluginOptions : HashMap<String, String?> {
|
||||
var id = ""
|
||||
|
||||
constructor() : super()
|
||||
constructor(initialCapacity: Int) : super(initialCapacity)
|
||||
constructor(initialCapacity: Int, loadFactor: Float) : super(initialCapacity, loadFactor)
|
||||
|
||||
private constructor(options: String?, parseId: Boolean) : this() {
|
||||
@Suppress("NAME_SHADOWING")
|
||||
var parseId = parseId
|
||||
if (options.isNullOrEmpty()) return
|
||||
val tokenizer = StringTokenizer(options + ';', "\\=;", true)
|
||||
val current = StringBuilder()
|
||||
var key: String? = null
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
val nextToken = tokenizer.nextToken()
|
||||
when (nextToken) {
|
||||
"\\" -> current.append(tokenizer.nextToken())
|
||||
"=" -> if (key == null) {
|
||||
key = current.toString()
|
||||
current.setLength(0)
|
||||
} else current.append(nextToken)
|
||||
";" -> {
|
||||
if (key != null) {
|
||||
put(key, current.toString())
|
||||
key = null
|
||||
} else if (current.isNotEmpty())
|
||||
if (parseId) id = current.toString() else put(current.toString(), null)
|
||||
current.setLength(0)
|
||||
parseId = false
|
||||
}
|
||||
else -> current.append(nextToken)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor(options: String?) : this(options, true)
|
||||
constructor(id: String, options: String?) : this(options, false) {
|
||||
this.id = id
|
||||
}
|
||||
|
||||
private fun append(result: StringBuilder, str: String) = (0 until str.length)
|
||||
.map { str[it] }
|
||||
.forEach {
|
||||
when (it) {
|
||||
'\\', '=', ';' -> {
|
||||
result.append('\\') // intentionally no break
|
||||
result.append(it)
|
||||
}
|
||||
else -> result.append(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun toString(trimId: Boolean): String {
|
||||
val result = StringBuilder()
|
||||
if (!trimId) if (id.isEmpty()) return "" else append(result, id)
|
||||
for ((key, value) in entries) {
|
||||
if (result.isNotEmpty()) result.append(';')
|
||||
append(result, key)
|
||||
if (value != null) {
|
||||
result.append('=')
|
||||
append(result, value)
|
||||
}
|
||||
}
|
||||
return result.toString()
|
||||
}
|
||||
|
||||
override fun toString(): String = toString(true)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
return javaClass == other?.javaClass && super.equals(other) && id == (other as PluginOptions).id
|
||||
}
|
||||
override fun hashCode(): Int = Objects.hash(super.hashCode(), id)
|
||||
}
|
||||
Reference in New Issue
Block a user