Initial draft for porting this project to Gradle + Kotlin
This is one of my stupidest idea ever.
This commit is contained in:
+26
@@ -0,0 +1,26 @@
|
||||
package com.github.shadowsocks.plugin;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() throws Exception {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getTargetContext();
|
||||
|
||||
assertEquals("com.github.shadowsocks.plugin.test", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.github.shadowsocks.plugin">
|
||||
<uses-sdk
|
||||
android:minSdkVersion="19"
|
||||
android:targetSdkVersion="27"/>
|
||||
<application
|
||||
android:theme="@style/Theme.Shadowsocks">
|
||||
android:theme="@style/Theme.Shadowsocks">
|
||||
<meta-data android:name="com.github.shadowsocks.plugin.version"
|
||||
android:value="0.0.4"/>
|
||||
android:value="0.1.0"/>
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* 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.app.Activity
|
||||
import android.content.Intent
|
||||
|
||||
/**
|
||||
* Base class for configuration activity. A configuration activity is started when user wishes to configure the
|
||||
* selected plugin. To create a configuration activity, extend this class, implement abstract methods, invoke
|
||||
* `saveChanges(options)` and `discardChanges()` when appropriate, and add it to your manifest like this:
|
||||
*
|
||||
* <pre class="prettyprint"><manifest>
|
||||
* ...
|
||||
* <application>
|
||||
* ...
|
||||
* <activity android:name=".ConfigureActivity">
|
||||
* <intent-filter>
|
||||
* <action android:name="com.github.shadowsocks.plugin.ACTION_CONFIGURE"/>
|
||||
* <category android:name="android.intent.category.DEFAULT"/>
|
||||
* <data android:scheme="plugin"
|
||||
* android:host="com.github.shadowsocks"
|
||||
* android:path="/$PLUGIN_ID"/>
|
||||
* </intent-filter>
|
||||
* </activity>
|
||||
* ...
|
||||
* </application>
|
||||
*</manifest></pre>
|
||||
*/
|
||||
abstract class ConfigurationActivity : OptionsCapableActivity() {
|
||||
/**
|
||||
* Equivalent to setResult(RESULT_CANCELED).
|
||||
*/
|
||||
fun discardChanges() = setResult(Activity.RESULT_CANCELED)
|
||||
|
||||
/**
|
||||
* Equivalent to setResult(RESULT_OK, args_with_correct_format).
|
||||
*
|
||||
* @param options PluginOptions to save.
|
||||
*/
|
||||
fun saveChanges(options: PluginOptions) =
|
||||
setResult(Activity.RESULT_OK, Intent().putExtra(PluginContract.EXTRA_OPTIONS, options.toString()))
|
||||
|
||||
/**
|
||||
* Finish this activity and request manual editor to pop up instead.
|
||||
*/
|
||||
fun fallbackToManualEditor() {
|
||||
setResult(PluginContract.RESULT_FALLBACK)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* 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
|
||||
|
||||
/**
|
||||
* Base class for a help activity. A help activity is started when user taps help when configuring options for your
|
||||
* plugin. To create a help activity, just extend this class, and add it to your manifest like this:
|
||||
*
|
||||
* <pre class="prettyprint"><manifest>
|
||||
* ...
|
||||
* <application>
|
||||
* ...
|
||||
* <activity android:name=".HelpActivity">
|
||||
* <intent-filter>
|
||||
* <action android:name="com.github.shadowsocks.plugin.ACTION_HELP"/>
|
||||
* <category android:name="android.intent.category.DEFAULT"/>
|
||||
* <data android:scheme="plugin"
|
||||
* android:host="com.github.shadowsocks"
|
||||
* android:path="/$PLUGIN_ID"/>
|
||||
* </intent-filter>
|
||||
* </activity>
|
||||
* ...
|
||||
* </application>
|
||||
*</manifest></pre>
|
||||
*/
|
||||
abstract class HelpActivity : OptionsCapableActivity() {
|
||||
override fun onInitializePluginOptions(options: PluginOptions) { }
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* 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.content.Intent
|
||||
|
||||
/**
|
||||
* HelpCallback is an HelpActivity but you just need to produce a CharSequence help message instead of having to
|
||||
* provide UI. To create a help callback, just extend this class, implement abstract methods, and add it to your
|
||||
* manifest following the same procedure as adding a HelpActivity.
|
||||
*/
|
||||
abstract class HelpCallback : HelpActivity() {
|
||||
abstract fun produceHelpMessage(options: PluginOptions): CharSequence
|
||||
|
||||
override fun onInitializePluginOptions(options: PluginOptions) {
|
||||
setResult(RESULT_OK, Intent().putExtra(PluginContract.EXTRA_HELP_MESSAGE, produceHelpMessage(options)))
|
||||
finish()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* 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.content.ContentProvider
|
||||
import android.content.ContentValues
|
||||
import android.database.Cursor
|
||||
import android.database.MatrixCursor
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.ParcelFileDescriptor
|
||||
|
||||
/**
|
||||
* Base class for a native plugin provider. A native plugin provider offers read-only access to files that are required
|
||||
* to run a plugin, such as binary files and other configuration files. To create a native plugin provider, extend this
|
||||
* class, implement the abstract methods, and add it to your manifest like this:
|
||||
*
|
||||
* <pre class="prettyprint"><manifest>
|
||||
* ...
|
||||
* <application>
|
||||
* ...
|
||||
* <provider android:name="com.github.shadowsocks.$PLUGIN_ID.BinaryProvider"
|
||||
* android:authorities="com.github.shadowsocks.plugin.$PLUGIN_ID.BinaryProvider">
|
||||
* <intent-filter>
|
||||
* <category android:name="com.github.shadowsocks.plugin.ACTION_NATIVE_PLUGIN" />
|
||||
* </intent-filter>
|
||||
* </provider>
|
||||
* ...
|
||||
* </application>
|
||||
*</manifest></pre>
|
||||
*/
|
||||
abstract class NativePluginProvider : ContentProvider() {
|
||||
override fun getType(p0: Uri?): String = "application/x-elf"
|
||||
|
||||
override fun onCreate(): Boolean = true
|
||||
|
||||
/**
|
||||
* Provide all files needed for native plugin.
|
||||
*
|
||||
* @param provider A helper object to use to add files.
|
||||
*/
|
||||
protected abstract fun populateFiles(provider: PathProvider)
|
||||
|
||||
override fun query(uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?,
|
||||
sortOrder: String?): Cursor {
|
||||
assert(selection == null && selectionArgs == null && sortOrder == null)
|
||||
val result = MatrixCursor(projection)
|
||||
populateFiles(PathProvider(uri, result))
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns executable entry absolute path. This is used if plugin is sharing UID with the host.
|
||||
*
|
||||
* Default behavior is throwing UnsupportedOperationException. If you don't wish to use this feature, use the default
|
||||
* behavior.
|
||||
*
|
||||
* @return Absolute path for executable entry.
|
||||
*/
|
||||
open fun getExecutable(): String = throw UnsupportedOperationException()
|
||||
|
||||
abstract fun openFile(uri: Uri?): ParcelFileDescriptor
|
||||
override fun openFile(uri: Uri?, mode: String?): ParcelFileDescriptor {
|
||||
assert(mode == "r")
|
||||
return openFile(uri)
|
||||
}
|
||||
|
||||
override fun call(method: String?, arg: String?, extras: Bundle?): Bundle = when (method) {
|
||||
PluginContract.METHOD_GET_EXECUTABLE -> {
|
||||
val out = Bundle()
|
||||
out.putString(PluginContract.EXTRA_ENTRY, getExecutable())
|
||||
out
|
||||
}
|
||||
else -> super.call(method, arg, extras)
|
||||
}
|
||||
|
||||
// Methods that should not be used
|
||||
override fun insert(p0: Uri?, p1: ContentValues?): Uri = throw UnsupportedOperationException()
|
||||
override fun update(p0: Uri?, p1: ContentValues?, p2: String?, p3: Array<out String>?): Int =
|
||||
throw UnsupportedOperationException()
|
||||
override fun delete(p0: Uri?, p1: String?, p2: Array<out String>?): Int = throw UnsupportedOperationException()
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* 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.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.widget.Toast
|
||||
|
||||
/**
|
||||
* Activity that's capable of getting EXTRA_OPTIONS input.
|
||||
*/
|
||||
abstract class OptionsCapableActivity : AppCompatActivity() {
|
||||
protected fun pluginOptions(intent: Intent = this.intent): PluginOptions {
|
||||
return try {
|
||||
PluginOptions(intent.getStringExtra(PluginContract.EXTRA_OPTIONS))
|
||||
} catch (exc: IllegalArgumentException) {
|
||||
Toast.makeText(this, exc.message, Toast.LENGTH_SHORT).show()
|
||||
PluginOptions()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate args to your user interface.
|
||||
*
|
||||
* @param options PluginOptions parsed.
|
||||
*/
|
||||
protected abstract fun onInitializePluginOptions(options: PluginOptions = pluginOptions())
|
||||
|
||||
override fun onPostCreate(savedInstanceState: Bundle?) {
|
||||
super.onPostCreate(savedInstanceState)
|
||||
if (savedInstanceState == null) onInitializePluginOptions()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* 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.database.MatrixCursor
|
||||
import android.net.Uri
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Helper class to provide relative paths of files to copy.
|
||||
*/
|
||||
class PathProvider internal constructor(baseUri: Uri, private val cursor: MatrixCursor) {
|
||||
private val basePath = baseUri.path?.trim('/') ?: ""
|
||||
|
||||
fun addPath(path: String, mode: String = "644"): PathProvider {
|
||||
val trimmed = path.trim('/')
|
||||
if (trimmed.startsWith(basePath)) cursor.newRow()
|
||||
.add(PluginContract.COLUMN_PATH, trimmed)
|
||||
.add(PluginContract.COLUMN_MODE, mode)
|
||||
return this
|
||||
}
|
||||
fun addTo(file: File, to: String = "", mode: String = "644"): PathProvider {
|
||||
var sub = to + file.name
|
||||
if (basePath.startsWith(sub)) if (file.isDirectory) {
|
||||
sub += '/'
|
||||
file.listFiles().forEach { addTo(it, sub, mode) }
|
||||
} else addPath(sub, mode)
|
||||
return this
|
||||
}
|
||||
fun addAt(file: File, at: String = "", mode: String = "644"): PathProvider {
|
||||
if (basePath.startsWith(at))
|
||||
if (file.isDirectory) file.listFiles().forEach { addTo(it, at, mode) } else addPath(at, mode)
|
||||
return this
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,29 @@
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @author Mygod
|
||||
*/
|
||||
public final class PluginContract {
|
||||
private PluginContract() { }
|
||||
|
||||
@@ -1,3 +1,23 @@
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* 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;
|
||||
@@ -10,8 +30,6 @@ 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
|
||||
*
|
||||
* @author Mygod
|
||||
*/
|
||||
public final class PluginOptions extends HashMap<String, String> {
|
||||
public PluginOptions() {
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
package com.github.shadowsocks.plugin
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
|
||||
/**
|
||||
* Base class for configuration activity. A configuration activity is started when user wishes to configure the
|
||||
* selected plugin. To create a configuration activity, extend this class, implement abstract methods, invoke
|
||||
* `saveChanges(options)` and `discardChanges()` when appropriate, and add it to your manifest like this:
|
||||
*
|
||||
* <pre class="prettyprint"><manifest>
|
||||
* ...
|
||||
* <application>
|
||||
* ...
|
||||
* <activity android:name=".ConfigureActivity">
|
||||
* <intent-filter>
|
||||
* <action android:name="com.github.shadowsocks.plugin.ACTION_CONFIGURE"/>
|
||||
* <category android:name="android.intent.category.DEFAULT"/>
|
||||
* <data android:scheme="plugin"
|
||||
* android:host="com.github.shadowsocks"
|
||||
* android:path="/$PLUGIN_ID"/>
|
||||
* </intent-filter>
|
||||
* </activity>
|
||||
* ...
|
||||
* </application>
|
||||
*</manifest></pre>
|
||||
*
|
||||
* @author Mygod
|
||||
*/
|
||||
trait ConfigurationActivity extends OptionsCapableActivity {
|
||||
/**
|
||||
* Equivalent to setResult(RESULT_CANCELED).
|
||||
*/
|
||||
final def discardChanges(): Unit = setResult(Activity.RESULT_CANCELED)
|
||||
|
||||
/**
|
||||
* Equivalent to setResult(RESULT_OK, args_with_correct_format).
|
||||
*
|
||||
* @param options PluginOptions to save.
|
||||
*/
|
||||
final def saveChanges(options: PluginOptions): Unit =
|
||||
setResult(Activity.RESULT_OK, new Intent().putExtra(PluginContract.EXTRA_OPTIONS, options.toString))
|
||||
|
||||
/**
|
||||
* Finish this activity and request manual editor to pop up instead.
|
||||
*/
|
||||
final def fallbackToManualEditor() {
|
||||
setResult(PluginContract.RESULT_FALLBACK)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package com.github.shadowsocks.plugin
|
||||
|
||||
/**
|
||||
* Base class for a help activity. A help activity is started when user taps help when configuring options for your
|
||||
* plugin. To create a help activity, just extend this class, and add it to your manifest like this:
|
||||
*
|
||||
* <pre class="prettyprint"><manifest>
|
||||
* ...
|
||||
* <application>
|
||||
* ...
|
||||
* <activity android:name=".HelpActivity">
|
||||
* <intent-filter>
|
||||
* <action android:name="com.github.shadowsocks.plugin.ACTION_HELP"/>
|
||||
* <category android:name="android.intent.category.DEFAULT"/>
|
||||
* <data android:scheme="plugin"
|
||||
* android:host="com.github.shadowsocks"
|
||||
* android:path="/$PLUGIN_ID"/>
|
||||
* </intent-filter>
|
||||
* </activity>
|
||||
* ...
|
||||
* </application>
|
||||
*</manifest></pre>
|
||||
*
|
||||
* @author Mygod
|
||||
*/
|
||||
trait HelpActivity extends OptionsCapableActivity {
|
||||
// HelpActivity can choose to ignore options
|
||||
override protected def onInitializePluginOptions(options: PluginOptions): Unit = ()
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.github.shadowsocks.plugin
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
|
||||
/**
|
||||
* HelpCallback is an HelpActivity but you just need to produce a CharSequence help message instead of having to
|
||||
* provide UI. To create a help callback, just extend this class, implement abstract methods, and add it to your
|
||||
* manifest following the same procedure as adding a HelpActivity.
|
||||
*
|
||||
* @author Mygod
|
||||
*/
|
||||
trait HelpCallback extends HelpActivity {
|
||||
def produceHelpMessage(options: PluginOptions): CharSequence
|
||||
|
||||
override protected def onInitializePluginOptions(options: PluginOptions) {
|
||||
setResult(Activity.RESULT_OK, new Intent().putExtra(PluginContract.EXTRA_HELP_MESSAGE, produceHelpMessage(options)))
|
||||
finish()
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
package com.github.shadowsocks.plugin
|
||||
|
||||
import android.content.{ContentProvider, ContentValues}
|
||||
import android.database.{Cursor, MatrixCursor}
|
||||
import android.net.Uri
|
||||
import android.os.{Bundle, ParcelFileDescriptor}
|
||||
|
||||
/**
|
||||
* Base class for a native plugin provider. A native plugin provider offers read-only access to files that are required
|
||||
* to run a plugin, such as binary files and other configuration files. To create a native plugin provider, extend this
|
||||
* class, implement the abstract methods, and add it to your manifest like this:
|
||||
*
|
||||
* <pre class="prettyprint"><manifest>
|
||||
* ...
|
||||
* <application>
|
||||
* ...
|
||||
* <provider android:name="com.github.shadowsocks.$PLUGIN_ID.BinaryProvider"
|
||||
* android:authorities="com.github.shadowsocks.plugin.$PLUGIN_ID.BinaryProvider">
|
||||
* <intent-filter>
|
||||
* <category android:name="com.github.shadowsocks.plugin.ACTION_NATIVE_PLUGIN" />
|
||||
* </intent-filter>
|
||||
* </provider>
|
||||
* ...
|
||||
* </application>
|
||||
*</manifest></pre>
|
||||
*
|
||||
* @author Mygod
|
||||
*/
|
||||
abstract class NativePluginProvider extends ContentProvider {
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* NativePluginProvider returns application/x-elf by default. It's implementer's responsibility to change this to
|
||||
* correct type.
|
||||
*/
|
||||
override def getType(uri: Uri): String = "application/x-elf"
|
||||
|
||||
override def onCreate(): Boolean = true
|
||||
|
||||
/**
|
||||
* Provide all files needed for native plugin.
|
||||
*
|
||||
* @param provider A helper object to use to add files.
|
||||
*/
|
||||
protected def populateFiles(provider: PathProvider)
|
||||
|
||||
override def query(uri: Uri, projection: Array[String], selection: String, selectionArgs: Array[String],
|
||||
sortOrder: String): Cursor = {
|
||||
assert(selection == null && selectionArgs == null && sortOrder == null)
|
||||
val result = new MatrixCursor(projection)
|
||||
populateFiles(new PathProvider(uri, result))
|
||||
result
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns executable entry absolute path. This is used if plugin is sharing UID with the host.
|
||||
*
|
||||
* Default behavior is throwing UnsupportedOperationException. If you don't wish to use this feature, use the default
|
||||
* behavior.
|
||||
*
|
||||
* @return Absolute path for executable entry.
|
||||
*/
|
||||
def getExecutable: String = throw new UnsupportedOperationException
|
||||
|
||||
def openFile(uri: Uri): ParcelFileDescriptor
|
||||
override def openFile(uri: Uri, mode: String): ParcelFileDescriptor = {
|
||||
assert(mode == "r")
|
||||
openFile(uri)
|
||||
}
|
||||
|
||||
override def call(method: String, arg: String, extras: Bundle): Bundle = method match {
|
||||
case PluginContract.METHOD_GET_EXECUTABLE =>
|
||||
val out = new Bundle()
|
||||
out.putString(PluginContract.EXTRA_ENTRY, getExecutable)
|
||||
out
|
||||
case _ => super.call(method, arg, extras)
|
||||
}
|
||||
|
||||
// Methods that should not be used
|
||||
override def update(uri: Uri, values: ContentValues, selection: String, selectionArgs: Array[String]): Int = ???
|
||||
override def insert(uri: Uri, values: ContentValues): Uri = ???
|
||||
override def delete(uri: Uri, selection: String, selectionArgs: Array[String]): Int = ???
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.github.shadowsocks.plugin
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.widget.Toast
|
||||
|
||||
/**
|
||||
* Activity that's capable of getting EXTRA_OPTIONS input.
|
||||
*
|
||||
* @author Mygod
|
||||
*/
|
||||
trait OptionsCapableActivity extends AppCompatActivity {
|
||||
protected def pluginOptions(intent: Intent = getIntent): PluginOptions =
|
||||
try new PluginOptions(intent.getStringExtra(PluginContract.EXTRA_OPTIONS)) catch {
|
||||
case exc: IllegalArgumentException =>
|
||||
Toast.makeText(this, exc.getMessage, Toast.LENGTH_SHORT).show()
|
||||
null
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate args to your user interface.
|
||||
*
|
||||
* @param options PluginOptions parsed.
|
||||
*/
|
||||
protected def onInitializePluginOptions(options: PluginOptions = pluginOptions()): Unit
|
||||
|
||||
override protected def onPostCreate(savedInstanceState: Bundle) {
|
||||
super.onPostCreate(savedInstanceState)
|
||||
if (savedInstanceState == null) onInitializePluginOptions()
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package com.github.shadowsocks.plugin
|
||||
|
||||
import java.io.File
|
||||
|
||||
import android.database.MatrixCursor
|
||||
import android.net.Uri
|
||||
|
||||
/**
|
||||
* Helper class to provide relative paths of files to copy.
|
||||
*
|
||||
* @author Mygod
|
||||
*/
|
||||
final class PathProvider private[plugin](baseUri: Uri, cursor: MatrixCursor) {
|
||||
private val basePath = baseUri.getPath match {
|
||||
case null => ""
|
||||
case p => p.stripPrefix("/").stripSuffix("/")
|
||||
}
|
||||
|
||||
def addPath(path: String, mode: String = "644"): PathProvider = {
|
||||
val stripped = path.stripPrefix("/").stripSuffix("/")
|
||||
if (stripped.startsWith(basePath)) cursor.newRow()
|
||||
.add(PluginContract.COLUMN_PATH, stripped)
|
||||
.add(PluginContract.COLUMN_MODE, mode)
|
||||
this
|
||||
}
|
||||
def addTo(file: File, to: String = "", mode: String = "644"): PathProvider = {
|
||||
var sub = to + file.getName
|
||||
if (basePath.startsWith(sub)) if (file.isDirectory) {
|
||||
sub += '/'
|
||||
file.listFiles().foreach(addTo(_, sub, mode))
|
||||
} else addPath(sub, mode)
|
||||
this
|
||||
}
|
||||
def addAt(file: File, at: String = "", mode: String = "644"): PathProvider = {
|
||||
if (basePath.startsWith(at))
|
||||
if (file.isDirectory) file.listFiles().foreach(addTo(_, at, mode)) else addPath(at, mode)
|
||||
this
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.github.shadowsocks.plugin;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() throws Exception {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user