From 5772b91b9a17643f3779557e5342817f7d8ae137 Mon Sep 17 00:00:00 2001 From: Administrator Date: Wed, 9 Jul 2025 18:24:08 +0800 Subject: [PATCH] . --- app/proguard-rules.pro | 62 +++++--- .../java/com/android/grape/data/AfInfo.kt | 19 --- .../java/com/android/grape/data/BigoInfo.kt | 21 --- .../java/com/android/grape/data/DeviceInfo.kt | 25 --- .../grape/job/DownloadAppJobService.kt | 5 +- .../grape/job/StartVpnPortJobService.kt | 28 +++- .../grape/job/StartVpnServerJobService.kt | 12 +- .../main/java/com/android/grape/net/MyGet.kt | 37 ++++- .../java/com/android/grape/util/ClashUtil.kt | 150 +++++++++++------- .../com/android/grape/util/CountryCode.kt | 18 +++ .../main/java/com/android/grape/util/Util.kt | 2 - 11 files changed, 225 insertions(+), 154 deletions(-) delete mode 100644 app/src/main/java/com/android/grape/data/AfInfo.kt delete mode 100644 app/src/main/java/com/android/grape/data/BigoInfo.kt delete mode 100644 app/src/main/java/com/android/grape/data/DeviceInfo.kt create mode 100644 app/src/main/java/com/android/grape/util/CountryCode.kt diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 481bb43..685426c 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,21 +1,47 @@ -# 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 +-keep class com.google.gson.** { *; } +-keep class com.google.gson.stream.** { *; } -# 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 *; -#} +# 保留所有注解 +-keepattributes *Annotation* +-keepattributes Signature -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable +# 保留枚举类 +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file +# 保留所有模型类(根据你的包结构调整) +-keep class com.android.grape.pad.** { *; } +-keep class com.android.grape.net.ApiResponse{ *; } +-keep class com.android.grape.net.ApiResponseList{ *; } + +# 保留所有使用 @SerializedName 注解的字段 +-keepclassmembers class * { + @com.google.gson.annotations.SerializedName ; +} + +# 保留所有模型类的无参构造函数 +-keepclassmembers class com.android.grape.pad.** { + public (); +} + +# 保留类型适配器 +-keep class * extends com.google.gson.TypeAdapter { + public com.google.gson.TypeAdapter create(com.google.gson.Gson, com.google.gson.reflect.TypeToken); +} +# 保留 Gson 创建的类 +-keep class com.google.gson.examples.android.model.** { *; } +-keepattributes Signature + +# 保留 TypeToken 类及其子类 +-keep class com.google.gson.reflect.TypeToken { *; } +-keep class * extends com.google.gson.reflect.TypeToken + +-keep class sun.misc.Unsafe { *; } + +# 保留注解信息 +-keepattributes *Annotation* + +# 保留 Kotlin 元数据(如果使用 Kotlin) +-keepclassmembers class **$TypeToken { *; } \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/data/AfInfo.kt b/app/src/main/java/com/android/grape/data/AfInfo.kt deleted file mode 100644 index c041610..0000000 --- a/app/src/main/java/com/android/grape/data/AfInfo.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.android.grape.data - -class AfInfo { - var advertiserId: String? = null - var model: String? = null - var brand: String? = null - var androidId: String? = null - var xPixels: Int = 0 - var yPixels: Int = 0 - var densityDpi: Int = 0 - var country: String? = null - var batteryLevel: String? = null - var stackInfo: String? = null - var product: String? = null - var network: String? = null - var langCode: String? = null - var cpuAbi: String? = null - var yDp: Long = 0 -} \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/data/BigoInfo.kt b/app/src/main/java/com/android/grape/data/BigoInfo.kt deleted file mode 100644 index ce85693..0000000 --- a/app/src/main/java/com/android/grape/data/BigoInfo.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.android.grape.data - -class BigoInfo { - var cpuClockSpeed: String? = null - var gaid: String? = null - var userAgent: String? = null - var osLang: String? = null - var osVer: String? = null - var tz: String? = null - var systemCountry: String? = null - var simCountry: String? = null - var romFreeIn: Long = 0 - var resolution: String? = null - var vendor: String? = null - var batteryScale: Int = 0 - var net: String? = null - var dpi: Long = 0 - var romFreeExt: Long = 0 - var dpiF: String? = null - var cpuCoreNum: Long = 0 -} diff --git a/app/src/main/java/com/android/grape/data/DeviceInfo.kt b/app/src/main/java/com/android/grape/data/DeviceInfo.kt deleted file mode 100644 index 1b387b1..0000000 --- a/app/src/main/java/com/android/grape/data/DeviceInfo.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.android.grape.data - -class DeviceInfo { - var lang: String? = null - var roProductBrand: String? = null - var roProductModel: String? = null - var roProductManufacturer: String? = null - var roProductDevice: String? = null - var roProductName: String? = null - var roBuildVersionIncremental: String? = null - var roBuildFingerprint: String? = null - var roOdmBuildFingerprint: String? = null - var roProductBuildFingerprint: String? = null - var roSystemBuildFingerprint: String? = null - var roSystemExtBuildFingerprint: String? = null - var roVendorBuildFingerprint: String? = null - var roBuildPlatform: String? = null - var persistSysCloudDrmId: String? = null - var persistSysCloudBatteryCapacity: Int = 0 - var persistSysCloudGpuGlVendor: String? = null - var persistSysCloudGpuGlRenderer: String? = null - var persistSysCloudGpuGlVersion: String? = null - var persistSysCloudGpuEglVendor: String? = null - var persistSysCloudGpuEglVersion: String? = null -} \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/job/DownloadAppJobService.kt b/app/src/main/java/com/android/grape/job/DownloadAppJobService.kt index a63951a..8b27a7f 100644 --- a/app/src/main/java/com/android/grape/job/DownloadAppJobService.kt +++ b/app/src/main/java/com/android/grape/job/DownloadAppJobService.kt @@ -23,9 +23,8 @@ class DownloadAppJobService : JobIntentService() { if (succ) { errTime = 0L - InstallService.onEvent(this) -// StartVpnServerJobService.onEvent(this) -// StartVpnPortJobService.onEvent(this) +// InstallService.onEvent(this) + StartVpnPortJobService.onEvent(this) } else { Util.isClickRet = false Util.setInstallRet(false) diff --git a/app/src/main/java/com/android/grape/job/StartVpnPortJobService.kt b/app/src/main/java/com/android/grape/job/StartVpnPortJobService.kt index 7d32b23..f503e37 100644 --- a/app/src/main/java/com/android/grape/job/StartVpnPortJobService.kt +++ b/app/src/main/java/com/android/grape/job/StartVpnPortJobService.kt @@ -6,13 +6,17 @@ import android.os.Handler import android.os.Looper import android.util.Log import androidx.core.app.JobIntentService +import com.android.grape.net.MyGet import com.android.grape.util.ClashUtil +import com.android.grape.util.ClashUtil.getProxyPort import com.android.grape.util.Util +import java.util.Locale class StartVpnPortJobService : JobIntentService() { override fun onHandleWork(intent: Intent) { Log.i(TAG, "start to handle work") - if (ClashUtil.checkProxy( this)) { + ClashUtil.switchProxyGroup("PROXY", "DIRECT", "http://127.0.0.1:6170") + if (exec()) { Handler(Looper.getMainLooper()).postDelayed({ StartVpnServerJobService.onEvent( this@StartVpnPortJobService @@ -25,6 +29,28 @@ class StartVpnPortJobService : JobIntentService() { } } + private fun exec(): Boolean { + try { + val port = getProxyPort() + var nRetryCount = 0 + do { + val url = + "http://39.103.73.250/tt/test/testProxy.jsp?port=$port&country=" + Util.proxyCountry + ?.lowercase(Locale.getDefault()) + val result: String = MyGet.get(url) + Log.d(TAG, "request url == $url result$result") + if (result.contains("ok")) { + return true + } + } while (nRetryCount++ < 3) + return false + } catch (err: Exception) { + err.printStackTrace() + return false + } + } + + companion object { private const val TAG = "IOSTQ:StartVpnPort" diff --git a/app/src/main/java/com/android/grape/job/StartVpnServerJobService.kt b/app/src/main/java/com/android/grape/job/StartVpnServerJobService.kt index 7e3d12e..ff51cfc 100644 --- a/app/src/main/java/com/android/grape/job/StartVpnServerJobService.kt +++ b/app/src/main/java/com/android/grape/job/StartVpnServerJobService.kt @@ -8,6 +8,7 @@ import android.util.Log import androidx.core.app.JobIntentService import com.android.grape.MainApplication import com.android.grape.util.ClashUtil +import com.android.grape.util.CountryCode import com.android.grape.util.Util /** @@ -27,19 +28,12 @@ class StartVpnServerJobService : JobIntentService() { onEvent( this@StartVpnServerJobService ) - }, 1000L) + }, 2000L) } } private fun exec(): Boolean { - ClashUtil.startProxy(MainApplication.instance) - ClashUtil.switchProxyGroup("GLOBAL", Util.proxyCountry, "http://127.0.0.1:6170") - if (!ClashUtil.checkProxy(MainApplication.instance)) { - println("IOSTQ:start vpn error") - return false - } - println("IOSTQ:start vpn ok") - return true + return ClashUtil.switchProxyGroup("PROXY", "my-socks5-proxy", "http://127.0.0.1:6170") } companion object { diff --git a/app/src/main/java/com/android/grape/net/MyGet.kt b/app/src/main/java/com/android/grape/net/MyGet.kt index 8b36608..0773232 100644 --- a/app/src/main/java/com/android/grape/net/MyGet.kt +++ b/app/src/main/java/com/android/grape/net/MyGet.kt @@ -2,16 +2,51 @@ package com.android.grape.net import android.content.Context import android.util.Log +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response import java.io.BufferedInputStream import java.io.ByteArrayOutputStream import java.io.IOException import java.io.InputStream import java.net.HttpURLConnection import java.net.URL +import java.util.concurrent.TimeUnit object MyGet { private const val TAG = "MyGet" + val affHttpClient: OkHttpClient = OkHttpClient() + + fun getAffHttpClient(): OkHttpClient { + affHttpClient.dispatcher.maxRequests = 1000 + affHttpClient.dispatcher.maxRequestsPerHost = 1000 + return affHttpClient + } + + fun get(url_: String): String { + var response: Response? = null + val client: OkHttpClient = getAffHttpClient().newBuilder() + .connectTimeout(5, TimeUnit.SECONDS) + .readTimeout(5, TimeUnit.SECONDS) + .build() + val request = Request.Builder() + .url(url_) + .addHeader("User-Agent", "PostmanRuntime/7.29.0") + .addHeader("Accept", "*/*") + .addHeader("Accept-Encoding", "gzip, deflate, br") + .addHeader("Connection", "keep-alive") + .build() + try { + response = client.newCall(request).execute() + return response.body?.string()?:"" + } catch (e: java.lang.Exception) { + e.printStackTrace() + } finally { + response?.close() + } + return "" + } fun getData(url_: String, ua: String?): String? { return getData(url_, ua, 0) @@ -31,7 +66,7 @@ object MyGet { httpUrlConnection = url.openConnection() as HttpURLConnection httpUrlConnection.allowUserInteraction = true - httpUrlConnection!!.doOutput = false + httpUrlConnection.doOutput = false httpUrlConnection.doInput = true httpUrlConnection.useCaches = false httpUrlConnection.setRequestProperty("Connection", "close") //add 20200428 diff --git a/app/src/main/java/com/android/grape/util/ClashUtil.kt b/app/src/main/java/com/android/grape/util/ClashUtil.kt index 72c3bfd..c8e3bff 100644 --- a/app/src/main/java/com/android/grape/util/ClashUtil.kt +++ b/app/src/main/java/com/android/grape/util/ClashUtil.kt @@ -4,24 +4,35 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.os.Environment import android.util.Log import androidx.core.content.ContextCompat import com.blankj.utilcode.util.LogUtils -import okhttp3.Call -import okhttp3.Callback import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.MediaType import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody -import okhttp3.Response +import okhttp3.logging.HttpLoggingInterceptor import org.json.JSONException import org.json.JSONObject +import java.io.BufferedReader +import java.io.File +import java.io.FileReader import java.io.IOException import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit object ClashUtil { + private val sharedClient: OkHttpClient = OkHttpClient.Builder() + .connectTimeout(10, TimeUnit.SECONDS) // 连接超时 + .readTimeout(30, TimeUnit.SECONDS) // 读取超时 + .addInterceptor(HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.BODY + }) + .build() + fun startProxy(context: Context) { val intent = Intent("com.github.kr328.clash.intent.action.SESSION_CREATE") intent.putExtra("profile", "default") // 可选择您在 Clash 中配置的 Profile @@ -35,6 +46,66 @@ object ClashUtil { }.start() } + fun getProxyPort(): Int { + val scriptDir = File(Environment.getExternalStorageDirectory(), "script") + val portFile = File(scriptDir, "ip.port.json") + val text = StringBuilder() + try { + BufferedReader(FileReader(portFile)).use { br -> + var line: String? + while ((br.readLine().also { line = it }) != null) { + text.append(line).append('\n') + } + } + } catch (e: IOException) { + Log.e("TAG", "getProxyPort: ", e) + return -1 + } + var port = -1 + try { + Log.d("TAG", "getProxyPort: $text") + val config = JSONObject(text.toString()) + port = config.optInt("port", -1) + } catch (e: java.lang.Exception) { + Log.e("TAG", "getProxyPort: ", e) + } + return port + } + + fun switchProxyWithPort(country: String?) { + val port = getProxyPort() + // 安全构建 URL + val url = "http://39.103.73.250/tt/test/testProxy.jsp".toHttpUrl() + .newBuilder() + .addQueryParameter("port", port.toString() + "") + .addQueryParameter("country", country) + .build() + + val request = Request.Builder() + .url(url) + .build() + + try { + sharedClient.newCall(request).execute().use { response -> + // 读取并记录响应内容 + val responseBody = response.body?.string() ?: "Empty response body" + if (response.isSuccessful) { + Log.d( + "ClashUtil", + "switchProxyGroup: Success | Status: " + response.code + " | Response: " + responseBody + ) + } else { + Log.d( + "ClashUtil", + "switchProxyGroup: Failed | Status: " + response.code + " | Response: " + responseBody + ) + } + } + } catch (e: java.lang.Exception) { + Log.d("ClashUtil", "switchProxyGroup: Unexpected error", e) + } + } + var isRunning: Boolean = false val clashStatusReceiver: BroadcastReceiver = object : BroadcastReceiver() { @@ -90,26 +161,7 @@ object ClashUtil { } - fun switchProxyGroup(groupName: String?, proxyName: String?, controllerUrl: String) { - if (groupName == null || groupName.trim { it <= ' ' } - .isEmpty() || proxyName == null || proxyName.trim { it <= ' ' }.isEmpty()) { - LogUtils.log( - Log.ERROR, - "ClashUtil", - "switchProxyGroup: Invalid arguments", - null - ) - throw IllegalArgumentException("Group name and proxy name must not be empty") - } - if (!controllerUrl.matches("^https?://.*".toRegex())) { - LogUtils.log( - Log.ERROR, - "ClashUtil", - "switchProxyGroup: Invalid controller URL", - null - ) - throw IllegalArgumentException("Invalid controller URL") - } + fun switchProxyGroup(groupName: String?, proxyName: String?, controllerUrl: String): Boolean { val client = OkHttpClient() val json = JSONObject() @@ -133,39 +185,27 @@ object ClashUtil { .put(requestBody) .build() - client.newCall(request).enqueue(object : Callback { - override fun onFailure(call: Call, e: IOException) { - LogUtils.log( - Log.ERROR, - "ClashUtil", - "switchProxyGroup: Failed to switch proxy", - e - ) - println("Failed to switch proxy: " + e.message) - } - - @Throws(IOException::class) - override fun onResponse(call: Call, response: Response) { - try { - if (response.body != null) { - LogUtils.log( - Log.INFO, - "ClashUtil", - "switchProxyGroup: Switch proxy response", - null - ) - } else { - LogUtils.log( - Log.ERROR, - "ClashUtil", - "switchProxyGroup: Response body is null", - null - ) - } - } finally { - response.close() + try { + val result = sharedClient.newCall(request).execute().use { response -> + // 读取并记录响应内容 + val responseBody = if (response.body != null) response.body?.string() else "Empty response body" + val isSuccess = if (response.isSuccessful) { + LogUtils.d( + "ClashUtil", + "switchProxyGroup: Success | Status: " + response.code + " | Response: " + responseBody) + true + } else { + LogUtils.d( + "ClashUtil", + "switchProxyGroup: Failed | Status: " + response.code + " | Response: " + responseBody) + false } + return isSuccess } - }) + return result + } catch (e: Exception) { + LogUtils.d("ClashUtil", "switchProxyGroup: Unexpected error", e) + } + return false } } \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/util/CountryCode.kt b/app/src/main/java/com/android/grape/util/CountryCode.kt new file mode 100644 index 0000000..c3932a7 --- /dev/null +++ b/app/src/main/java/com/android/grape/util/CountryCode.kt @@ -0,0 +1,18 @@ +package com.android.grape.util + +import android.util.Log +import com.blankj.utilcode.util.LogUtils + +object CountryCode { + const val DEVICE_TYPE: Int = 2 + const val US: String = "US" + const val RU: String = "RU" + val DEFAULT: String = US + var currentCountry: String = DEFAULT + + fun switchCountry(): String { + currentCountry = if (currentCountry == US) RU else US + LogUtils.d("TAG", "Switched country to: $currentCountry") + return currentCountry + } +} \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/util/Util.kt b/app/src/main/java/com/android/grape/util/Util.kt index 9cab7fa..28285ba 100644 --- a/app/src/main/java/com/android/grape/util/Util.kt +++ b/app/src/main/java/com/android/grape/util/Util.kt @@ -946,8 +946,6 @@ object Util { videoProxy = extJo.getString("videoProxy") } - - if (extJo.has("proxy") && !extJo.isNull("proxy")) { val proxyJo = extJo.getJSONObject("proxy") proxyIp = proxyJo.getString("proxyIp")