diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..b87085c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "app/libs/tun2socks"] + path = app/libs/tun2socks + url = https://github.com/dovecoteescapee/tun2socks.git +[submodule "app/src/main/cpp/byedpi"] + path = app/src/main/cpp/byedpi + url = https://github.com/hufrea/byedpi.git diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..7475813 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +ByeDpi \ No newline at end of file diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..7643783 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,123 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..b589d56 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml new file mode 100644 index 0000000..0c0c338 --- /dev/null +++ b/.idea/deploymentTargetDropDown.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..0897082 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..8d81632 --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..8978d23 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..31f9673 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..4e49cdd --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# ByeDPI for Android + +Application for Android that start a local VPN service to bypass DPI (Deep Packet Inspection) and unblock the internet. + +## Features + +See [ByeDPI description](https://github.com/hufrea/byedpi/readme.txt). + +## Dependencies + +- [ByeDPI](https://github.com/hufrea/byedpi) +- [Tun2Socks](https://github.com/dovecoteescapee/tun2socks) diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..4c49c4e --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,3 @@ +/build +*.aar +*.jar \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..f429cf4 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,87 @@ +import org.jetbrains.kotlin.gradle.dsl.KotlinCompile + +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") +} + +android { + namespace = "io.github.dovecoteescapee.byedpi" + compileSdk = 34 + + defaultConfig { + applicationId = "io.github.dovecoteescapee.byedpi" + minSdk = 24 + targetSdk = 34 + versionCode = 1 + versionName = "0.1.0-alpha" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } + externalNativeBuild { + cmake { + path = file("src/main/cpp/CMakeLists.txt") + version = "3.22.1" + } + } + buildFeatures { + viewBinding = true + } +} + +dependencies { + implementation(files("libs/tun2socks.aar")) + + implementation("androidx.fragment:fragment-ktx:1.6.2") + implementation("androidx.core:core-ktx:1.12.0") + implementation("androidx.appcompat:appcompat:1.6.1") + implementation("androidx.preference:preference-ktx:1.2.1") + implementation("com.takisoft.preferencex:preferencex:1.1.0") + implementation("com.google.android.material:material:1.11.0") + implementation("androidx.constraintlayout:constraintlayout:2.1.4") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0") + implementation("androidx.lifecycle:lifecycle-service:2.7.0") + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.5") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") +} + +abstract class BuildTun2Socks : DefaultTask() { + @TaskAction + fun buildTun2Socks() { + val projectDir = project.projectDir + val tun2socksDir = projectDir.resolve("libs/tun2socks") + val tun2socksOutput = projectDir.resolve("libs/tun2socks.aar") + + if (tun2socksOutput.exists()) { + return + } + project.exec { + workingDir = tun2socksDir + commandLine("gomobile", "bind", "-o", tun2socksOutput, "./engine") + } + } +} + +tasks.register("buildTun2Socks") { + group = "build" + description = "Build tun2socks" +} + +tasks.withType(KotlinCompile::class).configureEach { + dependsOn("buildTun2Socks") +} diff --git a/app/libs/tun2socks b/app/libs/tun2socks new file mode 160000 index 0000000..792ee44 --- /dev/null +++ b/app/libs/tun2socks @@ -0,0 +1 @@ +Subproject commit 792ee44a8efa7ec4daadd2cbfda762329d1cbff2 diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/release/byedpi.apk b/app/release/byedpi.apk new file mode 100644 index 0000000..0b8e873 Binary files /dev/null and b/app/release/byedpi.apk differ diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json new file mode 100644 index 0000000..c402b78 --- /dev/null +++ b/app/release/output-metadata.json @@ -0,0 +1,20 @@ +{ + "version": 3, + "artifactType": { + "type": "APK", + "kind": "Directory" + }, + "applicationId": "io.github.dovecoteescapee.byedpi", + "variantName": "release", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "attributes": [], + "versionCode": 1, + "versionName": "0.1.0-alpha", + "outputFile": "app-release.apk" + } + ], + "elementType": "File" +} \ No newline at end of file diff --git a/app/src/androidTest/java/io/github/dovecoteescapee/byedpi/ExampleInstrumentedTest.kt b/app/src/androidTest/java/io/github/dovecoteescapee/byedpi/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..d96bea0 --- /dev/null +++ b/app/src/androidTest/java/io/github/dovecoteescapee/byedpi/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package io.github.dovecoteescapee.byedpi + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("io.github.dovecoteescapee.byedpi", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..1719579 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000..351b18e --- /dev/null +++ b/app/src/main/cpp/CMakeLists.txt @@ -0,0 +1,46 @@ +# For more information about using CMake with Android Studio, read the +# documentation: https://d.android.com/studio/projects/add-native-code.html. +# For more examples on how to use CMake, see https://github.com/android/ndk-samples. + +# Sets the minimum CMake version required for this project. +cmake_minimum_required(VERSION 3.22.1) + +# Declares the project name. The project name can be accessed via ${ PROJECT_NAME}, +# Since this is the top level CMakeLists.txt, the project name is also accessible +# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level +# build script scope). +project("byedpi") + +# Creates and names a library, sets it as either STATIC +# or SHARED, and provides the relative paths to its source code. +# You can define multiple libraries, and CMake builds them for you. +# Gradle automatically packages shared libraries with your APK. +# +# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define +# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME} +# is preferred for the same purpose. +# +# In order to load a library into your app from Java/Kotlin, you must call +# System.loadLibrary() and pass the name of the library defined here; +# for GameActivity/NativeActivity derived applications, the same library name must be +# used in the AndroidManifest.xml file. +add_library(${CMAKE_PROJECT_NAME} SHARED + # List C/C++ source files with relative paths to this CMakeLists.txt. + byedpi/conev.c + byedpi/desync.c + byedpi/packets.c + byedpi/proxy.c + native-lib.c + ) + +include_directories("byedpi") + +set(CMAKE_C_FLAGS "-std=c99 -O2 -D_XOPEN_SOURCE=500") + +# Specifies libraries CMake should link to your target library. You +# can link libraries from various origins, such as libraries defined in this +# build script, prebuilt third-party libraries, or Android system libraries. +target_link_libraries(${CMAKE_PROJECT_NAME} + # List libraries link to the target library + android + log) \ No newline at end of file diff --git a/app/src/main/cpp/byedpi b/app/src/main/cpp/byedpi new file mode 160000 index 0000000..d880b04 --- /dev/null +++ b/app/src/main/cpp/byedpi @@ -0,0 +1 @@ +Subproject commit d880b0441f8c31e0609fb98be90d722190cd737b diff --git a/app/src/main/cpp/native-lib.c b/app/src/main/cpp/native-lib.c new file mode 100644 index 0000000..2e482a9 --- /dev/null +++ b/app/src/main/cpp/native-lib.c @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include + +extern int big_loop(int fd); + +struct packet fake_tls = { + sizeof(tls_data), tls_data +}, + fake_http = { + sizeof(http_data), http_data +}; + +struct params params = { + .ttl = 8, + .split = 3, + .sfdelay = 3000, + .attack = DESYNC_NONE, + .split_host = 0, + .def_ttl = 0, + .custom_ttl = 0, + .mod_http = 0, + .tlsrec = 0, + .tlsrec_pos = 0, + .tlsrec_sni = 0, + .de_known = 0, + + .ipv6 = 1, + .resolve = 1, + .max_open = 512, + .bfsize = 16384, + .baddr = { + .sin6_family = AF_INET6 + }, + .debug = 2 +}; + +int get_default_ttl() +{ + int orig_ttl = -1, fd; + socklen_t tsize = sizeof(orig_ttl); + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + uniperror("socket"); + return -1; + } + if (getsockopt(fd, IPPROTO_IP, IP_TTL, + (char *)&orig_ttl, &tsize) < 0) { + uniperror("getsockopt IP_TTL"); + } + close(fd); + return orig_ttl; +} + +JNIEXPORT jint JNICALL +Java_io_github_dovecoteescapee_byedpi_ByeDpiVpnService_startProxy__IIIIZZIIZIZZZIZ( + JNIEnv *env, + jobject thiz, + jint port, + jint max_connections, + jint buffer_size, + jint default_ttl, + jboolean no_domain, + jboolean desync_known, + jint desync_method, + jint split_position, + jboolean split_at_host, + jint fake_ttl, + jboolean host_mixed_case, + jboolean domain_mixed_case, + jboolean host_remove_space, + jint tls_record_split, + jboolean tls_record_split_at_sni) { + enum demode desync_methods[] = {DESYNC_NONE, DESYNC_SPLIT, DESYNC_DISORDER, DESYNC_FAKE}; + + params.max_open = max_connections; + params.bfsize = buffer_size; + params.def_ttl = default_ttl; + params.resolve = !no_domain; + params.de_known = desync_known; + params.attack = desync_methods[desync_method]; + params.split = split_position; + params.split_host = split_at_host; + params.ttl = fake_ttl; + params.mod_http |= host_mixed_case ? MH_HMIX : 0; + params.mod_http |= domain_mixed_case ? MH_DMIX : 0; + params.mod_http |= host_remove_space ? MH_SPACE : 0; + params.tlsrec = tls_record_split; + params.tlsrec_sni = tls_record_split_at_sni; + + if (!params.def_ttl && params.attack != DESYNC_NONE) { + if ((params.def_ttl = get_default_ttl()) < 1) { + return -1; + } + } + + struct sockaddr_ina srv = { + .in = { + .sin_family = AF_INET, + .sin_port = htons(port), + } + }; + + return listener(srv); +} \ No newline at end of file diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000..b815cc6 Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ diff --git a/app/src/main/java/io/github/dovecoteescapee/byedpi/ByeDpiProxyPreferences.kt b/app/src/main/java/io/github/dovecoteescapee/byedpi/ByeDpiProxyPreferences.kt new file mode 100644 index 0000000..177d39e --- /dev/null +++ b/app/src/main/java/io/github/dovecoteescapee/byedpi/ByeDpiProxyPreferences.kt @@ -0,0 +1,54 @@ +package io.github.dovecoteescapee.byedpi + +class ByeDpiProxyPreferences( + port: Int? = null, + maxConnections: Int? = null, + bufferSize: Int? = null, + defaultTtl: Int? = null, + noDomain: Boolean? = null, + desyncKnown: Boolean? = null, + desyncMethod: DesyncMethod? = null, + splitPosition: Int? = null, + splitAtHost: Boolean? = null, + fakeTtl: Int? = null, + hostMixedCase: Boolean? = null, + domainMixedCase: Boolean? = null, + hostRemoveSpaces: Boolean? = null, + tlsRecordSplit: Int? = null, + tlsRecordSplitAtSni: Boolean? = null, +) { + val port: Int = port ?: 1080 + val maxConnections: Int = maxConnections ?: 512 + val bufferSize: Int = bufferSize ?: 16384 + val defaultTtl: Int = defaultTtl ?: 0 + val noDomain: Boolean = noDomain ?: false + val desyncKnown: Boolean = desyncKnown ?: false + val desyncMethod: DesyncMethod = desyncMethod ?: DesyncMethod.None + val splitPosition: Int = splitPosition ?: 3 + val splitAtHost: Boolean = splitAtHost ?: false + val fakeTtl: Int = fakeTtl ?: 8 + val hostMixedCase: Boolean = hostMixedCase ?: false + val domainMixedCase: Boolean = domainMixedCase ?: false + val hostRemoveSpaces: Boolean = hostRemoveSpaces ?: false + val tlsRecordSplit: Int = tlsRecordSplit ?: 0 + val tlsRecordSplitAtSni: Boolean = tlsRecordSplitAtSni ?: false + + enum class DesyncMethod { + None, + Split, + Disorder, + Fake; + + companion object { + fun fromName(name: String): DesyncMethod { + return when (name) { + "none" -> None + "split" -> Split + "disorder" -> Disorder + "fake" -> Fake + else -> throw IllegalArgumentException("Unknown desync method: $name") + } + } + } + } +} diff --git a/app/src/main/java/io/github/dovecoteescapee/byedpi/ByeDpiVpnService.kt b/app/src/main/java/io/github/dovecoteescapee/byedpi/ByeDpiVpnService.kt new file mode 100644 index 0000000..f3f954b --- /dev/null +++ b/app/src/main/java/io/github/dovecoteescapee/byedpi/ByeDpiVpnService.kt @@ -0,0 +1,246 @@ +package io.github.dovecoteescapee.byedpi + +import android.app.PendingIntent +import android.content.Intent +import android.net.VpnService +import android.os.Build +import android.os.IBinder +import android.util.Log +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.ServiceLifecycleDispatcher +import androidx.lifecycle.lifecycleScope +import androidx.preference.PreferenceManager +import engine.Engine +import engine.Key +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch + + +class ByeDpiVpnService : VpnService(), LifecycleOwner { + private val TAG: String = this::class.java.simpleName + private var proxyJob: Job? = null + + private val dispatcher = ServiceLifecycleDispatcher(this) + override val lifecycle: Lifecycle + get() = dispatcher.lifecycle + + companion object { + var status: Status = Status.STOPPED + private set + } + + override fun onCreate() { + dispatcher.onServicePreSuperOnCreate() + super.onCreate() + } + + override fun onBind(intent: Intent?): IBinder? { + dispatcher.onServicePreSuperOnBind() + return super.onBind(intent) + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + if (intent != null) { + return when (intent.action) { + "start" -> { + run() + START_STICKY + } + "stop" -> { + stop() + START_NOT_STICKY + } + + else -> { + throw IllegalArgumentException("Unknown action ${intent.action}") + } + } + } + return super.onStartCommand(intent, flags, startId) + } + + override fun onRevoke() { + super.onRevoke() + Log.i(TAG, "VPN revoked") + stop() + } + + override fun onDestroy() { + dispatcher.onServicePreSuperOnDestroy() + super.onDestroy() + Log.i(TAG, "Service destroyed") + stop() + } + + private fun run() { + val preferences = getPreferences(); + + status = Status.RUNNING + + if (proxyJob != null) { + Log.w(TAG, "Proxy already running") + return + } + + proxyJob = lifecycleScope.launch(Dispatchers.IO) { + runProxy(preferences) + } + + val vpn = getBuilder().establish() + if (vpn == null) { + Log.e(TAG, "VPN connection failed") + return + } + + startTun2Socks(vpn.detachFd(), preferences.port) + } + + private fun getPreferences(): ByeDpiProxyPreferences { + val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) + + return ByeDpiProxyPreferences( + port = + sharedPreferences.getString("byedpi_proxy_port", null)?.toInt(), + maxConnections = + sharedPreferences.getString("byedpi_max_connections", null)?.toInt(), + bufferSize = + sharedPreferences.getString("byedpi_buffer_size", null)?.toInt(), + defaultTtl = + sharedPreferences.getString("byedpi_default_ttl", null)?.toInt(), + noDomain = + sharedPreferences.getBoolean("byedpi_no_domain", false), + desyncKnown = + sharedPreferences.getBoolean("byedpi_desync_known", false), + desyncMethod = + sharedPreferences.getString("byedpi_desync_method", null) + ?.let { ByeDpiProxyPreferences.DesyncMethod.fromName(it) }, + splitPosition = + sharedPreferences.getString("byedpi_split_position", null)?.toInt(), + splitAtHost = + sharedPreferences.getBoolean("byedpi_split_at_host", false), + fakeTtl = + sharedPreferences.getString("byedpi_fake_ttl", null)?.toInt(), + hostMixedCase = + sharedPreferences.getBoolean("byedpi_host_mixed_case", false), + domainMixedCase = + sharedPreferences.getBoolean("byedpi_domain_mixed_case", false), + hostRemoveSpaces = + sharedPreferences.getBoolean("byedpi_host_remove_spaces", false), + tlsRecordSplit = + sharedPreferences.getString("byedpi_tlsrec", null)?.toInt(), + tlsRecordSplitAtSni = + sharedPreferences.getBoolean("byedpi_tlsrec_at_sni", false), + ) + } + + private fun stop() { + status = Status.STOPPED + stopTun2Socks() + stopProxy() + } + + private fun runProxy(preferences: ByeDpiProxyPreferences) : Int { + Log.i(TAG, "Proxy started") + val res = startProxy( + port = preferences.port, + maxConnections = preferences.maxConnections, + bufferSize = preferences.bufferSize, + defaultTtl = preferences.defaultTtl, + noDomain = preferences.noDomain, + desyncKnown = preferences.desyncKnown, + desyncMethod = preferences.desyncMethod.ordinal, + splitPosition = preferences.splitPosition, + splitAtHost = preferences.splitAtHost, + fakeTtl = preferences.fakeTtl, + hostMixedCase = preferences.hostMixedCase, + domainMixedCase = preferences.domainMixedCase, + hostRemoveSpace = preferences.hostRemoveSpaces, + tlsRecordSplit = preferences.tlsRecordSplit, + tlsRecordSplitAtSni = preferences.tlsRecordSplitAtSni, + ) + Log.i(TAG, "Proxy stopped") + return res + } + + private fun stopProxy() { + proxyJob?.let { + Log.i(TAG, "Proxy stopped") + it.cancel() + proxyJob = null + } ?: Log.w(TAG, "Proxy not running") + } + + private fun startTun2Socks(fd: Int, port:Int) { + val key = Key().apply { + mark = 0 + mtu = 0 + device = "fd://$fd" + + setInterface("") + logLevel = "debug" + udpProxy = "direct://" + tcpProxy = "socks5://127.0.0.1:$port" + + restAPI = "" + tcpSendBufferSize = "" + tcpReceiveBufferSize = "" + tcpModerateReceiveBuffer = false + } + + Engine.insert(key) + + Log.i(TAG, "Tun2Socks started") + Engine.start() + } + + private fun stopTun2Socks() { + Log.i(TAG, "Tun2socks stopped") + Engine.stop() + } + + private external fun startProxy( + port: Int, + maxConnections: Int, + bufferSize: Int, + defaultTtl: Int, + noDomain: Boolean, + desyncKnown: Boolean, + desyncMethod: Int, + splitPosition: Int, + splitAtHost: Boolean, + fakeTtl: Int, + hostMixedCase: Boolean, + domainMixedCase: Boolean, + hostRemoveSpace: Boolean, + tlsRecordSplit: Int, + tlsRecordSplitAtSni: Boolean, + ): Int + + private fun getBuilder(): Builder { + val builder = Builder() + builder.setSession("ByeDPI") + builder.setConfigureIntent( + PendingIntent.getActivity( + this, + 0, + Intent(this, MainActivity::class.java), + PendingIntent.FLAG_IMMUTABLE, + ) + ) + + builder.addAddress("10.10.10.10", 32) + builder.addRoute("0.0.0.0", 0) + builder.addRoute("0:0:0:0:0:0:0:0", 0) + builder.addDnsServer("1.1.1.1") + builder.addDnsServer("1.0.0.1") + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + builder.setMetered(false) + } + + builder.addDisallowedApplication("io.github.dovecoteescapee.byedpi") + + return builder + } +} diff --git a/app/src/main/java/io/github/dovecoteescapee/byedpi/MainActivity.kt b/app/src/main/java/io/github/dovecoteescapee/byedpi/MainActivity.kt new file mode 100644 index 0000000..184f67b --- /dev/null +++ b/app/src/main/java/io/github/dovecoteescapee/byedpi/MainActivity.kt @@ -0,0 +1,89 @@ +package io.github.dovecoteescapee.byedpi + +import android.content.Intent +import android.net.VpnService +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts +import androidx.preference.PreferenceManager +import io.github.dovecoteescapee.byedpi.databinding.ActivityMainBinding + +class MainActivity : AppCompatActivity() { + private lateinit var binding: ActivityMainBinding + + private val register = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + if (it.resultCode == RESULT_OK) { + startVpnService() + } else { + Toast.makeText(this, R.string.vpn_permission_denied, Toast.LENGTH_SHORT).show() + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + + binding.statusButton.setOnClickListener { + when (ByeDpiVpnService.status) { + Status.STOPPED -> { + val intentPrepare = VpnService.prepare(this) + if (intentPrepare != null) { + register.launch(intentPrepare) + } else { + startVpnService() + } + updateStatus(Status.RUNNING) + } + Status.RUNNING -> { + stopVpnService() + updateStatus(Status.STOPPED) + } + } + } + + binding.settingsButton.setOnClickListener { + val intent = Intent(this, SettingsActivity::class.java) + if (ByeDpiVpnService.status == Status.RUNNING) { + Toast.makeText(this, R.string.settings_unavailable, Toast.LENGTH_SHORT) + .show() + } else { + startActivity(intent) + } + } + + updateStatus(ByeDpiVpnService.status) + } + + private fun startVpnService() { + val intent = Intent(this, ByeDpiVpnService::class.java) + intent.action = "start" + startService(intent) + } + + private fun stopVpnService() { + val intent = Intent(this, ByeDpiVpnService::class.java) + intent.action = "stop" + startService(intent) + } + + private fun updateStatus(status : Status) { + when (status) { + Status.STOPPED -> { + binding.statusButton.setText(R.string.start) + } + Status.RUNNING -> { + binding.statusButton.setText(R.string.stop) + } + } + } + + companion object { + // Used to load the 'byedpi' library on application startup. + init { + System.loadLibrary("byedpi") + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/dovecoteescapee/byedpi/SettingsActivity.kt b/app/src/main/java/io/github/dovecoteescapee/byedpi/SettingsActivity.kt new file mode 100644 index 0000000..df414ae --- /dev/null +++ b/app/src/main/java/io/github/dovecoteescapee/byedpi/SettingsActivity.kt @@ -0,0 +1,16 @@ +package io.github.dovecoteescapee.byedpi + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity + +class SettingsActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_settings) + + supportFragmentManager + .beginTransaction() + .replace(R.id.settings, SettingsFragment()) + .commit() + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/dovecoteescapee/byedpi/SettingsFragment.kt b/app/src/main/java/io/github/dovecoteescapee/byedpi/SettingsFragment.kt new file mode 100644 index 0000000..2fcc5d8 --- /dev/null +++ b/app/src/main/java/io/github/dovecoteescapee/byedpi/SettingsFragment.kt @@ -0,0 +1,10 @@ +package io.github.dovecoteescapee.byedpi + +import android.os.Bundle +import androidx.preference.PreferenceFragmentCompat + +class SettingsFragment : PreferenceFragmentCompat() { + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.settings, rootKey) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/dovecoteescapee/byedpi/Status.kt b/app/src/main/java/io/github/dovecoteescapee/byedpi/Status.kt new file mode 100644 index 0000000..9c9cbf4 --- /dev/null +++ b/app/src/main/java/io/github/dovecoteescapee/byedpi/Status.kt @@ -0,0 +1,6 @@ +package io.github.dovecoteescapee.byedpi + +enum class Status { + RUNNING, + STOPPED +} \ No newline at end of file diff --git a/app/src/main/res/drawable/outline_settings_24.xml b/app/src/main/res/drawable/outline_settings_24.xml new file mode 100644 index 0000000..549f086 --- /dev/null +++ b/app/src/main/res/drawable/outline_settings_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..45544b4 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,38 @@ + + + + + + + + diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml new file mode 100644 index 0000000..67b8bf2 --- /dev/null +++ b/app/src/main/res/layout/activity_settings.xml @@ -0,0 +1,5 @@ + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..036d09b --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..036d09b --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..9a593d9 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..776d4cf Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..b7c638f Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..974e392 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..06858dc Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..e2f5074 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..6a2a36b Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..7ed7293 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..4a193e2 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..b932dd2 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..f4158e0 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..d9eef0f Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..9e0b76b Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp new file mode 100644 index 0000000..13da154 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..a9ab65c Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml new file mode 100644 index 0000000..31ce441 --- /dev/null +++ b/app/src/main/res/values/arrays.xml @@ -0,0 +1,15 @@ + + + + None + Split + Disorder + Fake + + + none + split + disorder + fake + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..c8524cd --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,5 @@ + + + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 0000000..ed74d8c --- /dev/null +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #061A23 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..397e633 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,8 @@ + + ByeDPI + Start + Stop + Settings + VPN permission denied + Please stop the VPN service before changing settings + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..e3796a2 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,16 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..fa0f996 --- /dev/null +++ b/app/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/settings.xml b/app/src/main/res/xml/settings.xml new file mode 100644 index 0000000..3a48600 --- /dev/null +++ b/app/src/main/res/xml/settings.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/test/java/io/github/dovecoteescapee/byedpi/ExampleUnitTest.kt b/app/src/test/java/io/github/dovecoteescapee/byedpi/ExampleUnitTest.kt new file mode 100644 index 0000000..46b3c16 --- /dev/null +++ b/app/src/test/java/io/github/dovecoteescapee/byedpi/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package io.github.dovecoteescapee.byedpi + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..b59bc38 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,5 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id("com.android.application") version "8.2.2" apply false + id("org.jetbrains.kotlin.android") version "1.9.22" apply false +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..3c5031e --- /dev/null +++ b/gradle.properties @@ -0,0 +1,23 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e708b1c Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ffe3892 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Feb 19 00:00:21 MSK 2024 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..4f906e0 --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..25f1463 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,17 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "ByeDpi" +include(":app")