Merge remote-tracking branch 'refs/remotes/origin/master'

This commit is contained in:
cyh 2025-03-13 10:18:05 +08:00
commit 0c395b1664
10 changed files with 475 additions and 14 deletions

View File

@ -164,6 +164,7 @@
android:name=".fragment.recharge.BackupRechargeActivity" android:name=".fragment.recharge.BackupRechargeActivity"
android:process=":background" android:process=":background"
android:screenOrientation="portrait" /> android:screenOrientation="portrait" />
<service android:name=".service.ConnectionService"/>
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"

View File

@ -12,8 +12,11 @@ import com.yingmao.clash.listener.AsyncCallBackListener
import com.yingmao.clash.net.http.HttpApi import com.yingmao.clash.net.http.HttpApi
import com.yingmao.clash.net.http.HttpReqType import com.yingmao.clash.net.http.HttpReqType
import com.yingmao.clash.net.http.NetService import com.yingmao.clash.net.http.NetService
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import org.yaml.snakeyaml.Yaml import org.yaml.snakeyaml.Yaml
import java.math.BigDecimal import java.math.BigDecimal
@ -138,4 +141,61 @@ class AccelerateViewModel : PublicViewMode() {
} }
} }
fun appStartupStatus() {
val httpApi = NetService.instances.createHttpsEncodeStr(HttpApi::class.java)
val token = MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_PH_TOKEN, "")!!
val phoneNumber =
MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME, "")!!
val params = mutableMapOf(
"phToken" to token,
"phoneNumber" to phoneNumber,
)
Log.d("appCheck==>$params")
doAsync(HttpReqType.AppStartupStatus) {
httpApi.appStartupStatus(params)
}
}
fun appStopStatus(addId:Int) {
val httpApi = NetService.instances.createHttpsEncodeStr(HttpApi::class.java)
val token = MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_PH_TOKEN, "")!!
val phoneNumber =
MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME, "")!!
val paramsToken = mutableMapOf(
"phToken" to token,
"phoneNumber" to phoneNumber,
)
val params = hashMapOf(
"id" to addId
)
val gson = Gson()
val jsonString = gson.toJson(params)
val requestBody: RequestBody = jsonString.toRequestBody("application/json; charset=utf-8".toMediaType())
doAsync(HttpReqType.AppStopStatus) {
httpApi.appStopStatus(paramsToken,requestBody)
}
}
fun appUpStatus(addId:Int) {
val httpApi = NetService.instances.createHttpsEncodeStr(HttpApi::class.java)
val token = MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_PH_TOKEN, "")!!
val phoneNumber =
MMKV.defaultMMKV().decodeString(BZYConstants.MMKV_KEY_REMEMBER_USERNAME, "")!!
val paramsToken = mutableMapOf(
"phToken" to token,
"phoneNumber" to phoneNumber,
)
val params = hashMapOf(
"id" to addId
)
val gson = Gson()
val jsonString = gson.toJson(params)
val requestBody: RequestBody = jsonString.toRequestBody("application/json; charset=utf-8".toMediaType())
doAsync(HttpReqType.AppUpStatus) {
httpApi.appUpStatus(paramsToken,requestBody)
}
}
} }

View File

@ -8,6 +8,7 @@ import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.ResultReceiver
import android.text.TextUtils import android.text.TextUtils
import android.view.View import android.view.View
import android.widget.Button import android.widget.Button
@ -20,6 +21,10 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity.RESULT_OK import androidx.appcompat.app.AppCompatActivity.RESULT_OK
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.cncat.vpn.common.constants.Intents
import com.cncat.vpn.common.log.Log
import com.cncat.vpn.core.Clash
import com.cncat.vpn.core.model.TunnelState
import com.orhanobut.logger.Logger import com.orhanobut.logger.Logger
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
import com.yingmao.clash.R import com.yingmao.clash.R
@ -28,14 +33,14 @@ import com.yingmao.clash.act.NewMainActivity
import com.yingmao.clash.act.NewMainActivity.Companion.isSubed import com.yingmao.clash.act.NewMainActivity.Companion.isSubed
import com.yingmao.clash.act.NewMainActivity.Companion.newUuid import com.yingmao.clash.act.NewMainActivity.Companion.newUuid
import com.yingmao.clash.act.ProxyModeActivity import com.yingmao.clash.act.ProxyModeActivity
import com.cncat.vpn.common.constants.Intents
import com.cncat.vpn.common.log.Log
import com.yingmao.clash.conf.BZYConstants import com.yingmao.clash.conf.BZYConstants
import com.yingmao.clash.conf.Conf import com.yingmao.clash.conf.Conf
import com.cncat.vpn.core.Clash
import com.cncat.vpn.core.model.TunnelState
import com.yingmao.clash.entity.ClashNodebBean import com.yingmao.clash.entity.ClashNodebBean
import com.yingmao.clash.listener.AsyncCallBackListener import com.yingmao.clash.listener.AsyncCallBackListener
import com.yingmao.clash.net.http.HttpReqType
import com.yingmao.clash.net.http.HttpStatus
import com.yingmao.clash.net.http.entity.AppStartupStatus
import com.yingmao.clash.service.ConnectionService
import com.yingmao.clash.util.ActivityResultLifecycle import com.yingmao.clash.util.ActivityResultLifecycle
import com.yingmao.clash.util.CommonUtils import com.yingmao.clash.util.CommonUtils
import com.yingmao.clash.util.startClashService import com.yingmao.clash.util.startClashService
@ -87,6 +92,7 @@ class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatch
// var currentNode: Profile? = null // var currentNode: Profile? = null
private var reConnection = false private var reConnection = false
private var connectId=-1
override fun init( override fun init(
titleBarRl: RelativeLayout, titleBarRl: RelativeLayout,
@ -112,7 +118,7 @@ class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatch
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
Logger.d("onActivityCreated") Logger.d("onActivityCreated")
accelerateViewModel = ViewModelProvider(this).get(AccelerateViewModel::class.java) accelerateViewModel = ViewModelProvider(this).get(AccelerateViewModel::class.java)
observe(this, accelerateViewModel)
initSS() initSS()
// initSub() // initSub()
getNodesStatus() getNodesStatus()
@ -245,6 +251,11 @@ class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatch
stopClash() stopClash()
rxDialogSureCancel.cancel() rxDialogSureCancel.cancel()
reConnection = false reConnection = false
Log.d("NewAccFragment connectId${connectId}")
if(connectId!=-1){
accelerateViewModel.appStopStatus(connectId)
}
} }
rxDialogSureCancel.show() rxDialogSureCancel.show()
@ -311,21 +322,20 @@ class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatch
} }
val vpnRequest = context?.startClashService() val vpnRequest = context?.startClashService()
try { try {
start_u_timer() start_u_timer()
if (vpnRequest != null) { if (vpnRequest != null) {
val result = startActivityForResult( val result = startActivityForResult(
ActivityResultContracts.StartActivityForResult(), vpnRequest ActivityResultContracts.StartActivityForResult(), vpnRequest
) )
if (result.resultCode == RESULT_OK) { if (result.resultCode == RESULT_OK) {
stateChanged(Clash_Starting, true) stateChanged(Clash_Starting, true)
context?.startClashService() context?.startClashService()
accelerateViewModel.appStartupStatus()
// clashRunning = true // clashRunning = true
} }
}else{
accelerateViewModel.appStartupStatus()
} }
} catch (e: Exception) { } catch (e: Exception) {
stop_u_timer() stop_u_timer()
@ -433,6 +443,7 @@ class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatch
} }
} }
// File("${Core.deviceStorage.noBackupFilesDir}/${BaseService.CONFIG_FILE}").delete() // File("${Core.deviceStorage.noBackupFilesDir}/${BaseService.CONFIG_FILE}").delete()
accelerateViewModel.appStartupStatus()
} }
Clash_Stop -> { Clash_Stop -> {
@ -828,4 +839,54 @@ class NewAccFragment : BaseFragment(), CoroutineScope by CoroutineScope(Dispatch
} }
} }
} }
private val resultReceiver: ResultReceiver = object : ResultReceiver(null) {
override fun onReceiveResult(resultCode: Int, resultData: Bundle) {
val data = resultData.getString("data")
Log.d("ping to from Service: $data")
if(connectId!=-1){
Log.d("activity connectId: $connectId")
accelerateViewModel.appUpStatus(connectId)
}
}
}
override fun httpLoading(httpStatus: HttpStatus.Loading) {
}
override fun httpSuccess(httpStatus: HttpStatus.Success) {
when(httpStatus.reqType){
HttpReqType.AppStartupStatus ->{
Log.d("AppStartupStatus is Success ${httpStatus.rsp}")
val dataInfo = httpStatus.rsp as AppStartupStatus
if(dataInfo.data!=null){
if(dataInfo.data.id!=null){
connectId= dataInfo.data.id
val serviceIntent= Intent(requireContext(), ConnectionService::class.java)
serviceIntent.putExtra("receiver",resultReceiver)
requireContext().startService(serviceIntent)
}
}
}
HttpReqType.AppStopStatus ->{
Log.d("AppStopStatus is Success ${httpStatus.rsp}")
val serviceIntent= Intent(requireContext(), ConnectionService::class.java)
requireContext().stopService(serviceIntent)
}
else->{
}
}
}
override fun httpFailure(httpStatus: HttpStatus.Failure) {
when(httpStatus.reqType){
HttpReqType.AppStartupStatus ->{
Log.d("AppStartupStatus is Failure ${httpStatus.msg}")
}
HttpReqType.AppStopStatus ->{
Log.d("AppStopStatus is Failure ${httpStatus.msg}")
}
else->{
}
}
}
} }

View File

@ -22,6 +22,8 @@ import com.yingmao.clash.net.http.entity.WXPayRsp
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
import com.yingmao.clash.conf.BZYConstants import com.yingmao.clash.conf.BZYConstants
import com.yingmao.clash.entity.PayUploadParamData import com.yingmao.clash.entity.PayUploadParamData
import com.yingmao.clash.net.http.entity.AppStartupStatus
import com.yingmao.clash.net.http.entity.AppStopStatus
import com.yingmao.clash.net.http.entity.AppVersionCheckRsp import com.yingmao.clash.net.http.entity.AppVersionCheckRsp
import com.yingmao.clash.net.http.entity.GetActivityRsp import com.yingmao.clash.net.http.entity.GetActivityRsp
import com.yingmao.clash.net.http.entity.LoginRsp2 import com.yingmao.clash.net.http.entity.LoginRsp2
@ -523,4 +525,39 @@ interface HttpApi {
BZYConstants.MMKV_KEY_TOKEN BZYConstants.MMKV_KEY_TOKEN
).toString() ).toString()
): PayUploadParamData ): PayUploadParamData
@Headers(
"User-Agent: Octopus_Android"
)
@POST("netbarcloud/open/connection/add")
suspend fun appStartupStatus(
@QueryMap params: Map<String, String>,
@Header("token") string: String = MMKV.defaultMMKV().decodeString(
BZYConstants.MMKV_KEY_TOKEN
).toString()
): AppStartupStatus
@Headers(
"User-Agent: Octopus_Android"
)
@POST("netbarcloud/open/connection/close")
suspend fun appStopStatus(
@QueryMap params: Map<String, String>,
@Body requestBody: RequestBody,
@Header("token") string: String = MMKV.defaultMMKV().decodeString(
BZYConstants.MMKV_KEY_TOKEN
).toString()
): AppStopStatus
@Headers(
"User-Agent: Octopus_Android"
)
@POST("netbarcloud/open/connection/update")
suspend fun appUpStatus(
@QueryMap params: Map<String, String>,
@Body requestBody: RequestBody,
@Header("token") string: String = MMKV.defaultMMKV().decodeString(
BZYConstants.MMKV_KEY_TOKEN
).toString()
): AppStopStatus
} }

View File

@ -48,5 +48,8 @@ enum class HttpReqType(type: Int) {
GetRuningNotice(44), GetRuningNotice(44),
unionPay(45), unionPay(45),
UploadParameters(46), UploadParameters(46),
StripeType(47) StripeType(47),
AppStartupStatus(48),
AppUpStatus(49),
AppStopStatus(50),
} }

View File

@ -87,10 +87,10 @@ class NetService private constructor() : INetService {
// .sslSocketFactory(getSSLSocketFactory(), CustomTrustManager())//信任所有证书 // .sslSocketFactory(getSSLSocketFactory(), CustomTrustManager())//信任所有证书
.sslSocketFactory(getSSLSocketFactory(), trustAllCerts[0] as X509TrustManager) .sslSocketFactory(getSSLSocketFactory(), trustAllCerts[0] as X509TrustManager)
.hostnameVerifier(getHostnameVerifier()!!) .hostnameVerifier(getHostnameVerifier()!!)
// if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
// builder.addInterceptor(loggingInterceptor) builder.addInterceptor(loggingInterceptor)
// } }
builder.addInterceptor(loggingInterceptor) // builder.addInterceptor(loggingInterceptor)
return builder.build() return builder.build()
} }

View File

@ -210,5 +210,29 @@ data class VipSignRspData(
@field:SerializedName("signGiftmins") @field:SerializedName("signGiftmins")
val signGiftmins: Int? = null val signGiftmins: Int? = null
) )
data class AppStartupStatus(
@field:SerializedName("code")
val code: String? = null,
@field:SerializedName("data")
val data: ConnectId? = null,
@field:SerializedName("message")
val message: String? = null
) : BaseRsp
data class ConnectId(
@field:SerializedName("id")
val id: Int? = -1,
)
data class AppStopStatus(
@field:SerializedName("code")
val code: String? = null,
@field:SerializedName("data")
val data: ConnectId? = null,
@field:SerializedName("message")
val message: String? = null
) : BaseRsp

View File

@ -0,0 +1,61 @@
package com.yingmao.clash.service;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.ResultReceiver;
import android.util.Log;
import com.yingmao.clash.fragment.AccelerateViewModel;
public class ConnectionService extends Service {
private static final String TAG = "TimerService";
private static final long INTERVAL_MILLIS = 10000; // 10
private Handler handler = new Handler();
private Intent intent;
private Runnable runnable = new Runnable() {
@Override
public void run() {
if(intent!=null){
ResultReceiver receiver = intent.getParcelableExtra("receiver");
if (receiver != null) {
Bundle resultData = new Bundle();
resultData.putString("ping", "ping to Service!");
receiver.send(1, resultData); // 1 是结果码
}
}
handler.postDelayed(this, INTERVAL_MILLIS);
}
};
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "Service onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "Service onStartCommand");
this.intent=intent;
handler.post(runnable); // 启动定时器
return START_STICKY; // 服务被杀死后会自动重启
}
@Override
public void onDestroy() {
super.onDestroy();
handler.removeCallbacks(runnable); // 停止定时器
Log.d(TAG, "Service onDestroy");
}
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "Service onBind");
return null; // 如果不支持绑定服务返回 null
}
}

View File

@ -0,0 +1,193 @@
package com.yingmao.clash.util;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.util.Log;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Collections;
import java.util.Enumeration;
public class IPAddrUtils {
/**
* 获取本机IPv4地址
*
* @param context
* @return 本机IPv4地址null无网络连接
*/
public static String getIpAddress(Context context) {
if (null == context)
return getIpAddress();
// 获取WiFi服务
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
// 判断WiFi是否开启
if (wifiManager.isWifiEnabled()) {
// 已经开启了WiFi
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
int ipAddress = wifiInfo.getIpAddress();
String ip = intToIp(ipAddress);
return ip;
} else {
// 未开启WiFi
return getIpAddress();
}
}
private static String intToIp(int ipAddress) {
return (ipAddress & 0xFF) + "." +
((ipAddress >> 8) & 0xFF) + "." +
((ipAddress >> 16) & 0xFF) + "." +
(ipAddress >> 24 & 0xFF);
}
/**
* 获取本机IPv4地址
*
* @return 本机IPv4地址null无网络连接
*/
public static String getIpAddress() {
try {
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress()
&& !inetAddress.isAnyLocalAddress()
&& inetAddress instanceof Inet4Address) {
return inetAddress.getHostAddress();
}
}
}
} catch (Exception e) {
}
return null;
}
public static String getIPv6Address() {
try {
// 获取所有网络接口
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
for (NetworkInterface networkInterface : Collections.list(networkInterfaces)) {
// 过滤掉回环接口和未启用的接口
if (networkInterface.isLoopback() || !networkInterface.isUp()) {
continue;
}
// 获取网络接口的 IP 地址
Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
Log.e("AAAAA","inetAddresses::"+inetAddresses.toString());
for (InetAddress inetAddress : Collections.list(inetAddresses)) {
// 检查是否是 IPv6 地址
boolean bc = inetAddress instanceof Inet6Address;
boolean linkLocalAddress = inetAddress.isLinkLocalAddress();
Log.e("AAAAAqqqq","bc::"+bc);
Log.e("AAAAAqqqq","linkLocalAddress::"+linkLocalAddress);
if (inetAddress instanceof Inet6Address && !inetAddress.isLinkLocalAddress()) {
return inetAddress.getHostAddress();
}
}
return null;
}
} catch (SocketException e) {
e.printStackTrace();
}
return null;
}
public static String getIPv6AddressAAA(Context context) {
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivityManager == null) {
return null;
}
Network activeNetwork = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
activeNetwork = connectivityManager.getActiveNetwork();
}
if (activeNetwork != null) {
NetworkCapabilities networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork);
if (networkCapabilities != null && networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
// 当前网络是 Wi-Fi
return getIPv6Address();
} else if (networkCapabilities != null && networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
// 当前网络是移动数据
return getIPv6Address();
}
}
return null;
}
/**
* 获取设备的 IPv4 地址
*
* @return IPv4 地址如果未找到则返回 null
*/
public static String getIPv4Address() {
try {
// 获取所有网络接口
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
for (NetworkInterface networkInterface : Collections.list(networkInterfaces)) {
// 过滤掉回环接口和未启用的接口
if (networkInterface.isLoopback() || !networkInterface.isUp()) {
continue;
}
// 获取网络接口的 IP 地址
Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
for (InetAddress inetAddress : Collections.list(inetAddresses)) {
// 检查是否是 IPv4 地址
if (inetAddress instanceof Inet4Address) {
return inetAddress.getHostAddress();
}
}
}
} catch (SocketException e) {
e.printStackTrace();
}
return null;
}
/**
* 获取当前活动网络的 IPv4 地址
*
* @param context 上下文
* @return IPv4 地址如果未找到则返回 null
*/
public static String getCurrentIPv4Address(Context context) {
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivityManager == null) {
return null;
}
// 获取当前活动网络
Network activeNetwork = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
activeNetwork = connectivityManager.getActiveNetwork();
}
if (activeNetwork == null) {
return null;
}
// 获取网络能力
NetworkCapabilities networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork);
if (networkCapabilities == null) {
return null;
}
// 检查网络类型Wi-Fi 或移动数据
if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
Log.d("NetworkHelper", "Current network is Wi-Fi");
} else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
Log.d("NetworkHelper", "Current network is Cellular");
}
// 获取 IPv4 地址
return getIPv4Address();
}
}

View File

@ -673,6 +673,27 @@ subprojects {
) )
) )
} }
create("3027") {
isDefault = true
dimension = flavorDimensionList[0]
buildConfigField("boolean", "PREMIUM", "Boolean.parseBoolean(\"false\")")
if (isApp) {
// applicationIdSuffix = ".meta"
}
buildConfigField("boolean", "is_back_cn", "false")
buildConfigField("boolean", "is_ym", "true")
buildConfigField("boolean", "is_free", "false")
buildConfigField("boolean", "is_tz", "false")
buildConfigField("String", "applicationId", "\"com.yingmao.clash\"")
buildConfigField("String", "service_url", "\"https://api.ymvpnpro.cc:8700\"")
buildConfigField("String", "productId", "\"3027\"")
manifestPlaceholders(
mapOf(
"APP_NAME" to "@string/app_name_gz", "APP_ICON" to "@mipmap/ic_logo_9972"
)
)
}
create("7595") { create("7595") {
isDefault = true isDefault = true
dimension = flavorDimensionList[0] dimension = flavorDimensionList[0]