Prevent Android from Wiping Unsaved Conversations Every Time the Foreground Reloads #857
|
@ -1 +1 @@
|
||||||
2024-02-26-09-28-v0.0.13
|
2024-02-26-18-01-v0.0.14
|
|
@ -177,6 +177,9 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
notificationManager.notify(getNotificationID(data.getString("ProfileOnion"), channelId), newNotification)
|
notificationManager.notify(getNotificationID(data.getString("ProfileOnion"), channelId), newNotification)
|
||||||
|
if (fh != null) {
|
||||||
|
fh.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -577,9 +577,6 @@ class MainActivity: FlutterActivity() {
|
||||||
result.success(Cwtch.searchConversations(profile, pattern))
|
result.success(Cwtch.searchConversations(profile, pattern))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
"ReconnectCwtchForeground" -> {
|
|
||||||
Cwtch.reconnectCwtchForeground()
|
|
||||||
}
|
|
||||||
"Shutdown" -> {
|
"Shutdown" -> {
|
||||||
Cwtch.shutdownCwtch();
|
Cwtch.shutdownCwtch();
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,6 @@ abstract class Cwtch {
|
||||||
|
|
||||||
// ignore: non_constant_identifier_names
|
// ignore: non_constant_identifier_names
|
||||||
Future<void> Start();
|
Future<void> Start();
|
||||||
// ignore: non_constant_identifier_names
|
|
||||||
Future<void> ReconnectCwtchForeground();
|
|
||||||
|
|
||||||
Future<String> getCwtchDir();
|
Future<String> getCwtchDir();
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'package:cwtch/cwtch/cwtch.dart';
|
||||||
import 'package:cwtch/main.dart';
|
import 'package:cwtch/main.dart';
|
||||||
import 'package:cwtch/models/appstate.dart';
|
import 'package:cwtch/models/appstate.dart';
|
||||||
import 'package:cwtch/models/contact.dart';
|
import 'package:cwtch/models/contact.dart';
|
||||||
|
import 'package:cwtch/models/message.dart';
|
||||||
import 'package:cwtch/models/profilelist.dart';
|
import 'package:cwtch/models/profilelist.dart';
|
||||||
import 'package:cwtch/models/remoteserver.dart';
|
import 'package:cwtch/models/remoteserver.dart';
|
||||||
import 'package:cwtch/models/servers.dart';
|
import 'package:cwtch/models/servers.dart';
|
||||||
|
@ -58,7 +59,14 @@ class CwtchNotifier {
|
||||||
// EnvironmentConfig.debugLog("NewEvent $type $data");
|
// EnvironmentConfig.debugLog("NewEvent $type $data");
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "CwtchStarted":
|
case "CwtchStarted":
|
||||||
flwtchState.cwtch.LoadProfiles(DefaultPassword);
|
if (data["Reload"] == "true" && profileCN.num > 0) {
|
||||||
|
// don't reload...
|
||||||
|
// unless we have loaded no profiles...then there isnt a risk and this
|
||||||
|
// might be a first time (e.g. new apk, existing service)
|
||||||
|
} else {
|
||||||
|
|||||||
|
flwtchState.cwtch.LoadProfiles(DefaultPassword);
|
||||||
|
}
|
||||||
|
|
||||||
appState.SetCwtchInit();
|
appState.SetCwtchInit();
|
||||||
break;
|
break;
|
||||||
case "CwtchStartError":
|
case "CwtchStartError":
|
||||||
|
@ -188,21 +196,9 @@ class CwtchNotifier {
|
||||||
var senderImage = data['picture'];
|
var senderImage = data['picture'];
|
||||||
var isAuto = data['Auto'] == "true";
|
var isAuto = data['Auto'] == "true";
|
||||||
String contenthash = data['ContentHash'];
|
String contenthash = data['ContentHash'];
|
||||||
|
|
||||||
var selectedProfile = appState.selectedProfile == data["ProfileOnion"];
|
var selectedProfile = appState.selectedProfile == data["ProfileOnion"];
|
||||||
var selectedConversation = selectedProfile && appState.selectedConversation == identifier;
|
var selectedConversation = selectedProfile && appState.selectedConversation == identifier;
|
||||||
var notification = data["notification"];
|
|
||||||
|
|
||||||
if (selectedConversation && seenMessageCallback != null) {
|
|
||||||
seenMessageCallback!(data["ProfileOnion"]!, identifier, DateTime.now().toUtc());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (notification == "SimpleEvent") {
|
|
||||||
notificationManager.notify(notificationSimple ?? "New Message", "", 0);
|
|
||||||
} else if (notification == "ContactInfo") {
|
|
||||||
var contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(identifier);
|
|
||||||
notificationManager.notify((notificationConversationInfo ?? "New Message from %1").replaceFirst("%1", (contact?.nickname ?? senderHandle.toString())), data["ProfileOnion"], identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
profileCN.getProfile(data["ProfileOnion"])?.newMessage(
|
profileCN.getProfile(data["ProfileOnion"])?.newMessage(
|
||||||
identifier,
|
identifier,
|
||||||
messageID,
|
messageID,
|
||||||
|
@ -215,6 +211,19 @@ class CwtchNotifier {
|
||||||
selectedProfile,
|
selectedProfile,
|
||||||
selectedConversation,
|
selectedConversation,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Now perform the notification logic...
|
||||||
|
var notification = data["notification"];
|
||||||
|
if (selectedConversation && seenMessageCallback != null) {
|
||||||
|
seenMessageCallback!(data["ProfileOnion"]!, identifier, DateTime.now().toUtc());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notification == "SimpleEvent") {
|
||||||
|
notificationManager.notify(notificationSimple ?? "New Message", "", 0);
|
||||||
|
} else if (notification == "ContactInfo") {
|
||||||
|
var contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(identifier);
|
||||||
|
notificationManager.notify((notificationConversationInfo ?? "New Message from %1").replaceFirst("%1", (contact?.nickname ?? senderHandle.toString())), data["ProfileOnion"], identifier);
|
||||||
|
}
|
||||||
appState.notifyProfileUnread();
|
appState.notifyProfileUnread();
|
||||||
break;
|
break;
|
||||||
case "PeerAcknowledgement":
|
case "PeerAcknowledgement":
|
||||||
|
|
|
@ -270,14 +270,6 @@ class CwtchFfi implements Cwtch {
|
||||||
return _cwtchDir;
|
return _cwtchDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore: non_constant_identifier_names
|
|
||||||
Future<void> ReconnectCwtchForeground() async {
|
|
||||||
var reconnectCwtch = library.lookup<NativeFunction<Void Function()>>("c_ReconnectCwtchForeground");
|
|
||||||
// ignore: non_constant_identifier_names
|
|
||||||
final ReconnectCwtchForeground = reconnectCwtch.asFunction<void Function()>();
|
|
||||||
ReconnectCwtchForeground();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called on object being disposed to (presumably on app close) to close the isolate that's listening to libcwtch-go events
|
// Called on object being disposed to (presumably on app close) to close the isolate that's listening to libcwtch-go events
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
|
|
@ -86,12 +86,6 @@ class CwtchGomobile implements Cwtch {
|
||||||
cwtchPlatform.invokeMethod("Start", {"appDir": _cwtchDir, "torPath": torPath});
|
cwtchPlatform.invokeMethod("Start", {"appDir": _cwtchDir, "torPath": torPath});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
// ignore: non_constant_identifier_names
|
|
||||||
Future<void> ReconnectCwtchForeground() async {
|
|
||||||
cwtchPlatform.invokeMethod("ReconnectCwtchForeground", {});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle libcwtch-go events (received via kotlin) and dispatch to the cwtchNotifier
|
// Handle libcwtch-go events (received via kotlin) and dispatch to the cwtchNotifier
|
||||||
Future<void> _handleAppbusEvent(MethodCall call) async {
|
Future<void> _handleAppbusEvent(MethodCall call) async {
|
||||||
final String json = call.arguments;
|
final String json = call.arguments;
|
||||||
|
|
|
@ -64,6 +64,7 @@ Message compileOverlay(MessageInfo messageInfo) {
|
||||||
|
|
||||||
abstract class CacheHandler {
|
abstract class CacheHandler {
|
||||||
Future<MessageInfo?> get(Cwtch cwtch, String profileOnion, int conversationIdentifier, MessageCache cache);
|
Future<MessageInfo?> get(Cwtch cwtch, String profileOnion, int conversationIdentifier, MessageCache cache);
|
||||||
|
Future<MessageInfo?> sync(Cwtch cwtch, String profileOnion, int conversationIdentifier, MessageCache cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ByIndex implements CacheHandler {
|
class ByIndex implements CacheHandler {
|
||||||
|
@ -128,7 +129,7 @@ class ByIndex implements CacheHandler {
|
||||||
List<dynamic> messagesWrapper = jsonDecode(msgs);
|
List<dynamic> messagesWrapper = jsonDecode(msgs);
|
||||||
|
|
||||||
for (; i < messagesWrapper.length; i++) {
|
for (; i < messagesWrapper.length; i++) {
|
||||||
var messageInfo = messageWrapperToInfo(profileOnion, conversationIdentifier, messagesWrapper[i]);
|
var messageInfo = MessageWrapperToInfo(profileOnion, conversationIdentifier, messagesWrapper[i]);
|
||||||
cache.addIndexed(messageInfo, start + i);
|
cache.addIndexed(messageInfo, start + i);
|
||||||
}
|
}
|
||||||
} catch (e, stacktrace) {
|
} catch (e, stacktrace) {
|
||||||
|
@ -143,6 +144,13 @@ class ByIndex implements CacheHandler {
|
||||||
void add(MessageCache cache, MessageInfo messageInfo) {
|
void add(MessageCache cache, MessageInfo messageInfo) {
|
||||||
cache.addIndexed(messageInfo, index);
|
cache.addIndexed(messageInfo, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<MessageInfo?> sync(Cwtch cwtch, String profileOnion, int conversationIdentifier, MessageCache cache) {
|
||||||
|
EnvironmentConfig.debugLog("performing a resync on message ${index}");
|
||||||
|
fetchAndProcess(index, 1, cwtch, profileOnion, conversationIdentifier, cache);
|
||||||
|
return get(cwtch, profileOnion, conversationIdentifier, cache);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ById implements CacheHandler {
|
class ById implements CacheHandler {
|
||||||
|
@ -172,6 +180,11 @@ class ById implements CacheHandler {
|
||||||
}
|
}
|
||||||
return fetch(cwtch, profileOnion, conversationIdentifier, cache);
|
return fetch(cwtch, profileOnion, conversationIdentifier, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<MessageInfo?> sync(Cwtch cwtch, String profileOnion, int conversationIdentifier, MessageCache cache) {
|
||||||
|
return get(cwtch, profileOnion, conversationIdentifier, cache);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ByContentHash implements CacheHandler {
|
class ByContentHash implements CacheHandler {
|
||||||
|
@ -200,6 +213,11 @@ class ByContentHash implements CacheHandler {
|
||||||
}
|
}
|
||||||
return fetch(cwtch, profileOnion, conversationIdentifier, cache);
|
return fetch(cwtch, profileOnion, conversationIdentifier, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<MessageInfo?> sync(Cwtch cwtch, String profileOnion, int conversationIdentifier, MessageCache cache) {
|
||||||
|
return get(cwtch, profileOnion, conversationIdentifier, cache);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Message> getReplies(MessageCache cache, int messageIdentifier) {
|
List<Message> getReplies(MessageCache cache, int messageIdentifier) {
|
||||||
|
@ -257,6 +275,16 @@ Future<Message> messageHandler(BuildContext context, String profileOnion, int co
|
||||||
|
|
||||||
MessageInfo? messageInfo = await cacheHandler.get(cwtch, profileOnion, conversationIdentifier, cache);
|
MessageInfo? messageInfo = await cacheHandler.get(cwtch, profileOnion, conversationIdentifier, cache);
|
||||||
|
|
||||||
|
if (messageInfo != null) {
|
||||||
|
if (messageInfo.metadata.ackd == false) {
|
||||||
|
if (messageInfo.metadata.lastChecked == null || messageInfo.metadata.lastChecked!.difference(DateTime.now()).abs().inSeconds > 30) {
|
||||||
|
messageInfo.metadata.lastChecked = DateTime.now();
|
||||||
|
// NOTE: Only ByIndex lookups will trigger
|
||||||
|
messageInfo = await cacheHandler.sync(cwtch, profileOnion, conversationIdentifier, cache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (messageInfo != null) {
|
if (messageInfo != null) {
|
||||||
return compileOverlay(messageInfo);
|
return compileOverlay(messageInfo);
|
||||||
} else {
|
} else {
|
||||||
|
@ -272,14 +300,14 @@ MessageInfo? messageJsonToInfo(String profileOnion, int conversationIdentifier,
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return messageWrapperToInfo(profileOnion, conversationIdentifier, messageWrapper);
|
return MessageWrapperToInfo(profileOnion, conversationIdentifier, messageWrapper);
|
||||||
} catch (e, stacktrace) {
|
} catch (e, stacktrace) {
|
||||||
EnvironmentConfig.debugLog("message handler exception on parse message and cache: " + e.toString() + " " + stacktrace.toString());
|
EnvironmentConfig.debugLog("message handler exception on parse message and cache: " + e.toString() + " " + stacktrace.toString());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageInfo messageWrapperToInfo(String profileOnion, int conversationIdentifier, dynamic messageWrapper) {
|
MessageInfo MessageWrapperToInfo(String profileOnion, int conversationIdentifier, dynamic messageWrapper) {
|
||||||
// Construct the initial metadata
|
// Construct the initial metadata
|
||||||
var messageID = messageWrapper['ID'];
|
var messageID = messageWrapper['ID'];
|
||||||
var timestamp = DateTime.tryParse(messageWrapper['Timestamp'])!;
|
var timestamp = DateTime.tryParse(messageWrapper['Timestamp'])!;
|
||||||
|
@ -312,6 +340,7 @@ class MessageMetadata extends ChangeNotifier {
|
||||||
|
|
||||||
final String? signature;
|
final String? signature;
|
||||||
final String contenthash;
|
final String contenthash;
|
||||||
|
DateTime? lastChecked;
|
||||||
|
|
||||||
dynamic get attributes => this._attributes;
|
dynamic get attributes => this._attributes;
|
||||||
|
|
||||||
|
|
|
@ -128,6 +128,7 @@ class MessageCache extends ChangeNotifier {
|
||||||
|
|
||||||
void addNew(String profileOnion, int conversation, int messageID, DateTime timestamp, String senderHandle, String senderImage, bool isAuto, String data, String contenthash) {
|
void addNew(String profileOnion, int conversation, int messageID, DateTime timestamp, String senderHandle, String senderImage, bool isAuto, String data, String contenthash) {
|
||||||
this.cache[messageID] = MessageInfo(MessageMetadata(profileOnion, conversation, messageID, timestamp, senderHandle, senderImage, "", {}, false, false, isAuto, contenthash), data);
|
this.cache[messageID] = MessageInfo(MessageMetadata(profileOnion, conversation, messageID, timestamp, senderHandle, senderImage, "", {}, false, false, isAuto, contenthash), data);
|
||||||
|
this.cache[messageID]?.metadata.lastChecked = DateTime.now(); // Don't check straight away...
|
||||||
this.cacheByIndex.insert(0, LocalIndexMessage(messageID));
|
this.cacheByIndex.insert(0, LocalIndexMessage(messageID));
|
||||||
if (contenthash != "") {
|
if (contenthash != "") {
|
||||||
this.cacheByHash[contenthash] = messageID;
|
this.cacheByHash[contenthash] = messageID;
|
||||||
|
|
|
@ -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.14.0+40
|
version: 1.14.7+41
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.17.0 <4.0.0"
|
sdk: ">=2.17.0 <4.0.0"
|
||||||
|
|
Loading…
Reference in New Issue
not reloading, presumably loading default pw profiles for the first time