diff --git a/app/src/main/java/com/github/kr328/clash/TileService.kt b/app/src/main/java/com/github/kr328/clash/TileService.kt index af65447..41fc087 100644 --- a/app/src/main/java/com/github/kr328/clash/TileService.kt +++ b/app/src/main/java/com/github/kr328/clash/TileService.kt @@ -7,6 +7,7 @@ import android.content.IntentFilter import android.graphics.drawable.Icon import android.service.quicksettings.Tile import android.service.quicksettings.TileService +import com.github.kr328.clash.common.Permissions import com.github.kr328.clash.remote.RemoteUtils import com.github.kr328.clash.common.ids.Intents import com.github.kr328.clash.utils.startClashService @@ -84,7 +85,9 @@ class TileService : TileService() { addAction(Intents.INTENT_ACTION_CLASH_STARTED) addAction(Intents.INTENT_ACTION_CLASH_STOPPED) addAction(Intents.INTENT_ACTION_PROFILE_LOADED) - } + }, + Permissions.PERMISSION_ACCESS_CLASH, + null ) val name = RemoteUtils.getCurrentClashProfileName(this) 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 6fcd3a3..4746957 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 @@ -8,6 +8,7 @@ import android.content.IntentFilter import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ProcessLifecycleOwner +import com.github.kr328.clash.common.Permissions import com.github.kr328.clash.common.ids.Intents object Broadcasts { @@ -70,8 +71,7 @@ object Broadcasts { addAction(Intents.INTENT_ACTION_CLASH_STOPPED) addAction(Intents.INTENT_ACTION_CLASH_STARTED) addAction(Intents.INTENT_ACTION_PROFILE_LOADED) - }) - + }, Permissions.PERMISSION_ACCESS_CLASH, null) val current = RemoteUtils.detectClashRunning(application) if (current != clashRunning) { diff --git a/service/src/main/AndroidManifest.xml b/service/src/main/AndroidManifest.xml index a6d0c2c..184afd0 100644 --- a/service/src/main/AndroidManifest.xml +++ b/service/src/main/AndroidManifest.xml @@ -33,7 +33,7 @@ android:process=":background" /> diff --git a/service/src/main/java/com/github/kr328/clash/service/ProfileProcessor.kt b/service/src/main/java/com/github/kr328/clash/service/ProfileProcessor.kt index 1a0fe46..245f41f 100644 --- a/service/src/main/java/com/github/kr328/clash/service/ProfileProcessor.kt +++ b/service/src/main/java/com/github/kr328/clash/service/ProfileProcessor.kt @@ -27,7 +27,15 @@ object ProfileProcessor { context.resolveBaseDir(metadata.id) ) - val entity = metadata.toProfileEntity() + val entity = if (metadata.type == Type.FILE) + metadata.copy( + uri = ProfileProvider.resolveUri( + context, + context.resolveProfileFile(metadata.id) + ) + ).toProfileEntity() + else + metadata.toProfileEntity() if (ProfileDao.queryById(metadata.id) == null) ProfileDao.insert(entity) diff --git a/service/src/main/java/com/github/kr328/clash/service/ProfileProvider.kt b/service/src/main/java/com/github/kr328/clash/service/ProfileProvider.kt new file mode 100644 index 0000000..d477f49 --- /dev/null +++ b/service/src/main/java/com/github/kr328/clash/service/ProfileProvider.kt @@ -0,0 +1,69 @@ +package com.github.kr328.clash.service + +import android.content.Context +import android.net.Uri +import android.os.Bundle +import androidx.core.content.FileProvider +import androidx.core.os.bundleOf +import com.github.kr328.clash.service.util.resolveProfileFile +import com.github.kr328.clash.service.util.resolveTempProfileFile +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.io.File +import java.io.FileNotFoundException + +class ProfileProvider : FileProvider() { + companion object { + private const val METHOD_ACQUIRE_TEMP = "acquireTemp" + private const val METHOD_RELEASE_TEMP = "releaseTemp" + + // service side functions + fun resolveUri(context: Context, file: File): Uri { + return getUriForFile( + context, + context.packageName + Constants.PROFILE_PROVIDER_SUFFIX, + file + ) + } + + // client side functions + suspend fun acquireTemp(context: Context, id: Long): Uri = withContext(Dispatchers.IO) { + val uri = Uri.parse(context.packageName + Constants.PROFILE_PROVIDER_SUFFIX) + + context.contentResolver + .call(uri, METHOD_ACQUIRE_TEMP, id.toString(), null)?.getParcelable("uri") + ?: throw FileNotFoundException("No such profile") + } + + suspend fun releaseTemp(context: Context, id: Long) = withContext(Dispatchers.IO) { + val uri = Uri.parse(context.packageName + Constants.PROFILE_PROVIDER_SUFFIX) + + context.contentResolver + .call(uri, METHOD_RELEASE_TEMP, id.toString(), null) + } + } + + override fun call(method: String, arg: String?, extras: Bundle?): Bundle? { + when (method) { + METHOD_ACQUIRE_TEMP -> { + val id = arg?.toLongOrNull() ?: return null + + val file = context!!.resolveProfileFile(id) + if (!file.exists()) + return null + + val temp = context!!.resolveTempProfileFile(id) + if (!temp.exists()) + file.copyTo(context!!.resolveTempProfileFile(id)) + + return bundleOf("uri" to resolveUri(context!!, temp)) + } + METHOD_RELEASE_TEMP -> { + val id = arg?.toLongOrNull() ?: return null + + context!!.resolveTempProfileFile(id).delete() + } + } + return super.call(method, arg, extras) + } +} \ No newline at end of file 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 bf9e781..33ee868 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 @@ -11,6 +11,7 @@ import com.github.kr328.clash.service.model.toProfileMetadata import com.github.kr328.clash.service.util.broadcastProfileChanged import com.github.kr328.clash.service.util.resolveBaseDir import com.github.kr328.clash.service.util.resolveProfileFile +import com.github.kr328.clash.service.util.resolveTempProfileFile import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.sync.Mutex @@ -87,11 +88,13 @@ class ProfileService : BaseService() { override fun delete(id: Long) { launch { lock.withLock { - pending.remove(id) + if (pending.remove(id) != null) + service.resolveBaseDir(id).deleteRecursively() ProfileDao.remove(id) } service.resolveProfileFile(id).delete() + service.resolveTempProfileFile(id).delete() service.resolveBaseDir(id).deleteRecursively() service.broadcastProfileChanged() diff --git a/service/src/main/java/com/github/kr328/clash/service/util/FileUtils.kt b/service/src/main/java/com/github/kr328/clash/service/util/FileUtils.kt index 62cf4ef..3d5129c 100644 --- a/service/src/main/java/com/github/kr328/clash/service/util/FileUtils.kt +++ b/service/src/main/java/com/github/kr328/clash/service/util/FileUtils.kt @@ -10,4 +10,8 @@ fun Context.resolveProfileFile(id: Long): File { fun Context.resolveBaseDir(id: Long): File { return filesDir.resolve(Constants.CLASH_DIR).resolve(id.toString()) +} + +fun Context.resolveTempProfileFile(id: Long): File { + return cacheDir.resolve(Constants.PROFILES_DIR).resolve("$id.yaml") } \ No newline at end of file