[WIP] refactor service

This commit is contained in:
Kr328 committed 2020-01-20 23:31:14 +08:00
1 parent 419c6301dd
commit 4ee8679d29
8 files changed
+141 -17

No files matched your search

@@ -1,5 +1,6 @@
package com.github.kr328.clash
object Constants {
const val CLASH_DIR = "clash"
const val PROFILES_DIR = "profiles"
}
@@ -10,6 +10,7 @@ import com.github.kr328.clash.core.model.ProxyGroup
import com.github.kr328.clash.core.transact.DoneCallbackImpl
import com.github.kr328.clash.core.transact.ProxyCollectionImpl
import com.github.kr328.clash.core.transact.ProxyGroupCollectionImpl
import java.io.File
import java.io.InputStream
import java.lang.IllegalStateException
import java.util.concurrent.BrokenBarrierException
@@ -55,27 +56,27 @@ object Clash{
Bridge.stopTunDevice()
}
fun loadProfile(path: String, baseDir: String): CompletableFuture<Unit> {
fun loadProfile(path: File, baseDir: File): CompletableFuture<Unit> {
return DoneCallbackImpl().apply {
Bridge.loadProfileFile(path, baseDir, this)
Bridge.loadProfileFile(path.absolutePath, baseDir.absolutePath, this)
}
}
fun downloadProfile(url: String, output: String, baseDir: String): CompletableFuture<Unit> {
fun downloadProfile(url: String, output: File, baseDir: File): CompletableFuture<Unit> {
return DoneCallbackImpl().apply {
Bridge.downloadProfileAndCheck(url, output, baseDir, this)
Bridge.downloadProfileAndCheck(url, output.absolutePath, baseDir.absolutePath, this)
}
}
fun saveProfile(data: ByteArray, output: String, baseDir: String): CompletableFuture<Unit> {
fun saveProfile(data: ByteArray, output: File, baseDir: File): CompletableFuture<Unit> {
return DoneCallbackImpl().apply {
Bridge.saveProfileAndCheck(data, output, baseDir, this)
Bridge.saveProfileAndCheck(data, output.absolutePath, baseDir.absolutePath, this)
}
}
fun moveProfile(source: String, target: String, baseDir: String): CompletableFuture<Unit> {
fun moveProfile(source: File, target: File, baseDir: File): CompletableFuture<Unit> {
return DoneCallbackImpl().apply {
Bridge.moveProfileAndCheck(source, target, baseDir, this)
Bridge.moveProfileAndCheck(source.absolutePath, target.absolutePath, baseDir.absolutePath, this)
}
}
@@ -1,9 +1,10 @@
package com.github.kr328.clash.service;
import com.github.kr328.clash.service.ipc.IPCParcelables;
import com.github.kr328.clash.service.data.ClashProfileEntity;
interface IClashProfileManager {
ParcelableCompletedFuture addProfile(String url);
ParcelableCompletedFuture addProfile(String name, int type, String uri);
ParcelableCompletedFuture updateProfile(int id);
ParcelableCompletedFuture queryAllProfiles();
ClashProfileEntity[] queryAllProfiles();
}
@@ -1,24 +1,114 @@
package com.github.kr328.clash.service
import android.content.Context
import android.net.Uri
import com.github.kr328.clash.core.Clash
import com.github.kr328.clash.service.data.ClashDatabase
import com.github.kr328.clash.service.data.ClashProfileEntity
import com.github.kr328.clash.service.ipc.ParcelableCompletedFuture
import com.github.kr328.clash.service.util.DefaultThreadPool
import com.github.kr328.clash.service.util.FileUtils
class ClashProfileManager(private val context: Context, private val database: ClashDatabase) :
IClashProfileManager.Stub() {
private val clashDir = context.filesDir.resolve(Constants.CLASH_DIR)
private val profileDir = context.filesDir.resolve(Constants.PROFILES_DIR)
class ClashProfileManager(private val database: ClashDatabase): IClashProfileManager.Stub() {
override fun updateProfile(id: Int): ParcelableCompletedFuture {
val entity = database.openClashProfileDao().queryProfileById(id)
require(entity != null && entity.type == ClashProfileEntity.Type.URL)
require(entity != null && (entity.type == ClashProfileEntity.Type.URL ||
entity.type == ClashProfileEntity.Type.FILE))
val result = ParcelableCompletedFuture()
downloadProfile(Uri.parse(entity.uri), entity.file, entity.base,
onSuccess = {
result.complete(null)
database.openClashProfileDao().touchProfile(id)
},
onFailure = {
result.completeExceptionally(it)
})
return result
}
override fun queryAllProfiles(): ParcelableCompletedFuture {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
override fun queryAllProfiles(): Array<ClashProfileEntity> {
return database.openClashProfileDao().queryProfiles()
}
override fun addProfile(url: String?): ParcelableCompletedFuture {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
override fun addProfile(name: String, type: Int, uri: String?): ParcelableCompletedFuture {
require(uri != null && (uri.startsWith("http") || uri.startsWith("content")))
val result = ParcelableCompletedFuture()
val fileName = FileUtils.generateRandomFileName(profileDir, ".yaml")
val baseDirName = FileUtils.generateRandomFileName(clashDir)
downloadProfile(Uri.parse(uri), fileName, baseDirName,
onSuccess = {
database.openClashProfileDao().addProfile(
ClashProfileEntity(
name,
ClashProfileEntity.intToType(type),
uri,
fileName,
baseDirName,
false,
System.currentTimeMillis()
)
)
result.complete(null)
},
onFailure = {
result.completeExceptionally(it)
})
return result
}
private fun downloadProfile(
uri: Uri?,
fileName: String,
baseDirName: String,
onSuccess: () -> Unit,
onFailure: (Throwable) -> Unit
) {
require(uri != null && uri != Uri.EMPTY)
if (uri.scheme == "http" || uri.scheme == "https") {
Clash.downloadProfile(
uri.toString(),
profileDir.resolve(fileName),
clashDir.resolve(baseDirName)
)
.whenComplete { _, u ->
if (u != null)
onFailure(u)
else
onSuccess()
}
} else {
DefaultThreadPool.submit {
try {
val input = context.contentResolver.openInputStream(uri)
?: throw NullPointerException("Unable to open profile")
input.use {
Clash.saveProfile(
it.readBytes(),
profileDir.resolve(fileName),
clashDir.resolve(baseDirName)
)
}
onSuccess()
} catch (e: Exception) {
onFailure(e)
}
}
}
}
}
@@ -3,4 +3,7 @@ package com.github.kr328.clash.service
object Constants {
const val CLASH_PROCESS_BROADCAST_ACTION = "com.github.kr328.clash.ClashService.ClashProcessEvent"
const val CLASH_RELOAD_BROADCAST_ACTION = "com.github.kr328.clash.ClashService.ProfileReloadEvent"
const val CLASH_DIR = "clash"
const val PROFILES_DIR = "profiles"
}
@@ -5,8 +5,9 @@ import android.os.Parcelable
import android.os.RemoteException
import java.util.concurrent.CompletableFuture
class ParcelableCompletedFuture(private val pipe: ParcelablePipe = ParcelablePipe()) :
class ParcelableCompletedFuture private constructor(private val pipe: ParcelablePipe) :
CompletableFuture<Parcelable?>(), Parcelable {
constructor(): this(ParcelablePipe())
constructor(parcel: Parcel) : this(
parcel.readParcelable<ParcelablePipe>(
ParcelableCompletedFuture::class.java.classLoader
@@ -0,0 +1,6 @@
package com.github.kr328.clash.service.util
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
val DefaultThreadPool: ExecutorService = Executors.newCachedThreadPool()
@@ -0,0 +1,21 @@
package com.github.kr328.clash.service.util
import java.io.File
import java.security.SecureRandom
import kotlin.math.absoluteValue
object FileUtils {
private val random = SecureRandom()
fun generateRandomFileName(dir: File, suffix: String = ""): String {
dir.mkdirs()
var fileName: String
do {
fileName = random.nextLong().absoluteValue.toString() + suffix
} while (dir.resolve(fileName).exists())
return fileName
}
}