Skip to content

Commit c104aab

Browse files
committed
Add apk build
1 parent 85f62dd commit c104aab

29 files changed

+791
-0
lines changed

apk/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.gradle/
2+
app/build/
3+
app/libs/

apk/app/build.gradle.kts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
plugins {
2+
id("com.android.application")
3+
id("org.jetbrains.kotlin.android")
4+
}
5+
6+
android {
7+
namespace = "io.nekohasekai.sagernet.plugin.naive"
8+
9+
signingConfigs {
10+
create("release") {
11+
storeFile = rootProject.file("release.keystore")
12+
storePassword = System.getenv("KEYSTORE_PASS")
13+
keyAlias = "release"
14+
keyPassword = System.getenv("KEYSTORE_PASS")
15+
}
16+
}
17+
18+
buildTypes {
19+
getByName("release") {
20+
isMinifyEnabled = true
21+
signingConfig = signingConfigs.getByName("release")
22+
}
23+
}
24+
25+
buildToolsVersion = "35.0.0"
26+
27+
compileSdk = 35
28+
29+
defaultConfig {
30+
minSdk = 24
31+
targetSdk = 35
32+
33+
applicationId = "io.nekohasekai.sagernet.plugin.naive"
34+
versionCode = System.getenv("APK_VERSION_NAME").removePrefix("v").split(".")[0].toInt() * 10 + System.getenv("APK_VERSION_NAME").removePrefix("v").split("-")[1].toInt()
35+
versionName = System.getenv("APK_VERSION_NAME").removePrefix("v")
36+
splits.abi {
37+
isEnable = true
38+
isUniversalApk = false
39+
reset()
40+
include(System.getenv("APK_ABI"))
41+
}
42+
}
43+
44+
compileOptions {
45+
sourceCompatibility = JavaVersion.VERSION_17
46+
targetCompatibility = JavaVersion.VERSION_17
47+
}
48+
49+
lint {
50+
showAll = true
51+
checkAllWarnings = true
52+
checkReleaseBuilds = false
53+
warningsAsErrors = true
54+
}
55+
56+
packaging {
57+
jniLibs.useLegacyPackaging = true
58+
}
59+
60+
applicationVariants.all {
61+
outputs.all {
62+
this as com.android.build.gradle.internal.api.BaseVariantOutputImpl
63+
outputFileName =
64+
outputFileName.replace(project.name, "naiveproxy-plugin-v$versionName")
65+
.replace("-release", "")
66+
.replace("-oss", "")
67+
}
68+
}
69+
70+
sourceSets.getByName("main") {
71+
jniLibs.srcDir("libs")
72+
}
73+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:tools="http://schemas.android.com/tools"
4+
android:installLocation="internalOnly"
5+
tools:ignore="MissingLeanbackLauncher">
6+
7+
<uses-feature
8+
android:name="android.software.leanback"
9+
android:required="false" />
10+
<uses-feature
11+
android:name="android.hardware.touchscreen"
12+
android:required="false" />
13+
14+
<application
15+
android:allowBackup="false"
16+
android:icon="@mipmap/ic_launcher"
17+
android:label="Naïve Plugin"
18+
android:roundIcon="@mipmap/ic_launcher_round">
19+
<provider
20+
android:name=".BinaryProvider"
21+
android:authorities="io.nekohasekai.sagernet.plugin.naive.BinaryProvider"
22+
android:directBootAware="true"
23+
android:exported="true"
24+
tools:ignore="ExportedContentProvider">
25+
<intent-filter>
26+
<action android:name="io.nekohasekai.sagernet.plugin.ACTION_NATIVE_PLUGIN" />
27+
</intent-filter>
28+
<intent-filter>
29+
<action android:name="io.nekohasekai.sagernet.plugin.ACTION_NATIVE_PLUGIN" />
30+
<data
31+
android:host="io.nekohasekai.sagernet"
32+
android:path="/naive-plugin"
33+
android:scheme="plugin" />
34+
</intent-filter>
35+
36+
<meta-data
37+
android:name="io.nekohasekai.sagernet.plugin.id"
38+
android:value="naive-plugin" />
39+
<meta-data
40+
android:name="io.nekohasekai.sagernet.plugin.executable_path"
41+
android:value="libnaive.so" />
42+
</provider>
43+
</application>
44+
45+
</manifest>
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/******************************************************************************
2+
* *
3+
* Copyright (C) 2021 by nekohasekai <[email protected]> *
4+
* *
5+
* This program is free software: you can redistribute it and/or modify *
6+
* it under the terms of the GNU General Public License as published by *
7+
* the Free Software Foundation, either version 3 of the License, or *
8+
* (at your option) any later version. *
9+
* *
10+
* This program is distributed in the hope that it will be useful, *
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13+
* GNU General Public License for more details. *
14+
* *
15+
* You should have received a copy of the GNU General Public License *
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
17+
* *
18+
******************************************************************************/
19+
20+
package io.nekohasekai.sagernet.plugin
21+
22+
import android.content.ContentProvider
23+
import android.content.ContentValues
24+
import android.database.Cursor
25+
import android.database.MatrixCursor
26+
import android.net.Uri
27+
import android.os.Bundle
28+
import android.os.ParcelFileDescriptor
29+
30+
abstract class NativePluginProvider : ContentProvider() {
31+
override fun getType(uri: Uri): String? = "application/x-elf"
32+
33+
override fun onCreate(): Boolean = true
34+
35+
/**
36+
* Provide all files needed for native plugin.
37+
*
38+
* @param provider A helper object to use to add files.
39+
*/
40+
protected abstract fun populateFiles(provider: PathProvider)
41+
42+
override fun query(
43+
uri: Uri,
44+
projection: Array<out String>?,
45+
selection: String?,
46+
selectionArgs: Array<out String>?,
47+
sortOrder: String?,
48+
): Cursor? {
49+
check(selection == null && selectionArgs == null && sortOrder == null)
50+
val result = MatrixCursor(projection)
51+
populateFiles(PathProvider(uri, result))
52+
return result
53+
}
54+
55+
/**
56+
* Returns executable entry absolute path.
57+
* This is used for fast mode initialization where ss-local launches your native binary at the path given directly.
58+
* In order for this to work, plugin app is encouraged to have the following in its AndroidManifest.xml:
59+
* - android:installLocation="internalOnly" for <manifest>
60+
* - android:extractNativeLibs="true" for <application>
61+
*
62+
* Default behavior is throwing UnsupportedOperationException. If you don't wish to use this feature, use the
63+
* default behavior.
64+
*
65+
* @return Absolute path for executable entry.
66+
*/
67+
open fun getExecutable(): String = throw UnsupportedOperationException()
68+
69+
abstract fun openFile(uri: Uri): ParcelFileDescriptor
70+
override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor {
71+
check(mode == "r")
72+
return openFile(uri)
73+
}
74+
75+
override fun call(method: String, arg: String?, extras: Bundle?): Bundle? = when (method) {
76+
PluginContract.METHOD_GET_EXECUTABLE -> {
77+
Bundle().apply {
78+
putString(PluginContract.EXTRA_ENTRY, getExecutable())
79+
}
80+
}
81+
else -> super.call(method, arg, extras)
82+
}
83+
84+
// Methods that should not be used
85+
override fun insert(uri: Uri, values: ContentValues?): Uri? =
86+
throw UnsupportedOperationException()
87+
88+
override fun update(
89+
uri: Uri,
90+
values: ContentValues?,
91+
selection: String?,
92+
selectionArgs: Array<out String>?,
93+
): Int =
94+
throw UnsupportedOperationException()
95+
96+
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int =
97+
throw UnsupportedOperationException()
98+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/******************************************************************************
2+
* *
3+
* Copyright (C) 2021 by nekohasekai <[email protected]> *
4+
* *
5+
* This program is free software: you can redistribute it and/or modify *
6+
* it under the terms of the GNU General Public License as published by *
7+
* the Free Software Foundation, either version 3 of the License, or *
8+
* (at your option) any later version. *
9+
* *
10+
* This program is distributed in the hope that it will be useful, *
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13+
* GNU General Public License for more details. *
14+
* *
15+
* You should have received a copy of the GNU General Public License *
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
17+
* *
18+
******************************************************************************/
19+
20+
package io.nekohasekai.sagernet.plugin
21+
22+
import android.database.MatrixCursor
23+
import android.net.Uri
24+
import java.io.File
25+
26+
/**
27+
* Helper class to provide relative paths of files to copy.
28+
*/
29+
class PathProvider internal constructor(baseUri: Uri, private val cursor: MatrixCursor) {
30+
private val basePath = baseUri.path?.trim('/') ?: ""
31+
32+
fun addPath(path: String, mode: Int = 0b110100100): PathProvider {
33+
val trimmed = path.trim('/')
34+
if (trimmed.startsWith(basePath)) cursor.newRow()
35+
.add(PluginContract.COLUMN_PATH, trimmed)
36+
.add(PluginContract.COLUMN_MODE, mode)
37+
return this
38+
}
39+
fun addTo(file: File, to: String = "", mode: Int = 0b110100100): PathProvider {
40+
var sub = to + file.name
41+
if (basePath.startsWith(sub)) if (file.isDirectory) {
42+
sub += '/'
43+
file.listFiles()!!.forEach { addTo(it, sub, mode) }
44+
} else addPath(sub, mode)
45+
return this
46+
}
47+
fun addAt(file: File, at: String = "", mode: Int = 0b110100100): PathProvider {
48+
if (basePath.startsWith(at)) {
49+
if (file.isDirectory) file.listFiles()!!.forEach { addTo(it, at, mode) } else addPath(at, mode)
50+
}
51+
return this
52+
}
53+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/******************************************************************************
2+
* *
3+
* Copyright (C) 2021 by nekohasekai <[email protected]> *
4+
* *
5+
* This program is free software: you can redistribute it and/or modify *
6+
* it under the terms of the GNU General Public License as published by *
7+
* the Free Software Foundation, either version 3 of the License, or *
8+
* (at your option) any later version. *
9+
* *
10+
* This program is distributed in the hope that it will be useful, *
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13+
* GNU General Public License for more details. *
14+
* *
15+
* You should have received a copy of the GNU General Public License *
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
17+
* *
18+
******************************************************************************/
19+
20+
package io.nekohasekai.sagernet.plugin
21+
22+
object PluginContract {
23+
24+
const val ACTION_NATIVE_PLUGIN = "io.nekohasekai.sagernet.plugin.ACTION_NATIVE_PLUGIN"
25+
const val EXTRA_ENTRY = "io.nekohasekai.sagernet.plugin.EXTRA_ENTRY"
26+
const val METADATA_KEY_ID = "io.nekohasekai.sagernet.plugin.id"
27+
const val METADATA_KEY_EXECUTABLE_PATH = "io.nekohasekai.sagernet.plguin.executable_path"
28+
const val METHOD_GET_EXECUTABLE = "sagernet:getExecutable"
29+
30+
const val COLUMN_PATH = "path"
31+
const val COLUMN_MODE = "mode"
32+
const val SCHEME = "plugin"
33+
const val AUTHORITY = "io.nekohasekai.sagernet"
34+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/******************************************************************************
2+
* *
3+
* Copyright (C) 2021 by nekohasekai <[email protected]> *
4+
* *
5+
* This program is free software: you can redistribute it and/or modify *
6+
* it under the terms of the GNU General Public License as published by *
7+
* the Free Software Foundation, either version 3 of the License, or *
8+
* (at your option) any later version. *
9+
* *
10+
* This program is distributed in the hope that it will be useful, *
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13+
* GNU General Public License for more details. *
14+
* *
15+
* You should have received a copy of the GNU General Public License *
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
17+
* *
18+
******************************************************************************/
19+
20+
package io.nekohasekai.sagernet.plugin.naive
21+
22+
import android.net.Uri
23+
import android.os.ParcelFileDescriptor
24+
import io.nekohasekai.sagernet.plugin.NativePluginProvider
25+
import io.nekohasekai.sagernet.plugin.PathProvider
26+
import java.io.File
27+
import java.io.FileNotFoundException
28+
29+
class BinaryProvider : NativePluginProvider() {
30+
override fun populateFiles(provider: PathProvider) {
31+
provider.addPath("naive-plugin", 0b111101101)
32+
}
33+
34+
override fun getExecutable() = context!!.applicationInfo.nativeLibraryDir + "/libnaive.so"
35+
override fun openFile(uri: Uri): ParcelFileDescriptor = when (uri.path) {
36+
"/naive-plugin" -> ParcelFileDescriptor.open(
37+
File(getExecutable()),
38+
ParcelFileDescriptor.MODE_READ_ONLY
39+
)
40+
else -> throw FileNotFoundException()
41+
}
42+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:width="108dp"
4+
android:height="108dp"
5+
android:viewportWidth="108"
6+
android:viewportHeight="108"
7+
android:tint="#FFFFFF">
8+
<group android:scaleX="0.13877095"
9+
android:scaleY="0.13877095"
10+
android:translateX="29.16"
11+
android:translateY="42.010185">
12+
<group android:translateY="138.67206">
13+
<path android:pathData="M4.171875,-0L34.546875,-0L34.546875,-5.890625L25.90625,-7.046875L23.3125,-9.796875L23.3125,-82.125L78.1875,-0L87.6875,-0L87.6875,-87.328125L90.28125,-89.9375L98.921875,-91.234375L98.921875,-97L68.671875,-97L68.671875,-91.234375L77.3125,-89.9375L79.90625,-87.328125L79.90625,-19.34375L30.8125,-93.390625L30.8125,-97L3.734375,-97L3.734375,-91.234375L12.953125,-90.359375L15.546875,-88.34375L15.546875,-9.796875L12.953125,-7.046875L4.171875,-5.890625L4.171875,-0Z"
14+
android:fillColor="#FFFFFF"/>
15+
<path android:pathData="M151.09375,-8.375L152.95312,0L173.26562,0L173.26562,-5.171875L165.625,-5.90625L163.03125,-8.234375L163.03125,-50.3125C163.03125,-64.796875,156.70312,-71,141.57812,-71C123.4375,-71,112.5,-63.5,112.5,-54.75C112.5,-51.3125,113.640625,-50.3125,116.953125,-50.3125L126.890625,-50.3125L126.890625,-62.359375C130.78125,-63.9375,134.23438,-64.65625,137.84375,-64.65625C148.20312,-64.65625,151.09375,-59.921875,151.09375,-48.578125L151.09375,-44C122.28125,-37.0625,109.046875,-32.6875,109.046875,-17.546875C109.046875,-6.625,116.09375,1,127.46875,1C134.39062,1.015625,142.01562,-2.15625,151.09375,-8.375ZM151.09375,-13.328125C143.89062,-9.09375,137.98438,-6.765625,133.23438,-6.765625C126.03125,-6.765625,121.71875,-11.578125,121.71875,-18.703125C121.71875,-29.34375,130.78125,-33.265625,151.09375,-38.953125L151.09375,-13.328125Z"
16+
android:fillColor="#FFFFFF"/>
17+
<path android:pathData="M197.59375,-70L181.03125,-65L181.03125,-60.53125L191.10938,-60.53125L191.10938,-8.203125L188.65625,-5.90625L181.03125,-5.171875L181.03125,0L213.4375,0L213.4375,-5.171875L205.65625,-5.90625L203.20312,-8.203125L203.20312,-70L197.59375,-70ZM182.76562,-99.53125C178.73438,-99.53125,175.42188,-96.375,175.42188,-92.1875C175.42188,-88.15625,178.73438,-85,182.76562,-85C186.79688,-85,189.95312,-88.15625,189.95312,-92.1875C189.95312,-96.375,186.79688,-99.53125,182.76562,-99.53125ZM209.54688,-99.53125C205.51562,-99.53125,202.20312,-96.375,202.20312,-92.1875C202.20312,-88.15625,205.51562,-85,209.54688,-85C213.57812,-85,216.75,-88.15625,216.75,-92.1875C216.75,-96.375,213.57812,-99.53125,209.54688,-99.53125Z"
18+
android:fillColor="#FFFFFF"/>
19+
<path android:pathData="M215.26562,-69L215.26562,-63.671875L224.0625,-62.796875L247.23438,0L255.29688,0L280.5,-62.515625L289,-63.671875L289,-69L263.51562,-69L263.51562,-63.671875L271.28125,-63.09375L272.4375,-60.796875L254,-13.90625L237.15625,-60.796875L238.89062,-63.09375L246.51562,-63.671875L246.51562,-69L215.26562,-69Z"
20+
android:fillColor="#FFFFFF"/>
21+
<path android:pathData="M351.5,-15.5C343.29688,-10.0625,336.09375,-7.203125,328.89062,-7.203125C314.35938,-7.203125,304.70312,-18.078125,304.70312,-36.421875C304.70312,-36.84375,304.70312,-37.421875,304.70312,-38L351.35938,-38C351.5,-39.578125,351.5,-41.015625,351.5,-42.453125C351.5,-60.296875,341.28125,-71,325.57812,-71C306.28125,-71,292.3125,-56.09375,292.3125,-34.265625C292.3125,-13.21875,305.42188,1,324.4375,1C333.5,1,342.85938,-1.875,351.5,-7.625L351.5,-15.5ZM338.6875,-44.046875L305.42188,-44.046875C306.28125,-56.375,314.5,-64.515625,323.71875,-64.515625C333.35938,-64.515625,338.6875,-58.125,338.6875,-46.359375C338.6875,-45.640625,338.6875,-44.765625,338.6875,-44.046875Z"
22+
android:fillColor="#FFFFFF"/>
23+
</group>
24+
</group>
25+
</vector>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
3+
<background android:drawable="@color/ic_launcher_background"/>
4+
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
5+
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
6+
</adaptive-icon>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
3+
<background android:drawable="@color/ic_launcher_background"/>
4+
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
5+
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
6+
</adaptive-icon>

0 commit comments

Comments
 (0)