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.
# 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
# 保留所有模型类(根据你的包结构调整)
-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 <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) {
errTime = 0L
InstallService.onEvent(this)
// StartVpnServerJobService.onEvent(this)
// StartVpnPortJobService.onEvent(this)
// InstallService.onEvent(this)
StartVpnPortJobService.onEvent(this)
} else {
Util.isClickRet = false
Util.setInstallRet(false)

View File

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

View File

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

View File

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

View File

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

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")
}
if (extJo.has("proxy") && !extJo.isNull("proxy")) {
val proxyJo = extJo.getJSONObject("proxy")
proxyIp = proxyJo.getString("proxyIp")