Merge pull request 'assorted android and sync progress fixes' (#391) from state into trunk
continuous-integration/drone/push Build is passing Details

Reviewed-on: #391
This commit is contained in:
Sarah Jamie Lewis 2022-03-04 21:17:41 +00:00
commit 62a99797ca
12 changed files with 390 additions and 358 deletions

View File

@ -1 +1 @@
2022-02-23-17-17-v1.6.0-2-ge8b2def 2022-03-03-19-53-v1.6.0-4-g4b881b9

View File

@ -1 +1 @@
2022-02-23-22-17-v1.6.0-2-ge8b2def 2022-03-04-00-54-v1.6.0-4-g4b881b9

View File

@ -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,6 +60,7 @@ 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) val a = JSONObject(args)
when (method) { when (method) {
"Start" -> { "Start" -> {
@ -64,11 +73,15 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
Log.i("FlwtchWorker.kt", "startCwtch success, starting coroutine AppbusEvent loop...") Log.i("FlwtchWorker.kt", "startCwtch success, starting coroutine AppbusEvent loop...")
val downloadIDs = mutableMapOf<String, Int>() val downloadIDs = mutableMapOf<String, Int>()
while(true) { while (true) {
try {
val evt = MainActivity.AppbusEvent(Cwtch.getAppBusEvent()) 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") { if (evt.EventType == "NewMessageFromPeer" || evt.EventType == "NewMessageFromGroup") {
val data = JSONObject(evt.Data) val data = JSONObject(evt.Data)
val handle = if (evt.EventType == "NewMessageFromPeer") data.getString("RemotePeer") else data.getString("GroupID"); val handle = data.getString("RemotePeer");
val conversationId = data.getInt("ConversationID").toString();
val notificationChannel = if (evt.EventType == "NewMessageFromPeer") handle else conversationId
if (data["RemotePeer"] != data["ProfileOnion"]) { if (data["RemotePeer"] != data["ProfileOnion"]) {
val notification = data["notification"] val notification = data["notification"]
@ -99,14 +112,16 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
} else if (notification == "ContactInfo") { } else if (notification == "ContactInfo") {
val channelId = val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createMessageNotificationChannel(handle, handle) createMessageNotificationChannel(notificationChannel, notificationChannel)
} else { } else {
// If earlier version channel ID is not used // 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) // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
"" ""
} }
val loader = FlutterInjector.instance().flutterLoader() val loader = FlutterInjector.instance().flutterLoader()
val key = loader.getLookupKeyForAsset("assets/" + data.getString("Picture"))//"assets/profiles/001-centaur.png") 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 fh = applicationContext.assets.open(key)
val clickIntent = Intent(applicationContext, MainActivity::class.java).also { intent -> val clickIntent = Intent(applicationContext, MainActivity::class.java).also { intent ->
@ -118,14 +133,15 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
val newNotification = NotificationCompat.Builder(applicationContext, channelId) val newNotification = NotificationCompat.Builder(applicationContext, channelId)
.setContentTitle(data.getString("Nick")) .setContentTitle(data.getString("Nick"))
.setContentText((notificationConversationInfo ?: "New Message From %1").replace("%1", data.getString("Nick"))) .setContentText((notificationConversationInfo
?: "New Message From %1").replace("%1", data.getString("Nick")))
.setLargeIcon(BitmapFactory.decodeStream(fh)) .setLargeIcon(BitmapFactory.decodeStream(fh))
.setSmallIcon(R.mipmap.knott_transparent) .setSmallIcon(R.mipmap.knott_transparent)
.setContentIntent(PendingIntent.getActivity(applicationContext, 1, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT)) .setContentIntent(PendingIntent.getActivity(applicationContext, 1, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.setAutoCancel(true) .setAutoCancel(true)
.build() .build()
notificationManager.notify(getNotificationID(data.getString("ProfileOnion"), handle), newNotification) notificationManager.notify(getNotificationID(data.getString("ProfileOnion"), channelId), newNotification)
} }
} }
@ -173,7 +189,7 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
val fileKey = data.getString("FileKey"); val fileKey = data.getString("FileKey");
if (tempFile != "" && tempFile != data.getString("FilePath")) { if (tempFile != "" && tempFile != data.getString("FilePath")) {
val filePath = data.getString("FilePath"); val filePath = data.getString("FilePath");
Log.i("FlwtchWorker", "moving "+tempFile+" to "+filePath); Log.i("FlwtchWorker", "moving " + tempFile + " to " + filePath);
val sourcePath = Paths.get(tempFile); val sourcePath = Paths.get(tempFile);
val targetUri = Uri.parse(filePath); val targetUri = Uri.parse(filePath);
val os = this.applicationContext.getContentResolver().openOutputStream(targetUri); val os = this.applicationContext.getContentResolver().openOutputStream(targetUri);
@ -186,7 +202,7 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
} }
} }
if (downloadIDs.containsKey(fileKey)) { if (downloadIDs.containsKey(fileKey)) {
notificationManager.cancel(downloadIDs.get(fileKey)?:0); notificationManager.cancel(downloadIDs.get(fileKey) ?: 0);
} }
} }
@ -197,8 +213,12 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
intent.putExtra("EventID", evt.EventID) intent.putExtra("EventID", evt.EventID)
LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent) LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent)
} }
} catch (e: Exception) {
Log.e("FlwtchWorker", "Error in handleCwtch: " + e.toString() + " :: " + e.getStackTrace());
} }
} }
}
"ReconnectCwtchForeground" -> { "ReconnectCwtchForeground" -> {
Cwtch.reconnectCwtchForeground() Cwtch.reconnectCwtchForeground()
} }
@ -265,6 +285,7 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
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 message = (a.get("message") as? String) ?: "" val message = (a.get("message") as? String) ?: ""
Log.i("FlwtchWorker.kt", "SendMessage: $message")
Cwtch.sendMessage(profile, conversation, message) Cwtch.sendMessage(profile, conversation, message)
} }
"SendInvitation" -> { "SendInvitation" -> {
@ -404,6 +425,7 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
return Result.failure() return Result.failure()
} }
} }
return Result.success() return Result.success()
} }
@ -411,8 +433,8 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
// 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)

View File

@ -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();

View File

@ -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));
} }
} }

View File

@ -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();

View File

@ -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) {

View File

@ -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();
} }

View File

@ -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) {

View File

@ -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,
)); ));
} }

View File

@ -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,
)), )),
], ],

View File

@ -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"