From 1cffea5c1a554ad6e747a692fcbcd30ae671079d Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Tue, 5 Apr 2022 18:36:18 -0700 Subject: [PATCH] port most gomobile FlwtchWorker calls to lcg to MainActivity; sendmessage sets lastSeen time --- .../kotlin/im/cwtch/flwtch/FlwtchWorker.kt | 197 +----------------- .../kotlin/im/cwtch/flwtch/MainActivity.kt | 189 +++++++++++++++++ lib/models/profile.dart | 3 +- lib/views/messageview.dart | 3 + 4 files changed, 199 insertions(+), 193 deletions(-) diff --git a/android/app/src/main/kotlin/im/cwtch/flwtch/FlwtchWorker.kt b/android/app/src/main/kotlin/im/cwtch/flwtch/FlwtchWorker.kt index 32d0a7bd..e46e3eda 100644 --- a/android/app/src/main/kotlin/im/cwtch/flwtch/FlwtchWorker.kt +++ b/android/app/src/main/kotlin/im/cwtch/flwtch/FlwtchWorker.kt @@ -86,6 +86,7 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) : try { val evt = MainActivity.AppbusEvent(Cwtch.getAppBusEvent()) // TODO replace this notification block with the NixNotification manager in dart as it has access to contact names and also needs less working around + if (evt.EventType == "NewMessageFromPeer" || evt.EventType == "NewMessageFromGroup") { val data = JSONObject(evt.Data) val handle = data.getString("RemotePeer"); @@ -222,212 +223,26 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) : intent.putExtra("EventID", evt.EventID) LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent) } + if (evt.EventType == "Shutdown") { + Log.i(TAG, "processing shutdown event, exiting FlwtchWorker/Start()..."); + return Result.success() + } } catch (e: Exception) { Log.e(TAG, "Error in handleCwtch: " + e.toString() + " :: " + e.getStackTrace()); } } } - - "ReconnectCwtchForeground" -> { - Cwtch.reconnectCwtchForeground() - } - "CreateProfile" -> { - val nick = (a.get("nick") as? String) ?: "" - val pass = (a.get("pass") as? String) ?: "" - Cwtch.createProfile(nick, pass) - } - "LoadProfiles" -> { - val pass = (a.get("pass") as? String) ?: "" - Cwtch.loadProfiles(pass) - } - "ChangePassword" -> { - val profile = (a.get("ProfileOnion") as? String) ?: "" - val pass = (a.get("OldPass") as? String) ?: "" - val passNew = (a.get("NewPass") as? String) ?: "" - val passNew2 = (a.get("NewPassAgain") as? String) ?: "" - Cwtch.changePassword(profile, pass, passNew, passNew2) - } - "GetMessage" -> { - val profile = (a.get("ProfileOnion") as? String) ?: "" - val conversation = a.getInt("conversation").toLong() - val indexI = a.getInt("index").toLong() - Log.d(TAG, "Cwtch GetMessage " + profile + " " + conversation.toString() + " " + indexI.toString()) - return Result.success(Data.Builder().putString("result", Cwtch.getMessage(profile, conversation, indexI)).build()) - } - "GetMessageByID" -> { - val profile = (a.get("ProfileOnion") as? String) ?: "" - val conversation = a.getInt("conversation").toLong() - val id = a.getInt("id").toLong() - return Result.success(Data.Builder().putString("result", Cwtch.getMessageByID(profile, conversation, id)).build()) - } - "GetMessageByContentHash" -> { - val profile = (a.get("ProfileOnion") as? String) ?: "" - val conversation = a.getInt("conversation").toLong() - val contentHash = (a.get("contentHash") as? String) ?: "" - return Result.success(Data.Builder().putString("result", Cwtch.getMessagesByContentHash(profile, conversation, contentHash)).build()) - } - "UpdateMessageAttribute" -> { - val profile = (a.get("ProfileOnion") as? String) ?: "" - val conversation = a.getInt("conversation").toLong() - val channel = a.getInt("chanenl").toLong() - val midx = a.getInt("midx").toLong() - val key = (a.get("key") as? String) ?: "" - val value = (a.get("value") as? String) ?: "" - Cwtch.setMessageAttribute(profile, conversation, channel, midx, key, value) - } - "AcceptConversation" -> { - val profile = (a.get("ProfileOnion") as? String) ?: "" - val conversation = a.getInt("conversation").toLong() - Cwtch.acceptConversation(profile, conversation) - } - "BlockContact" -> { - val profile = (a.get("ProfileOnion") as? String) ?: "" - val conversation = a.getInt("conversation").toLong() - Cwtch.blockContact(profile, conversation) - } - "UnblockContact" -> { - val profile = (a.get("ProfileOnion") as? String) ?: "" - val conversation = a.getInt("conversation").toLong() - Cwtch.unblockContact(profile, conversation) - } - - "DownloadFile" -> { - val profile = (a.get("ProfileOnion") as? String) ?: "" - val conversation = a.getInt("conversation").toLong() - val filepath = (a.get("filepath") as? String) ?: "" - val manifestpath = (a.get("manifestpath") as? String) ?: "" - val filekey = (a.get("filekey") as? String) ?: "" - // FIXME: Prevent spurious calls by Intent - if (profile != "") { - Cwtch.downloadFile(profile, conversation, filepath, manifestpath, filekey) - } - } - "CheckDownloadStatus" -> { - val profile = (a.get("ProfileOnion") as? String) ?: "" - val fileKey = (a.get("fileKey") as? String) ?: "" - Cwtch.checkDownloadStatus(profile, fileKey) - } - "VerifyOrResumeDownload" -> { - val profile = (a.get("ProfileOnion") as? String) ?: "" - val conversation = a.getInt("conversation").toLong() - val fileKey = (a.get("fileKey") as? String) ?: "" - Cwtch.verifyOrResumeDownload(profile, conversation, fileKey) - } - "SendProfileEvent" -> { - val onion = (a.get("onion") as? String) ?: "" - val jsonEvent = (a.get("jsonEvent") as? String) ?: "" - Cwtch.sendProfileEvent(onion, jsonEvent) - } - "SendAppEvent" -> { - val jsonEvent = (a.get("jsonEvent") as? String) ?: "" - Cwtch.sendAppEvent(jsonEvent) - } - "ResetTor" -> { - Cwtch.resetTor() - } - "ImportBundle" -> { - val profile = (a.get("ProfileOnion") as? String) ?: "" - val bundle = (a.get("bundle") as? String) ?: "" - Cwtch.importBundle(profile, bundle) - } - "CreateGroup" -> { - val profile = (a.get("ProfileOnion") as? String) ?: "" - val server = (a.get("server") as? String) ?: "" - val groupName = (a.get("groupName") as? String) ?: "" - Cwtch.createGroup(profile, server, groupName) - } - "DeleteProfile" -> { - val profile = (a.get("ProfileOnion") as? String) ?: "" - val pass = (a.get("pass") as? String) ?: "" - Cwtch.deleteProfile(profile, pass) - } - "ArchiveConversation" -> { - val profile = (a.get("ProfileOnion") as? String) ?: "" - val conversation = a.getInt("conversation").toLong() - Cwtch.archiveConversation(profile, conversation) - } - "DeleteConversation" -> { - val profile = (a.get("ProfileOnion") as? String) ?: "" - val conversation = a.getInt("conversation").toLong() - Cwtch.deleteContact(profile, conversation) - } - "SetProfileAttribute" -> { - val profile = (a.get("ProfileOnion") as? String) ?: "" - val key = (a.get("Key") as? String) ?: "" - val v = (a.get("Val") as? String) ?: "" - Cwtch.setProfileAttribute(profile, key, v) - } - "SetConversationAttribute" -> { - val profile = (a.get("ProfileOnion") as? String) ?: "" - val conversation = a.getInt("conversation").toLong() - val key = (a.get("Key") as? String) ?: "" - val v = (a.get("Val") as? String) ?: "" - Cwtch.setConversationAttribute(profile, conversation, key, v) - } - "Shutdown" -> { - Cwtch.shutdownCwtch(); - return Result.success() - } - "LoadServers" -> { - val password = (a.get("Password") as? String) ?: "" - Cwtch.loadServers(password) - } - "CreateServer" -> { - val password = (a.get("Password") as? String) ?: "" - val desc = (a.get("Description") as? String) ?: "" - val autostart = (a.get("Autostart") as? Boolean) ?: false - Cwtch.createServer(password, desc, autostart) - } - "DeleteServer" -> { - val serverOnion = (a.get("ServerOnion") as? String) ?: "" - val password = (a.get("Password") as? String) ?: "" - Cwtch.deleteServer(serverOnion, password) - } - "LaunchServers" -> { - Cwtch.launchServers() - } - "LaunchServer" -> { - val serverOnion = (a.get("ServerOnion") as? String) ?: "" - Cwtch.launchServer(serverOnion) - } - "StopServer" -> { - val serverOnion = (a.get("ServerOnion") as? String) ?: "" - Cwtch.stopServer(serverOnion) - } - "StopServers" -> { - Cwtch.stopServers() - } - "DestroyServers" -> { - Cwtch.destroyServers() - } - "SetServerAttribute" -> { - val serverOnion = (a.get("ServerOnion") as? String) ?: "" - val key = (a.get("Key") as? String) ?: "" - val v = (a.get("Val") as? String) ?: "" - Cwtch.setServerAttribute(serverOnion, key, v) - } + // Event passing translations from Flutter to Kotlin worker scope so the worker can use them "L10nInit" -> { notificationSimple = (a.get("notificationSimple") as? String) ?: "New Message" notificationConversationInfo = (a.get("notificationConversationInfo") as? String) ?: "New Message From " } - "ExportProfile" -> { - val profileOnion = (a.get("ProfileOnion") as? String) ?: "" - val file = StringBuilder().append(this.applicationContext.cacheDir).append("/").append((a.get("file") as? String) ?: "").toString() - Log.i("FlwtchWorker", "constructing exported file " + file); - Cwtch.exportProfile(profileOnion,file) - } - "ImportProfile" -> { - val file = (a.get("file") as? String) ?: "" - val pass = (a.get("pass") as? String) ?: "" - return Result.success(Data.Builder().putString("result", Cwtch.importProfile(file, pass)).build()); - } else -> { Log.i(TAG, "unknown command: " + method); return Result.failure() } } - return Result.success() } diff --git a/android/app/src/main/kotlin/im/cwtch/flwtch/MainActivity.kt b/android/app/src/main/kotlin/im/cwtch/flwtch/MainActivity.kt index 48f5f9a1..fc6e54dc 100644 --- a/android/app/src/main/kotlin/im/cwtch/flwtch/MainActivity.kt +++ b/android/app/src/main/kotlin/im/cwtch/flwtch/MainActivity.kt @@ -55,6 +55,7 @@ class MainActivity: FlutterActivity() { private val CHANNEL_NOTIF_CLICK = "im.cwtch.flwtch/notificationClickHandler" private val CHANNEL_SHUTDOWN_CLICK = "im.cwtch.flwtch/shutdownClickHandler" + private val TAG: String = "MainActivity.kt" // WorkManager tag applied to all Start() infinite coroutines val WORKER_TAG = "cwtchEventBusWorker" @@ -267,6 +268,194 @@ class MainActivity: FlutterActivity() { val filepath: String = call.argument("filepath") ?: "" result.success(Cwtch.shareFile(profile, conversation.toLong(), filepath)) } + + "CreateProfile" -> { + val nick: String = call.argument("nick") ?: "" + val pass: String = call.argument("pass") ?: "" + Cwtch.createProfile(nick, pass) + } + "LoadProfiles" -> { + val pass: String = call.argument("pass") ?: "" + Cwtch.loadProfiles(pass) + } + "ChangePassword" -> { + val profile: String = call.argument("ProfileOnion") ?: "" + val pass: String = call.argument("OldPass") ?: "" + val passNew: String = call.argument("NewPass") ?: "" + val passNew2: String = call.argument("NewPassAgain") ?: "" + Cwtch.changePassword(profile, pass, passNew, passNew2) + } + "GetMessage" -> { + val profile: String = call.argument("ProfileOnion") ?: "" + val conversation: Int = call.argument("conversation") ?: 0 + val indexI: Int = call.argument("index") ?: 0 + result.success(Data.Builder().putString("result", Cwtch.getMessage(profile, conversation.toLong(), indexI.toLong())).build()) + } + "GetMessageByID" -> { + val profile: String = call.argument("ProfileOnion") ?: "" + val conversation: Int = call.argument("conversation") ?: 0 + val id: Int = call.argument("id") ?: 0 + result.success(Data.Builder().putString("result", Cwtch.getMessageByID(profile, conversation.toLong(), id.toLong())).build()) + } + "GetMessageByContentHash" -> { + val profile: String = call.argument("ProfileOnion") ?: "" + val conversation: Int = call.argument("conversation") ?: 0 + val contentHash: String = call.argument("contentHash") ?: "" + result.success(Data.Builder().putString("result", Cwtch.getMessagesByContentHash(profile, conversation.toLong(), contentHash)).build()) + } + "SetMessageAttribute" -> { + val profile: String = call.argument("ProfileOnion") ?: "" + val conversation: Int = call.argument("conversation") ?: 0 + val channel: Int = call.argument("Chanenl") ?: 0 + val midx: Int = call.argument("Message") ?: 0 + val key: String = call.argument("key") ?: "" + val value: String = call.argument("value") ?: "" + Cwtch.setMessageAttribute(profile, conversation.toLong(), channel.toLong(), midx.toLong(), key, value) + } + "AcceptConversation" -> { + val profile: String = call.argument("ProfileOnion") ?: "" + val conversation: Int = call.argument("conversation") ?: 0 + Cwtch.acceptConversation(profile, conversation.toLong()) + } + "BlockContact" -> { + val profile: String = call.argument("ProfileOnion") ?: "" + val conversation: Int = call.argument("conversation") ?: 0 + Cwtch.blockContact(profile, conversation.toLong()) + } + "UnblockContact" -> { + val profile: String = call.argument("ProfileOnion") ?: "" + val conversation: Int = call.argument("conversation") ?: 0 + Cwtch.unblockContact(profile, conversation.toLong()) + } + + "DownloadFile" -> { + val profile: String = call.argument("ProfileOnion") ?: "" + val conversation: Int = call.argument("conversation") ?: 0 + val filepath: String = call.argument("filepath") ?: "" + val manifestpath: String = call.argument("manifestpath") ?: "" + val filekey: String = call.argument("filekey") ?: "" + // FIXME: Prevent spurious calls by Intent + if (profile != "") { + Cwtch.downloadFile(profile, conversation.toLong(), filepath, manifestpath, filekey) + } + } + "CheckDownloadStatus" -> { + val profile: String = call.argument("ProfileOnion") ?: "" + val fileKey: String = call.argument("fileKey") ?: "" + Cwtch.checkDownloadStatus(profile, fileKey) + } + "VerifyOrResumeDownload" -> { + val profile: String = call.argument("ProfileOnion") ?: "" + val conversation: Int = call.argument("conversation") ?: 0 + val fileKey: String = call.argument("fileKey") ?: "" + Cwtch.verifyOrResumeDownload(profile, conversation.toLong(), fileKey) + } + "SendProfileEvent" -> { + val onion: String= call.argument("onion") ?: "" + val jsonEvent: String = call.argument("jsonEvent") ?: "" + Cwtch.sendProfileEvent(onion, jsonEvent) + } + "SendAppEvent" -> { + val jsonEvent: String = call.argument("jsonEvent") ?: "" + Cwtch.sendAppEvent(jsonEvent) + } + "ResetTor" -> { + Cwtch.resetTor() + } + "ImportBundle" -> { + val profile: String = call.argument("ProfileOnion") ?: "" + val bundle: String = call.argument("bundle") ?: "" + Cwtch.importBundle(profile, bundle) + } + "CreateGroup" -> { + val profile: String = call.argument("ProfileOnion") ?: "" + val server: String = call.argument("server") ?: "" + val groupName: String = call.argument("groupName") ?: "" + Cwtch.createGroup(profile, server, groupName) + } + "DeleteProfile" -> { + val profile: String = call.argument("ProfileOnion") ?: "" + val pass: String = call.argument("pass") ?: "" + Cwtch.deleteProfile(profile, pass) + } + "ArchiveConversation" -> { + val profile: String = call.argument("ProfileOnion") ?: "" + val conversation: Int = call.argument("conversation") ?: 0 + Cwtch.archiveConversation(profile, conversation.toLong()) + } + "DeleteConversation" -> { + val profile: String = call.argument("ProfileOnion") ?: "" + val conversation: Int = call.argument("conversation") ?: 0 + Cwtch.deleteContact(profile, conversation.toLong()) + } + "SetProfileAttribute" -> { + val profile: String = call.argument("ProfileOnion") ?: "" + val key: String = call.argument("Key") ?: "" + val v: String = call.argument("Val") ?: "" + Cwtch.setProfileAttribute(profile, key, v) + } + "SetConversationAttribute" -> { + val profile: String = call.argument("ProfileOnion") ?: "" + val conversation: Int = call.argument("conversation") ?: 0 + val key: String = call.argument("Key") ?: "" + val v: String = call.argument("Val") ?: "" + Cwtch.setConversationAttribute(profile, conversation.toLong(), key, v) + } + "LoadServers" -> { + val password: String = call.argument("Password") ?: "" + Cwtch.loadServers(password) + } + "CreateServer" -> { + val password: String = call.argument("Password") ?: "" + val desc: String = call.argument("Description") ?: "" + val autostart: Boolean = call.argument("Autostart") ?: false + Cwtch.createServer(password, desc, autostart) + } + "DeleteServer" -> { + val serverOnion: String = call.argument("ServerOnion") ?: "" + val password: String = call.argument("Password") ?: "" + Cwtch.deleteServer(serverOnion, password) + } + "LaunchServers" -> { + Cwtch.launchServers() + } + "LaunchServer" -> { + val serverOnion: String = call.argument("ServerOnion") ?: "" + Cwtch.launchServer(serverOnion) + } + "StopServer" -> { + val serverOnion: String = call.argument("ServerOnion") ?: "" + Cwtch.stopServer(serverOnion) + } + "StopServers" -> { + Cwtch.stopServers() + } + "DestroyServers" -> { + Cwtch.destroyServers() + } + "SetServerAttribute" -> { + val serverOnion: String = call.argument("ServerOnion") ?: "" + val key: String = call.argument("Key") ?: "" + val v: String = call.argument("Val") ?: "" + Cwtch.setServerAttribute(serverOnion, key, v) + } + "ExportProfile" -> { + val profileOnion: String = call.argument("ProfileOnion") ?: "" + val file: String = StringBuilder().append(this.applicationContext.cacheDir).append("/").append(call.argument("file") ?: "").toString() + Log.i("FlwtchWorker", "constructing exported file " + file); + Cwtch.exportProfile(profileOnion,file) + } + "ImportProfile" -> { + val file: String = call.argument("file") ?: "" + val pass: String = call.argument("pass") ?: "" + Data.Builder().putString("result", Cwtch.importProfile(file, pass)).build() + } + "ReconnectCwtchForeground" -> { + Cwtch.reconnectCwtchForeground() + } + "Shutdown" -> { + Cwtch.shutdownCwtch(); + } else -> { // ...otherwise fallthru to a normal ffi method call (and return the result using the result callback) val data: Data = Data.Builder().putString(FlwtchWorker.KEY_METHOD, method).putString(FlwtchWorker.KEY_ARGS, JSONObject(argmap).toString()).build() diff --git a/lib/models/profile.dart b/lib/models/profile.dart index 16dcdf0b..19df02d1 100644 --- a/lib/models/profile.dart +++ b/lib/models/profile.dart @@ -175,7 +175,7 @@ class ProfileInfoState extends ChangeNotifier { this._unreadMessages += contact["numUnread"] as int; if (profileContact != null) { profileContact.status = contact["status"]; - profileContact.totalMessages = contact["numMessages"]; // Todo: trigger cache update (bulk upload) + profileContact.totalMessages = contact["numMessages"]; profileContact.unreadMessages = contact["numUnread"]; profileContact.lastMessageTime = DateTime.fromMillisecondsSinceEpoch(1000 * int.parse(contact["lastMsgTime"])); } else { @@ -198,7 +198,6 @@ class ProfileInfoState extends ChangeNotifier { notificationPolicy: contact["notificationPolicy"] ?? "ConversationNotificationPolicy.Default", )); } - unreadMessages += int.parse(contact["numUnread"]); }); } this._contacts.resort(); diff --git a/lib/views/messageview.dart b/lib/views/messageview.dart index df1a1789..efb50ae3 100644 --- a/lib/views/messageview.dart +++ b/lib/views/messageview.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:crypto/crypto.dart'; +import 'package:cwtch/cwtch/cwtch.dart'; import 'package:cwtch/cwtch_icons_icons.dart'; import 'package:cwtch/models/appstate.dart'; import 'package:cwtch/models/chatmessage.dart'; @@ -278,6 +279,8 @@ class _MessageViewState extends State { ctrlrCompose.clear(); focusNode.requestFocus(); + + Provider.of(context, listen: false).cwtch.SetConversationAttribute(profileOnion, identifier, LastMessageSeenTimeKey, DateTime.now().toIso8601String()); } Widget _buildComposeBox() {