diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6481955..5699688 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -9,6 +9,8 @@ + @@ -41,6 +43,7 @@ android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" + android:networkSecurityConfig="@xml/network_security_config" android:requestLegacyExternalStorage="true" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" @@ -117,8 +120,6 @@ android:name="android.accessibilityservice" android:resource="@xml/accessibility_service_config" /> - - \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/MainActivity.kt b/app/src/main/java/com/android/grape/MainActivity.kt index ad4fc42..7d25c15 100644 --- a/app/src/main/java/com/android/grape/MainActivity.kt +++ b/app/src/main/java/com/android/grape/MainActivity.kt @@ -13,18 +13,24 @@ import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat +import androidx.lifecycle.lifecycleScope import com.android.grape.databinding.ActivityMainBinding import com.android.grape.job.MonitorService import com.android.grape.receiver.ScriptReceiver import com.android.grape.util.ClashUtil +import com.android.grape.util.HttpUtil +import com.android.grape.util.NotificationPermissionHandler import com.android.grape.util.StoragePermissionHelper import com.blankj.utilcode.util.LogUtils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch class MainActivity : AppCompatActivity() { private val viewModel by viewModels() private lateinit var viewBinding: ActivityMainBinding private var intentFilter: IntentFilter? = null private var scriptReceiver: ScriptReceiver? = null + private lateinit var permissionHandler: NotificationPermissionHandler override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() @@ -39,15 +45,14 @@ class MainActivity : AppCompatActivity() { registerReceiver() viewBinding.start.setOnClickListener { try { - ClashUtil.startProxy(this) // 在主线程中调用 - ClashUtil.switchProxyGroup("GLOBAL", "us", "http://127.0.0.1:6170") - } catch (e: Exception) { - LogUtils.log( - Log.ERROR, - "MainActivity", - "startProxyVpn: Failed to start VPN", - e + ClashUtil.startProxy(this) + ClashUtil.switchProxyGroup( + "GLOBAL", + ClashUtil.getRandomLocale(), + "http://127.0.0.1:6170" ) + } catch (e: Exception) { + LogUtils.e("startProxyVpn: Failed to start VPN", e) Toast.makeText( this, "Failed to start VPN: " + (if (e.message != null) e.message else "Unknown error"), @@ -77,7 +82,11 @@ class MainActivity : AppCompatActivity() { } private fun checkPermission() { - viewModel.checkAccessibilityService() + permissionHandler = NotificationPermissionHandler(this) { result -> + handlePermissionResult(result) + } + checkNotificationPermission() + StoragePermissionHelper.requestFullStoragePermission( activity = this, onGranted = { performFileOperation() }, @@ -85,10 +94,56 @@ class MainActivity : AppCompatActivity() { ) } + private fun checkNotificationPermission() { + when (val result = NotificationPermissionHandler.checkPermissionState(this)) { + NotificationPermissionHandler.PermissionResult.GRANTED, + NotificationPermissionHandler.PermissionResult.NOT_NEEDED -> { + showNotification() + } + + NotificationPermissionHandler.PermissionResult.DENIED, + NotificationPermissionHandler.PermissionResult.NEEDS_SETTINGS -> { + // 请求权限 + permissionHandler.requestNotificationPermission() + } + } + } + + /** + * 处理权限结果回调 + */ + private fun handlePermissionResult(result: NotificationPermissionHandler.PermissionResult) { + when (result) { + NotificationPermissionHandler.PermissionResult.GRANTED -> { + // 权限已授予,显示通知 + showNotification() + Toast.makeText(this, "通知权限已开启", Toast.LENGTH_SHORT).show() + } + + NotificationPermissionHandler.PermissionResult.DENIED -> { + // 权限被拒绝 + Toast.makeText(this, "通知权限被拒绝", Toast.LENGTH_SHORT).show() + } + + NotificationPermissionHandler.PermissionResult.NEEDS_SETTINGS -> { + Toast.makeText(this, "通知权限被拒绝", Toast.LENGTH_SHORT).show() + } + + NotificationPermissionHandler.PermissionResult.NOT_NEEDED -> { + // 不需要特殊权限(旧设备) + showNotification() + } + } + } + + private fun showNotification() { + viewModel.checkAccessibilityService() + } + private fun performFileOperation() { // 执行文件读写操作 // Toast.makeText(this, "文件访问权限已授予", Toast.LENGTH_SHORT).show() - MonitorService.onEvent(this) +// MonitorService.onEvent(this) } private fun showPermissionDeniedDialog() { diff --git a/app/src/main/java/com/android/grape/data/As.kt b/app/src/main/java/com/android/grape/data/As.kt new file mode 100644 index 0000000..2df87f4 --- /dev/null +++ b/app/src/main/java/com/android/grape/data/As.kt @@ -0,0 +1,8 @@ +package com.android.grape.data + + +import com.google.gson.annotations.SerializedName + +data class As( + var nameValuePairs: NameValuePairs = NameValuePairs() +) \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/data/DdlInfo.kt b/app/src/main/java/com/android/grape/data/DdlInfo.kt new file mode 100644 index 0000000..33bbb73 --- /dev/null +++ b/app/src/main/java/com/android/grape/data/DdlInfo.kt @@ -0,0 +1,11 @@ +package com.android.grape.data + + +import com.google.gson.annotations.SerializedName + +data class DdlInfo( + var fromfg: Int = 0, + var net0: Int = 0, + var rfrwait: Int = 0, + var status: String = "" +) \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/data/Device.kt b/app/src/main/java/com/android/grape/data/Device.kt new file mode 100644 index 0000000..0609d6a --- /dev/null +++ b/app/src/main/java/com/android/grape/data/Device.kt @@ -0,0 +1,113 @@ +package com.android.grape.data + + +import com.google.gson.annotations.SerializedName + +data class Device( + var advertiserId: String = "", + @SerializedName("af_preinstalled") + var afPreinstalled: Boolean = false, + var androidId: String = "", + var arch: String = "", + @SerializedName("as") + var asX: As = As(), + var battery: Int = 0, + var battery1: Int = 0, + var batteryLevel: Int = 0, + var batteryType: Int = 0, + var bn: String = "", + var brand: String = "", + var btch: String = "", + @SerializedName("build_display_id") + var buildDisplayId: String = "", + var carrier: String = "", + @SerializedName("click_ts") + var clickTs: Int = 0, + var clk: String = "", + var country: String = "", + @SerializedName("cpu_abi") + var cpuAbi: String = "", + @SerializedName("cpu_abi2") + var cpuAbi2: String = "", + var ddlInfo: DdlInfo = DdlInfo(), + var debug: Boolean = false, + var device: String = "", + var deviceCity: String = "", + var deviceType: String = "", + var dim: Dim = Dim(), + var dimId: Int = 0, + var dimReset: Int = 0, + var dimType: Int = 0, + var disk: String = "", + var diskRate: Double = 0.0, + var expand: Expand = Expand(), + var fetchAdIdLatency: Int = 0, + @SerializedName("from_fg") + var fromFg: Int = 0, + @SerializedName("google_custom") + var googleCustom: GoogleCustom = GoogleCustom(), + var hook: String = "", + @SerializedName("init_to_fg") + var initToFg: Int = 0, + var install: String = "", + @SerializedName("install_begin_ts") + var installBeginTs: Int = 0, + @SerializedName("installer_package") + var installerPackage: String = "", + var instant: Boolean = false, + var ip: String = "", + var ivc: Boolean = false, + var lang: String = "", + @SerializedName("lang_code") + var langCode: String = "", + @SerializedName("last_boot_time") + var lastBootTime: Long = 0, + var lastBootTimeOff: Int = 0, + var latency: Int = 0, + var locale: Locale = Locale(), + var manufactor: String = "", + var mcc: Int = 0, + var mnc: Int = 0, + var model: String = "", + @SerializedName("native_dir") + var nativeDir: Boolean = false, + var network: String = "", + var noRcLatency: Boolean = false, + @SerializedName("open_referrer") + var openReferrer: String = "", + @SerializedName("open_referrerReset") + var openReferrerReset: Int = 0, + var opener: String = "", + var `operator`: String = "", + var platformextension: String = "", + var pr: Pr = Pr(), + var product: String = "", + var rawDevice: String = "", + var rawProduct: String = "", + @SerializedName("rc.delay") + var rcDelay: Int = 0, + @SerializedName("rc.latency") + var rcLatency: Int = 0, + var referrer: String = "", + @SerializedName("sc_o") + var scO: String = "", + var sdk: String = "", + var sdkVer: String = "", + var sensor: List = listOf(), + var sensorReset: Int = 0, + @SerializedName("sensor_size") + var sensorSize: Int = 0, + @SerializedName("sig_n") + var sigN: String = "", + @SerializedName("sys_ua") + var sysUa: String = "", + var timepassedsincelastlaunch: Int = 0, + var tzDisplayName: String = "", + var tzOffTime: Int = 0, + @SerializedName("val") + var valX: String = "", + var vendingVersionCode: String = "", + var vendingVersionName: String = "", + @SerializedName("web_ua") + var webUa: String = "" +) \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/data/Dim.kt b/app/src/main/java/com/android/grape/data/Dim.kt new file mode 100644 index 0000000..a9ee4bc --- /dev/null +++ b/app/src/main/java/com/android/grape/data/Dim.kt @@ -0,0 +1,16 @@ +package com.android.grape.data + + +import com.google.gson.annotations.SerializedName + +data class Dim( + @SerializedName("d_dpi") + var dDpi: String = "", + var size: String = "", + @SerializedName("x_px") + var xPx: String = "", + var xdp: String = "", + @SerializedName("y_px") + var yPx: String = "", + var ydp: String = "" +) \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/data/Expand.kt b/app/src/main/java/com/android/grape/data/Expand.kt new file mode 100644 index 0000000..9514251 --- /dev/null +++ b/app/src/main/java/com/android/grape/data/Expand.kt @@ -0,0 +1,87 @@ +package com.android.grape.data + + +import com.google.gson.annotations.SerializedName + +data class Expand( + @SerializedName("ABI") + var aBI: String = "", + @SerializedName("API") + var aPI: String = "", + var amGetConfig: String = "", + @SerializedName("AndroidID") + var androidID: String = "", + @SerializedName("AndroidVer") + var androidVer: String = "", + @SerializedName("BSSID") + var bSSID: String = "", + var baseband: String = "", + var batteryLevel: Int = 0, + var board: String = "", + var bootLoader: String = "", + var brand: String = "", + var country: String = "", + @SerializedName("CountryCode") + var countryCode: String = "", + var cpuinfobuf: String = "", + var cpuinfostr: String = "", + @SerializedName("DPI") + var dPI: Int = 0, + var dataStatfs: Long = 0, + var device: String = "", + var display: String = "", + var displayLang: String = "", + var downloadCacheStatfs: Long = 0, + var fingerprint: String = "", + var firstInstallTime: Int = 0, + var gaid: String = "", + var glExtensions: String = "", + var glRenderer: String = "", + var glVendor: String = "", + var glVersion: String = "", + var height: Int = 0, + @SerializedName("ID") + var iD: String = "", + var incremental: String = "", + var lang: String = "", + var lastUpdateTime: Int = 0, + var latitude: Double = 0.0, + var localip: String = "", + var longtitude: Double = 0.0, + var lyMAC: String = "", + @SerializedName("Manufacture") + var manufacture: String = "", + var model: String = "", + var network: String = "", + var phoneBootTime: Int = 0, + var phoneUsedTime: Int = 0, + var pmfea: String = "", + var pmlib: String = "", + var procStat: String = "", + var procVersion: String = "", + var ratioVersion: String = "", + var referrerClickTime: Long = 0, + var referrerFromGP: String = "", + var regionid: String = "", + var roBoardPlatform: String = "", + var roBootimageBuildFingerprint: String = "", + var roBuildDateUtc: Int = 0, + var roBuildDescription: String = "", + var roHardware: String = "", + var roProductCpuAbilist: String = "", + var roProductName: String = "", + var rootStatfs: Int = 0, + var screenBrightness: Int = 0, + var sdStatfs: Long = 0, + var sdcardCreateTime: Int = 0, + var soRomRam: String = "", + var timerawoff: Int = 0, + var tmdisplayname: String = "", + var uname: String = "", + var width: Int = 0, + @SerializedName("WifiMAC") + var wifiMAC: String = "", + @SerializedName("WifiName") + var wifiName: String = "", + var wvua: String = "" +) \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/data/GoogleCustom.kt b/app/src/main/java/com/android/grape/data/GoogleCustom.kt new file mode 100644 index 0000000..52b0eb6 --- /dev/null +++ b/app/src/main/java/com/android/grape/data/GoogleCustom.kt @@ -0,0 +1,14 @@ +package com.android.grape.data + + +import com.google.gson.annotations.SerializedName + +data class GoogleCustom( + @SerializedName("click_server_ts") + var clickServerTs: Int = 0, + @SerializedName("install_begin_server_ts") + var installBeginServerTs: Int = 0, + @SerializedName("install_version") + var installVersion: String = "", + var instant: Boolean = false +) \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/data/Locale.kt b/app/src/main/java/com/android/grape/data/Locale.kt new file mode 100644 index 0000000..cd8a682 --- /dev/null +++ b/app/src/main/java/com/android/grape/data/Locale.kt @@ -0,0 +1,10 @@ +package com.android.grape.data + + +import com.google.gson.annotations.SerializedName + +data class Locale( + var country: String = "", + var displayLang: String = "", + var lang: String = "" +) \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/data/NameValuePairs.kt b/app/src/main/java/com/android/grape/data/NameValuePairs.kt new file mode 100644 index 0000000..4325e89 --- /dev/null +++ b/app/src/main/java/com/android/grape/data/NameValuePairs.kt @@ -0,0 +1,11 @@ +package com.android.grape.data + + +import com.google.gson.annotations.SerializedName + +data class NameValuePairs( + var cav: Int = 0, + @SerializedName("null") + var nullX: Int = 0, + var other: Int = 0 +) \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/data/NameValuePairsX.kt b/app/src/main/java/com/android/grape/data/NameValuePairsX.kt new file mode 100644 index 0000000..3b52f3b --- /dev/null +++ b/app/src/main/java/com/android/grape/data/NameValuePairsX.kt @@ -0,0 +1,22 @@ +package com.android.grape.data + + +import com.google.gson.annotations.SerializedName + +data class NameValuePairsX( + var ac: String = "", + var ah: String = "", + var ai: String = "", + var ak: String = "", + var al: String = "", + var am: String = "", + var an: String = "", + var ap: String = "", + var aq: String = "", + var ar: String = "", + @SerializedName("as") + var asX: String = "", + var at: String = "", + var au: String = "", + var av: String = "" +) \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/data/Pr.kt b/app/src/main/java/com/android/grape/data/Pr.kt new file mode 100644 index 0000000..cc45e0f --- /dev/null +++ b/app/src/main/java/com/android/grape/data/Pr.kt @@ -0,0 +1,8 @@ +package com.android.grape.data + + +import com.google.gson.annotations.SerializedName + +data class Pr( + var nameValuePairs: NameValuePairsX = NameValuePairsX() +) \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/data/Sensor.kt b/app/src/main/java/com/android/grape/data/Sensor.kt new file mode 100644 index 0000000..ae1b569 --- /dev/null +++ b/app/src/main/java/com/android/grape/data/Sensor.kt @@ -0,0 +1,12 @@ +package com.android.grape.data + + +import com.google.gson.annotations.SerializedName + +data class Sensor( + var sN: String = "", + var sT: Int = 0, + var sV: String = "", + var sVE: List = listOf(), + var sVS: List = listOf() +) \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/job/OpenAppService.kt b/app/src/main/java/com/android/grape/job/OpenAppService.kt index 04ddaa3..8e7e6aa 100644 --- a/app/src/main/java/com/android/grape/job/OpenAppService.kt +++ b/app/src/main/java/com/android/grape/job/OpenAppService.kt @@ -4,6 +4,7 @@ import android.content.Context import android.content.Intent import android.util.Log import androidx.core.app.JobIntentService +import com.android.grape.util.ChangeDeviceInfoUtil import com.android.grape.util.MockTools import com.android.grape.util.ServiceUtils import com.android.grape.util.Util @@ -28,13 +29,15 @@ class OpenAppService : JobIntentService() { Util.doScript(this, Util.AUTO_JSPACKAGENAME) //autojs } try { - if (Util.isNeedRestored) { - Log.d("IOSTQ:", "执行留存任务") - Util.setRrInfo(this) - } else { - Log.d("IOSTQ:", "执行新装任务") - Util.setInfo(this) - } + //todo fix 改机流程 +// if (Util.isNeedRestored) { +// Log.d("IOSTQ:", "执行留存任务") +// Util.setRrInfo(this) +// } else { +// Log.d("IOSTQ:", "执行新装任务") +// Util.setInfo(this) +// } + ChangeDeviceInfoUtil.changeDeviceInfo() } catch (e: IOException) { e.printStackTrace() } diff --git a/app/src/main/java/com/android/grape/util/ChangeDeviceInfoUtil.kt b/app/src/main/java/com/android/grape/util/ChangeDeviceInfoUtil.kt index 7df6551..1e82f97 100644 --- a/app/src/main/java/com/android/grape/util/ChangeDeviceInfoUtil.kt +++ b/app/src/main/java/com/android/grape/util/ChangeDeviceInfoUtil.kt @@ -3,329 +3,150 @@ package com.android.grape.util import android.content.ContentResolver import android.content.Context import android.util.Log -import com.android.grape.data.AfInfo -import com.android.grape.data.BigoInfo -import com.android.grape.data.DeviceInfo +import com.android.grape.MainApplication +import com.android.grape.data.Device +import com.android.grape.util.Util.paramsJson import com.blankj.utilcode.util.LogUtils import org.json.JSONObject import java.lang.reflect.InvocationTargetException object ChangeDeviceInfoUtil { fun changeDeviceInfo( - current_pkg_name: String, - context: Context?, - bigoDeviceObject: JSONObject?, - afDeviceObject: JSONObject? ) { - var bigoDeviceObject = bigoDeviceObject - var afDeviceObject = afDeviceObject try { - val bigoDevice: BigoInfo - if (bigoDeviceObject != null) { - // BIGO - val cpuClockSpeed = bigoDeviceObject.optString("cpu_clock_speed") - val gaid = bigoDeviceObject.optString("gaid") - val userAgent = bigoDeviceObject.optString("User-Agent") - val osLang = bigoDeviceObject.optString("os_lang") - val osVer = bigoDeviceObject.optString("os_ver") - val tz = bigoDeviceObject.optString("tz") - val systemCountry = bigoDeviceObject.optString("system_country") - val simCountry = bigoDeviceObject.optString("sim_country") - val romFreeIn = bigoDeviceObject.optLong("rom_free_in") - val resolution = bigoDeviceObject.optString("resolution") - val vendor = bigoDeviceObject.optString("vendor") - val batteryScale = bigoDeviceObject.optInt("bat_scale") - // String model = deviceObject.optString("model"); - val net = bigoDeviceObject.optString("net") - val dpi = bigoDeviceObject.optLong("dpi") - val romFreeExt = bigoDeviceObject.optLong("rom_free_ext") - val dpiF = bigoDeviceObject.optString("dpi_f") - val cpuCoreNum = bigoDeviceObject.optLong("cpu_core_num") - - bigoDevice = BigoInfo() - bigoDevice.cpuClockSpeed = cpuClockSpeed - bigoDevice.gaid = gaid - bigoDevice.userAgent = userAgent - bigoDevice.osLang = osLang - bigoDevice.osVer = osVer - bigoDevice.tz = tz - bigoDevice.systemCountry = systemCountry - bigoDevice.simCountry = simCountry - bigoDevice.romFreeIn = romFreeIn - bigoDevice.resolution = resolution - bigoDevice.vendor = vendor - bigoDevice.batteryScale = batteryScale - bigoDevice.net = net - bigoDevice.dpi = dpi - bigoDevice.romFreeExt = romFreeExt - bigoDevice.dpiF = dpiF - bigoDevice.cpuCoreNum = cpuCoreNum -// TaskUtil.setBigoDevice(bigoDevice) - try { - callVCloudSettings_put( - "$current_pkg_name.system_country", - systemCountry, - context - ) - callVCloudSettings_put("$current_pkg_name.sim_country", simCountry, context) - callVCloudSettings_put( - "$current_pkg_name.rom_free_in", - romFreeIn.toString(), - context - ) - callVCloudSettings_put("$current_pkg_name.resolution", resolution, context) - callVCloudSettings_put("$current_pkg_name.vendor", vendor, context) - callVCloudSettings_put( - "$current_pkg_name.battery_scale", - batteryScale.toString(), - context - ) - callVCloudSettings_put("$current_pkg_name.os_lang", osLang, context) - // callVCloudSettings_put(current_pkg_name + ".model", model, context); - callVCloudSettings_put("$current_pkg_name.net", net, context) - callVCloudSettings_put("$current_pkg_name.dpi", dpi.toString(), context) - callVCloudSettings_put( - "$current_pkg_name.rom_free_ext", - romFreeExt.toString(), - context - ) - callVCloudSettings_put("$current_pkg_name.dpi_f", dpiF, context) - callVCloudSettings_put( - "$current_pkg_name.cpu_core_num", - cpuCoreNum.toString(), - context - ) - callVCloudSettings_put( - "$current_pkg_name.cpu_clock_speed", - cpuClockSpeed, - context - ) - callVCloudSettings_put(current_pkg_name + "_gaid", gaid, context) - // **User-Agent** - callVCloudSettings_put(current_pkg_name + "_user_agent", userAgent, context) - // **os_lang**系统语言 - callVCloudSettings_put(current_pkg_name + "_os_lang", osLang, context) - // **os_ver** - callVCloudSettings_put(current_pkg_name + "_os_ver", osVer, context) - // **tz** (时区) - callVCloudSettings_put(current_pkg_name + "_tz", tz, context) - } catch (e: Throwable) { - LogUtils.e( - Log.ERROR, - "ChangeDeviceInfoUtil", - "Error occurred while changing device info", - e - ) - throw RuntimeException("Error occurred in changeDeviceInfo", e) - } - bigoDeviceObject = null + val deviceObject = paramsJson?.getJSONObject("device") + if (deviceObject == null) { + LogUtils.d("ERROR", "ChangeDeviceInfoUtil", "device is null") + return } + val context = MainApplication.instance + val device = GsonUtils.fromJsonObject(deviceObject.toString(), Device::class.java) + val currentPkgName = MainApplication.instance.packageName + vcloudsettingsPut( + "$currentPkgName.system_country", + device.country, + context + ) + vcloudsettingsPut("$currentPkgName.sim_country", device.country, context) +// callVCloudSettings_put( +// "$currentPkgName.rom_free_in", +// romFreeIn.toString(), +// context +// ) + vcloudsettingsPut( + "$currentPkgName.resolution", + "${device.expand.width}x${device.expand.height}}", + context + ) + vcloudsettingsPut("$currentPkgName.vendor", device.expand.glVendor, context) +// callVCloudSettings_put("$currentPkgName.battery_scale", batteryScale.toString(), context) + vcloudsettingsPut("$currentPkgName.os_lang", device.lang, context) + vcloudsettingsPut("$currentPkgName.model", device.model, context); + vcloudsettingsPut("$currentPkgName.net", device.network, context) + vcloudsettingsPut("$currentPkgName.dpi", device.expand.dPI.toString(), context) +// callVCloudSettings_put( +// "$currentPkgName.rom_free_ext", +// romFreeExt.toString(), +// context +// ) +// callVCloudSettings_put("$currentPkgName.dpi_f", dpiF, context) +// callVCloudSettings_put("$currentPkgName.cpu_core_num", cpuCoreNum.toString(), context) +// callVCloudSettings_put("$currentPkgName.cpu_clock_speed", cpuClockSpeed, context) + vcloudsettingsPut(currentPkgName + "_gaid", device.expand.gaid, context) + // **User-Agent** +// callVCloudSettings_put(currentPkgName + "_user_agent", userAgent, context) + // **os_lang**系统语言 + vcloudsettingsPut(currentPkgName + "_os_lang", device.lang, context) + // **os_ver** + vcloudsettingsPut(currentPkgName + "_os_ver", device.sdkVer, context) + // **tz** (时区) + vcloudsettingsPut(currentPkgName + "_tz", device.tzOffTime.toString(), context) - val deviceInfo: DeviceInfo - val afDevice: AfInfo - if (afDeviceObject != null) { - val advertiserId = afDeviceObject.optString(".advertiserId") - val model = afDeviceObject.optString(".model") - val brand = afDeviceObject.optString(".brand") - val androidId = afDeviceObject.optString(".android_id") - val xPixels = afDeviceObject.optInt(".deviceData.dim.x_px") - val yPixels = afDeviceObject.optInt(".deviceData.dim.y_px") - val densityDpi = afDeviceObject.optInt(".deviceData.dim.d_dpi") - val country = afDeviceObject.optString(".country") - val batteryLevel = afDeviceObject.optString(".batteryLevel") - val stackInfo = Thread.currentThread().stackTrace[2].toString() - val product = afDeviceObject.optString(".product") - val network = afDeviceObject.optString(".network") - val langCode = afDeviceObject.optString(".lang_code") - val cpuAbi = afDeviceObject.optString(".deviceData.cpu_abi") - val yDp = afDeviceObject.optLong(".deviceData.dim.ydp") + vcloudsettingsPut("$currentPkgName.advertiserId", device.advertiserId, context) + vcloudsettingsPut("$currentPkgName.brand", device.brand, context) + vcloudsettingsPut("$currentPkgName.android_id", device.androidId, context) + vcloudsettingsPut("$currentPkgName.lang", device.lang, context) + vcloudsettingsPut("$currentPkgName.country", device.country, context) + vcloudsettingsPut( + "$currentPkgName.batteryLevel", + device.batteryLevel.toString(), context + ) +// callVCloudSettings_put( +// currentPkgName + "_screen.optMetrics.stack", +// stackInfo, +// context +// ) + vcloudsettingsPut("$currentPkgName.product", device.product, context) + vcloudsettingsPut("$currentPkgName.network", device.network, context) + vcloudsettingsPut("$currentPkgName.cpu_abi", device.cpuAbi, context) + vcloudsettingsPut("$currentPkgName.lang_code", device.langCode, context) + // **广告标识符 (advertiserId)** 及 **启用状态** + val isAdIdEnabled = true // 默认启用广告 ID + vcloudsettingsPut( + "$currentPkgName.advertiserIdEnabled", + isAdIdEnabled.toString(), + context + ) - afDevice = AfInfo() - afDevice.advertiserId = advertiserId - afDevice.model = model - afDevice.brand = brand - afDevice.androidId = androidId - afDevice.xPixels = xPixels - afDevice.yPixels = yPixels - afDevice.densityDpi = densityDpi - afDevice.country = country - afDevice.batteryLevel = batteryLevel - afDevice.stackInfo = stackInfo - afDevice.product = product - afDevice.network = network - afDevice.langCode = langCode - afDevice.cpuAbi = cpuAbi - afDevice.yDp = yDp -// TaskUtil.setAfDevice(afDevice) + val displayMetrics = JSONObject() - val lang = afDeviceObject.optString(".lang") - val ro_product_brand = afDeviceObject.optString("ro.product.brand", "") - val ro_product_model = afDeviceObject.optString("ro.product.model", "") - val ro_product_manufacturer = - afDeviceObject.optString("ro.product.manufacturer", "") - val ro_product_device = afDeviceObject.optString("ro.product.device", "") - val ro_product_name = afDeviceObject.optString("ro.product.name", "") - val ro_build_version_incremental = - afDeviceObject.optString("ro.build.version.incremental", "") - val ro_build_fingerprint = afDeviceObject.optString("ro.build.fingerprint", "") - val ro_odm_build_fingerprint = - afDeviceObject.optString("ro.odm.build.fingerprint", "") - val ro_product_build_fingerprint = - afDeviceObject.optString("ro.product.build.fingerprint", "") - val ro_system_build_fingerprint = - afDeviceObject.optString("ro.system.build.fingerprint", "") - val ro_system_ext_build_fingerprint = - afDeviceObject.optString("ro.system_ext.build.fingerprint", "") - val ro_vendor_build_fingerprint = - afDeviceObject.optString("ro.vendor.build.fingerprint", "") - val ro_build_platform = afDeviceObject.optString("ro.board.platform", "") - val persist_sys_cloud_drm_id = - afDeviceObject.optString("persist.sys.cloud.drm.id", "") - val persist_sys_cloud_battery_capacity = - afDeviceObject.optInt("persist.sys.cloud.battery.capacity", -1) - val persist_sys_cloud_gpu_gl_vendor = - afDeviceObject.optString("persist.sys.cloud.gpu.gl_vendor", "") - val persist_sys_cloud_gpu_gl_renderer = - afDeviceObject.optString("persist.sys.cloud.gpu.gl_renderer", "") - val persist_sys_cloud_gpu_gl_version = - afDeviceObject.optString("persist.sys.cloud.gpu.gl_version", "") - val persist_sys_cloud_gpu_egl_vendor = - afDeviceObject.optString("persist.sys.cloud.gpu.egl_vendor", "") - val persist_sys_cloud_gpu_egl_version = - afDeviceObject.optString("persist.sys.cloud.gpu.egl_version", "") - val global_android_id = afDeviceObject.optString(".android_id", "") - val anticheck_pkgs = afDeviceObject.optString(".anticheck_pkgs", "") - val pm_list_features = afDeviceObject.optString(".pm_list_features", "") - val pm_list_libraries = afDeviceObject.optString(".pm_list_libraries", "") - val system_http_agent = afDeviceObject.optString("system.http.agent", "") - val webkit_http_agent = afDeviceObject.optString("webkit.http.agent", "") - val com_fk_tools_pkgInfo = afDeviceObject.optString(".pkg_info", "") - val appsflyerKey = afDeviceObject.optString(".appsflyerKey", "") - val appUserId = afDeviceObject.optString(".appUserId", "") - val disk = afDeviceObject.optString(".disk", "") - val operator = afDeviceObject.optString(".operator", "") - val cell_mcc = afDeviceObject.optString(".cell.mcc", "") - val cell_mnc = afDeviceObject.optString(".cell.mnc", "") - val date1 = afDeviceObject.optString(".date1", "") - val date2 = afDeviceObject.optString(".date2", "") - val bootId = afDeviceObject.optString("BootId", "") + displayMetrics.put("widthPixels", device.expand.width) - deviceInfo = DeviceInfo() - deviceInfo.lang = lang - deviceInfo.roProductBrand = ro_product_brand - deviceInfo.roProductModel = ro_product_model - deviceInfo.roProductManufacturer = ro_product_manufacturer - deviceInfo.roProductDevice = ro_product_device - deviceInfo.roProductName = ro_product_name - deviceInfo.roBuildVersionIncremental = ro_build_version_incremental - deviceInfo.roBuildFingerprint = ro_build_fingerprint - deviceInfo.roOdmBuildFingerprint = ro_odm_build_fingerprint - deviceInfo.roProductBuildFingerprint = ro_product_build_fingerprint - deviceInfo.roSystemBuildFingerprint = ro_system_build_fingerprint - deviceInfo.roSystemExtBuildFingerprint = ro_system_ext_build_fingerprint - deviceInfo.roVendorBuildFingerprint = ro_vendor_build_fingerprint - deviceInfo.roBuildPlatform = ro_build_platform - deviceInfo.persistSysCloudDrmId = persist_sys_cloud_drm_id - deviceInfo.persistSysCloudBatteryCapacity = persist_sys_cloud_battery_capacity - deviceInfo.persistSysCloudGpuGlVendor = persist_sys_cloud_gpu_gl_vendor - deviceInfo.persistSysCloudGpuGlRenderer = persist_sys_cloud_gpu_gl_renderer - deviceInfo.persistSysCloudGpuGlVersion = persist_sys_cloud_gpu_gl_version - deviceInfo.persistSysCloudGpuEglVendor = persist_sys_cloud_gpu_egl_vendor - deviceInfo.persistSysCloudGpuEglVersion = persist_sys_cloud_gpu_egl_version -// TaskUtil.setDeviceInfo(deviceInfo) - try { - callVCloudSettings_put("$current_pkg_name.advertiserId", advertiserId, context) - callVCloudSettings_put("$current_pkg_name.model", model, context) - callVCloudSettings_put("$current_pkg_name.brand", brand, context) - callVCloudSettings_put("$current_pkg_name.android_id", androidId, context) - callVCloudSettings_put("$current_pkg_name.lang", lang, context) - callVCloudSettings_put("$current_pkg_name.country", country, context) - callVCloudSettings_put("$current_pkg_name.batteryLevel", batteryLevel, context) - callVCloudSettings_put( - current_pkg_name + "_screen.optMetrics.stack", - stackInfo, - context - ) - callVCloudSettings_put("$current_pkg_name.product", product, context) - callVCloudSettings_put("$current_pkg_name.network", network, context) - callVCloudSettings_put("$current_pkg_name.cpu_abi", cpuAbi, context) - callVCloudSettings_put("$current_pkg_name.lang_code", langCode, context) - // **广告标识符 (advertiserId)** 及 **启用状态** - val isAdIdEnabled = true // 默认启用广告 ID - callVCloudSettings_put( - "$current_pkg_name.advertiserIdEnabled", - isAdIdEnabled.toString(), - context - ) + displayMetrics.put("heightPixels", device.expand.height) + displayMetrics.put("densityDpi", device.expand.dPI) + displayMetrics.put("yDp", device.dim.ydp) + vcloudsettingsPut( + "screen.device.displayMetrics", + displayMetrics.toString(), + context + ) - val displayMetrics = JSONObject() - - displayMetrics.put("widthPixels", xPixels) - - displayMetrics.put("heightPixels", yPixels) - displayMetrics.put("densityDpi", densityDpi) - displayMetrics.put("yDp", yDp) - callVCloudSettings_put( - "screen.device.displayMetrics", - displayMetrics.toString(), - context - ) - - if (!ShellUtils.hasRootAccess()) { - LogUtils.d( - "ERROR", - "ChangeDeviceInfoUtil", - "Root access is required to execute system property changes" - ) - } - // 设置机型, 直接设置属性 - ShellUtils.execRootCmd("setprop ro.product.brand $ro_product_brand") - ShellUtils.execRootCmd("setprop ro.product.model $ro_product_model") - ShellUtils.execRootCmd("setprop ro.product.manufacturer $ro_product_manufacturer") - ShellUtils.execRootCmd("setprop ro.product.device $ro_product_device") - ShellUtils.execRootCmd("setprop ro.product.name $ro_product_name") - ShellUtils.execRootCmd("setprop ro.build.version.incremental $ro_build_version_incremental") - ShellUtils.execRootCmd("setprop ro.build.fingerprint $ro_build_fingerprint") - ShellUtils.execRootCmd("setprop ro.odm.build.fingerprint $ro_odm_build_fingerprint") - ShellUtils.execRootCmd("setprop ro.product.build.fingerprint $ro_product_build_fingerprint") - ShellUtils.execRootCmd("setprop ro.system.build.fingerprint $ro_system_build_fingerprint") - ShellUtils.execRootCmd("setprop ro.system_ext.build.fingerprint $ro_system_ext_build_fingerprint") - ShellUtils.execRootCmd("setprop ro.vendor.build.fingerprint $ro_vendor_build_fingerprint") - ShellUtils.execRootCmd("setprop ro.board.platform $ro_build_platform") - - // Native.setBootId(bootId); - // 修改drm id - ShellUtils.execRootCmd("setprop persist.sys.cloud.drm.id $persist_sys_cloud_drm_id") - // 电量模拟需要大于1000 - ShellUtils.execRootCmd("setprop persist.sys.cloud.battery.capacity $persist_sys_cloud_battery_capacity") - ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.gl_vendor $persist_sys_cloud_gpu_gl_vendor") - ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.gl_renderer $persist_sys_cloud_gpu_gl_renderer") - // 这个值不能随便改 必须是 OpenGL ES %d.%d 这个格式 - ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.gl_version $persist_sys_cloud_gpu_gl_version") - ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.egl_vendor $persist_sys_cloud_gpu_egl_vendor") - ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.egl_version $persist_sys_cloud_gpu_egl_version") - } catch (e: Throwable) { - LogUtils.e( - Log.ERROR, - "ChangeDeviceInfoUtil", - "Error occurred in changeDeviceInfo", - e - ) - throw RuntimeException("Error occurred in changeDeviceInfo", e) - } - afDeviceObject = null + if (!ShellUtils.hasRootAccess()) { + LogUtils.d( + "ERROR", + "ChangeDeviceInfoUtil", + "Root access is required to execute system property changes" + ) } - } catch (e: Exception) { - e.printStackTrace() + // 设置机型, 直接设置属性 + ShellUtils.execRootCmd("setprop ro.product.brand ${device.brand}") + ShellUtils.execRootCmd("setprop ro.product.model ${device.model}") + ShellUtils.execRootCmd("setprop ro.product.manufacturer ${device.manufactor}") + ShellUtils.execRootCmd("setprop ro.product.device ${device.device}") + ShellUtils.execRootCmd("setprop ro.product.name ${device.expand.roProductName}") + ShellUtils.execRootCmd("setprop ro.build.version.incremental ${device.expand.incremental}") + ShellUtils.execRootCmd("setprop ro.build.fingerprint ${device.expand.roBootimageBuildFingerprint}") + ShellUtils.execRootCmd("setprop ro.odm.build.fingerprint ${device.expand.roBootimageBuildFingerprint}") + ShellUtils.execRootCmd("setprop ro.product.build.fingerprint ${device.expand.roBootimageBuildFingerprint}") + ShellUtils.execRootCmd("setprop ro.system.build.fingerprint ${device.expand.roBootimageBuildFingerprint}") + ShellUtils.execRootCmd("setprop ro.system_ext.build.fingerprint ${device.expand.roBootimageBuildFingerprint}") + ShellUtils.execRootCmd("setprop ro.vendor.build.fingerprint ${device.expand.roBootimageBuildFingerprint}") + ShellUtils.execRootCmd("setprop ro.board.platform ${device.expand.roBoardPlatform}") + + // 修改drm id +// ShellUtils.execRootCmd("setprop persist.sys.cloud.drm.id $persist_sys_cloud_drm_id") + // 电量模拟需要大于1000 + ShellUtils.execRootCmd("setprop persist.sys.cloud.battery.capacity ${device.battery}") + ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.gl_vendor ${device.expand.glVendor}") + ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.gl_renderer ${device.expand.glRenderer}") + // 这个值不能随便改 必须是 OpenGL ES %d.%d 这个格式 + ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.gl_version ${device.expand.glVersion}") +// ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.egl_vendor $persist_sys_cloud_gpu_egl_vendor") +// ShellUtils.execRootCmd("setprop persist.sys.cloud.gpu.egl_version $persist_sys_cloud_gpu_egl_version") + } catch (e: Throwable) { + LogUtils.e( + Log.ERROR, + "ChangeDeviceInfoUtil", + "Error occurred while changing device info", + e + ) + throw RuntimeException("Error occurred in changeDeviceInfo", e) } + } - private fun callVCloudSettings_put(key: String?, value: String?, context: Context?) { - if (context == null) { - LogUtils.e(Log.ERROR, "ChangeDeviceInfoUtil", "Context cannot be null", null) - throw IllegalArgumentException("Context cannot be null") - } - if (key == null || key.isEmpty()) { + private fun vcloudsettingsPut(key: String?, value: String?, context: Context) { + if (key.isNullOrEmpty()) { LogUtils.e(Log.ERROR, "ChangeDeviceInfoUtil", "Key cannot be null or empty", null) throw IllegalArgumentException("Key cannot be null or empty") } @@ -350,15 +171,11 @@ object ChangeDeviceInfoUtil { Log.d("Debug", "putString executed successfully.") } catch (e: ClassNotFoundException) { LogUtils.e( - Log.WARN, - "ChangeDeviceInfoUtil", "Class not found: android.provider.VCloudSettings\$Global. This may not be supported on this device.", e ) } catch (e: NoSuchMethodException) { LogUtils.e( - Log.WARN, - "ChangeDeviceInfoUtil", "Method not found: android.provider.VCloudSettings\$Global.putString. This may not be supported on this", e ) @@ -366,23 +183,17 @@ object ChangeDeviceInfoUtil { val cause = e.targetException if (cause is SecurityException) { LogUtils.e( - Log.ERROR, - "ChangeDeviceInfoUtil", "Error occurred in changeDeviceInfo", cause ) } else { LogUtils.e( - Log.ERROR, - "ChangeDeviceInfoUtil", "Error occurred in changeDeviceInfo", cause ) } } catch (e: Exception) { LogUtils.e( - Log.ERROR, - "ChangeDeviceInfoUtil", "Unexpected error during putString invocation", e ) 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 c0bc5f1..72c3bfd 100644 --- a/app/src/main/java/com/android/grape/util/ClashUtil.kt +++ b/app/src/main/java/com/android/grape/util/ClashUtil.kt @@ -77,9 +77,19 @@ object ClashUtil { } fun unregisterReceiver(context: Context) { - context.unregisterReceiver(clashStatusReceiver) + try { + context.unregisterReceiver(clashStatusReceiver) + } catch (e: Exception) { + e.printStackTrace() + } } + fun getRandomLocale(): String { + val locales = listOf("ru", "us") + return locales.random() + } + + fun switchProxyGroup(groupName: String?, proxyName: String?, controllerUrl: String) { if (groupName == null || groupName.trim { it <= ' ' } .isEmpty() || proxyName == null || proxyName.trim { it <= ' ' }.isEmpty()) { diff --git a/app/src/main/java/com/android/grape/util/GsonUtils.kt b/app/src/main/java/com/android/grape/util/GsonUtils.kt new file mode 100644 index 0000000..e094dee --- /dev/null +++ b/app/src/main/java/com/android/grape/util/GsonUtils.kt @@ -0,0 +1,72 @@ +package com.android.grape.util + +import android.os.Build +import androidx.annotation.RequiresApi +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import com.google.gson.JsonDeserializer +import com.google.gson.JsonObject +import com.google.gson.reflect.TypeToken +import java.lang.reflect.Type +import java.util.* + +object GsonUtils { + // 可配置的Gson实例 + private val gson: Gson by lazy { + GsonBuilder() +// .setDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'") + .create() + } + + // 自定义Gson实例 + private val customGson = mutableMapOf() + + /** + * 通用转换函数 + * @param json JsonObject + * @param clazz 目标类型 + */ + fun fromJsonObject(json: String, clazz: Class): T { + return gson.fromJson(json, clazz) + } + + /** + * 通用转换函数(带泛型) + * @param json JsonObject + * @param typeToken 类型Token + */ + fun fromJsonObject(json: JsonObject, typeToken: TypeToken): T { + return gson.fromJson(json, typeToken.type) + } + + /** + * 使用自定义适配器转换 + * @param json JsonObject + * @param clazz 目标类型 + * @param adapters 适配器列表 + */ + @RequiresApi(Build.VERSION_CODES.P) + fun fromJsonObject( + json: JsonObject, + clazz: Class, + vararg adapters: Pair + ): T { + val gsonKey = clazz.name + adapters.joinToString { it.first.typeName } + val customGsonInstance = customGson[gsonKey] ?: buildCustomGson(adapters).also { + customGson[gsonKey] = it + } + + return customGsonInstance.fromJson(json, clazz) + } + + private fun buildCustomGson(adapters: Array>): Gson { + val builder = GsonBuilder() + adapters.forEach { (type, adapter) -> + when (adapter) { + is JsonDeserializer<*> -> builder.registerTypeAdapter(type, adapter) + else -> throw IllegalArgumentException("Unsupported adapter type") + } + } + return builder.create() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/android/grape/util/HttpUtil.java b/app/src/main/java/com/android/grape/util/HttpUtil.java new file mode 100644 index 0000000..97a420a --- /dev/null +++ b/app/src/main/java/com/android/grape/util/HttpUtil.java @@ -0,0 +1,49 @@ +package com.android.grape.util; + +import android.content.Context; +import android.provider.Settings; +import android.util.Log; + +import com.blankj.utilcode.util.LogUtils; + +import org.json.JSONObject; + +import java.nio.charset.StandardCharsets; + +public class HttpUtil { + + public static void execReloginTask(Context context) { + String url = "http://39.103.73.250/tt/ddj/preRequest!requestRelogin.do"; + String ANDROID_ID = Settings.System.getString(context.getContentResolver(), Settings.System.ANDROID_ID); + String params = "platform=Android&tag=" + "119" + "&uuid=" + ANDROID_ID; + System.out.println("IOSTQ:execReloginTask->url:" + url + "?" + params); + try { + + String result = new MyPost().PostData(context, " ".getBytes(StandardCharsets.UTF_8), url + "?" + params); + + LogUtils.e("request result : " + result); + + } catch (Exception e) { + Log.i("TAG", "execTask error:" + e.getMessage()); + } + } + + public static void execInstallTask(Context context) { + String url = "http://39.103.73.250/tt/ddj/preRequest!requestInstall.do"; + + String ANDROID_ID = Settings.System.getString(context.getContentResolver(), Settings.System.ANDROID_ID); + + String params = "platform=Android&tag=" + "119" + "&uuid=" + ANDROID_ID; + LogUtils.e("IOSTQ:request result : " + url + "?" + params); + try { + + String result = new MyPost().PostData(context, " ".getBytes("utf-8"), url + "?" + params); + + LogUtils.e("request result : " + result); + + } catch (Exception e) { + e.printStackTrace(); + LogUtils.i("TAG", "execTask error:" + e.getMessage()); + } + } +} diff --git a/app/src/main/java/com/android/grape/util/MyPost.java b/app/src/main/java/com/android/grape/util/MyPost.java new file mode 100644 index 0000000..fb058be --- /dev/null +++ b/app/src/main/java/com/android/grape/util/MyPost.java @@ -0,0 +1,168 @@ +package com.android.grape.util; + +import android.content.Context; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.URL; + +public class MyPost { + + private static final String TAG = "MyPost"; +// private static final String TAG = TT.dd("4B7HyaIsVcs="); + + private static okhttp3.OkHttpClient oklient = new okhttp3.OkHttpClient(); + + public String PostData(Context context, byte[] byt, String url_) { + + byte[] bytes = postDataBytes(context, byt, url_); + + if (bytes != null && bytes.length > 0) { + + try { + return new String(bytes, "UTF-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + + + return null; + } + + public String PostDataCommon(Context context, byte[] byt, String url_) { + byte[] bytes = postDataBytes(context, byt, url_); + + if (bytes != null && bytes.length >= 0) { + if (bytes.length == 0) { + return ""; + } else { + try { + return new String(bytes, "UTF-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + } + return null; + } + + + public String httpGet(String url) throws Exception { + + URL obj = new URL(url); + HttpURLConnection con = (HttpURLConnection) obj.openConnection(); + + //默认值我GET + con.setRequestMethod("GET"); + + int responseCode = con.getResponseCode(); + System.out.println("\nSending 'GET' request to URL : " + url); + System.out.println("Response Code : " + responseCode); + + BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream())); + String inputLine; + StringBuffer response = new StringBuffer(); + + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + in.close(); + + //打印结果 + System.out.println(response.toString()); + return response.toString(); + + } + + public byte[] postDataBytes(Context context, byte[] byt, String url_) { + String result = null; + HttpURLConnection httpUrlConnection = null; + InputStream inStrm = null; + ByteArrayOutputStream baos = null; + BufferedInputStream bis = null; + + try { + httpUrlConnection = NetWorkUtils.getHttpURLConnection(context, url_); + + httpUrlConnection.setAllowUserInteraction(true); + httpUrlConnection.setDoOutput(true); + httpUrlConnection.setDoInput(true); + httpUrlConnection.setUseCaches(false); + httpUrlConnection.setRequestProperty("Connection", "close");//add 20200428 + httpUrlConnection.setRequestProperty("Content-type", "application/x-java-serialized-object"); +// httpUrlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + httpUrlConnection.setRequestMethod("POST"); + httpUrlConnection.setConnectTimeout(20000); + OutputStream outStrm = null; + + + outStrm = httpUrlConnection.getOutputStream(); + + + outStrm.write(byt); + outStrm.flush(); + outStrm.close(); + + inStrm = httpUrlConnection.getInputStream(); + + baos = new ByteArrayOutputStream(); + + bis = new BufferedInputStream(inStrm); + byte[] buf = new byte[1024]; + int readSize = -1; + + while ((readSize = bis.read(buf)) != -1) { + baos.write(buf, 0, readSize); + } + + byte[] data = baos.toByteArray(); + + return data; + } catch (Exception e) { + e.printStackTrace(); + result = null; + } finally { + + if (baos != null) { + try { + baos.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (bis != null) { + try { + bis.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + if (inStrm != null) { + try { + inStrm.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + if (httpUrlConnection != null) { + httpUrlConnection.disconnect(); + httpUrlConnection = null; + } + + System.gc(); + } + + return null; + } +} diff --git a/app/src/main/java/com/android/grape/util/NetWorkUtils.java b/app/src/main/java/com/android/grape/util/NetWorkUtils.java new file mode 100644 index 0000000..27a82d4 --- /dev/null +++ b/app/src/main/java/com/android/grape/util/NetWorkUtils.java @@ -0,0 +1,27 @@ +package com.android.grape.util; + +import android.content.Context; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; + +public class NetWorkUtils { + + public static HttpURLConnection getHttpURLConnection(Context context, + String _url) throws IOException { + return getHttpURLConnection(context, _url, false); + } + + private static int dynamicPort0 = 20380; + + public static HttpURLConnection getHttpURLConnection(Context context, String _url, boolean isProxy) throws IOException { + URL url = new URL(_url); + + HttpURLConnection httpUrlConnection = null; + + httpUrlConnection = (HttpURLConnection)url.openConnection(); + return httpUrlConnection; + } + +} diff --git a/app/src/main/java/com/android/grape/util/NotificationPermissionHelper.kt b/app/src/main/java/com/android/grape/util/NotificationPermissionHelper.kt new file mode 100644 index 0000000..233c1d9 --- /dev/null +++ b/app/src/main/java/com/android/grape/util/NotificationPermissionHelper.kt @@ -0,0 +1,309 @@ +package com.android.grape.util + +import android.Manifest +import android.app.NotificationManager +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import android.os.Build +import android.provider.Settings +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import androidx.annotation.RequiresApi +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat +import androidx.core.app.NotificationManagerCompat +import androidx.core.content.ContextCompat + +/** + * 通知权限管理工具 + * + * 使用示例: + * + * class MainActivity : AppCompatActivity() { + * private lateinit var permissionHandler: NotificationPermissionHandler + * + * override fun onCreate(savedInstanceState: Bundle?) { + * super.onCreate(savedInstanceState) + * + * // 初始化权限处理器 + * permissionHandler = NotificationPermissionHandler(this, ::onPermissionResult) + * + * // 检查通知权限 + * if (permissionHandler.shouldRequestNotificationPermission()) { + * permissionHandler.requestNotificationPermission() + * } + * } + * + * override fun onResume() { + * super.onResume() + * // 处理从设置返回的情况 + * permissionHandler.handleReturnFromSettings() + * } + * + * // 权限回调处理 + * private fun onPermissionResult(result: PermissionResult) { + * when(result) { + * PermissionResult.GRANTED -> showNotification() + * PermissionResult.DENIED -> showRationale() + * PermissionResult.NEEDS_SETTINGS -> showSettingsGuide() + * PermissionResult.NOT_NEEDED -> showNotification() + * } + * } + * } + */ +class NotificationPermissionHandler( + private val context: Context, + private val callback: (PermissionResult) -> Unit +) { + // Android 13+ 通知权限请求启动器 + private var permissionLauncher: ActivityResultLauncher? = null + + // 请求状态跟踪 + private var permissionRequestPending = false + private var lastPermissionState = false + + // 用于跟踪从设置页面返回的状态 + private var returnFromSettings = false + + init { + registerPermissionLauncherIfNeeded() + } + + /** + * 检查是否需要请求通知权限 + */ + fun shouldRequestNotificationPermission(): Boolean { + return !areNotificationsEnabled() + } + + /** + * 检查通知权限是否已授予 + */ + fun areNotificationsEnabled(): Boolean { + return when { + // Android 13+ (TIRAMISU) 需要运行时权限 + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> { + ContextCompat.checkSelfPermission( + context, + Manifest.permission.POST_NOTIFICATIONS + ) == PackageManager.PERMISSION_GRANTED + } + // Android 8.0+ (Oreo) 检查通知设置 + Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> { + NotificationManagerCompat.from(context).areNotificationsEnabled() + } + // Android 7.1 及以下不需要特别权限 + else -> true + } + } + + /** + * 请求通知权限 + */ + fun requestNotificationPermission() { + // 保存当前权限状态,用于比较设置后是否发生变化 + lastPermissionState = areNotificationsEnabled() + + when { + // Android 13+ - 使用运行时权限请求 + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> { + requestPermissionApi33() + } + // Android 8.0-12 - 跳转到应用通知设置 + Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> { + openNotificationSettings() + permissionRequestPending = true + } + // Android 7.1 及以下 - 默认启用 + else -> { + callback(PermissionResult.NOT_NEEDED) + } + } + } + + /** + * 处理从设置页面返回的情况 + * 应在Activity的onResume中调用 + */ + fun handleReturnFromSettings() { + if (returnFromSettings) { + returnFromSettings = false + checkSettingsChanged() + } + } + + /** + * 打开应用的通知设置页面 + */ + fun openNotificationSettings() { + val intent = when { + // Android 8.0及以上 + Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> { + Intent().apply { + action = Settings.ACTION_APP_NOTIFICATION_SETTINGS + putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName) + + // 处理特定设备需要额外设置的情况 + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + putExtra(Settings.EXTRA_CHANNEL_ID, context.applicationInfo.uid) + } + } + } + // Android 7.1及以下 + else -> { + Intent().apply { + action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS + addCategory(Intent.CATEGORY_DEFAULT) + data = Uri.parse("package:${context.packageName}") + } + } + } + + if (context is AppCompatActivity) { + returnFromSettings = true + context.startActivity(intent) + } + } + + /** + * 显示权限解释对话框 + */ + fun showPermissionRationaleDialog() { + if (context !is AppCompatActivity) return + + val activity = context as AppCompatActivity + + AlertDialog.Builder(activity) + .setTitle("需要通知权限") + .setMessage("我们需要通知权限来向你发送重要信息、更新提醒等内容。请允许通知权限以使用完整功能。") + .setPositiveButton("确定") { _, _ -> + requestNotificationPermission() + } + .setNegativeButton("取消", null) + .show() + } + + // 私有方法 -------------------------------------------- + + /** + * Android 13+ 权限请求处理 + */ + @RequiresApi(33) + private fun requestPermissionApi33() { + if (permissionLauncher == null) { + throw IllegalStateException("Permission launcher not registered. Call registerPermissionLauncherIfNeeded() first.") + } + + val activity = context as? AppCompatActivity ?: return + + when { + // 已经拥有权限 + areNotificationsEnabled() -> { + callback(PermissionResult.GRANTED) + } + // 需要显示权限解释 + ActivityCompat.shouldShowRequestPermissionRationale( + activity, + Manifest.permission.POST_NOTIFICATIONS + ) -> { + showPermissionRationaleDialog() + } + // 请求权限 + else -> { + permissionRequestPending = true + permissionLauncher?.launch(Manifest.permission.POST_NOTIFICATIONS) + } + } + } + + /** + * 注册权限启动器(仅Android 13+需要) + */ + private fun registerPermissionLauncherIfNeeded() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return + if (context !is AppCompatActivity) return + + val activity = context as AppCompatActivity + + permissionLauncher = activity.registerForActivityResult( + ActivityResultContracts.RequestPermission() + ) { isGranted -> + if (isGranted) { + callback(PermissionResult.GRANTED) + } else { + // 如果用户勾选了"不再询问" + if (!ActivityCompat.shouldShowRequestPermissionRationale( + activity, + Manifest.permission.POST_NOTIFICATIONS + )) { + callback(PermissionResult.NEEDS_SETTINGS) + } else { + callback(PermissionResult.DENIED) + } + } + permissionRequestPending = false + } + } + + /** + * 检查设置页面返回后的权限变化 + */ + private fun checkSettingsChanged() { + if (!permissionRequestPending) return + + permissionRequestPending = false + + val currentState = areNotificationsEnabled() + when { + currentState -> callback(PermissionResult.GRANTED) + currentState != lastPermissionState -> callback(PermissionResult.DENIED) + // 状态未改变 + else -> Unit // 无操作 + } + } + + /** + * 权限请求结果枚举 + */ + enum class PermissionResult { + GRANTED, // 权限已授予 + DENIED, // 权限被拒绝 + NEEDS_SETTINGS, // 需要跳转到设置 + NOT_NEEDED // 不需要权限(Android 7.1及以下) + } + + companion object { + /** + * 快速检查当前设备的通知权限状态 + */ + @JvmStatic + fun checkPermissionState(context: Context): PermissionResult { + return when { + // Android 13+ 需要运行时权限 + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> { + if (ContextCompat.checkSelfPermission( + context, + Manifest.permission.POST_NOTIFICATIONS + ) == PackageManager.PERMISSION_GRANTED) { + PermissionResult.GRANTED + } else { + PermissionResult.DENIED + } + } + // Android 8.0+ 检查通知设置 + Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> { + if (NotificationManagerCompat.from(context).areNotificationsEnabled()) { + PermissionResult.GRANTED + } else { + PermissionResult.NEEDS_SETTINGS + } + } + // Android 7.1 及以下默认启用通知 + else -> PermissionResult.NOT_NEEDED + } + } + } +} \ 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 e9b0787..647498c 100644 --- a/app/src/main/java/com/android/grape/util/Util.kt +++ b/app/src/main/java/com/android/grape/util/Util.kt @@ -696,7 +696,7 @@ object Util { val valid = false println("IOSTQ:execReloginTask->url:$url?$params") try { - val result: String? = MyPost.postData(" ".toByteArray(charset("utf-8")), + val result: String? = MyPost.postData(" ".toByteArray(charset = Charsets.UTF_8), "$url?$params" ) @@ -748,7 +748,7 @@ object Util { // boolean valid = false; printStr("IOSTQ:request result : $url?$params") try { - val result: String? = MyPost.postData(" ".toByteArray(charset("utf-8")), + val result: String? = MyPost.postData(" ".toByteArray(charset = Charsets.UTF_8), "$url?$params" ) @@ -2394,7 +2394,7 @@ object Util { // Log.i(TAG, "setFinish"); MonitorService.setRunning(false) - killRecordProcess(context, "com.tunnelworkshop.postern") +// killRecordProcess(context, "com.tunnelworkshop.postern") killRecordProcess(context, AUTO_JSPACKAGENAME) Handler(Looper.getMainLooper()).postDelayed(Runnable { MonitorService.onEvent( diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml new file mode 100644 index 0000000..d092877 --- /dev/null +++ b/app/src/main/res/xml/network_security_config.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + 127.0.0.1 + + \ No newline at end of file