From a2a5baa3ed56c727f35bb7b4954cae67b2633d5f Mon Sep 17 00:00:00 2001 From: Jing <42014615+jing332@users.noreply.github.com> Date: Mon, 15 Jan 2024 19:05:48 +0800 Subject: [PATCH] admin password --- android/app/build.gradle | 2 +- android/app/src/main/AndroidManifest.xml | 5 +- .../github/jing332/pigeon/GeneratedApi.java | 38 +++++++++++++ .../jing332/alistflutter/AListService.kt | 44 ++++++++------- .../jing332/alistflutter/MainActivity.kt | 19 ++++--- .../alistflutter/bridge/AndroidBridge.kt | 2 +- .../jing332/alistflutter/model/alist/AList.kt | 33 ++++++------ .../jing332/alistflutter/utils/MyTools.kt | 3 +- android/settings.gradle | 1 + android/utils/build.gradle | 35 ++++++++++++ android/utils/build.gradle.kts | 54 ------------------- lib/generated_api.dart | 28 ++++++++++ lib/main.dart | 3 ++ lib/pages/alist.dart | 3 ++ lib/pages/pwd_edit_dialog.dart | 15 +++--- pigeons/pigeon.dart | 5 ++ 16 files changed, 175 insertions(+), 115 deletions(-) create mode 100644 android/utils/build.gradle delete mode 100644 android/utils/build.gradle.kts diff --git a/android/app/build.gradle b/android/app/build.gradle index e392095..d0dccaa 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -130,7 +130,7 @@ flutter { //} dependencies { // compileOnly files("$flutterRoot/bin/cache/artifacts/engine/android-arm/flutter.jar") - + implementation project(":utils") //noinspection GradleDependency implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0' diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index cedbe6e..4792a71 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -39,7 +39,10 @@ tools:ignore="UnusedAttribute"> + android:exported="true" + android:launchMode="singleTask" + android:taskAffinity="alist.switch" + android:theme="@android:style/Theme.NoDisplay" /> getCodec() { + return new StandardMessageCodec(); + } + public void getLocalIpAddress(@NonNull Result result) { + final String channelName = "dev.flutter.pigeon.alist_flutter.Flutter.getLocalIpAddress"; + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, channelName, getCodec()); + channel.send( + null, + channelReply -> { + if (channelReply instanceof List) { + List listReply = (List) channelReply; + if (listReply.size() > 1) { + result.error(new FlutterError((String) listReply.get(0), (String) listReply.get(1), (String) listReply.get(2))); + } else if (listReply.get(0) == null) { + result.error(new FlutterError("null-error", "Flutter api returned null value for non-null return value.", "")); + } else { + @SuppressWarnings("ConstantConditions") + String output = (String) listReply.get(0); + result.success(output); + } + } else { + result.error(createConnectionError(channelName)); + } + }); + } + } + /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */ public static class Event { private final @NonNull BinaryMessenger binaryMessenger; diff --git a/android/app/src/main/kotlin/com/github/jing332/alistflutter/AListService.kt b/android/app/src/main/kotlin/com/github/jing332/alistflutter/AListService.kt index e4f8d22..db08db5 100644 --- a/android/app/src/main/kotlin/com/github/jing332/alistflutter/AListService.kt +++ b/android/app/src/main/kotlin/com/github/jing332/alistflutter/AListService.kt @@ -14,17 +14,16 @@ import android.os.Build import android.os.IBinder import android.os.PowerManager import androidx.localbroadcastmanager.content.LocalBroadcastManager -import com.github.jing332.alistflutter.model.alist.AList -import com.github.jing332.alistflutter.BridgeUtils.invokeMethodSync import com.github.jing332.alistflutter.config.AppConfig +import com.github.jing332.alistflutter.model.alist.AList import com.github.jing332.alistflutter.utils.AndroidUtils.registerReceiverCompat import com.github.jing332.alistflutter.utils.ClipboardUtils import com.github.jing332.alistflutter.utils.ToastUtils.toast +import com.github.jing332.utils.NativeLib import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import splitties.systemservices.powerManager @@ -50,6 +49,7 @@ class AListService : Service() { private val mNotificationReceiver = NotificationActionReceiver() private val mReceiver = MyReceiver() private var mWakeLock: PowerManager.WakeLock? = null + private var mLocalAddress: String = "" override fun onBind(p0: Intent?): IBinder? = null @@ -62,7 +62,7 @@ class AListService : Service() { override fun onCreate() { super.onCreate() - initNotification() + initOrUpdateNotification() if (AppConfig.isWakeLockEnabled) { mWakeLock = powerManager.newWakeLock( @@ -73,7 +73,10 @@ class AListService : Service() { } LocalBroadcastManager.getInstance(this) - .registerReceiver(mReceiver, IntentFilter(ACTION_STATUS_CHANGED)) + .registerReceiver( + mReceiver, + IntentFilter(ACTION_STATUS_CHANGED) + ) registerReceiverCompat( mNotificationReceiver, ACTION_SHUTDOWN, @@ -117,28 +120,24 @@ class AListService : Service() { @Suppress("DEPRECATION") inner class MyReceiver : BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { - if (intent?.action == ACTION_STATUS_CHANGED) { - if (!isRunning) { - stopForeground(true) - stopSelf() + override fun onReceive(context: Context?, intent: Intent) { + when (intent.action) { + ACTION_STATUS_CHANGED -> { + if (!isRunning) { + stopForeground(true) + stopSelf() + } } } + } } - private fun httpAddress(): String = runBlocking { - val channel = BridgeUtils.getMethodChannel(this@AListService) + private fun localAddress(): String = NativeLib.getLocalIp() -// return@runBlocking when (val ret = channel.invokeMethodSync("getHttpAddress", null)) { -// is BridgeUtils.MethodChannelResult.Success -> ret.result as String -// else -> "" -// } - return@runBlocking "" - } @Suppress("DEPRECATION") - private fun initNotification() { + private fun initOrUpdateNotification() { // Android 12(S)+ 必须指定PendingIntent.FLAG_ val pendingIntentFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) PendingIntent.FLAG_IMMUTABLE @@ -196,24 +195,23 @@ class AListService : Service() { val notification = builder // .setColor(color) .setContentTitle(getString(R.string.alist_server_running)) - .setContentText(httpAddress()) + .setContentText(localAddress()) .setSmallIcon(smallIconRes) .setContentIntent(pendingIntent) .addAction(0, getString(R.string.shutdown), shutdownAction) .addAction(0, getString(R.string.copy_address), copyAddressPendingIntent) + .build() - // 前台服务 startForeground(FOREGROUND_ID, notification) } inner class NotificationActionReceiver : BroadcastReceiver() { override fun onReceive(ctx: Context?, intent: Intent?) { when (intent?.action) { -// ACTION_SHUTDOWN -> /*AList.shutdown()*/ ACTION_COPY_ADDRESS -> { - ClipboardUtils.copyText("AList", httpAddress()) + ClipboardUtils.copyText("AList", localAddress()) toast(R.string.address_copied) } } diff --git a/android/app/src/main/kotlin/com/github/jing332/alistflutter/MainActivity.kt b/android/app/src/main/kotlin/com/github/jing332/alistflutter/MainActivity.kt index 52d2062..2f3e884 100644 --- a/android/app/src/main/kotlin/com/github/jing332/alistflutter/MainActivity.kt +++ b/android/app/src/main/kotlin/com/github/jing332/alistflutter/MainActivity.kt @@ -70,18 +70,21 @@ class MainActivity : FlutterActivity() { inner class MyReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { - if (intent.action == AListService.ACTION_STATUS_CHANGED) { - Log.e(TAG, "onReceive: ${AListService.isRunning}") - mEvent?.onServiceStatusChanged(AListService.isRunning, object : VoidResult { - override fun success() { + when (intent.action) { + AListService.ACTION_STATUS_CHANGED -> { + Log.d(TAG, "onReceive: ACTION_STATUS_CHANGED") + + mEvent?.onServiceStatusChanged(AListService.isRunning, object : VoidResult { + override fun success() {} + override fun error(error: Throwable) { + } + }) + } - } - override fun error(error: Throwable) { - } - }) } + } } diff --git a/android/app/src/main/kotlin/com/github/jing332/alistflutter/bridge/AndroidBridge.kt b/android/app/src/main/kotlin/com/github/jing332/alistflutter/bridge/AndroidBridge.kt index 4119d05..b4da51e 100644 --- a/android/app/src/main/kotlin/com/github/jing332/alistflutter/bridge/AndroidBridge.kt +++ b/android/app/src/main/kotlin/com/github/jing332/alistflutter/bridge/AndroidBridge.kt @@ -18,7 +18,7 @@ class AndroidBridge(private val context: Context) : GeneratedApi.Android { context, context.getString(R.string.app_switch), "alist_flutter_switch", - R.drawable.ic_launcher_foreground, + R.drawable.alist_switch, Intent(context, SwitchServerActivity::class.java) ) } diff --git a/android/app/src/main/kotlin/com/github/jing332/alistflutter/model/alist/AList.kt b/android/app/src/main/kotlin/com/github/jing332/alistflutter/model/alist/AList.kt index 735c4c4..8b184a6 100644 --- a/android/app/src/main/kotlin/com/github/jing332/alistflutter/model/alist/AList.kt +++ b/android/app/src/main/kotlin/com/github/jing332/alistflutter/model/alist/AList.kt @@ -1,12 +1,10 @@ package com.github.jing332.alistflutter.model.alist import android.annotation.SuppressLint -import android.util.Log import com.github.jing332.alistflutter.R import com.github.jing332.alistflutter.app import com.github.jing332.alistflutter.constant.LogLevel import com.github.jing332.alistflutter.data.entities.ServerLog.Companion.evalLog -import com.github.jing332.alistflutter.utils.FileUtils.readAllText import com.github.jing332.alistflutter.utils.StringUtils.removeAnsiCodes import com.github.jing332.alistflutter.utils.ToastUtils.longToast import kotlinx.coroutines.CoroutineScope @@ -36,11 +34,12 @@ object AList { fun setAdminPassword(pwd: String) { - val log = execWithParams( + execWithParams( redirect = true, params = arrayOf("admin", "set", pwd, "--data", dataPath) - ).inputStream.readAllText() -// appDb.serverLogDao.insert(ServerLog(level = LogLevel.INFO, message = log.removeAnsiCodes())) + ).inputStream.logWatcher { + handleLog(it) + } } @@ -54,14 +53,21 @@ object AList { private var mProcess: Process? = null - private suspend fun InputStream.logWatcher(onNewLine: (String) -> Unit) { + private fun handleLog(log:String){ + log.removeAnsiCodes().evalLog()?.let { + Logger.log(level = it.level, time = it.time, msg = it.message) + } ?: run { + Logger.log(level = LogLevel.INFO, time = "", msg = log) + } + } + + private fun InputStream.logWatcher(onNewLine: (String) -> Unit) { bufferedReader().use { - while (coroutineContext.isActive) { + while (true) { runCatching { val line = it.readLine() ?: return@use onNewLine(line) }.onFailure { - Log.e(TAG, "logWatcher: ", it) return@use } } @@ -72,19 +78,12 @@ object AList { private val mScope = CoroutineScope(Dispatchers.IO + Job()) private fun initOutput() { - val onNewLine = { msg: String -> - msg.removeAnsiCodes().evalLog()?.let { - Logger.log(level = it.level, time = it.time, msg = it.message) - } ?: run { - Logger.log(level = LogLevel.INFO, time = "", msg = msg) - } - } mScope.launch { - mProcess?.inputStream?.logWatcher(onNewLine) + mProcess?.inputStream?.logWatcher(::handleLog) } mScope.launch { - mProcess?.errorStream?.logWatcher(onNewLine) + mProcess?.errorStream?.logWatcher(::handleLog) } } diff --git a/android/app/src/main/kotlin/com/github/jing332/alistflutter/utils/MyTools.kt b/android/app/src/main/kotlin/com/github/jing332/alistflutter/utils/MyTools.kt index c129831..4e215af 100644 --- a/android/app/src/main/kotlin/com/github/jing332/alistflutter/utils/MyTools.kt +++ b/android/app/src/main/kotlin/com/github/jing332/alistflutter/utils/MyTools.kt @@ -40,9 +40,8 @@ object MyTools { iconResId: Int, launcherIntent: Intent ) { + ctx.longToast("如失败 请手动授予权限") if (Build.VERSION.SDK_INT < 26) { /* Android8.0 */ - ctx.longToast("如失败 请手动授予权限") - val addShortcutIntent = Intent("com.android.launcher.action.INSTALL_SHORTCUT") // 不允许重复创建 addShortcutIntent.putExtra("duplicate", false) // 经测试不是根据快捷方式的名字判断重复的 diff --git a/android/settings.gradle b/android/settings.gradle index bd1b49c..43c40ae 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -27,3 +27,4 @@ plugins { } include ":app" +include ":utils" \ No newline at end of file diff --git a/android/utils/build.gradle b/android/utils/build.gradle new file mode 100644 index 0000000..fd3d7d5 --- /dev/null +++ b/android/utils/build.gradle @@ -0,0 +1,35 @@ +plugins { + id("com.android.library") + id("org.jetbrains.kotlin.android") +} + +android { + namespace "com.github.jing332.utils" + compileSdk 34 + + defaultConfig { +// minSdk + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + externalNativeBuild { + cmake { + path "src/main/cpp/CMakeLists.txt" + version "3.22.1" + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.5") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") +} \ No newline at end of file diff --git a/android/utils/build.gradle.kts b/android/utils/build.gradle.kts deleted file mode 100644 index a54ed45..0000000 --- a/android/utils/build.gradle.kts +++ /dev/null @@ -1,54 +0,0 @@ -plugins { - id("com.android.library") - id("org.jetbrains.kotlin.android") -} - -android { - namespace = "com.github.jing332.utils" - compileSdk = 34 - - defaultConfig { - minSdk = 21 - - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") - externalNativeBuild { - cmake { - cppFlags("") - } - } - } - - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) - } - } - externalNativeBuild { - cmake { - path("src/main/cpp/CMakeLists.txt") - version = "3.22.1" - } - } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = "1.8" - } -} - -dependencies { - - implementation("androidx.core:core-ktx:1.12.0") - implementation("androidx.appcompat:appcompat:1.6.1") - - testImplementation("junit:junit:4.13.2") - androidTestImplementation("androidx.test.ext:junit:1.1.5") - androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") -} \ No newline at end of file diff --git a/lib/generated_api.dart b/lib/generated_api.dart index f37793c..6553d40 100644 --- a/lib/generated_api.dart +++ b/lib/generated_api.dart @@ -412,6 +412,34 @@ class Android { } } +abstract class Flutter { + static const MessageCodec pigeonChannelCodec = StandardMessageCodec(); + + String getLocalIpAddress(); + + static void setup(Flutter? api, {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + 'dev.flutter.pigeon.alist_flutter.Flutter.getLocalIpAddress', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + __pigeon_channel.setMessageHandler(null); + } else { + __pigeon_channel.setMessageHandler((Object? message) async { + try { + final String output = api.getLocalIpAddress(); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } +} + abstract class Event { static const MessageCodec pigeonChannelCodec = StandardMessageCodec(); diff --git a/lib/main.dart b/lib/main.dart index 007d40e..fb807b2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,6 @@ +import 'dart:async'; + +import 'package:alist_flutter/generated_api.dart'; import 'package:alist_flutter/pages/alist.dart'; import 'package:alist_flutter/pages/settings.dart'; import 'package:alist_flutter/pages/web.dart'; diff --git a/lib/pages/alist.dart b/lib/pages/alist.dart index 4e9264c..b45cd02 100644 --- a/lib/pages/alist.dart +++ b/lib/pages/alist.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:alist_flutter/generated_api.dart'; import 'package:alist_flutter/pages/pwd_edit_dialog.dart'; import 'package:alist_flutter/widgets/switch_floating_action_button.dart'; @@ -29,6 +31,7 @@ class AListScreen extends StatelessWidget { showDialog( context: context, builder: (context) => PwdEditDialog(onConfirm: (pwd) { + log("pwd: $pwd"); Android().setAdminPwd(pwd); })); }, diff --git a/lib/pages/pwd_edit_dialog.dart b/lib/pages/pwd_edit_dialog.dart index a109441..0bc63a8 100644 --- a/lib/pages/pwd_edit_dialog.dart +++ b/lib/pages/pwd_edit_dialog.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:get/get.dart'; class PwdEditDialog extends StatefulWidget { final ValueChanged onConfirm; @@ -15,7 +16,6 @@ class _PwdEditDialogState extends State with SingleTickerProviderStateMixin { final TextEditingController pwdController = TextEditingController(); - @override void dispose() { pwdController.dispose(); @@ -26,11 +26,12 @@ class _PwdEditDialogState extends State Widget build(BuildContext context) { return AlertDialog( title: const Text("修改admin密码"), - content: const Column( + content: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( - decoration: InputDecoration( + controller: pwdController, + decoration: const InputDecoration( labelText: "admin密码", ), ), @@ -38,15 +39,13 @@ class _PwdEditDialogState extends State ), actions: [ TextButton( - onPressed: () { - Navigator.of(context).pop(); - widget.onConfirm(pwdController.text); - }, + onPressed: () {Get.back();}, child: const Text("取消"), ), FilledButton( onPressed: () { - Navigator.of(context).pop(); + Get.back(); + widget.onConfirm(pwdController.text); }, child: const Text("确定"), ), diff --git a/pigeons/pigeon.dart b/pigeons/pigeon.dart index 5e1468d..221eb9b 100644 --- a/pigeons/pigeon.dart +++ b/pigeons/pigeon.dart @@ -36,6 +36,11 @@ abstract class Android { void longToast(String msg); } +@FlutterApi() +abstract class Flutter { + String getLocalIpAddress(); +} + @FlutterApi() abstract class Event { void onServiceStatusChanged(bool isRunning);