From c5fd7ab0db8b80f596846659729aa75cdf0de923 Mon Sep 17 00:00:00 2001
From: Jing <42014615+jing332@users.noreply.github.com>
Date: Thu, 11 Jan 2024 10:27:13 +0800
Subject: [PATCH] action
---
.gitignore | 2 +
.idea/kotlinc.xml | 2 +-
app/build.gradle | 25 ++-
.../alistandroid/data/entities/ServerLog.kt | 23 ++-
.../jing332/alistandroid/model/alist/AList.kt | 164 +++++++++++++-----
.../alistandroid/service/AlistService.kt | 41 ++++-
.../alistandroid/ui/nav/alist/AListScreen.kt | 15 +-
.../jing332/alistandroid/util/StringUtils.kt | 5 +
build.gradle | 11 +-
9 files changed, 205 insertions(+), 83 deletions(-)
diff --git a/.gitignore b/.gitignore
index 3beaf00..57cf795 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
# Project exclude paths
+/.idea/
/.gradle/
/app/build/
/build/
@@ -16,4 +17,5 @@ alist-main
*.tgz
*.jar
*.zip
+*.so
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index fdf8d99..ae3f30a 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index ed71671..17eeb5e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -45,9 +45,6 @@ android {
buildConfig true
}
- composeOptions {
- kotlinCompilerExtensionVersion = "1.5.1"
- }
signingConfigs {
release {
storeFile file(pro["KEY_PATH"])
@@ -100,7 +97,9 @@ android {
universalApk true
}
}
-
+ composeOptions {
+ kotlinCompilerExtensionVersion = "1.5.7"
+ }
compileOptions {
// coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_17
@@ -141,11 +140,11 @@ dependencies {
implementation("com.github.jeziellago:compose-markdown:0.3.4")
implementation("androidx.documentfile:documentfile:1.0.1")
- implementation("androidx.core:core-ktx:1.10.1")
+ implementation("androidx.core:core-ktx:1.12.0")
implementation("io.coil-kt:coil-compose:2.4.0")
implementation("com.charleskorn.kaml:kaml:0.55.0")
- implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
+ implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
// Room
implementation("androidx.room:room-runtime:$room_version")
@@ -156,12 +155,12 @@ dependencies {
// IO & NET
implementation 'com.squareup.okio:okio:3.3.0'
implementation 'com.squareup.okhttp3:okhttp:4.11.0'
- implementation 'com.github.liangjingkanji:Net:3.5.8'
+ implementation 'com.github.liangjingkanji:Net:3.6.4'
- implementation("com.google.android.exoplayer:exoplayer-core:2.19.0")
+ implementation("com.google.android.exoplayer:exoplayer-core:2.19.1")
implementation("org.apache.commons:commons-lang3:3.12.0")
- implementation("com.github.FunnySaltyFish.ComposeDataSaver:data-saver:v1.1.6")
+ implementation("com.github.FunnySaltyFish.ComposeDataSaver:data-saver:v1.1.8")
implementation("com.louiscad.splitties:splitties-systemservices:3.0.0")
implementation("androidx.localbroadcastmanager:localbroadcastmanager:1.1.0")
@@ -171,12 +170,12 @@ dependencies {
implementation("androidx.constraintlayout:constraintlayout-compose:1.0.1")
- implementation("androidx.navigation:navigation-compose:2.7.0")
+ implementation("androidx.navigation:navigation-compose:2.7.6")
- implementation 'androidx.activity:activity-compose:1.7.2'
- implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1'
+ implementation 'androidx.activity:activity-compose:1.8.2'
+ implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2'
// def composeBom = platform('androidx.compose:compose-bom:2023.06.01')
- def composeBom = platform("dev.chrisbanes.compose:compose-bom:2023.07.00-alpha02")
+ def composeBom = platform("dev.chrisbanes.compose:compose-bom:2023.12.00-alpha04")
implementation composeBom
androidTestImplementation composeBom
diff --git a/app/src/main/java/com/github/jing332/alistandroid/data/entities/ServerLog.kt b/app/src/main/java/com/github/jing332/alistandroid/data/entities/ServerLog.kt
index ca3a6fa..d9ac640 100644
--- a/app/src/main/java/com/github/jing332/alistandroid/data/entities/ServerLog.kt
+++ b/app/src/main/java/com/github/jing332/alistandroid/data/entities/ServerLog.kt
@@ -12,4 +12,25 @@ data class ServerLog(
@LogLevel val level: Int,
val message: String,
val description: String? = null,
-)
\ No newline at end of file
+) {
+ companion object {
+
+ @Suppress("RegExpRedundantEscape")
+ fun String.evalLog(): ServerLog? {
+ val logPattern = """(\w+)\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] (.+)""".toRegex()
+ val result = logPattern.find(this)
+ if (result != null) {
+ val (level, time, msg) = result.destructured
+ val l = when (level[0].toString()) {
+ "D" -> LogLevel.DEBUG
+ "I" -> LogLevel.INFO
+ "W" -> LogLevel.WARN
+ "E" -> LogLevel.ERROR
+ else -> LogLevel.INFO
+ }
+ return ServerLog(level = l, message = msg, description = time)
+ }
+ return null
+ }
+ }
+}
diff --git a/app/src/main/java/com/github/jing332/alistandroid/model/alist/AList.kt b/app/src/main/java/com/github/jing332/alistandroid/model/alist/AList.kt
index 452af53..d0558cb 100644
--- a/app/src/main/java/com/github/jing332/alistandroid/model/alist/AList.kt
+++ b/app/src/main/java/com/github/jing332/alistandroid/model/alist/AList.kt
@@ -1,21 +1,27 @@
package com.github.jing332.alistandroid.model.alist
-import alistlib.Alistlib
-import alistlib.Event
+import android.annotation.SuppressLint
import android.content.Intent
import android.util.Log
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.github.jing332.alistandroid.R
import com.github.jing332.alistandroid.app
-import com.github.jing332.alistandroid.constant.AppConst
import com.github.jing332.alistandroid.constant.LogLevel
import com.github.jing332.alistandroid.data.appDb
import com.github.jing332.alistandroid.data.entities.ServerLog
+import com.github.jing332.alistandroid.data.entities.ServerLog.Companion.evalLog
import com.github.jing332.alistandroid.service.AlistService
+import com.github.jing332.alistandroid.util.FileUtils.readAllText
+import com.github.jing332.alistandroid.util.StringUtils.removeAnsiCodes
import com.github.jing332.alistandroid.util.ToastUtils.longToast
-import kotlinx.serialization.ExperimentalSerializationApi
-import kotlinx.serialization.json.decodeFromStream
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.isActive
+import kotlinx.coroutines.launch
import java.io.File
+import java.io.IOException
+import kotlin.coroutines.coroutineContext
object AList {
const val ACTION_STATUS_CHANGED =
@@ -25,6 +31,10 @@ object AList {
const val TYPE_HTTPS = "https"
const val TYPE_UNIX = "unix"
+ private val execPath by lazy {
+ context.applicationInfo.nativeLibraryDir + File.separator + "libalist.so"
+ }
+
val context = app
val dataPath: String
@@ -37,37 +47,15 @@ object AList {
* 是否有服务正在运行
*/
val hasRunning: Boolean
- get() = when {
- Alistlib.isRunning(TYPE_HTTP) -> true
- Alistlib.isRunning(TYPE_HTTPS) -> true
- Alistlib.isRunning(TYPE_UNIX) -> true
- else -> false
- }
+ get() = false
fun init() {
- Alistlib.setConfigData(dataPath)
+// Alistlib.setConfigData(dataPath)
// Alistlib.setConfigDebug(BuildConfig.DEBUG)
- Alistlib.setConfigLogStd(true)
-
- Alistlib.init(object : Event {
- override fun onShutdown(type: String) {
- notifyStatusChanged()
- }
-
- override fun onStartError(type: String, msg: String) {
- appDb.serverLogDao.insert(
- ServerLog(
- level = LogLevel.ERROR,
- message = "${type}: $msg"
- )
- )
- notifyStatusChanged()
- }
+// Alistlib.setConfigLogStd(true)
- }) { level, msg ->
- Log.i(AlistService.TAG, "level=${level}, msg=$msg")
- appDb.serverLogDao.insert(ServerLog(level = level.toInt(), message = msg))
- }
+// Log.i(AlistService.TAG, "level=${level}, msg=$msg")
+// appDb.serverLogDao.insert(ServerLog(level = level.toInt(), message = msg))
}
fun setAdminPassword(pwd: String) {
@@ -75,33 +63,117 @@ object AList {
init()
}
- Alistlib.setAdminPassword(pwd)
+ val log = execWithParams(
+ redirect = true,
+ params = arrayOf("admin", "set", pwd, "--data", dataPath)
+ ).inputStream.readAllText()
+ appDb.serverLogDao.insert(ServerLog(level = LogLevel.INFO, message = log.removeAnsiCodes()))
}
- @Suppress("DEPRECATION")
- private fun notifyStatusChanged() {
- LocalBroadcastManager.getInstance(context)
- .sendBroadcast(Intent(ACTION_STATUS_CHANGED))
- }
fun shutdown(timeout: Long = 5000L) {
runCatching {
- Alistlib.shutdown(timeout)
+ mProcess?.destroy()
}.onFailure {
context.longToast(R.string.server_shutdown_failed, it.toString())
}
}
+ private var mProcess: Process? = null
+
+ private suspend fun errorLogWatcher(onNewLine: (String) -> Unit) {
+ mProcess?.apply {
+ errorStream.bufferedReader().use {
+ while (coroutineContext.isActive) {
+ val line = it.readLine() ?: break
+ Log.d(AlistService.TAG, "Process errorStream: $line")
+ onNewLine(line)
+ }
+ }
+ }
+ }
+
+ private suspend fun logWatcher(onNewLine: (String) -> Unit) {
+ mProcess?.apply {
+ inputStream.bufferedReader().use {
+ while (coroutineContext.isActive) {
+ val line = it.readLine() ?: break
+ Log.d(AlistService.TAG, "Process inputStream: $line")
+ onNewLine(line)
+ }
+ }
+ }
+ }
+
+ private val mScope = CoroutineScope(Dispatchers.IO + Job())
+ private fun initOutput() {
+ val dao = appDb.serverLogDao
+ mScope.launch {
+ runCatching {
+ logWatcher { msg ->
+ msg.removeAnsiCodes().evalLog()?.let {
+ dao.insert(
+ ServerLog(
+ level = it.level,
+ message = it.message
+ )
+ )
+ return@logWatcher
+ }
+
+ dao.insert(
+ ServerLog(
+ level = if (msg.startsWith("fail")) LogLevel.ERROR else LogLevel.INFO,
+ message = msg
+ )
+ )
- fun startup() {
- if (Alistlib.isRunning("")) {
- context.longToast("服务已在运行中")
- return
+ }
+ }.onFailure {
+ it.printStackTrace()
+ }
}
+ mScope.launch {
+ runCatching {
+ errorLogWatcher { msg ->
+ val log = msg.removeAnsiCodes().evalLog() ?: return@errorLogWatcher
+ dao.insert(
+ ServerLog(
+ level = log.level,
+ message = log.message,
+// description = log.time + "\n" + log.code
+ )
+ )
+ }
+ }.onFailure {
+ it.printStackTrace()
+ }
+ }
+ }
+
+
+ @SuppressLint("SdCardPath")
+ fun startup(
+ dataFolder: String = context.getExternalFilesDir("data")?.absolutePath
+ ?: "/data/data/${context.packageName}/files/data"
+ ): Int {
appDb.serverLogDao.deleteAll()
+ mProcess =
+ execWithParams(params = arrayOf("server", "--data", dataFolder))
+ initOutput()
+
+ return mProcess!!.waitFor()
+ }
+
- init()
- Alistlib.start()
- notifyStatusChanged()
+ private fun execWithParams(
+ redirect: Boolean = false,
+ vararg params: String
+ ): Process {
+ val cmdline = arrayOfNulls(params.size + 1)
+ cmdline[0] = execPath
+ System.arraycopy(params, 0, cmdline, 1, params.size)
+ return ProcessBuilder(*cmdline).redirectErrorStream(redirect).start()
+ ?: throw IOException("Process is null!")
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/github/jing332/alistandroid/service/AlistService.kt b/app/src/main/java/com/github/jing332/alistandroid/service/AlistService.kt
index 7f41ded..ac70d40 100644
--- a/app/src/main/java/com/github/jing332/alistandroid/service/AlistService.kt
+++ b/app/src/main/java/com/github/jing332/alistandroid/service/AlistService.kt
@@ -1,6 +1,5 @@
package com.github.jing332.alistandroid.service
-import alistlib.Alistlib
import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationChannel
@@ -15,6 +14,8 @@ import android.os.Build
import android.os.IBinder
import android.os.PowerManager
import androidx.core.content.ContextCompat
+import androidx.localbroadcastmanager.content.LocalBroadcastManager
+import com.drake.net.utils.withMain
import com.github.jing332.alistandroid.R
import com.github.jing332.alistandroid.config.AppConfig
import com.github.jing332.alistandroid.constant.AppConst
@@ -23,9 +24,12 @@ import com.github.jing332.alistandroid.model.alist.AListConfigManager
import com.github.jing332.alistandroid.ui.MainActivity
import com.github.jing332.alistandroid.ui.theme.androidColor
import com.github.jing332.alistandroid.util.ClipboardUtils
+import com.github.jing332.alistandroid.util.ToastUtils.longToast
import com.github.jing332.alistandroid.util.ToastUtils.toast
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
import splitties.systemservices.powerManager
class AlistService : Service() {
@@ -37,8 +41,13 @@ class AlistService : Service() {
const val ACTION_COPY_ADDRESS =
"com.github.jing332.alistandroid.service.AlistService.ACTION_COPY_ADDRESS"
+ const val ACTION_STATUS_CHANGED =
+ "com.github.jing332.alistandroid.service.AlistService.ACTION_STATUS_CHANGED"
+
const val NOTIFICATION_CHAN_ID = "alist_server"
const val FOREGROUND_ID = 5224
+
+ var isRunning: Boolean = false
}
private val mScope = CoroutineScope(Job())
@@ -48,6 +57,12 @@ class AlistService : Service() {
override fun onBind(p0: Intent?): IBinder? = null
+ @Suppress("DEPRECATION")
+ private fun notifyStatusChanged() {
+ LocalBroadcastManager.getInstance(this)
+ .sendBroadcast(Intent(ACTION_STATUS_CHANGED))
+ }
+
@SuppressLint("WakelockTimeout")
override fun onCreate() {
super.onCreate()
@@ -73,8 +88,6 @@ class AlistService : Service() {
ContextCompat.RECEIVER_EXPORTED
)
initNotification()
-
- AList.startup();
}
@@ -89,14 +102,23 @@ class AlistService : Service() {
AppConst.localBroadcast.unregisterReceiver(mReceiver)
unregisterReceiver(mNotificationReceiver)
-
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent?.action == ACTION_SHUTDOWN) {
AList.shutdown()
- } else if (!AList.hasRunning) {
- AList.startup()
+ } else {
+ isRunning = true
+ notifyStatusChanged()
+ mScope.launch(Dispatchers.IO) {
+ val ret = AList.startup()
+ isRunning = false
+ withMain {
+ if (ret != 0) toast("code: $ret")
+ stopSelf()
+ notifyStatusChanged()
+ }
+ }
}
return super.onStartCommand(intent, flags, startId)
@@ -116,8 +138,9 @@ class AlistService : Service() {
private fun httpAddress(): String {
val cfg = AListConfigManager.config()
- val ip = Alistlib.getOutboundIPString()
- return "http://${ip}:${cfg.scheme.httpPort}"
+// val ip = Alistlib.getOutboundIPString()
+// return "http://${ip}:${cfg.scheme.httpPort}"
+ return "none"
}
@Suppress("DEPRECATION")
@@ -183,7 +206,7 @@ class AlistService : Service() {
.setSmallIcon(smallIconRes)
.setContentIntent(pendingIntent)
.addAction(0, getString(R.string.shutdown), shutdownAction)
- .addAction(0, getString(R.string.copy_address), copyAddressPendingIntent)
+// .addAction(0, getString(R.string.copy_address), copyAddressPendingIntent)
.build()
// 前台服务
diff --git a/app/src/main/java/com/github/jing332/alistandroid/ui/nav/alist/AListScreen.kt b/app/src/main/java/com/github/jing332/alistandroid/ui/nav/alist/AListScreen.kt
index d462427..e750fbb 100644
--- a/app/src/main/java/com/github/jing332/alistandroid/ui/nav/alist/AListScreen.kt
+++ b/app/src/main/java/com/github/jing332/alistandroid/ui/nav/alist/AListScreen.kt
@@ -17,6 +17,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.Send
import androidx.compose.material.icons.filled.AddBusiness
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.Password
@@ -66,20 +67,18 @@ fun AListScreen() {
val context = LocalContext.current
val mainVM = LocalMainViewModel.current
val view = LocalView.current
- var alistRunning by remember { mutableStateOf(AList.hasRunning) }
+ var alistRunning by remember { mutableStateOf(AlistService.isRunning) }
- LocalBroadcastReceiver(intentFilter = IntentFilter(AList.ACTION_STATUS_CHANGED)) {
- println(it?.action)
- if (it?.action == AList.ACTION_STATUS_CHANGED) {
- alistRunning = AList.hasRunning
- }
+ LocalBroadcastReceiver(intentFilter = IntentFilter(AlistService.ACTION_STATUS_CHANGED)) {
+ if (it?.action == AList.ACTION_STATUS_CHANGED)
+ alistRunning = AlistService.isRunning
}
fun switch() {
context.startService(Intent(context, AlistService::class.java).apply {
action = if (alistRunning) AlistService.ACTION_SHUTDOWN else ""
})
-// alistRunning = !alistRunning
+ alistRunning = !alistRunning
}
var showPwdDialog by remember { mutableStateOf(false) }
@@ -225,7 +224,7 @@ fun AListScreen() {
@Composable
fun SwitchFloatingButton(modifier: Modifier, switch: Boolean, onSwitchChange: (Boolean) -> Unit) {
val targetIcon =
- if (switch) Icons.Filled.Stop else Icons.Filled.Send
+ if (switch) Icons.Filled.Stop else Icons.AutoMirrored.Filled.Send
val rotationAngle by animateFloatAsState(targetValue = if (switch) 360f else 0f, label = "")
val color =
diff --git a/app/src/main/java/com/github/jing332/alistandroid/util/StringUtils.kt b/app/src/main/java/com/github/jing332/alistandroid/util/StringUtils.kt
index 63440f8..eb18b7f 100644
--- a/app/src/main/java/com/github/jing332/alistandroid/util/StringUtils.kt
+++ b/app/src/main/java/com/github/jing332/alistandroid/util/StringUtils.kt
@@ -21,4 +21,9 @@ object StringUtils {
fun String.toNumberInt(): Int {
return this.replace(Regex("[^0-9]"), "").toIntOrNull() ?: 0
}
+
+ fun String.removeAnsiCodes(): String {
+ val ansiRegex = Regex("\\x1B\\[[0-9;]*[m|K]")
+ return this.replace(ansiRegex, "")
+ }
}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 1cb8735..4621b76 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,10 +1,11 @@
buildscript {
ext {
- kotlin_version = '1.9.0'
- agp_version = '8.2.0-alpha16'
- room_version = '2.5.2'
- ksp_version = '1.9.0-1.0.13'
- about_lib_version = "10.8.3"
+ kotlin_version = '1.9.21'
+ agp_version = '8.2.0'
+ compose_compiler = "1.5.7"
+ room_version = '2.6.1'
+ ksp_version = '1.9.21-1.0.16'
+ about_lib_version = "10.9.2"
}
}