This commit is contained in:
Administrator 2025-07-09 18:24:08 +08:00
parent ce0933ee94
commit 5772b91b9a
11 changed files with 225 additions and 154 deletions

View File

@ -1,21 +1,47 @@
# Add project specific ProGuard rules here. -keep class com.google.gson.** { *; }
# You can control the set of applied configuration files using the -keep class com.google.gson.stream.** { *; }
# 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 -keepattributes *Annotation*
# class: -keepattributes Signature
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for # 保留枚举类
# debugging stack traces. -keepclassmembers enum * {
#-keepattributes SourceFile,LineNumberTable 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. -keep class com.android.grape.pad.** { *; }
#-renamesourcefileattribute SourceFile -keep class com.android.grape.net.ApiResponse{ *; }
-keep class com.android.grape.net.ApiResponseList{ *; }
# 保留所有使用 @SerializedName 注解的字段
-keepclassmembers class * {
@com.google.gson.annotations.SerializedName <fields>;
}
# 保留所有模型类的无参构造函数
-keepclassmembers class com.android.grape.pad.** {
public <init>();
}
# 保留类型适配器
-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 { *; }

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -23,9 +23,8 @@ class DownloadAppJobService : JobIntentService() {
if (succ) { if (succ) {
errTime = 0L errTime = 0L
InstallService.onEvent(this) // InstallService.onEvent(this)
// StartVpnServerJobService.onEvent(this) StartVpnPortJobService.onEvent(this)
// StartVpnPortJobService.onEvent(this)
} else { } else {
Util.isClickRet = false Util.isClickRet = false
Util.setInstallRet(false) Util.setInstallRet(false)

View File

@ -6,13 +6,17 @@ import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Log import android.util.Log
import androidx.core.app.JobIntentService import androidx.core.app.JobIntentService
import com.android.grape.net.MyGet
import com.android.grape.util.ClashUtil import com.android.grape.util.ClashUtil
import com.android.grape.util.ClashUtil.getProxyPort
import com.android.grape.util.Util import com.android.grape.util.Util
import java.util.Locale
class StartVpnPortJobService : JobIntentService() { class StartVpnPortJobService : JobIntentService() {
override fun onHandleWork(intent: Intent) { override fun onHandleWork(intent: Intent) {
Log.i(TAG, "start to handle work") 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({ Handler(Looper.getMainLooper()).postDelayed({
StartVpnServerJobService.onEvent( StartVpnServerJobService.onEvent(
this@StartVpnPortJobService 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 { companion object {
private const val TAG = "IOSTQ:StartVpnPort" private const val TAG = "IOSTQ:StartVpnPort"

View File

@ -8,6 +8,7 @@ import android.util.Log
import androidx.core.app.JobIntentService import androidx.core.app.JobIntentService
import com.android.grape.MainApplication import com.android.grape.MainApplication
import com.android.grape.util.ClashUtil import com.android.grape.util.ClashUtil
import com.android.grape.util.CountryCode
import com.android.grape.util.Util import com.android.grape.util.Util
/** /**
@ -27,19 +28,12 @@ class StartVpnServerJobService : JobIntentService() {
onEvent( onEvent(
this@StartVpnServerJobService this@StartVpnServerJobService
) )
}, 1000L) }, 2000L)
} }
} }
private fun exec(): Boolean { private fun exec(): Boolean {
ClashUtil.startProxy(MainApplication.instance) return ClashUtil.switchProxyGroup("PROXY", "my-socks5-proxy", "http://127.0.0.1:6170")
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
} }
companion object { companion object {

View File

@ -2,16 +2,51 @@ package com.android.grape.net
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.URL import java.net.URL
import java.util.concurrent.TimeUnit
object MyGet { object MyGet {
private const val TAG = "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? { fun getData(url_: String, ua: String?): String? {
return getData(url_, ua, 0) return getData(url_, ua, 0)
@ -31,7 +66,7 @@ object MyGet {
httpUrlConnection = url.openConnection() as HttpURLConnection httpUrlConnection = url.openConnection() as HttpURLConnection
httpUrlConnection.allowUserInteraction = true httpUrlConnection.allowUserInteraction = true
httpUrlConnection!!.doOutput = false httpUrlConnection.doOutput = false
httpUrlConnection.doInput = true httpUrlConnection.doInput = true
httpUrlConnection.useCaches = false httpUrlConnection.useCaches = false
httpUrlConnection.setRequestProperty("Connection", "close") //add 20200428 httpUrlConnection.setRequestProperty("Connection", "close") //add 20200428

View File

@ -4,24 +4,35 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.os.Environment
import android.util.Log import android.util.Log
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.blankj.utilcode.util.LogUtils import com.blankj.utilcode.util.LogUtils
import okhttp3.Call
import okhttp3.Callback
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.MediaType import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response import okhttp3.logging.HttpLoggingInterceptor
import org.json.JSONException import org.json.JSONException
import org.json.JSONObject import org.json.JSONObject
import java.io.BufferedReader
import java.io.File
import java.io.FileReader
import java.io.IOException import java.io.IOException
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
object ClashUtil { 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) { fun startProxy(context: Context) {
val intent = Intent("com.github.kr328.clash.intent.action.SESSION_CREATE") val intent = Intent("com.github.kr328.clash.intent.action.SESSION_CREATE")
intent.putExtra("profile", "default") // 可选择您在 Clash 中配置的 Profile intent.putExtra("profile", "default") // 可选择您在 Clash 中配置的 Profile
@ -35,6 +46,66 @@ object ClashUtil {
}.start() }.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 var isRunning: Boolean = false
val clashStatusReceiver: BroadcastReceiver = object : BroadcastReceiver() { val clashStatusReceiver: BroadcastReceiver = object : BroadcastReceiver() {
@ -90,26 +161,7 @@ object ClashUtil {
} }
fun switchProxyGroup(groupName: String?, proxyName: String?, controllerUrl: String) { fun switchProxyGroup(groupName: String?, proxyName: String?, controllerUrl: String): Boolean {
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")
}
val client = OkHttpClient() val client = OkHttpClient()
val json = JSONObject() val json = JSONObject()
@ -133,39 +185,27 @@ object ClashUtil {
.put(requestBody) .put(requestBody)
.build() .build()
client.newCall(request).enqueue(object : Callback { try {
override fun onFailure(call: Call, e: IOException) { val result = sharedClient.newCall(request).execute().use { response ->
LogUtils.log( // 读取并记录响应内容
Log.ERROR, val responseBody = if (response.body != null) response.body?.string() else "Empty response body"
"ClashUtil", val isSuccess = if (response.isSuccessful) {
"switchProxyGroup: Failed to switch proxy", LogUtils.d(
e "ClashUtil",
) "switchProxyGroup: Success | Status: " + response.code + " | Response: " + responseBody)
println("Failed to switch proxy: " + e.message) true
} } else {
LogUtils.d(
@Throws(IOException::class) "ClashUtil",
override fun onResponse(call: Call, response: Response) { "switchProxyGroup: Failed | Status: " + response.code + " | Response: " + responseBody)
try { false
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()
} }
return isSuccess
} }
}) return result
} catch (e: Exception) {
LogUtils.d("ClashUtil", "switchProxyGroup: Unexpected error", e)
}
return false
} }
} }

View File

@ -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
}
}

View File

@ -946,8 +946,6 @@ object Util {
videoProxy = extJo.getString("videoProxy") videoProxy = extJo.getString("videoProxy")
} }
if (extJo.has("proxy") && !extJo.isNull("proxy")) { if (extJo.has("proxy") && !extJo.isNull("proxy")) {
val proxyJo = extJo.getJSONObject("proxy") val proxyJo = extJo.getJSONObject("proxy")
proxyIp = proxyJo.getString("proxyIp") proxyIp = proxyJo.getString("proxyIp")