Implement send notification

This commit is contained in:
世界 2024-11-07 12:48:48 +08:00
parent e38f352c48
commit 108f8b05af
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
15 changed files with 116 additions and 45 deletions

View file

@ -10,7 +10,7 @@ plugins {
android { android {
namespace "io.nekohasekai.sfa" namespace "io.nekohasekai.sfa"
compileSdk 34 compileSdk 35
ndkVersion "27.2.12479018" ndkVersion "27.2.12479018"
@ -22,7 +22,7 @@ android {
defaultConfig { defaultConfig {
applicationId "io.nekohasekai.sfa" applicationId "io.nekohasekai.sfa"
minSdk 21 minSdk 21
targetSdk 34 targetSdk 35
versionCode getVersionProps("VERSION_CODE").toInteger() versionCode getVersionProps("VERSION_CODE").toInteger()
versionName getVersionProps("VERSION_NAME") versionName getVersionProps("VERSION_NAME")
setProperty("archivesBaseName", "SFA-" + versionName) setProperty("archivesBaseName", "SFA-" + versionName)
@ -90,23 +90,23 @@ android {
dependencies { dependencies {
implementation(fileTree("libs")) implementation(fileTree("libs"))
implementation "androidx.core:core-ktx:1.13.1" implementation "androidx.core:core-ktx:1.15.0"
implementation "androidx.appcompat:appcompat:1.7.0" implementation "androidx.appcompat:appcompat:1.7.0"
implementation "com.google.android.material:material:1.12.0" implementation "com.google.android.material:material:1.12.0"
implementation "androidx.constraintlayout:constraintlayout:2.1.4" implementation "androidx.constraintlayout:constraintlayout:2.2.0"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.8.6" implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.8.7"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.6" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7"
implementation "androidx.navigation:navigation-fragment-ktx:2.8.3" implementation "androidx.navigation:navigation-fragment-ktx:2.8.3"
implementation "androidx.navigation:navigation-ui-ktx:2.8.3" implementation "androidx.navigation:navigation-ui-ktx:2.8.3"
implementation "com.google.zxing:core:3.5.3" implementation "com.google.zxing:core:3.5.3"
implementation "androidx.room:room-runtime:2.6.1" implementation "androidx.room:room-runtime:2.6.1"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0" implementation "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
implementation "androidx.preference:preference-ktx:1.2.1" implementation "androidx.preference:preference-ktx:1.2.1"
implementation "androidx.camera:camera-view:1.3.4" implementation "androidx.camera:camera-view:1.4.0"
implementation "androidx.camera:camera-lifecycle:1.3.4" implementation "androidx.camera:camera-lifecycle:1.4.0"
implementation "androidx.camera:camera-camera2:1.3.4" implementation "androidx.camera:camera-camera2:1.4.0"
ksp "androidx.room:room-compiler:2.6.1" ksp "androidx.room:room-compiler:2.6.1"
implementation "androidx.work:work-runtime-ktx:2.9.1" implementation "androidx.work:work-runtime-ktx:2.10.0"
implementation "androidx.browser:browser:1.8.0" implementation "androidx.browser:browser:1.8.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0"
@ -120,6 +120,8 @@ dependencies {
implementation "com.google.guava:guava:33.0.0-android" implementation "com.google.guava:guava:33.0.0-android"
playImplementation "com.google.android.play:app-update-ktx:2.1.0" playImplementation "com.google.android.play:app-update-ktx:2.1.0"
playImplementation "com.google.android.gms:play-services-mlkit-barcode-scanning:18.3.1" playImplementation "com.google.android.gms:play-services-mlkit-barcode-scanning:18.3.1"
implementation "com.github.tiann:FreeReflection:3.1.0"
} }
def playCredentialsJSON = rootProject.file("service-account-credentials.json") def playCredentialsJSON = rootProject.file("service-account-credentials.json")

View file

@ -16,13 +16,14 @@ import io.nekohasekai.sfa.bg.UpdateProfileWork
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import me.weishu.reflection.Reflection
import io.nekohasekai.sfa.Application as BoxApplication import io.nekohasekai.sfa.Application as BoxApplication
class Application : Application() { class Application : Application() {
override fun attachBaseContext(base: Context?) { override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base) super.attachBaseContext(base)
Reflection.unseal(base)
application = this application = this
} }

View file

@ -1,15 +1,20 @@
package io.nekohasekai.sfa.bg package io.nekohasekai.sfa.bg
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service import android.app.Service
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.IBinder import android.os.IBinder
import android.os.ParcelFileDescriptor import android.os.ParcelFileDescriptor
import android.os.PowerManager import android.os.PowerManager
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import go.Seq import go.Seq
@ -17,6 +22,7 @@ import io.nekohasekai.libbox.BoxService
import io.nekohasekai.libbox.CommandServer import io.nekohasekai.libbox.CommandServer
import io.nekohasekai.libbox.CommandServerHandler import io.nekohasekai.libbox.CommandServerHandler
import io.nekohasekai.libbox.Libbox import io.nekohasekai.libbox.Libbox
import io.nekohasekai.libbox.Notification
import io.nekohasekai.libbox.PlatformInterface import io.nekohasekai.libbox.PlatformInterface
import io.nekohasekai.libbox.SystemProxyStatus import io.nekohasekai.libbox.SystemProxyStatus
import io.nekohasekai.sfa.Application import io.nekohasekai.sfa.Application
@ -27,6 +33,7 @@ import io.nekohasekai.sfa.constant.Status
import io.nekohasekai.sfa.database.ProfileManager import io.nekohasekai.sfa.database.ProfileManager
import io.nekohasekai.sfa.database.Settings import io.nekohasekai.sfa.database.Settings
import io.nekohasekai.sfa.ktx.hasPermission import io.nekohasekai.sfa.ktx.hasPermission
import io.nekohasekai.sfa.ui.MainActivity
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
@ -36,8 +43,7 @@ import kotlinx.coroutines.withContext
import java.io.File import java.io.File
class BoxService( class BoxService(
private val service: Service, private val service: Service, private val platformInterface: PlatformInterface
private val platformInterface: PlatformInterface
) : CommandServerHandler { ) : CommandServerHandler {
companion object { companion object {
@ -101,8 +107,7 @@ class BoxService(
} }
private fun startCommandServer() { private fun startCommandServer() {
val commandServer = val commandServer = CommandServer(this, 300)
CommandServer(this, 300)
commandServer.start() commandServer.start()
this.commandServer = commandServer this.commandServer = commandServer
} }
@ -328,4 +333,45 @@ class BoxService(
commandServer?.writeMessage(message) commandServer?.writeMessage(message)
} }
internal fun sendNotification(notification: Notification) {
val builder =
NotificationCompat.Builder(service, notification.identifier).setShowWhen(false)
.setContentTitle(notification.title)
.setContentText(notification.body)
.setOnlyAlertOnce(true)
.setSmallIcon(R.drawable.ic_menu)
.setCategory(NotificationCompat.CATEGORY_EVENT)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
if (!notification.subtitle.isNullOrBlank()) {
builder.setContentInfo(notification.subtitle)
}
if (!notification.openURL.isNullOrBlank()) {
builder.setContentIntent(
PendingIntent.getActivity(
service,
0,
Intent(
service, MainActivity::class.java
).apply {
setAction(Action.OPEN_URL).setData(Uri.parse(notification.openURL))
setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
},
ServiceNotification.flags,
)
)
}
GlobalScope.launch(Dispatchers.Main) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Application.notification.createNotificationChannel(
NotificationChannel(
notification.identifier,
notification.typeName,
NotificationManager.IMPORTANCE_HIGH
)
)
}
Application.notification.notify(notification.typeID, builder.build())
}
}
} }

View file

@ -1,5 +1,6 @@
package io.nekohasekai.sfa.bg package io.nekohasekai.sfa.bg
import android.annotation.SuppressLint
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import android.os.Process import android.os.Process
@ -136,6 +137,9 @@ interface PlatformInterfaceWrapper : PlatformInterface {
element.interfaceAddresses.mapTo(mutableListOf()) { it.toPrefix() } element.interfaceAddresses.mapTo(mutableListOf()) { it.toPrefix() }
.iterator() .iterator()
) )
runCatching {
flags = element.flags
}
} }
} }
@ -146,6 +150,13 @@ interface PlatformInterfaceWrapper : PlatformInterface {
"${address.hostAddress}/${networkPrefixLength}" "${address.hostAddress}/${networkPrefixLength}"
} }
} }
private val NetworkInterface.flags: Int
@SuppressLint("SoonBlockedPrivateApi")
get() {
val getFlagsMethod = NetworkInterface::class.java.getDeclaredMethod("getFlags")
return getFlagsMethod.invoke(this) as Int
}
} }
private class StringArray(private val iterator: Iterator<String>) : StringIterator { private class StringArray(private val iterator: Iterator<String>) : StringIterator {

View file

@ -2,6 +2,7 @@ package io.nekohasekai.sfa.bg
import android.app.Service import android.app.Service
import android.content.Intent import android.content.Intent
import io.nekohasekai.libbox.Notification
class ProxyService : Service(), PlatformInterfaceWrapper { class ProxyService : Service(), PlatformInterfaceWrapper {
@ -14,4 +15,8 @@ class ProxyService : Service(), PlatformInterfaceWrapper {
override fun onDestroy() = service.onDestroy() override fun onDestroy() = service.onDestroy()
override fun writeLog(message: String) = service.writeLog(message) override fun writeLog(message: String) = service.writeLog(message)
override fun sendNotification(notification: Notification) =
service.sendNotification(notification)
} }

View file

@ -33,7 +33,7 @@ class ServiceNotification(
companion object { companion object {
private const val notificationId = 1 private const val notificationId = 1
private const val notificationChannel = "service" private const val notificationChannel = "service"
private val flags = val flags =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0
fun checkPermission(): Boolean { fun checkPermission(): Boolean {
@ -83,7 +83,7 @@ class ServiceNotification(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Application.notification.createNotificationChannel( Application.notification.createNotificationChannel(
NotificationChannel( NotificationChannel(
notificationChannel, "sing-box service", NotificationManager.IMPORTANCE_LOW notificationChannel, "Service Notifications", NotificationManager.IMPORTANCE_LOW
) )
) )
} }

View file

@ -6,6 +6,7 @@ import android.net.ProxyInfo
import android.net.VpnService import android.net.VpnService
import android.os.Build import android.os.Build
import android.os.IBinder import android.os.IBinder
import io.nekohasekai.libbox.Notification
import io.nekohasekai.libbox.TunOptions import io.nekohasekai.libbox.TunOptions
import io.nekohasekai.sfa.database.Settings import io.nekohasekai.sfa.database.Settings
import io.nekohasekai.sfa.ktx.toIpPrefix import io.nekohasekai.sfa.ktx.toIpPrefix
@ -188,4 +189,7 @@ class VPNService : VpnService(), PlatformInterfaceWrapper {
override fun writeLog(message: String) = service.writeLog(message) override fun writeLog(message: String) = service.writeLog(message)
override fun sendNotification(notification: Notification) =
service.sendNotification(notification)
} }

View file

@ -3,4 +3,5 @@ package io.nekohasekai.sfa.constant
object Action { object Action {
const val SERVICE = "io.nekohasekai.sfa.SERVICE" const val SERVICE = "io.nekohasekai.sfa.SERVICE"
const val SERVICE_CLOSE = "io.nekohasekai.sfa.SERVICE_CLOSE" const val SERVICE_CLOSE = "io.nekohasekai.sfa.SERVICE_CLOSE"
const val OPEN_URL = "io.nekohasekai.sfa.SERVICE_OPEN_URL"
} }

View file

@ -33,6 +33,7 @@ import io.nekohasekai.sfa.Application
import io.nekohasekai.sfa.R import io.nekohasekai.sfa.R
import io.nekohasekai.sfa.bg.ServiceConnection import io.nekohasekai.sfa.bg.ServiceConnection
import io.nekohasekai.sfa.bg.ServiceNotification import io.nekohasekai.sfa.bg.ServiceNotification
import io.nekohasekai.sfa.constant.Action
import io.nekohasekai.sfa.constant.Alert import io.nekohasekai.sfa.constant.Alert
import io.nekohasekai.sfa.constant.ServiceMode import io.nekohasekai.sfa.constant.ServiceMode
import io.nekohasekai.sfa.constant.Status import io.nekohasekai.sfa.constant.Status
@ -43,6 +44,7 @@ import io.nekohasekai.sfa.database.TypedProfile
import io.nekohasekai.sfa.databinding.ActivityMainBinding import io.nekohasekai.sfa.databinding.ActivityMainBinding
import io.nekohasekai.sfa.ktx.errorDialogBuilder import io.nekohasekai.sfa.ktx.errorDialogBuilder
import io.nekohasekai.sfa.ktx.hasPermission import io.nekohasekai.sfa.ktx.hasPermission
import io.nekohasekai.sfa.ktx.launchCustomTab
import io.nekohasekai.sfa.ui.profile.NewProfileActivity import io.nekohasekai.sfa.ui.profile.NewProfileActivity
import io.nekohasekai.sfa.ui.settings.CoreFragment import io.nekohasekai.sfa.ui.settings.CoreFragment
import io.nekohasekai.sfa.ui.shared.AbstractActivity import io.nekohasekai.sfa.ui.shared.AbstractActivity
@ -127,6 +129,12 @@ class MainActivity : AbstractActivity<ActivityMainBinding>(),
override public fun onNewIntent(intent: Intent) { override public fun onNewIntent(intent: Intent) {
super.onNewIntent(intent) super.onNewIntent(intent)
val uri = intent.data ?: return val uri = intent.data ?: return
when (intent.action) {
Action.OPEN_URL -> {
launchCustomTab(uri.toString())
return
}
}
if (uri.scheme == "sing-box" && uri.host == "import-remote-profile") { if (uri.scheme == "sing-box" && uri.host == "import-remote-profile") {
val profile = try { val profile = try {
Libbox.parseRemoteProfileImportLink(uri.toString()) Libbox.parseRemoteProfileImportLink(uri.toString())

View file

@ -25,7 +25,6 @@ import io.nekohasekai.sfa.databinding.ViewClashModeButtonBinding
import io.nekohasekai.sfa.databinding.ViewProfileItemBinding import io.nekohasekai.sfa.databinding.ViewProfileItemBinding
import io.nekohasekai.sfa.ktx.errorDialogBuilder import io.nekohasekai.sfa.ktx.errorDialogBuilder
import io.nekohasekai.sfa.ktx.getAttrColor import io.nekohasekai.sfa.ktx.getAttrColor
import io.nekohasekai.sfa.ktx.launchCustomTab
import io.nekohasekai.sfa.ui.MainActivity import io.nekohasekai.sfa.ui.MainActivity
import io.nekohasekai.sfa.utils.CommandClient import io.nekohasekai.sfa.utils.CommandClient
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -41,7 +40,7 @@ class OverviewFragment : Fragment() {
private val activity: MainActivity? get() = super.getActivity() as MainActivity? private val activity: MainActivity? get() = super.getActivity() as MainActivity?
private var binding: FragmentDashboardOverviewBinding? = null private var binding: FragmentDashboardOverviewBinding? = null
private val statusClient = private val statusClient =
CommandClient(lifecycleScope, CommandClient.ConnectionType.Status, StatusClient(), true) CommandClient(lifecycleScope, CommandClient.ConnectionType.Status, StatusClient())
private val clashModeClient = private val clashModeClient =
CommandClient(lifecycleScope, CommandClient.ConnectionType.ClashMode, ClashModeClient()) CommandClient(lifecycleScope, CommandClient.ConnectionType.ClashMode, ClashModeClient())
@ -172,10 +171,6 @@ class OverviewFragment : Fragment() {
} }
} }
override fun openURL(url: String) {
requireContext().launchCustomTab(url)
}
} }
inner class ClashModeClient : CommandClient.Handler { inner class ClashModeClient : CommandClient.Handler {

View file

@ -78,9 +78,9 @@ class VPNScanActivity : AbstractActivity<ActivityVpnScanBinding>() {
RecyclerView.ViewHolder(binding.root) { RecyclerView.ViewHolder(binding.root) {
fun bind(element: AppInfo) { fun bind(element: AppInfo) {
binding.appIcon.setImageDrawable(element.packageInfo.applicationInfo.loadIcon(binding.root.context.packageManager)) binding.appIcon.setImageDrawable(element.packageInfo.applicationInfo!!.loadIcon(binding.root.context.packageManager))
binding.appName.text = binding.appName.text =
element.packageInfo.applicationInfo.loadLabel(binding.root.context.packageManager) element.packageInfo.applicationInfo!!.loadLabel(binding.root.context.packageManager)
binding.packageName.text = element.packageInfo.packageName binding.packageName.text = element.packageInfo.packageName
val appType = element.vpnType.appType val appType = element.vpnType.appType
if (appType != null) { if (appType != null) {
@ -129,7 +129,8 @@ class VPNScanActivity : AbstractActivity<ActivityVpnScanBinding>() {
} }
val vpnAppList = val vpnAppList =
installedPackages.filter { installedPackages.filter {
it.services?.any { it.permission == Manifest.permission.BIND_VPN_SERVICE } ?: false it.services?.any { it.permission == Manifest.permission.BIND_VPN_SERVICE && it.applicationInfo != null }
?: false
} }
for ((index, packageInfo) in vpnAppList.withIndex()) { for ((index, packageInfo) in vpnAppList.withIndex()) {
val appType = runCatching { getVPNAppType(packageInfo) }.getOrNull() val appType = runCatching { getVPNAppType(packageInfo) }.getOrNull()
@ -181,7 +182,7 @@ class VPNScanActivity : AbstractActivity<ActivityVpnScanBinding>() {
} }
private fun getVPNAppType(packageInfo: PackageInfo): String? { private fun getVPNAppType(packageInfo: PackageInfo): String? {
ZipFile(File(packageInfo.applicationInfo.publicSourceDir)).use { packageFile -> ZipFile(File(packageInfo.applicationInfo!!.publicSourceDir)).use { packageFile ->
for (packageEntry in packageFile.entries()) { for (packageEntry in packageFile.entries()) {
if (!(packageEntry.name.startsWith("classes") && packageEntry.name.endsWith( if (!(packageEntry.name.startsWith("classes") && packageEntry.name.endsWith(
".dex" ".dex"
@ -235,8 +236,8 @@ class VPNScanActivity : AbstractActivity<ActivityVpnScanBinding>() {
} }
private fun getVPNCoreType(packageInfo: PackageInfo): VPNCoreType? { private fun getVPNCoreType(packageInfo: PackageInfo): VPNCoreType? {
val packageFiles = mutableListOf(packageInfo.applicationInfo.publicSourceDir) val packageFiles = mutableListOf(packageInfo.applicationInfo!!.publicSourceDir)
packageInfo.applicationInfo.splitPublicSourceDirs?.also { packageInfo.applicationInfo!!.splitPublicSourceDirs?.also {
packageFiles.addAll(it) packageFiles.addAll(it)
} }
val vpnType = try { val vpnType = try {

View file

@ -53,24 +53,25 @@ class PerAppProxyActivity : AbstractActivity<ActivityPerAppProxyBinding>() {
inner class PackageCache( inner class PackageCache(
private val packageInfo: PackageInfo, private val packageInfo: PackageInfo,
private val appInfo: ApplicationInfo,
) { ) {
val packageName: String get() = packageInfo.packageName val packageName: String get() = packageInfo.packageName
val uid get() = packageInfo.applicationInfo.uid val uid get() = packageInfo.applicationInfo!!.uid
val installTime get() = packageInfo.firstInstallTime val installTime get() = packageInfo.firstInstallTime
val updateTime get() = packageInfo.lastUpdateTime val updateTime get() = packageInfo.lastUpdateTime
val isSystem get() = packageInfo.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM == 1 val isSystem get() = appInfo.flags and ApplicationInfo.FLAG_SYSTEM == 1
val isOffline get() = packageInfo.requestedPermissions?.contains(Manifest.permission.INTERNET) != true val isOffline get() = packageInfo.requestedPermissions?.contains(Manifest.permission.INTERNET) != true
val isDisabled get() = packageInfo.applicationInfo.flags and ApplicationInfo.FLAG_INSTALLED == 0 val isDisabled get() = appInfo.flags and ApplicationInfo.FLAG_INSTALLED == 0
val applicationIcon by lazy { val applicationIcon by lazy {
packageInfo.applicationInfo.loadIcon(packageManager) appInfo.loadIcon(packageManager)
} }
val applicationLabel by lazy { val applicationLabel by lazy {
packageInfo.applicationInfo.loadLabel(packageManager).toString() appInfo.loadLabel(packageManager).toString()
} }
} }
@ -130,7 +131,8 @@ class PerAppProxyActivity : AbstractActivity<ActivityPerAppProxyBinding>() {
val packages = mutableListOf<PackageCache>() val packages = mutableListOf<PackageCache>()
for (packageInfo in installedPackages) { for (packageInfo in installedPackages) {
if (packageInfo.packageName == packageName) continue if (packageInfo.packageName == packageName) continue
packages.add(PackageCache(packageInfo)) val appInfo = packageInfo.applicationInfo ?: continue
packages.add(PackageCache(packageInfo, appInfo))
} }
val selectedPackageNames = Settings.perAppProxyList.toMutableSet() val selectedPackageNames = Settings.perAppProxyList.toMutableSet()
val selectedUIDs = mutableSetOf<Int>() val selectedUIDs = mutableSetOf<Int>()
@ -699,6 +701,7 @@ class PerAppProxyActivity : AbstractActivity<ActivityPerAppProxyBinding>() {
packageName, packageManagerFlags packageName, packageManagerFlags
) )
} }
val appInfo = packageInfo.applicationInfo ?: return false
packageInfo.services?.forEach { packageInfo.services?.forEach {
if (it.name.matches(chinaAppRegex)) { if (it.name.matches(chinaAppRegex)) {
Log.d("PerAppProxyActivity", "Match service ${it.name} in $packageName") Log.d("PerAppProxyActivity", "Match service ${it.name} in $packageName")
@ -723,7 +726,7 @@ class PerAppProxyActivity : AbstractActivity<ActivityPerAppProxyBinding>() {
return true return true
} }
} }
ZipFile(File(packageInfo.applicationInfo.publicSourceDir)).use { ZipFile(File(appInfo.publicSourceDir)).use {
for (packageEntry in it.entries()) { for (packageEntry in it.entries()) {
if (packageEntry.name.startsWith("firebase-")) return false if (packageEntry.name.startsWith("firebase-")) return false
} }

View file

@ -21,7 +21,6 @@ open class CommandClient(
private val scope: CoroutineScope, private val scope: CoroutineScope,
private val connectionType: ConnectionType, private val connectionType: ConnectionType,
private val handler: Handler, private val handler: Handler,
private val isMainClient: Boolean = false
) { ) {
enum class ConnectionType { enum class ConnectionType {
@ -34,7 +33,6 @@ open class CommandClient(
fun onDisconnected() {} fun onDisconnected() {}
fun updateStatus(status: StatusMessage) {} fun updateStatus(status: StatusMessage) {}
fun openURL(url: String) {}
fun clearLogs() {} fun clearLogs() {}
fun appendLogs(message: List<String>) {} fun appendLogs(message: List<String>) {}
@ -57,7 +55,6 @@ open class CommandClient(
ConnectionType.Log -> Libbox.CommandLog ConnectionType.Log -> Libbox.CommandLog
ConnectionType.ClashMode -> Libbox.CommandClashMode ConnectionType.ClashMode -> Libbox.CommandClashMode
} }
options.isMainClient = isMainClient
options.statusInterval = 1 * 1000 * 1000 * 1000 options.statusInterval = 1 * 1000 * 1000 * 1000
val commandClient = CommandClient(clientHandler, options) val commandClient = CommandClient(clientHandler, options)
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
@ -129,10 +126,6 @@ open class CommandClient(
handler.updateStatus(message) handler.updateStatus(message)
} }
override fun openURL(url: String) {
handler.openURL(url)
}
override fun initializeClashMode(modeList: StringIterator, currentMode: String) { override fun initializeClashMode(modeList: StringIterator, currentMode: String) {
handler.initializeClashMode(modeList.toList(), currentMode) handler.initializeClashMode(modeList.toList(), currentMode)
} }

View file

@ -5,8 +5,8 @@ buildscript {
} }
plugins { plugins {
id 'com.android.application' version '8.7.1' apply false id 'com.android.application' version '8.7.2' apply false
id 'com.android.library' version '8.7.1' apply false id 'com.android.library' version '8.7.2' apply false
id 'org.jetbrains.kotlin.android' version '1.9.23' apply false id 'org.jetbrains.kotlin.android' version '1.9.23' apply false
id 'com.google.devtools.ksp' version '1.9.23-1.0.20' apply false id 'com.google.devtools.ksp' version '1.9.23-1.0.20' apply false
id 'com.github.triplet.play' version '3.8.4' apply false id 'com.github.triplet.play' version '3.8.4' apply false

View file

@ -10,6 +10,7 @@ dependencyResolutionManagement {
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
maven { url "https://jitpack.io" }
} }
} }
rootProject.name = "sing-box" rootProject.name = "sing-box"