diff --git a/app/src/main/java/com/github/kr328/clash/CreateProfileActivity.kt b/app/src/main/java/com/github/kr328/clash/CreateProfileActivity.kt index 18d66eb..40a0bdf 100644 --- a/app/src/main/java/com/github/kr328/clash/CreateProfileActivity.kt +++ b/app/src/main/java/com/github/kr328/clash/CreateProfileActivity.kt @@ -15,6 +15,7 @@ import android.widget.BaseAdapter import android.widget.TextView import com.github.kr328.clash.common.util.intent import com.github.kr328.clash.remote.withProfile +import com.github.kr328.clash.service.model.Profile import com.github.kr328.clash.service.model.Profile.Type import kotlinx.android.synthetic.main.activity_create_profile.* import kotlinx.coroutines.Dispatchers @@ -47,7 +48,7 @@ class CreateProfileActivity : BaseActivity() { self.launch { val id = withProfile { - acquireUnused(item.type) + acquireUnused(item.type, item.intent?.toUri(0)) } startActivityForResult( diff --git a/app/src/main/java/com/github/kr328/clash/ProfileEditActivity.kt b/app/src/main/java/com/github/kr328/clash/ProfileEditActivity.kt index 1fdf50a..56f724c 100644 --- a/app/src/main/java/com/github/kr328/clash/ProfileEditActivity.kt +++ b/app/src/main/java/com/github/kr328/clash/ProfileEditActivity.kt @@ -118,7 +118,7 @@ class ProfileEditActivity : BaseActivity() { launch { try { withProfile { - updateMetadata(metadata.id, metadata) + update(metadata.id, metadata) commitAsync(metadata.id).await() } diff --git a/app/src/main/java/com/github/kr328/clash/ProfilesActivity.kt b/app/src/main/java/com/github/kr328/clash/ProfilesActivity.kt index d0bd2d3..d55e027 100644 --- a/app/src/main/java/com/github/kr328/clash/ProfilesActivity.kt +++ b/app/src/main/java/com/github/kr328/clash/ProfilesActivity.kt @@ -67,7 +67,7 @@ class ProfilesActivity : BaseActivity(), ProfileAdapter.Callback, ProfilesMenu.C val profile = editorStack.pop() withProfile { - updateMetadata(profile.id, profile) + update(profile.id, profile) startUpdate(profile.id) } } diff --git a/app/src/main/java/com/github/kr328/clash/remote/Broadcasts.kt b/app/src/main/java/com/github/kr328/clash/remote/Broadcasts.kt index 18df48c..85359be 100644 --- a/app/src/main/java/com/github/kr328/clash/remote/Broadcasts.kt +++ b/app/src/main/java/com/github/kr328/clash/remote/Broadcasts.kt @@ -5,11 +5,11 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter -import androidx.lifecycle.DefaultLifecycleObserver -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.ProcessLifecycleOwner +import com.github.kr328.clash.common.Global import com.github.kr328.clash.common.Permissions import com.github.kr328.clash.common.ids.Intents +import com.github.kr328.clash.common.utils.Log +import com.github.kr328.clash.utils.ApplicationObserver object Broadcasts { interface Receiver { @@ -63,35 +63,37 @@ object Broadcasts { receivers.remove(receiver) } - fun init(application: Application) { - ProcessLifecycleOwner.get().lifecycle.addObserver(object : DefaultLifecycleObserver { - override fun onStart(owner: LifecycleOwner) { - application.registerReceiver(broadcastReceiver, IntentFilter().apply { - addAction(Intents.INTENT_ACTION_PROFILE_CHANGED) - addAction(Intents.INTENT_ACTION_CLASH_STOPPED) - addAction(Intents.INTENT_ACTION_CLASH_STARTED) - addAction(Intents.INTENT_ACTION_PROFILE_LOADED) - }, Permissions.PERMISSION_RECEIVE_BROADCASTS, null) + private val observer = ApplicationObserver { + Log.d("Global Broadcast Receiver State = $it") - val current = RemoteUtils.detectClashRunning(application) - if (current != clashRunning) { - clashRunning = current + if ( it ) { + Global.application.registerReceiver(broadcastReceiver, IntentFilter().apply { + addAction(Intents.INTENT_ACTION_PROFILE_CHANGED) + addAction(Intents.INTENT_ACTION_CLASH_STOPPED) + addAction(Intents.INTENT_ACTION_CLASH_STARTED) + addAction(Intents.INTENT_ACTION_PROFILE_LOADED) + }, Permissions.PERMISSION_RECEIVE_BROADCASTS, null) - if (current) { - receivers.forEach { - it.onStarted() - } - } else { - receivers.forEach { - it.onStopped(null) - } + val current = RemoteUtils.detectClashRunning(Global.application) + if (current != clashRunning) { + clashRunning = current + + if (current) { + receivers.forEach { receiver -> + receiver.onStarted() + } + } else { + receivers.forEach { receiver -> + receiver.onStopped(null) } } } + } else { + Global.application.unregisterReceiver(broadcastReceiver) + } + } - override fun onStop(owner: LifecycleOwner) { - application.unregisterReceiver(broadcastReceiver) - } - }) + fun init(application: Application) { + observer.register(application) } } \ No newline at end of file diff --git a/app/src/main/java/com/github/kr328/clash/remote/ProfileClient.kt b/app/src/main/java/com/github/kr328/clash/remote/ProfileClient.kt index 631456f..81a3df3 100644 --- a/app/src/main/java/com/github/kr328/clash/remote/ProfileClient.kt +++ b/app/src/main/java/com/github/kr328/clash/remote/ProfileClient.kt @@ -10,8 +10,8 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext class ProfileClient(private val service: IProfileService) { - suspend fun acquireUnused(type: Profile.Type) = withContext(Dispatchers.IO) { - service.acquireUnused(type.name) + suspend fun acquireUnused(type: Profile.Type, source: String?) = withContext(Dispatchers.IO) { + service.acquireUnused(type.name, source) } suspend fun acquireCloned(id: Long) = withContext(Dispatchers.IO) { @@ -22,8 +22,8 @@ class ProfileClient(private val service: IProfileService) { service.acquireTempUri(id) } - suspend fun updateMetadata(id: Long, metadata: Profile) = withContext(Dispatchers.IO) { - service.updateMetadata(id, metadata) + suspend fun update(id: Long, metadata: Profile) = withContext(Dispatchers.IO) { + service.update(id, metadata) } suspend fun commitAsync(id: Long) = withContext(Dispatchers.IO) { diff --git a/app/src/main/java/com/github/kr328/clash/remote/Remote.kt b/app/src/main/java/com/github/kr328/clash/remote/Remote.kt index a619003..e2ab26d 100644 --- a/app/src/main/java/com/github/kr328/clash/remote/Remote.kt +++ b/app/src/main/java/com/github/kr328/clash/remote/Remote.kt @@ -9,16 +9,16 @@ import android.os.Build import android.os.Handler import android.os.IBinder import androidx.core.content.edit -import androidx.lifecycle.DefaultLifecycleObserver -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.ProcessLifecycleOwner import com.github.kr328.clash.ApkBrokenActivity import com.github.kr328.clash.Constants +import com.github.kr328.clash.common.Global import com.github.kr328.clash.common.util.intent +import com.github.kr328.clash.common.utils.Log import com.github.kr328.clash.service.ClashManagerService import com.github.kr328.clash.service.IClashManager import com.github.kr328.clash.service.IProfileService import com.github.kr328.clash.service.ProfileService +import com.github.kr328.clash.utils.ApplicationObserver import com.microsoft.appcenter.crashes.Crashes import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel @@ -82,60 +82,63 @@ object Remote { } } - fun init(application: Application) { - val handler = Handler() + private val handler = Handler() + private val observer = ApplicationObserver { + Log.d("Remote Connection State = $it") - ProcessLifecycleOwner.get().lifecycle.addObserver(object : DefaultLifecycleObserver { - override fun onStart(owner: LifecycleOwner) { - handler.removeMessages(0) + val application = Global.application - GlobalScope.launch { - val valid = withContext(Dispatchers.IO) { - verifyApk(application) - } + if ( it ) { + handler.removeMessages(0) - if (!valid) { - application.startActivity( - ApkBrokenActivity::class.intent - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - ) - return@launch - } + GlobalScope.launch { + val valid = withContext(Dispatchers.IO) { + verifyApk(application) + } - clashConnection = ClashConnection().apply { - application.bindService( - ClashManagerService::class.intent, - this, - Context.BIND_AUTO_CREATE - ) - } + if (!valid) { + application.startActivity( + ApkBrokenActivity::class.intent + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + ) + return@launch + } - profileConnection = ProfileConnection().apply { - application.bindService( - ProfileService::class.intent, - this, - Context.BIND_AUTO_CREATE - ) - } + clashConnection = ClashConnection().apply { + application.bindService( + ClashManagerService::class.intent, + this, + Context.BIND_AUTO_CREATE + ) + } + + profileConnection = ProfileConnection().apply { + application.bindService( + ProfileService::class.intent, + this, + Context.BIND_AUTO_CREATE + ) } } + } else { + handler.postDelayed({ + clashConnection?.also { + application.unbindService(it) + it.onServiceDisconnected(null) + } + profileConnection?.also { + application.unbindService(it) + it.onServiceDisconnected(null) + } - override fun onStop(owner: LifecycleOwner) { - handler.postDelayed({ - clashConnection?.also { - application.unbindService(it) - it.onServiceDisconnected(null) - } - profileConnection?.also { - application.unbindService(it) - it.onServiceDisconnected(null) - } + clashConnection = null + profileConnection = null + }, 5000) + } + } - clashConnection = null - profileConnection = null - }, 5000) - } - }) + fun init(application: Application) { + observer.register(application) } private fun verifyApk(application: Application): Boolean { diff --git a/app/src/main/java/com/github/kr328/clash/utils/ApplicationObserver.kt b/app/src/main/java/com/github/kr328/clash/utils/ApplicationObserver.kt new file mode 100644 index 0000000..31215f6 --- /dev/null +++ b/app/src/main/java/com/github/kr328/clash/utils/ApplicationObserver.kt @@ -0,0 +1,40 @@ +package com.github.kr328.clash.utils + +import android.app.Activity +import android.app.Application +import android.os.Bundle + +class ApplicationObserver(val stateChanged: (Boolean) -> Unit) { + private var applicationRunning = false + private set(value) { + if ( field != value ) + stateChanged(value) + + field = value + } + private var activityCount: Int = 0 + + private val activityObserver = object: Application.ActivityLifecycleCallbacks { + override fun onActivityPaused(activity: Activity) {} + override fun onActivityStarted(activity: Activity) {} + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {} + override fun onActivityStopped(activity: Activity) {} + override fun onActivityResumed(activity: Activity) {} + override fun onActivityDestroyed(activity: Activity) { + synchronized(this) { + activityCount-- + applicationRunning = activityCount > 0 + } + } + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { + synchronized(this) { + activityCount++ + applicationRunning = activityCount > 0 + } + } + } + + fun register(application: Application) { + application.registerActivityLifecycleCallbacks(activityObserver) + } +} \ No newline at end of file diff --git a/service/src/main/aidl/com/github/kr328/clash/service/IProfileService.aidl b/service/src/main/aidl/com/github/kr328/clash/service/IProfileService.aidl index 6f3ca47..a3b936e 100644 --- a/service/src/main/aidl/com/github/kr328/clash/service/IProfileService.aidl +++ b/service/src/main/aidl/com/github/kr328/clash/service/IProfileService.aidl @@ -4,11 +4,11 @@ import com.github.kr328.clash.service.transact.IStreamCallback; import com.github.kr328.clash.service.model.Profile; interface IProfileService { - long acquireUnused(String type); + long acquireUnused(String type, String source); long acquireCloned(long id); String acquireTempUri(long id); void release(long id); - void updateMetadata(long id, in Profile metadata); + void update(long id, in Profile metadata); void commit(long id, in IStreamCallback callback); void delete(long id); void clear(long id); diff --git a/service/src/main/java/com/github/kr328/clash/service/ProfileService.kt b/service/src/main/java/com/github/kr328/clash/service/ProfileService.kt index e5faf08..f311093 100644 --- a/service/src/main/java/com/github/kr328/clash/service/ProfileService.kt +++ b/service/src/main/java/com/github/kr328/clash/service/ProfileService.kt @@ -52,7 +52,7 @@ class ProfileService : BaseService() { } } - override fun acquireUnused(type: String): Long { + override fun acquireUnused(type: String, source: String?): Long { return runBlocking { lock.withLock { val id = generateNextId() @@ -62,7 +62,7 @@ class ProfileService : BaseService() { name = "", type = Profile.Type.valueOf(type), uri = Uri.EMPTY, - source = null, + source = source?.let { Uri.parse(it) }, active = false, interval = 0, lastModified = 0 @@ -152,7 +152,7 @@ class ProfileService : BaseService() { return ProfileProvider.resolveUri(service, tempFile).toString() } - override fun updateMetadata(id: Long, metadata: Profile?) { + override fun update(id: Long, metadata: Profile?) { launch { lock.withLock { pending[id] = metadata ?: return@launch