Merge pull request 'assorted android and sync progress fixes' (#391) from state into trunk
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
Reviewed-on: #391
This commit is contained in:
commit
62a99797ca
|
@ -1 +1 @@
|
||||||
2022-02-23-17-17-v1.6.0-2-ge8b2def
|
2022-03-03-19-53-v1.6.0-4-g4b881b9
|
|
@ -1 +1 @@
|
||||||
2022-02-23-22-17-v1.6.0-2-ge8b2def
|
2022-03-04-00-54-v1.6.0-4-g4b881b9
|
|
@ -32,13 +32,21 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
|
||||||
private var notificationSimple: String? = null
|
private var notificationSimple: String? = null
|
||||||
private var notificationConversationInfo: String? = null
|
private var notificationConversationInfo: String? = null
|
||||||
|
|
||||||
|
|
||||||
override suspend fun doWork(): Result {
|
override suspend fun doWork(): Result {
|
||||||
|
// Hack to uncomment and deploy if your device has zombie workers you need to kill
|
||||||
|
// We need a proper solution but this will clear those out for now
|
||||||
|
/*if (notificationSimple == null) {
|
||||||
|
Log.e("FlwtchWorker", "doWork found notificationSimple is null, app has not started, this is a stale thread, terminating")
|
||||||
|
return Result.failure()
|
||||||
|
}*/
|
||||||
|
|
||||||
val method = inputData.getString(KEY_METHOD)
|
val method = inputData.getString(KEY_METHOD)
|
||||||
?: return Result.failure()
|
?: return Result.failure()
|
||||||
val args = inputData.getString(KEY_ARGS)
|
val args = inputData.getString(KEY_ARGS)
|
||||||
?: return Result.failure()
|
?: return Result.failure()
|
||||||
// Mark the Worker as important
|
// Mark the Worker as important
|
||||||
val progress = "Cwtch is keeping Tor running in the background"//todo:translate
|
val progress = "Cwtch is keeping Tor running in the background" // TODO: translate
|
||||||
setForeground(createForegroundInfo(progress))
|
setForeground(createForegroundInfo(progress))
|
||||||
return handleCwtch(method, args)
|
return handleCwtch(method, args)
|
||||||
}
|
}
|
||||||
|
@ -52,367 +60,381 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleCwtch(method: String, args: String): Result {
|
private fun handleCwtch(method: String, args: String): Result {
|
||||||
val a = JSONObject(args)
|
|
||||||
when (method) {
|
|
||||||
"Start" -> {
|
|
||||||
Log.i("FlwtchWorker.kt", "handleAppInfo Start")
|
|
||||||
val appDir = (a.get("appDir") as? String) ?: ""
|
|
||||||
val torPath = (a.get("torPath") as? String) ?: "tor"
|
|
||||||
Log.i("FlwtchWorker.kt", "appDir: '$appDir' torPath: '$torPath'")
|
|
||||||
|
|
||||||
if (Cwtch.startCwtch(appDir, torPath) != 0.toLong()) return Result.failure()
|
val a = JSONObject(args)
|
||||||
|
when (method) {
|
||||||
|
"Start" -> {
|
||||||
|
Log.i("FlwtchWorker.kt", "handleAppInfo Start")
|
||||||
|
val appDir = (a.get("appDir") as? String) ?: ""
|
||||||
|
val torPath = (a.get("torPath") as? String) ?: "tor"
|
||||||
|
Log.i("FlwtchWorker.kt", "appDir: '$appDir' torPath: '$torPath'")
|
||||||
|
|
||||||
Log.i("FlwtchWorker.kt", "startCwtch success, starting coroutine AppbusEvent loop...")
|
if (Cwtch.startCwtch(appDir, torPath) != 0.toLong()) return Result.failure()
|
||||||
val downloadIDs = mutableMapOf<String, Int>()
|
|
||||||
while(true) {
|
|
||||||
val evt = MainActivity.AppbusEvent(Cwtch.getAppBusEvent())
|
|
||||||
if (evt.EventType == "NewMessageFromPeer" || evt.EventType == "NewMessageFromGroup") {
|
|
||||||
val data = JSONObject(evt.Data)
|
|
||||||
val handle = if (evt.EventType == "NewMessageFromPeer") data.getString("RemotePeer") else data.getString("GroupID");
|
|
||||||
if (data["RemotePeer"] != data["ProfileOnion"]) {
|
|
||||||
val notification = data["notification"]
|
|
||||||
|
|
||||||
if (notification == "SimpleEvent") {
|
Log.i("FlwtchWorker.kt", "startCwtch success, starting coroutine AppbusEvent loop...")
|
||||||
val channelId =
|
val downloadIDs = mutableMapOf<String, Int>()
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
while (true) {
|
||||||
createMessageNotificationChannel("Cwtch", "Cwtch")
|
|
||||||
} else {
|
|
||||||
// If earlier version channel ID is not used
|
|
||||||
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
|
|
||||||
""
|
|
||||||
}
|
|
||||||
|
|
||||||
val clickIntent = Intent(applicationContext, MainActivity::class.java).also { intent ->
|
|
||||||
intent.action = Intent.ACTION_RUN
|
|
||||||
intent.putExtra("EventType", "NotificationClicked")
|
|
||||||
}
|
|
||||||
|
|
||||||
val newNotification = NotificationCompat.Builder(applicationContext, channelId)
|
|
||||||
.setContentTitle("Cwtch")
|
|
||||||
.setContentText(notificationSimple ?: "New Message")
|
|
||||||
.setSmallIcon(R.mipmap.knott_transparent)
|
|
||||||
.setContentIntent(PendingIntent.getActivity(applicationContext, 1, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT))
|
|
||||||
.setAutoCancel(true)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
notificationManager.notify(getNotificationID("Cwtch", "Cwtch"), newNotification)
|
|
||||||
} else if (notification == "ContactInfo") {
|
|
||||||
val channelId =
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
createMessageNotificationChannel(handle, handle)
|
|
||||||
} else {
|
|
||||||
// If earlier version channel ID is not used
|
|
||||||
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
|
|
||||||
""
|
|
||||||
}
|
|
||||||
val loader = FlutterInjector.instance().flutterLoader()
|
|
||||||
val key = loader.getLookupKeyForAsset("assets/" + data.getString("Picture"))//"assets/profiles/001-centaur.png")
|
|
||||||
val fh = applicationContext.assets.open(key)
|
|
||||||
|
|
||||||
val clickIntent = Intent(applicationContext, MainActivity::class.java).also { intent ->
|
|
||||||
intent.action = Intent.ACTION_RUN
|
|
||||||
intent.putExtra("EventType", "NotificationClicked")
|
|
||||||
intent.putExtra("ProfileOnion", data.getString("ProfileOnion"))
|
|
||||||
intent.putExtra("Handle", handle)
|
|
||||||
}
|
|
||||||
|
|
||||||
val newNotification = NotificationCompat.Builder(applicationContext, channelId)
|
|
||||||
.setContentTitle(data.getString("Nick"))
|
|
||||||
.setContentText((notificationConversationInfo ?: "New Message From %1").replace("%1", data.getString("Nick")))
|
|
||||||
.setLargeIcon(BitmapFactory.decodeStream(fh))
|
|
||||||
.setSmallIcon(R.mipmap.knott_transparent)
|
|
||||||
.setContentIntent(PendingIntent.getActivity(applicationContext, 1, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT))
|
|
||||||
.setAutoCancel(true)
|
|
||||||
.build()
|
|
||||||
|
|
||||||
notificationManager.notify(getNotificationID(data.getString("ProfileOnion"), handle), newNotification)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
} else if (evt.EventType == "FileDownloadProgressUpdate") {
|
|
||||||
try {
|
try {
|
||||||
val data = JSONObject(evt.Data);
|
val evt = MainActivity.AppbusEvent(Cwtch.getAppBusEvent())
|
||||||
val fileKey = data.getString("FileKey");
|
// TODO replace this notification block with the NixNotification manager in dart as it has access to contact names and also needs less working around
|
||||||
val title = data.getString("NameSuggestion");
|
if (evt.EventType == "NewMessageFromPeer" || evt.EventType == "NewMessageFromGroup") {
|
||||||
val progress = data.getString("Progress").toInt();
|
val data = JSONObject(evt.Data)
|
||||||
val progressMax = data.getString("FileSizeInChunks").toInt();
|
val handle = data.getString("RemotePeer");
|
||||||
if (!downloadIDs.containsKey(fileKey)) {
|
val conversationId = data.getInt("ConversationID").toString();
|
||||||
downloadIDs.put(fileKey, downloadIDs.count());
|
val notificationChannel = if (evt.EventType == "NewMessageFromPeer") handle else conversationId
|
||||||
|
if (data["RemotePeer"] != data["ProfileOnion"]) {
|
||||||
|
val notification = data["notification"]
|
||||||
|
|
||||||
|
if (notification == "SimpleEvent") {
|
||||||
|
val channelId =
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
createMessageNotificationChannel("Cwtch", "Cwtch")
|
||||||
|
} else {
|
||||||
|
// If earlier version channel ID is not used
|
||||||
|
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
|
||||||
|
""
|
||||||
|
}
|
||||||
|
|
||||||
|
val clickIntent = Intent(applicationContext, MainActivity::class.java).also { intent ->
|
||||||
|
intent.action = Intent.ACTION_RUN
|
||||||
|
intent.putExtra("EventType", "NotificationClicked")
|
||||||
|
}
|
||||||
|
|
||||||
|
val newNotification = NotificationCompat.Builder(applicationContext, channelId)
|
||||||
|
.setContentTitle("Cwtch")
|
||||||
|
.setContentText(notificationSimple ?: "New Message")
|
||||||
|
.setSmallIcon(R.mipmap.knott_transparent)
|
||||||
|
.setContentIntent(PendingIntent.getActivity(applicationContext, 1, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT))
|
||||||
|
.setAutoCancel(true)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
notificationManager.notify(getNotificationID("Cwtch", "Cwtch"), newNotification)
|
||||||
|
} else if (notification == "ContactInfo") {
|
||||||
|
val channelId =
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
createMessageNotificationChannel(notificationChannel, notificationChannel)
|
||||||
|
} else {
|
||||||
|
// If earlier version channel ID is not used
|
||||||
|
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
|
||||||
|
""
|
||||||
|
}
|
||||||
|
val loader = FlutterInjector.instance().flutterLoader()
|
||||||
|
Log.i("FlwtchWorker.kt", "notification for " + evt.EventType + " " + handle + " " + conversationId + " " + channelId)
|
||||||
|
Log.i("FlwtchWorker.kt", data.toString());
|
||||||
|
val key = loader.getLookupKeyForAsset(data.getString("picture"))//"assets/profiles/001-centaur.png")
|
||||||
|
val fh = applicationContext.assets.open(key)
|
||||||
|
|
||||||
|
val clickIntent = Intent(applicationContext, MainActivity::class.java).also { intent ->
|
||||||
|
intent.action = Intent.ACTION_RUN
|
||||||
|
intent.putExtra("EventType", "NotificationClicked")
|
||||||
|
intent.putExtra("ProfileOnion", data.getString("ProfileOnion"))
|
||||||
|
intent.putExtra("Handle", handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
val newNotification = NotificationCompat.Builder(applicationContext, channelId)
|
||||||
|
.setContentTitle(data.getString("Nick"))
|
||||||
|
.setContentText((notificationConversationInfo
|
||||||
|
?: "New Message From %1").replace("%1", data.getString("Nick")))
|
||||||
|
.setLargeIcon(BitmapFactory.decodeStream(fh))
|
||||||
|
.setSmallIcon(R.mipmap.knott_transparent)
|
||||||
|
.setContentIntent(PendingIntent.getActivity(applicationContext, 1, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT))
|
||||||
|
.setAutoCancel(true)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
notificationManager.notify(getNotificationID(data.getString("ProfileOnion"), channelId), newNotification)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} else if (evt.EventType == "FileDownloadProgressUpdate") {
|
||||||
|
try {
|
||||||
|
val data = JSONObject(evt.Data);
|
||||||
|
val fileKey = data.getString("FileKey");
|
||||||
|
val title = data.getString("NameSuggestion");
|
||||||
|
val progress = data.getString("Progress").toInt();
|
||||||
|
val progressMax = data.getString("FileSizeInChunks").toInt();
|
||||||
|
if (!downloadIDs.containsKey(fileKey)) {
|
||||||
|
downloadIDs.put(fileKey, downloadIDs.count());
|
||||||
|
}
|
||||||
|
var dlID = downloadIDs.get(fileKey);
|
||||||
|
if (dlID == null) {
|
||||||
|
dlID = 0;
|
||||||
|
}
|
||||||
|
if (progress >= 0) {
|
||||||
|
val channelId =
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
createDownloadNotificationChannel(fileKey, fileKey)
|
||||||
|
} else {
|
||||||
|
// If earlier version channel ID is not used
|
||||||
|
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
|
||||||
|
""
|
||||||
|
};
|
||||||
|
val newNotification = NotificationCompat.Builder(applicationContext, channelId)
|
||||||
|
.setOngoing(true)
|
||||||
|
.setContentTitle("Downloading")//todo: translate
|
||||||
|
.setContentText(title)
|
||||||
|
.setSmallIcon(android.R.drawable.stat_sys_download)
|
||||||
|
.setProgress(progressMax, progress, false)
|
||||||
|
.setSound(null)
|
||||||
|
//.setSilent(true)
|
||||||
|
.build();
|
||||||
|
notificationManager.notify(dlID, newNotification);
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.d("FlwtchWorker->FileDownloadProgressUpdate", e.toString() + " :: " + e.getStackTrace());
|
||||||
|
}
|
||||||
|
} else if (evt.EventType == "FileDownloaded") {
|
||||||
|
Log.d("FlwtchWorker", "file downloaded!");
|
||||||
|
val data = JSONObject(evt.Data);
|
||||||
|
val tempFile = data.getString("TempFile");
|
||||||
|
val fileKey = data.getString("FileKey");
|
||||||
|
if (tempFile != "" && tempFile != data.getString("FilePath")) {
|
||||||
|
val filePath = data.getString("FilePath");
|
||||||
|
Log.i("FlwtchWorker", "moving " + tempFile + " to " + filePath);
|
||||||
|
val sourcePath = Paths.get(tempFile);
|
||||||
|
val targetUri = Uri.parse(filePath);
|
||||||
|
val os = this.applicationContext.getContentResolver().openOutputStream(targetUri);
|
||||||
|
val bytesWritten = Files.copy(sourcePath, os);
|
||||||
|
Log.d("FlwtchWorker", "copied " + bytesWritten.toString() + " bytes");
|
||||||
|
if (bytesWritten != 0L) {
|
||||||
|
os?.flush();
|
||||||
|
os?.close();
|
||||||
|
Files.delete(sourcePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (downloadIDs.containsKey(fileKey)) {
|
||||||
|
notificationManager.cancel(downloadIDs.get(fileKey) ?: 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var dlID = downloadIDs.get(fileKey);
|
|
||||||
if (dlID == null) {
|
Intent().also { intent ->
|
||||||
dlID = 0;
|
intent.action = "im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS"
|
||||||
}
|
intent.putExtra("EventType", evt.EventType)
|
||||||
if (progress >= 0) {
|
intent.putExtra("Data", evt.Data)
|
||||||
val channelId =
|
intent.putExtra("EventID", evt.EventID)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent)
|
||||||
createDownloadNotificationChannel(fileKey, fileKey)
|
|
||||||
} else {
|
|
||||||
// If earlier version channel ID is not used
|
|
||||||
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
|
|
||||||
""
|
|
||||||
};
|
|
||||||
val newNotification = NotificationCompat.Builder(applicationContext, channelId)
|
|
||||||
.setOngoing(true)
|
|
||||||
.setContentTitle("Downloading")//todo: translate
|
|
||||||
.setContentText(title)
|
|
||||||
.setSmallIcon(android.R.drawable.stat_sys_download)
|
|
||||||
.setProgress(progressMax, progress, false)
|
|
||||||
.setSound(null)
|
|
||||||
//.setSilent(true)
|
|
||||||
.build();
|
|
||||||
notificationManager.notify(dlID, newNotification);
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.d("FlwtchWorker->FileDownloadProgressUpdate", e.toString() + " :: " + e.getStackTrace());
|
Log.e("FlwtchWorker", "Error in handleCwtch: " + e.toString() + " :: " + e.getStackTrace());
|
||||||
}
|
|
||||||
} else if (evt.EventType == "FileDownloaded") {
|
|
||||||
Log.d("FlwtchWorker", "file downloaded!");
|
|
||||||
val data = JSONObject(evt.Data);
|
|
||||||
val tempFile = data.getString("TempFile");
|
|
||||||
val fileKey = data.getString("FileKey");
|
|
||||||
if (tempFile != "" && tempFile != data.getString("FilePath")) {
|
|
||||||
val filePath = data.getString("FilePath");
|
|
||||||
Log.i("FlwtchWorker", "moving "+tempFile+" to "+filePath);
|
|
||||||
val sourcePath = Paths.get(tempFile);
|
|
||||||
val targetUri = Uri.parse(filePath);
|
|
||||||
val os = this.applicationContext.getContentResolver().openOutputStream(targetUri);
|
|
||||||
val bytesWritten = Files.copy(sourcePath, os);
|
|
||||||
Log.d("FlwtchWorker", "copied " + bytesWritten.toString() + " bytes");
|
|
||||||
if (bytesWritten != 0L) {
|
|
||||||
os?.flush();
|
|
||||||
os?.close();
|
|
||||||
Files.delete(sourcePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (downloadIDs.containsKey(fileKey)) {
|
|
||||||
notificationManager.cancel(downloadIDs.get(fileKey)?:0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Intent().also { intent ->
|
"ReconnectCwtchForeground" -> {
|
||||||
intent.action = "im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS"
|
Cwtch.reconnectCwtchForeground()
|
||||||
intent.putExtra("EventType", evt.EventType)
|
}
|
||||||
intent.putExtra("Data", evt.Data)
|
"CreateProfile" -> {
|
||||||
intent.putExtra("EventID", evt.EventID)
|
val nick = (a.get("nick") as? String) ?: ""
|
||||||
LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent)
|
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("FlwtchWorker", "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)
|
||||||
|
}
|
||||||
|
"SendMessage" -> {
|
||||||
|
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
||||||
|
val conversation = a.getInt("conversation").toLong()
|
||||||
|
val message = (a.get("message") as? String) ?: ""
|
||||||
|
Log.i("FlwtchWorker.kt", "SendMessage: $message")
|
||||||
|
Cwtch.sendMessage(profile, conversation, message)
|
||||||
|
}
|
||||||
|
"SendInvitation" -> {
|
||||||
|
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
||||||
|
val conversation = a.getInt("conversation").toLong()
|
||||||
|
val target = a.getInt("target").toLong()
|
||||||
|
Cwtch.sendInvitation(profile, conversation, target)
|
||||||
|
}
|
||||||
|
"ShareFile" -> {
|
||||||
|
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
||||||
|
val conversation = a.getInt("conversation").toLong()
|
||||||
|
val filepath = (a.get("filepath") as? String) ?: ""
|
||||||
|
Cwtch.shareFile(profile, conversation, filepath)
|
||||||
|
}
|
||||||
|
"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" -> {
|
||||||
"ReconnectCwtchForeground" -> {
|
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
||||||
Cwtch.reconnectCwtchForeground()
|
val fileKey = (a.get("fileKey") as? String) ?: ""
|
||||||
}
|
Cwtch.checkDownloadStatus(profile, fileKey)
|
||||||
"CreateProfile" -> {
|
}
|
||||||
val nick = (a.get("nick") as? String) ?: ""
|
"VerifyOrResumeDownload" -> {
|
||||||
val pass = (a.get("pass") as? String) ?: ""
|
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
||||||
Cwtch.createProfile(nick, pass)
|
val conversation = a.getInt("conversation").toLong()
|
||||||
}
|
val fileKey = (a.get("fileKey") as? String) ?: ""
|
||||||
"LoadProfiles" -> {
|
Cwtch.verifyOrResumeDownload(profile, conversation, fileKey)
|
||||||
val pass = (a.get("pass") as? String) ?: ""
|
}
|
||||||
Cwtch.loadProfiles(pass)
|
"SendProfileEvent" -> {
|
||||||
}
|
val onion = (a.get("onion") as? String) ?: ""
|
||||||
"ChangePassword" -> {
|
val jsonEvent = (a.get("jsonEvent") as? String) ?: ""
|
||||||
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
Cwtch.sendProfileEvent(onion, jsonEvent)
|
||||||
val pass = (a.get("OldPass") as? String) ?: ""
|
}
|
||||||
val passNew = (a.get("NewPass") as? String) ?: ""
|
"SendAppEvent" -> {
|
||||||
val passNew2 = (a.get("NewPassAgain") as? String) ?: ""
|
val jsonEvent = (a.get("jsonEvent") as? String) ?: ""
|
||||||
Cwtch.changePassword(profile, pass, passNew, passNew2)
|
Cwtch.sendAppEvent(jsonEvent)
|
||||||
}
|
}
|
||||||
"GetMessage" -> {
|
"ResetTor" -> {
|
||||||
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
Cwtch.resetTor()
|
||||||
val conversation = a.getInt("conversation").toLong()
|
}
|
||||||
val indexI = a.getInt("index").toLong()
|
"ImportBundle" -> {
|
||||||
Log.d("FlwtchWorker", "Cwtch GetMessage " + profile + " " + conversation.toString() + " " + indexI.toString())
|
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
||||||
return Result.success(Data.Builder().putString("result", Cwtch.getMessage(profile, conversation, indexI)).build())
|
val bundle = (a.get("bundle") as? String) ?: ""
|
||||||
}
|
Cwtch.importBundle(profile, bundle)
|
||||||
"GetMessageByID" -> {
|
}
|
||||||
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
"CreateGroup" -> {
|
||||||
val conversation = a.getInt("conversation").toLong()
|
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
||||||
val id = a.getInt("id").toLong()
|
val server = (a.get("server") as? String) ?: ""
|
||||||
return Result.success(Data.Builder().putString("result", Cwtch.getMessageByID(profile, conversation, id)).build())
|
val groupName = (a.get("groupName") as? String) ?: ""
|
||||||
}
|
Cwtch.createGroup(profile, server, groupName)
|
||||||
"GetMessageByContentHash" -> {
|
}
|
||||||
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
"DeleteProfile" -> {
|
||||||
val conversation = a.getInt("conversation").toLong()
|
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
||||||
val contentHash = (a.get("contentHash") as? String) ?: ""
|
val pass = (a.get("pass") as? String) ?: ""
|
||||||
return Result.success(Data.Builder().putString("result", Cwtch.getMessagesByContentHash(profile, conversation, contentHash)).build())
|
Cwtch.deleteProfile(profile, pass)
|
||||||
}
|
}
|
||||||
"UpdateMessageAttribute" -> {
|
"ArchiveConversation" -> {
|
||||||
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
||||||
val conversation = a.getInt("conversation").toLong()
|
val conversation = a.getInt("conversation").toLong()
|
||||||
val channel = a.getInt("chanenl").toLong()
|
Cwtch.archiveConversation(profile, conversation)
|
||||||
val midx = a.getInt("midx").toLong()
|
}
|
||||||
val key = (a.get("key") as? String) ?: ""
|
"DeleteConversation" -> {
|
||||||
val value = (a.get("value") as? String) ?: ""
|
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
||||||
Cwtch.setMessageAttribute(profile, conversation, channel, midx, key, value)
|
val conversation = a.getInt("conversation").toLong()
|
||||||
}
|
Cwtch.deleteContact(profile, conversation)
|
||||||
"AcceptConversation" -> {
|
}
|
||||||
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
"SetProfileAttribute" -> {
|
||||||
val conversation = a.getInt("conversation").toLong()
|
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
||||||
Cwtch.acceptConversation(profile, conversation)
|
val key = (a.get("Key") as? String) ?: ""
|
||||||
}
|
val v = (a.get("Val") as? String) ?: ""
|
||||||
"BlockContact" -> {
|
Cwtch.setProfileAttribute(profile, key, v)
|
||||||
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
}
|
||||||
val conversation = a.getInt("conversation").toLong()
|
"SetConversationAttribute" -> {
|
||||||
Cwtch.blockContact(profile, conversation)
|
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
||||||
}
|
val conversation = a.getInt("conversation").toLong()
|
||||||
"UnblockContact" -> {
|
val key = (a.get("Key") as? String) ?: ""
|
||||||
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
val v = (a.get("Val") as? String) ?: ""
|
||||||
val conversation = a.getInt("conversation").toLong()
|
Cwtch.setConversationAttribute(profile, conversation, key, v)
|
||||||
Cwtch.unblockContact(profile, conversation)
|
}
|
||||||
}
|
"Shutdown" -> {
|
||||||
"SendMessage" -> {
|
Cwtch.shutdownCwtch();
|
||||||
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
return Result.success()
|
||||||
val conversation = a.getInt("conversation").toLong()
|
}
|
||||||
val message = (a.get("message") as? String) ?: ""
|
"LoadServers" -> {
|
||||||
Cwtch.sendMessage(profile, conversation, message)
|
val password = (a.get("Password") as? String) ?: ""
|
||||||
}
|
Cwtch.loadServers(password)
|
||||||
"SendInvitation" -> {
|
}
|
||||||
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
"CreateServer" -> {
|
||||||
val conversation = a.getInt("conversation").toLong()
|
val password = (a.get("Password") as? String) ?: ""
|
||||||
val target = a.getInt("target").toLong()
|
val desc = (a.get("Description") as? String) ?: ""
|
||||||
Cwtch.sendInvitation(profile, conversation, target)
|
val autostart = (a.get("Autostart") as? Boolean) ?: false
|
||||||
}
|
Cwtch.createServer(password, desc, autostart)
|
||||||
"ShareFile" -> {
|
}
|
||||||
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
"DeleteServer" -> {
|
||||||
val conversation = a.getInt("conversation").toLong()
|
val serverOnion = (a.get("ServerOnion") as? String) ?: ""
|
||||||
val filepath = (a.get("filepath") as? String) ?: ""
|
val password = (a.get("Password") as? String) ?: ""
|
||||||
Cwtch.shareFile(profile, conversation, filepath)
|
Cwtch.deleteServer(serverOnion, password)
|
||||||
}
|
}
|
||||||
"DownloadFile" -> {
|
"LaunchServers" -> {
|
||||||
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
Cwtch.launchServers()
|
||||||
val conversation = a.getInt("conversation").toLong()
|
}
|
||||||
val filepath = (a.get("filepath") as? String) ?: ""
|
"LaunchServer" -> {
|
||||||
val manifestpath = (a.get("manifestpath") as? String) ?: ""
|
val serverOnion = (a.get("ServerOnion") as? String) ?: ""
|
||||||
val filekey = (a.get("filekey") as? String) ?: ""
|
Cwtch.launchServer(serverOnion)
|
||||||
// FIXME: Prevent spurious calls by Intent
|
}
|
||||||
if (profile != "") {
|
"StopServer" -> {
|
||||||
Cwtch.downloadFile(profile, conversation, filepath, manifestpath, filekey)
|
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)
|
||||||
|
}
|
||||||
|
"L10nInit" -> {
|
||||||
|
notificationSimple = (a.get("notificationSimple") as? String) ?: "New Message"
|
||||||
|
notificationConversationInfo = (a.get("notificationConversationInfo") as? String)
|
||||||
|
?: "New Message From "
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Log.i("FlwtchWorker", "unknown command: " + method);
|
||||||
|
return Result.failure()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"CheckDownloadStatus" -> {
|
|
||||||
val profile = (a.get("ProfileOnion") as? String) ?: ""
|
return Result.success()
|
||||||
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)
|
|
||||||
}
|
|
||||||
"L10nInit" -> {
|
|
||||||
notificationSimple = (a.get("notificationSimple") as? String) ?: "New Message"
|
|
||||||
notificationConversationInfo = (a.get("notificationConversationInfo") as? String)
|
|
||||||
?: "New Message From "
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
Log.i("FlwtchWorker", "unknown command: " + method);
|
|
||||||
return Result.failure()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Result.success()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates an instance of ForegroundInfo which can be used to update the
|
// Creates an instance of ForegroundInfo which can be used to update the
|
||||||
// ongoing notification.
|
// ongoing notification.
|
||||||
private fun createForegroundInfo(progress: String): ForegroundInfo {
|
private fun createForegroundInfo(progress: String): ForegroundInfo {
|
||||||
val id = "flwtch"
|
val id = "flwtch"
|
||||||
val title = "Flwtch"
|
val title = "Flwtch" // TODO: change
|
||||||
val cancel = "Shut down"//todo: translate
|
val cancel = "Shut down" // TODO: translate
|
||||||
val channelId =
|
val channelId =
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
createForegroundNotificationChannel(id, id)
|
createForegroundNotificationChannel(id, id)
|
||||||
|
|
|
@ -100,7 +100,7 @@ abstract class Cwtch {
|
||||||
void SetServerAttribute(String serverOnion, String key, String val);
|
void SetServerAttribute(String serverOnion, String key, String val);
|
||||||
|
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
void Shutdown();
|
Future<void> Shutdown();
|
||||||
|
|
||||||
// non-ffi
|
// non-ffi
|
||||||
String defaultDownloadPath();
|
String defaultDownloadPath();
|
||||||
|
|
|
@ -153,6 +153,7 @@ class CwtchNotifier {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "NewMessageFromPeer":
|
case "NewMessageFromPeer":
|
||||||
|
|
||||||
var identifier = int.parse(data["ConversationID"]);
|
var identifier = int.parse(data["ConversationID"]);
|
||||||
var messageID = int.parse(data["Index"]);
|
var messageID = int.parse(data["Index"]);
|
||||||
var timestamp = DateTime.tryParse(data['TimestampReceived'])!;
|
var timestamp = DateTime.tryParse(data['TimestampReceived'])!;
|
||||||
|
@ -317,7 +318,7 @@ class CwtchNotifier {
|
||||||
server: groupInvite["ServerHost"],
|
server: groupInvite["ServerHost"],
|
||||||
status: status,
|
status: status,
|
||||||
isGroup: true,
|
isGroup: true,
|
||||||
lastMessageTime: DateTime.fromMillisecondsSinceEpoch(0)));
|
lastMessageTime: DateTime.now()));
|
||||||
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(identifier, DateTime.fromMillisecondsSinceEpoch(0));
|
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(identifier, DateTime.fromMillisecondsSinceEpoch(0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,9 +63,9 @@ class FlwtchState extends State<Flwtch> with WindowListener {
|
||||||
|
|
||||||
final GlobalKey<NavigatorState> navKey = GlobalKey<NavigatorState>();
|
final GlobalKey<NavigatorState> navKey = GlobalKey<NavigatorState>();
|
||||||
|
|
||||||
Future<dynamic> shutdownDirect(MethodCall call) {
|
Future<dynamic> shutdownDirect(MethodCall call) async {
|
||||||
print(call);
|
print(call);
|
||||||
cwtch.Shutdown();
|
await cwtch.Shutdown();
|
||||||
return Future.value({});
|
return Future.value({});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,9 +179,9 @@ class FlwtchState extends State<Flwtch> with WindowListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> shutdown() async {
|
Future<void> shutdown() async {
|
||||||
cwtch.Shutdown();
|
await cwtch.Shutdown();
|
||||||
// Wait a few seconds as shutting down things takes a little time..
|
// Wait a few seconds as shutting down things takes a little time..
|
||||||
Future.delayed(Duration(seconds: 2)).then((value) {
|
Future.delayed(Duration(seconds: 1)).then((value) {
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
SystemNavigator.pop();
|
SystemNavigator.pop();
|
||||||
} else if (Platform.isLinux || Platform.isWindows || Platform.isMacOS) {
|
} else if (Platform.isLinux || Platform.isWindows || Platform.isMacOS) {
|
||||||
|
@ -247,8 +247,8 @@ class FlwtchState extends State<Flwtch> with WindowListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() async {
|
||||||
cwtch.Shutdown();
|
await cwtch.Shutdown();
|
||||||
windowManager.removeListener(this);
|
windowManager.removeListener(this);
|
||||||
cwtch.dispose();
|
cwtch.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
|
|
@ -80,7 +80,9 @@ class ProfileInfoState extends ChangeNotifier {
|
||||||
List<dynamic> servers = jsonDecode(serversJson);
|
List<dynamic> servers = jsonDecode(serversJson);
|
||||||
this._servers.replace(servers.map((server) {
|
this._servers.replace(servers.map((server) {
|
||||||
// TODO Keys...
|
// TODO Keys...
|
||||||
return RemoteServerInfoState(server["onion"], server["identifier"], server["description"], server["status"]);
|
var preSyncStartTime = DateTime.tryParse(server["syncProgress"]["startTime"]);
|
||||||
|
var lastMessageTime = DateTime.tryParse(server["syncProgress"]["lastMessageTime"]);
|
||||||
|
return RemoteServerInfoState(server["onion"], server["identifier"], server["description"], server["status"], lastPreSyncMessageTime: preSyncStartTime, mostRecentMessageTime: lastMessageTime);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._contacts.contacts.forEach((contact) {
|
this._contacts.contacts.forEach((contact) {
|
||||||
|
|
|
@ -12,7 +12,12 @@ class RemoteServerInfoState extends ChangeNotifier {
|
||||||
double syncProgress = 0;
|
double syncProgress = 0;
|
||||||
DateTime lastPreSyncMessagTime = new DateTime(2020);
|
DateTime lastPreSyncMessagTime = new DateTime(2020);
|
||||||
|
|
||||||
RemoteServerInfoState(this.onion, this.identifier, this.description, this._status);
|
RemoteServerInfoState(this.onion, this.identifier, this.description, this._status, {lastPreSyncMessageTime, mostRecentMessageTime}) {
|
||||||
|
if (_status == "Authenticated") {
|
||||||
|
this.lastPreSyncMessagTime = lastPreSyncMessageTime;
|
||||||
|
updateSyncProgressFor(mostRecentMessageTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void updateDescription(String newDescription) {
|
void updateDescription(String newDescription) {
|
||||||
this.description = newDescription;
|
this.description = newDescription;
|
||||||
|
@ -45,8 +50,8 @@ class RemoteServerInfoState extends ChangeNotifier {
|
||||||
// updateSyncProgressFor point takes a message's time, and updates the server sync progress,
|
// updateSyncProgressFor point takes a message's time, and updates the server sync progress,
|
||||||
// based on that point in time between the precalculated lastPreSyncMessagTime and Now
|
// based on that point in time between the precalculated lastPreSyncMessagTime and Now
|
||||||
void updateSyncProgressFor(DateTime point) {
|
void updateSyncProgressFor(DateTime point) {
|
||||||
var range = lastPreSyncMessagTime.difference(DateTime.now());
|
var range = lastPreSyncMessagTime.toUtc().difference(DateTime.now().toUtc());
|
||||||
var pointFromStart = lastPreSyncMessagTime.difference(point);
|
var pointFromStart = lastPreSyncMessagTime.toUtc().difference(point.toUtc());
|
||||||
syncProgress = pointFromStart.inSeconds / range.inSeconds;
|
syncProgress = pointFromStart.inSeconds / range.inSeconds;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,7 +117,7 @@ class NixNotificationManager implements NotificationsManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationsManager newDesktopNotificationsManager(Future<void> Function(String profileOnion, int convoId) notificationSelectConvo) {
|
NotificationsManager newDesktopNotificationsManager(Future<void> Function(String profileOnion, int convoId) notificationSelectConvo) {
|
||||||
if (Platform.isLinux || Platform.isMacOS) {
|
if ((Platform.isLinux && !Platform.isAndroid) || Platform.isMacOS) {
|
||||||
try {
|
try {
|
||||||
return NixNotificationManager(notificationSelectConvo);
|
return NixNotificationManager(notificationSelectConvo);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -32,6 +32,7 @@ class _ContactRowState extends State<ContactRow> {
|
||||||
visible: contact.isGroup && contact.status == "Authenticated",
|
visible: contact.isGroup && contact.status == "Authenticated",
|
||||||
child: LinearProgressIndicator(
|
child: LinearProgressIndicator(
|
||||||
color: Provider.of<Settings>(context).theme.defaultButtonActiveColor,
|
color: Provider.of<Settings>(context).theme.defaultButtonActiveColor,
|
||||||
|
backgroundColor: Provider.of<Settings>(context).theme.defaultButtonDisabledColor,
|
||||||
value: Provider.of<ProfileInfoState>(context).serverList.getServer(contact.server ?? "")?.syncProgress,
|
value: Provider.of<ProfileInfoState>(context).serverList.getServer(contact.server ?? "")?.syncProgress,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,7 @@ class _RemoteServerRowState extends State<RemoteServerRow> {
|
||||||
visible: server.status == "Authenticated",
|
visible: server.status == "Authenticated",
|
||||||
child: LinearProgressIndicator(
|
child: LinearProgressIndicator(
|
||||||
color: Provider.of<Settings>(context).theme.defaultButtonActiveColor,
|
color: Provider.of<Settings>(context).theme.defaultButtonActiveColor,
|
||||||
|
backgroundColor: Provider.of<Settings>(context).theme.defaultButtonDisabledColor,
|
||||||
value: server.syncProgress,
|
value: server.syncProgress,
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
|
|
|
@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
|
||||||
# Read more about iOS versioning at
|
# Read more about iOS versioning at
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
version: 1.6.1+26
|
version: 1.6.2+27
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.15.0 <3.0.0"
|
sdk: ">=2.15.0 <3.0.0"
|
||||||
|
|
Loading…
Reference in New Issue