From 82f2192074c59ce85bbf1e83d7da83fa0b9a86dc Mon Sep 17 00:00:00 2001 From: Boehrsi Date: Thu, 30 Apr 2020 15:05:17 +0200 Subject: [PATCH 01/16] Optimize tearDown and cleanup #app-501 OT-779 Update gradle Renamed methods --- android/build.gradle | 2 +- .../deltachatcore/DeltaChatCorePlugin.java | 34 ++++++-------- .../NativeInteractionManager.java | 2 +- .../handlers/EventChannelHandler.java | 8 ++-- example/android/build.gradle | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 4 +- example/lib/main.dart | 4 +- lib/delta_chat_core.dart | 46 ++++++++++++------- 8 files changed, 56 insertions(+), 46 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 3633759..d6a3075 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.3' + classpath 'com.android.tools.build:gradle:3.6.3' } } diff --git a/android/src/main/java/com/openxchange/deltachatcore/DeltaChatCorePlugin.java b/android/src/main/java/com/openxchange/deltachatcore/DeltaChatCorePlugin.java index f319565..2a5e53d 100644 --- a/android/src/main/java/com/openxchange/deltachatcore/DeltaChatCorePlugin.java +++ b/android/src/main/java/com/openxchange/deltachatcore/DeltaChatCorePlugin.java @@ -90,8 +90,7 @@ public class DeltaChatCorePlugin implements MethodCallHandler, PluginRegistry.Vi private static final String METHOD_BASE_INIT = "base_init"; private static final String METHOD_BASE_SYSTEM_INFO = "base_systemInfo"; - private static final String METHOD_BASE_START = "base_start"; - private static final String METHOD_BASE_STOP = "base_stop"; + private static final String METHOD_BASE_TEAR_DOWN = "base_tearDown"; private static final String METHOD_BASE_LOGOUT = "base_logout"; private static final String ARGUMENT_REMOVE_CACHE_IDENTIFIER = "removeCacheIdentifier"; @@ -104,7 +103,7 @@ public class DeltaChatCorePlugin implements MethodCallHandler, PluginRegistry.Vi private Context context; private BinaryMessenger messenger; - private MethodChannel channel; + private MethodChannel methodChannel; private final IdCache chatCache = new IdCache<>(); private final IdCache contactCache = new IdCache<>(); @@ -142,16 +141,14 @@ public void onAttachedToEngine(FlutterPluginBinding binding) { private void onAttachedToEngine(Context context, BinaryMessenger messenger) { this.context = context; this.messenger = messenger; - channel = new MethodChannel(messenger, CHANNEL_DELTA_CHAT_CORE); - channel.setMethodCallHandler(this); + methodChannel = new MethodChannel(messenger, CHANNEL_DELTA_CHAT_CORE); + methodChannel.setMethodCallHandler(this); } @Override public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { logEventAndDelegate(eventChannelHandler, DEBUG, TAG, "Detaching plugin via v2 embedding"); - channel.setMethodCallHandler(null); - channel = null; - eventChannelHandler.close(); + tearDownJavaInstance(); } @Override @@ -240,12 +237,9 @@ private void handleBaseCalls(MethodCall methodCall, Result result) { case METHOD_BASE_SYSTEM_INFO: systemInfo(result); break; - case METHOD_BASE_START: - start(result); - break; - case METHOD_BASE_STOP: + case METHOD_BASE_TEAR_DOWN: case METHOD_BASE_LOGOUT: - stop(result); + tearDown(result); break; default: result.notImplemented(); @@ -273,21 +267,23 @@ private void systemInfo(Result result) { result.success(android.os.Build.VERSION.RELEASE); } - private void start(Result result) { - nativeInteractionManager.start(); + private void tearDown(Result result) { + stopNativeInteractionManager(); result.success(null); } - private void stop(Result result) { - stopNativeInteractionManager(); - result.success(null); + private void tearDownJavaInstance() { + if (methodChannel != null) { + methodChannel.setMethodCallHandler(null); + methodChannel = null; + } + eventChannelHandler.close(); } private void stopNativeInteractionManager() { nativeInteractionManager.stop(); } - private void handleContextCalls(MethodCall methodCall, Result result) { contextCallHandler.handleCall(methodCall, result); } diff --git a/android/src/main/java/com/openxchange/deltachatcore/NativeInteractionManager.java b/android/src/main/java/com/openxchange/deltachatcore/NativeInteractionManager.java index f533d93..8557eb4 100644 --- a/android/src/main/java/com/openxchange/deltachatcore/NativeInteractionManager.java +++ b/android/src/main/java/com/openxchange/deltachatcore/NativeInteractionManager.java @@ -122,7 +122,7 @@ public void onAvailable(Network network) { } } - void start() { + private void start() { logEventAndDelegate(eventChannelHandler, DEBUG, TAG, "Starting threads"); startThreads(INTERRUPT_IDLE); waitForThreadsRunning(); diff --git a/android/src/main/java/com/openxchange/deltachatcore/handlers/EventChannelHandler.java b/android/src/main/java/com/openxchange/deltachatcore/handlers/EventChannelHandler.java index f9ae944..93b94e9 100644 --- a/android/src/main/java/com/openxchange/deltachatcore/handlers/EventChannelHandler.java +++ b/android/src/main/java/com/openxchange/deltachatcore/handlers/EventChannelHandler.java @@ -122,13 +122,15 @@ public void onCancel(Object o) { if (eventSink == null) { return; } - eventSink.endOfStream(); eventSink = null; } public void close() { - eventChannel.setStreamHandler(null); - eventChannel = null; + eventSink = null; + if (eventChannel != null) { + eventChannel.setStreamHandler(null); + eventChannel = null; + } } private boolean isDelegateEvent(int eventId) { diff --git a/example/android/build.gradle b/example/android/build.gradle index 6de3728..11e3d09 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.3' + classpath 'com.android.tools.build:gradle:3.6.3' } } diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index 92af958..fbd5fb9 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Dec 02 14:02:27 CET 2019 +#Thu Apr 30 08:23:22 CEST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip diff --git a/example/lib/main.dart b/example/lib/main.dart index 35b3ab0..c35e1f8 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -67,7 +67,7 @@ class _MyAppState extends State { _addListItem(information: true, text: "Information\n[Test succeeded] Informational text [assertion == result]"); try { DeltaChatCore core = DeltaChatCore(); - bool init = await core.init("messenger.db"); + bool init = await core.setupAsync("messenger.db"); if (init) { _addListItem(text: "Init done"); Context context = Context(); @@ -156,7 +156,7 @@ class _MyAppState extends State { var chatCntAfterDeletingChat = await chatList.getChatCnt(); await chatList.tearDown(); _addListItem(text: "deleteChat", assertion: 0, result: chatCntAfterDeletingChat); - core.stop(); + core.tearDownAsync(); var dbFile = File(core.dbPath); dbFile.deleteSync(); } diff --git a/lib/delta_chat_core.dart b/lib/delta_chat_core.dart index 828b20e..ca610f1 100644 --- a/lib/delta_chat_core.dart +++ b/lib/delta_chat_core.dart @@ -61,22 +61,22 @@ export 'src/context.dart'; class DeltaChatCore { static const String _channelDeltaChatCore = 'deltaChatCore'; - static const String _channelDeltaChatCoreEvents = 'deltaChatCoreEvents'; + static const String _channelDeltaChatCoreEvents = 'deltaChatCoreEvents'; static const String methodBaseInit = 'base_init'; - static const String methodBaseStart = "base_start"; - static const String methodBaseStop = "base_stop"; - static const String methodBaseLogout = "base_logout"; + static const String methodBaseTearDown = "base_tearDown"; + static const String methodBaseLogout = "base_logout"; static const String argumentDBName = "dbName"; static DeltaChatCore _instance; - final _logger = Logger("delta_chat_core"); final MethodChannel _methodChannel; + final _eventChannel = EventChannel(_channelDeltaChatCoreEvents); final _eventChannelSubscribers = Map>(); + Logger _logger = Logger("delta_chat_core"); String _dbPath; String get dbPath => _dbPath; @@ -86,13 +86,15 @@ class DeltaChatCore { factory DeltaChatCore() { if (_instance == null) { final methodChannel = MethodChannel(_channelDeltaChatCore); - _instance = new DeltaChatCore._createInstance(methodChannel); + _instance = new DeltaChatCore._internal(methodChannel); setupLogger(); } return _instance; } - DeltaChatCore._createInstance(this._methodChannel); + DeltaChatCore._internal(this._methodChannel) { + _logger.info("Creating new Dart core instance"); + } Future invokeMethod(String method, [dynamic arguments]) async { if (!_init) { @@ -101,8 +103,9 @@ class DeltaChatCore { return _methodChannel.invokeMethod(method, arguments); } - Future init(String dbName) async { + Future setupAsync(String dbName) async { if (!_init) { + _logger.info("Setting up new core"); _dbPath = await _methodChannel.invokeMethod(methodBaseInit, {argumentDBName: dbName}); _init = true; _setupEventChannelListener(); @@ -110,16 +113,29 @@ class DeltaChatCore { return _init; } - Future start() async { - await _methodChannel.invokeMethod(methodBaseStart); + Future tearDownAsync() async { + _logger.info("Tearing down core"); + _unregisterListeners(); + await _methodChannel.invokeMethod(methodBaseTearDown); + _tearDownDartInstance(); } - Future stop() async { - await _methodChannel.invokeMethod(methodBaseStop); + void _unregisterListeners() { + _eventChannelSubscription.cancel(); + _eventChannelSubscribers.clear(); + _methodChannel.setMethodCallHandler(null); + } + + void _tearDownDartInstance() { + _instance = null; + _init = false; } Future logout() async { + _logger.info("Logout started"); + _unregisterListeners(); await _methodChannel.invokeMethod(methodBaseLogout); + _tearDownDartInstance(); } _setupEventChannelListener() { @@ -137,10 +153,6 @@ class DeltaChatCore { }); } - tearDown() { - _eventChannelSubscription.cancel(); - } - void addListener({int eventId, List eventIdList, @required StreamController streamController}) { if (streamController == null || (eventId == null && eventIdList == null)) { throw ArgumentError("Either eventId or eventIdList must be set and a stream controller must be not null"); @@ -199,4 +211,4 @@ class DeltaChatCore { void addStreamEvent(Event event) { _delegateEventToSubscribers(event); } -} +} \ No newline at end of file From dd729839acc040469431f26be6b22966b31d2c7c Mon Sep 17 00:00:00 2001 From: Boehrsi Date: Tue, 5 May 2020 15:39:25 +0200 Subject: [PATCH 02/16] Cleanup and prepare DCC minimal setup --- example/lib/main.dart | 2 +- lib/delta_chat_core.dart | 29 +++++++++++++++++++---------- lib/src/chat.dart | 2 +- lib/src/chatmsg.dart | 2 +- lib/src/contact.dart | 2 +- 5 files changed, 23 insertions(+), 14 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index c35e1f8..4074f89 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -67,7 +67,7 @@ class _MyAppState extends State { _addListItem(information: true, text: "Information\n[Test succeeded] Informational text [assertion == result]"); try { DeltaChatCore core = DeltaChatCore(); - bool init = await core.setupAsync("messenger.db"); + bool init = await core.setupAsync(dbName: "messenger.db", minimalSetup: false); if (init) { _addListItem(text: "Init done"); Context context = Context(); diff --git a/lib/delta_chat_core.dart b/lib/delta_chat_core.dart index ca610f1..d1845ac 100644 --- a/lib/delta_chat_core.dart +++ b/lib/delta_chat_core.dart @@ -61,32 +61,37 @@ export 'src/context.dart'; class DeltaChatCore { static const String _channelDeltaChatCore = 'deltaChatCore'; - static const String _channelDeltaChatCoreEvents = 'deltaChatCoreEvents'; - static const String methodBaseInit = 'base_init'; + static const String methodBaseInit = 'base_init'; static const String methodBaseTearDown = "base_tearDown"; static const String methodBaseLogout = "base_logout"; - static const String argumentDBName = "dbName"; - static DeltaChatCore _instance; + static const String argumentDatabaseName = "dbName"; + static const String argumentMinimalSetup = "minimalSetup"; - final MethodChannel _methodChannel; + static DeltaChatCore _instance; final _eventChannel = EventChannel(_channelDeltaChatCoreEvents); final _eventChannelSubscribers = Map>(); + final _logger = Logger("delta_chat_core"); + + final MethodChannel _methodChannel; - Logger _logger = Logger("delta_chat_core"); + bool _minimalSetup; String _dbPath; + String get dbPath => _dbPath; + bool get minimalSetup => _minimalSetup; + StreamSubscription _eventChannelSubscription; bool _init = false; factory DeltaChatCore() { if (_instance == null) { final methodChannel = MethodChannel(_channelDeltaChatCore); - _instance = new DeltaChatCore._internal(methodChannel); + _instance = DeltaChatCore._internal(methodChannel); setupLogger(); } return _instance; @@ -103,10 +108,14 @@ class DeltaChatCore { return _methodChannel.invokeMethod(method, arguments); } - Future setupAsync(String dbName) async { + Future setupAsync({@required String dbName, @required bool minimalSetup}) async { if (!_init) { + _minimalSetup = minimalSetup; _logger.info("Setting up new core"); - _dbPath = await _methodChannel.invokeMethod(methodBaseInit, {argumentDBName: dbName}); + _dbPath = await _methodChannel.invokeMethod(methodBaseInit, { + argumentDatabaseName: dbName, + argumentMinimalSetup: minimalSetup, + }); _init = true; _setupEventChannelListener(); } @@ -211,4 +220,4 @@ class DeltaChatCore { void addStreamEvent(Event event) { _delegateEventToSubscribers(event); } -} \ No newline at end of file +} diff --git a/lib/src/chat.dart b/lib/src/chat.dart index 5880ac6..f7e0518 100644 --- a/lib/src/chat.dart +++ b/lib/src/chat.dart @@ -120,6 +120,6 @@ class Chat extends Base { Map getDefaultArguments() => {Base.argumentId: _id}; static Function getCreator() { - return (id) => new Chat._internal(id); + return (id) => Chat._internal(id); } } diff --git a/lib/src/chatmsg.dart b/lib/src/chatmsg.dart index 7cbe6e0..ed4fb7b 100644 --- a/lib/src/chatmsg.dart +++ b/lib/src/chatmsg.dart @@ -197,6 +197,6 @@ class ChatMsg extends Base { getSummaryArguments(int characterCount) => {Base.argumentId: _id, Base.argumentCount: characterCount}; static Function getCreator() { - return (id) => new ChatMsg._internal(id); + return (id) => ChatMsg._internal(id); } } diff --git a/lib/src/contact.dart b/lib/src/contact.dart index 1fd3328..8871cd4 100644 --- a/lib/src/contact.dart +++ b/lib/src/contact.dart @@ -111,6 +111,6 @@ class Contact extends Base { Map getDefaultArguments() => {Base.argumentId: _id}; static Function getCreator() { - return (id) => new Contact._internal(id); + return (id) => Contact._internal(id); } } From 7bea46fe6be3d30e3d056767d29be3eef88158e2 Mon Sep 17 00:00:00 2001 From: Boehrsi Date: Wed, 6 May 2020 13:22:25 +0200 Subject: [PATCH 03/16] Logging --- .../deltachatcore/DeltaChatCorePlugin.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/android/src/main/java/com/openxchange/deltachatcore/DeltaChatCorePlugin.java b/android/src/main/java/com/openxchange/deltachatcore/DeltaChatCorePlugin.java index 2a5e53d..58d0d68 100644 --- a/android/src/main/java/com/openxchange/deltachatcore/DeltaChatCorePlugin.java +++ b/android/src/main/java/com/openxchange/deltachatcore/DeltaChatCorePlugin.java @@ -44,6 +44,7 @@ import android.content.Context; +import android.util.Log; import com.b44t.messenger.DcChat; import com.b44t.messenger.DcContact; @@ -120,11 +121,13 @@ public class DeltaChatCorePlugin implements MethodCallHandler, PluginRegistry.Vi @SuppressWarnings("WeakerAccess") public DeltaChatCorePlugin() { + Log.d("dboehrs", "DeltaChatCorePlugin: constructor"); // Required for Flutter plugin embedding v2 } // Flutter plugin v1 embedding public static void registerWith(Registrar registrar) { + Log.d("dboehrs", "registerWith: "); logEventAndDelegate(null, DEBUG, TAG, "Attaching plugin via v1 embedding"); DeltaChatCorePlugin plugin = new DeltaChatCorePlugin(); plugin.onAttachedToEngine(registrar.context(), registrar.messenger()); @@ -134,11 +137,13 @@ public static void registerWith(Registrar registrar) { // Flutter plugin v2 embedding @Override public void onAttachedToEngine(FlutterPluginBinding binding) { + Log.d("dboehrs", "onAttachedToEngine: Override"); logEventAndDelegate(eventChannelHandler, DEBUG, TAG, "Attaching plugin via v2 embedding"); onAttachedToEngine(binding.getApplicationContext(), binding.getBinaryMessenger()); } private void onAttachedToEngine(Context context, BinaryMessenger messenger) { + Log.d("dboehrs", "onAttachedToEngine: "); this.context = context; this.messenger = messenger; methodChannel = new MethodChannel(messenger, CHANNEL_DELTA_CHAT_CORE); @@ -147,27 +152,32 @@ private void onAttachedToEngine(Context context, BinaryMessenger messenger) { @Override public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + Log.d("dboehrs", "onDetachedFromEngine: "); logEventAndDelegate(eventChannelHandler, DEBUG, TAG, "Detaching plugin via v2 embedding"); tearDownJavaInstance(); } @Override public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { + Log.d("dboehrs", "onAttachedToActivity: "); // No implementation required, as no activity context is used by the plugin } @Override public void onDetachedFromActivityForConfigChanges() { + Log.d("dboehrs", "onDetachedFromActivityForConfigChanges: "); // No implementation required, as no activity context is used by the plugin } @Override public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { + Log.d("dboehrs", "onReattachedToActivityForConfigChanges: "); // No implementation required, as no activity context is used by the plugin } @Override public void onDetachedFromActivity() { + Log.d("dboehrs", "onDetachedFromActivity: "); logEventAndDelegate(eventChannelHandler, DEBUG, TAG, "Stopping threads via v2 embedding"); stopNativeInteractionManager(); } @@ -268,11 +278,13 @@ private void systemInfo(Result result) { } private void tearDown(Result result) { + Log.d("dboehrs", "tearDown: "); stopNativeInteractionManager(); result.success(null); } private void tearDownJavaInstance() { + Log.d("dboehrs", "tearDownJavaInstance: "); if (methodChannel != null) { methodChannel.setMethodCallHandler(null); methodChannel = null; @@ -281,6 +293,7 @@ private void tearDownJavaInstance() { } private void stopNativeInteractionManager() { + Log.d("dboehrs", "stopNativeInteractionManager: "); nativeInteractionManager.stop(); } @@ -306,6 +319,7 @@ private void handleMessageCalls(MethodCall methodCall, Result result) { @Override public boolean onViewDestroy(FlutterNativeView flutterNativeView) { + Log.d("dboehrs", "onViewDestroy: "); stopNativeInteractionManager(); return false; } From 2dd5c3054f6a2b02353cda457b9b64c2cdec9bac Mon Sep 17 00:00:00 2001 From: Boehrsi Date: Thu, 7 May 2020 08:08:25 +0200 Subject: [PATCH 04/16] Plugin v2 only Remove unneeded code Streamline flows Optimize tearDown --- .../deltachatcore/DeltaChatCorePlugin.java | 113 +++------ .../NativeInteractionManager.java | 229 +++++++++--------- .../android/app/src/main/AndroidManifest.xml | 11 +- .../deltachatcoreexample/MainActivity.java | 13 - 4 files changed, 149 insertions(+), 217 deletions(-) delete mode 100644 example/android/app/src/main/java/com/openxchange/deltachatcoreexample/MainActivity.java diff --git a/android/src/main/java/com/openxchange/deltachatcore/DeltaChatCorePlugin.java b/android/src/main/java/com/openxchange/deltachatcore/DeltaChatCorePlugin.java index 58d0d68..adbc659 100644 --- a/android/src/main/java/com/openxchange/deltachatcore/DeltaChatCorePlugin.java +++ b/android/src/main/java/com/openxchange/deltachatcore/DeltaChatCorePlugin.java @@ -61,21 +61,16 @@ import androidx.annotation.NonNull; import io.flutter.embedding.engine.plugins.FlutterPlugin; -import io.flutter.embedding.engine.plugins.activity.ActivityAware; -import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel.MethodCallHandler; import io.flutter.plugin.common.MethodChannel.Result; -import io.flutter.plugin.common.PluginRegistry; -import io.flutter.plugin.common.PluginRegistry.Registrar; -import io.flutter.view.FlutterNativeView; import static android.util.Log.DEBUG; import static com.openxchange.deltachatcore.Utils.logEventAndDelegate; -public class DeltaChatCorePlugin implements MethodCallHandler, PluginRegistry.ViewDestroyListener, FlutterPlugin, ActivityAware { +public class DeltaChatCorePlugin implements MethodCallHandler, FlutterPlugin { static final String TAG = "coi"; private static final String LIBRARY_NAME = "native-utils"; @@ -90,7 +85,6 @@ public class DeltaChatCorePlugin implements MethodCallHandler, PluginRegistry.Vi private static final String METHOD_PREFIX_MSG = "msg"; private static final String METHOD_BASE_INIT = "base_init"; - private static final String METHOD_BASE_SYSTEM_INFO = "base_systemInfo"; private static final String METHOD_BASE_TEAR_DOWN = "base_tearDown"; private static final String METHOD_BASE_LOGOUT = "base_logout"; @@ -102,84 +96,50 @@ public class DeltaChatCorePlugin implements MethodCallHandler, PluginRegistry.Vi private static final String CACHE_IDENTIFIER_CHAT_MESSAGE = "chatMessage"; private static final String CACHE_IDENTIFIER_CONTACT = "contact"; + // Only change during onAttachedToEngine / onDetachedFromEngine private Context context; private BinaryMessenger messenger; private MethodChannel methodChannel; + // Clear on tearDown private final IdCache chatCache = new IdCache<>(); private final IdCache contactCache = new IdCache<>(); private final IdCache messageCache = new IdCache<>(); + // Null on tearDown private NativeInteractionManager nativeInteractionManager; private ChatCallHandler chatCallHandler; private ChatListCallHandler chatListCallHandler; private ContactCallHandler contactCallHandler; private ContextCallHandler contextCallHandler; private MessageCallHandler messageCallHandler; - @SuppressWarnings("FieldCanBeLocal") private EventChannelHandler eventChannelHandler; - @SuppressWarnings("WeakerAccess") public DeltaChatCorePlugin() { - Log.d("dboehrs", "DeltaChatCorePlugin: constructor"); // Required for Flutter plugin embedding v2 } - // Flutter plugin v1 embedding - public static void registerWith(Registrar registrar) { - Log.d("dboehrs", "registerWith: "); - logEventAndDelegate(null, DEBUG, TAG, "Attaching plugin via v1 embedding"); - DeltaChatCorePlugin plugin = new DeltaChatCorePlugin(); - plugin.onAttachedToEngine(registrar.context(), registrar.messenger()); - registrar.addViewDestroyListener(plugin); - } - - // Flutter plugin v2 embedding @Override public void onAttachedToEngine(FlutterPluginBinding binding) { - Log.d("dboehrs", "onAttachedToEngine: Override"); + Log.d("dboehrs" + this.hashCode(), "onAttachedToEngine:"); logEventAndDelegate(eventChannelHandler, DEBUG, TAG, "Attaching plugin via v2 embedding"); - onAttachedToEngine(binding.getApplicationContext(), binding.getBinaryMessenger()); - } - - private void onAttachedToEngine(Context context, BinaryMessenger messenger) { - Log.d("dboehrs", "onAttachedToEngine: "); - this.context = context; - this.messenger = messenger; + context = binding.getApplicationContext(); + messenger = binding.getBinaryMessenger(); methodChannel = new MethodChannel(messenger, CHANNEL_DELTA_CHAT_CORE); methodChannel.setMethodCallHandler(this); } @Override public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { - Log.d("dboehrs", "onDetachedFromEngine: "); + Log.d("dboehrs" + this.hashCode(), "onDetachedFromEngine: "); logEventAndDelegate(eventChannelHandler, DEBUG, TAG, "Detaching plugin via v2 embedding"); - tearDownJavaInstance(); - } - - @Override - public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { - Log.d("dboehrs", "onAttachedToActivity: "); - // No implementation required, as no activity context is used by the plugin - } - - @Override - public void onDetachedFromActivityForConfigChanges() { - Log.d("dboehrs", "onDetachedFromActivityForConfigChanges: "); - // No implementation required, as no activity context is used by the plugin - } - - @Override - public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) { - Log.d("dboehrs", "onReattachedToActivityForConfigChanges: "); - // No implementation required, as no activity context is used by the plugin - } - - @Override - public void onDetachedFromActivity() { - Log.d("dboehrs", "onDetachedFromActivity: "); - logEventAndDelegate(eventChannelHandler, DEBUG, TAG, "Stopping threads via v2 embedding"); - stopNativeInteractionManager(); + context = null; + messenger = null; + if (methodChannel != null) { + methodChannel.setMethodCallHandler(null); + methodChannel = null; + } + tearDown(null); } @Override @@ -244,9 +204,6 @@ private void handleBaseCalls(MethodCall methodCall, Result result) { case METHOD_BASE_INIT: init(methodCall, result); break; - case METHOD_BASE_SYSTEM_INFO: - systemInfo(result); - break; case METHOD_BASE_TEAR_DOWN: case METHOD_BASE_LOGOUT: tearDown(result); @@ -273,28 +230,23 @@ private void init(MethodCall methodCall, Result result) { result.success(nativeInteractionManager.getDbPath()); } - private void systemInfo(Result result) { - result.success(android.os.Build.VERSION.RELEASE); - } - private void tearDown(Result result) { - Log.d("dboehrs", "tearDown: "); - stopNativeInteractionManager(); - result.success(null); - } - - private void tearDownJavaInstance() { - Log.d("dboehrs", "tearDownJavaInstance: "); - if (methodChannel != null) { - methodChannel.setMethodCallHandler(null); - methodChannel = null; + Log.d("dboehrs" + this.hashCode(), "tearDown: "); + nativeInteractionManager.stop(); + nativeInteractionManager = null; + contextCallHandler = null; + chatListCallHandler = null; + messageCallHandler = null; + contactCallHandler = null; + chatCallHandler = null; + if (result != null) { + result.success(null); } eventChannelHandler.close(); - } - - private void stopNativeInteractionManager() { - Log.d("dboehrs", "stopNativeInteractionManager: "); - nativeInteractionManager.stop(); + eventChannelHandler = null; + chatCache.clear(); + contactCache.clear(); + messageCache.clear(); } private void handleContextCalls(MethodCall methodCall, Result result) { @@ -316,11 +268,4 @@ private void handleContactCalls(MethodCall methodCall, Result result) { private void handleMessageCalls(MethodCall methodCall, Result result) { messageCallHandler.handleCall(methodCall, result); } - - @Override - public boolean onViewDestroy(FlutterNativeView flutterNativeView) { - Log.d("dboehrs", "onViewDestroy: "); - stopNativeInteractionManager(); - return false; - } } diff --git a/android/src/main/java/com/openxchange/deltachatcore/NativeInteractionManager.java b/android/src/main/java/com/openxchange/deltachatcore/NativeInteractionManager.java index 8557eb4..fd3d6fc 100644 --- a/android/src/main/java/com/openxchange/deltachatcore/NativeInteractionManager.java +++ b/android/src/main/java/com/openxchange/deltachatcore/NativeInteractionManager.java @@ -81,94 +81,59 @@ public class NativeInteractionManager extends DcContext { logEventAndDelegate(eventChannelHandler, ERROR, TAG, "Cannot create wakeLocks"); } start(); - ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - if (connectivityManager != null) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - registerConnectivityReceiverAndroidN(connectivityManager); - } else { - registerConnectivityReceiver(context, connectivityManager); - } - } + setupConnectivityObserver(context); } - @SuppressWarnings("deprecation") - private void registerConnectivityReceiver(Context context, ConnectivityManager connectivityManager) { - context.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); - boolean isConnected = networkInfo != null && networkInfo.isConnected(); - if (isConnected) { - startOnNetworkAvailable(); - } - } - }, new IntentFilter(android.net.ConnectivityManager.CONNECTIVITY_ACTION)); - } + @Override + public long handleEvent(final int eventId, long data1, long data2) { + switch (eventId) { + case DC_EVENT_INFO: + logEventAndDelegate(eventChannelHandler, INFO, TAG, dataToString(data2)); + break; - private void startOnNetworkAvailable() { - logEventAndDelegate(eventChannelHandler, INFO, TAG, "###################### Network is connected again. ######################"); - start(); - } + case DC_EVENT_WARNING: + logEventAndDelegate(eventChannelHandler, WARN, TAG, dataToString(data2)); + break; - @RequiresApi(api = Build.VERSION_CODES.N) - private void registerConnectivityReceiverAndroidN(ConnectivityManager connectivityManager) { - if (connectivityManager != null) { - connectivityManager.registerDefaultNetworkCallback(new ConnectivityManager.NetworkCallback() { - @Override - public void onAvailable(Network network) { - startOnNetworkAvailable(); - } - }); + case DC_EVENT_ERROR: + // Intended fall through + case DC_EVENT_ERROR_NETWORK: + // Intended fall through + case DC_EVENT_ERROR_SELF_NOT_IN_GROUP: + delegateError(eventId, data1, dataToString(data2)); + break; + default: + delegateEvent(eventId, data1, data2); + break; } + return 0; } - private void start() { - logEventAndDelegate(eventChannelHandler, DEBUG, TAG, "Starting threads"); - startThreads(INTERRUPT_IDLE); - waitForThreadsRunning(); + private void delegateEvent(int eventId, long data1, long data2) { + final Object data1Object = getData1Object(eventId, data1); + final Object data2Object = getData2Object(eventId, data2); + if (eventChannelHandler != null) { + eventChannelHandler.handleEvent(eventId, data1Object, data2Object); + } } - private void waitForThreadsRunning() { - try { - synchronized (imapThreadSynchronized) { - while (!imapThreadStarted) { - imapThreadSynchronized.wait(); - } - } - - synchronized (mvboxThreadSynchronized) { - while (!mvboxThreadStarted) { - mvboxThreadSynchronized.wait(); - } - } + private Object getData1Object(int eventId, long data1) { + return data1IsString(eventId) ? dataToString(data1) : data1; + } - synchronized (sentBoxThreadSynchronized) { - while (!sentBoxThreadStarted) { - sentBoxThreadSynchronized.wait(); - } - } + private Object getData2Object(int eventId, long data2) { + return data2IsString(eventId) ? dataToString(data2) : data2; + } - synchronized (smtpThreadSynchronized) { - while (!smtpThreadStarted) { - smtpThreadSynchronized.wait(); - } - } - } catch (Exception e) { - e.printStackTrace(); - } + private void delegateError(int eventId, long data1, String error) { + final Object data1Object = getData1Object(eventId, data1); + eventChannelHandler.handleEvent(eventId, data1Object, error); } - void stop() { - logEventAndDelegate(eventChannelHandler, DEBUG, TAG, "Stopping threads"); - stopThreads(); - interruptImapIdle(); - interruptMvboxIdle(); - interruptSentboxIdle(); - interruptSmtpIdle(); - imapThread = null; - mvboxThread = null; - sentBoxThread = null; - smtpThread = null; + private void start() { + logEventAndDelegate(eventChannelHandler, DEBUG, TAG, "Starting threads"); + startThreads(INTERRUPT_IDLE); + waitForThreadsRunning(); } private void startThreads(@SuppressWarnings("SameParameterValue") int flags) { @@ -337,6 +302,51 @@ private void startThreads(@SuppressWarnings("SameParameterValue") int flags) { } } + private void waitForThreadsRunning() { + try { + synchronized (imapThreadSynchronized) { + while (!imapThreadStarted) { + imapThreadSynchronized.wait(); + } + } + + synchronized (mvboxThreadSynchronized) { + while (!mvboxThreadStarted) { + mvboxThreadSynchronized.wait(); + } + } + + synchronized (sentBoxThreadSynchronized) { + while (!sentBoxThreadStarted) { + sentBoxThreadSynchronized.wait(); + } + } + + synchronized (smtpThreadSynchronized) { + while (!smtpThreadStarted) { + smtpThreadSynchronized.wait(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + void stop() { + logEventAndDelegate(eventChannelHandler, DEBUG, TAG, "Stopping threads"); + stopThreads(); + interruptImapIdle(); + interruptMvboxIdle(); + interruptSentboxIdle(); + interruptSmtpIdle(); + imapThread = null; + mvboxThread = null; + sentBoxThread = null; + smtpThread = null; + logEventAndDelegate(eventChannelHandler, DEBUG, TAG, "Closing database"); + close(); + } + private void stopThreads() { if (imapThread != null) { imapThread.interrupt(); @@ -352,50 +362,45 @@ private void stopThreads() { } } - @Override - public long handleEvent(final int eventId, long data1, long data2) { - switch (eventId) { - case DC_EVENT_INFO: - logEventAndDelegate(eventChannelHandler, INFO, TAG, dataToString(data2)); - break; - - case DC_EVENT_WARNING: - logEventAndDelegate(eventChannelHandler, WARN, TAG, dataToString(data2)); - break; - - case DC_EVENT_ERROR: - // Intended fall through - case DC_EVENT_ERROR_NETWORK: - // Intended fall through - case DC_EVENT_ERROR_SELF_NOT_IN_GROUP: - delegateError(eventId, data1, dataToString(data2)); - break; - default: - delegateEvent(eventId, data1, data2); - break; - } - return 0; - } - - private void delegateEvent(int eventId, long data1, long data2) { - final Object data1Object = getData1Object(eventId, data1); - final Object data2Object = getData2Object(eventId, data2); - if (eventChannelHandler != null) { - eventChannelHandler.handleEvent(eventId, data1Object, data2Object); + private void setupConnectivityObserver(Context context) { + ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + if (connectivityManager != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + registerConnectivityReceiverAndroidN(connectivityManager); + } else { + registerConnectivityReceiver(context, connectivityManager); + } } } - private Object getData1Object(int eventId, long data1) { - return data1IsString(eventId) ? dataToString(data1) : data1; + private void registerConnectivityReceiver(Context context, ConnectivityManager connectivityManager) { + context.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); + boolean isConnected = networkInfo != null && networkInfo.isConnected(); + if (isConnected) { + startOnNetworkAvailable(); + } + } + }, new IntentFilter(android.net.ConnectivityManager.CONNECTIVITY_ACTION)); } - private Object getData2Object(int eventId, long data2) { - return data2IsString(eventId) ? dataToString(data2) : data2; + private void startOnNetworkAvailable() { + logEventAndDelegate(eventChannelHandler, INFO, TAG, "###################### Network is connected again. ######################"); + start(); } - private void delegateError(int eventId, long data1, String error) { - final Object data1Object = getData1Object(eventId, data1); - eventChannelHandler.handleEvent(eventId, data1Object, error); + @RequiresApi(api = Build.VERSION_CODES.N) + private void registerConnectivityReceiverAndroidN(ConnectivityManager connectivityManager) { + if (connectivityManager != null) { + connectivityManager.registerDefaultNetworkCallback(new ConnectivityManager.NetworkCallback() { + @Override + public void onAvailable(Network network) { + startOnNetworkAvailable(); + } + }); + } } String getDbPath() { diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 6f82ca3..29955de 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -13,23 +13,18 @@ additional functionality it is fine to subclass or reimplement FlutterApplication and put your custom class here. --> - + android:name="flutterEmbedding" + android:value="2" /> diff --git a/example/android/app/src/main/java/com/openxchange/deltachatcoreexample/MainActivity.java b/example/android/app/src/main/java/com/openxchange/deltachatcoreexample/MainActivity.java deleted file mode 100644 index ee5fde1..0000000 --- a/example/android/app/src/main/java/com/openxchange/deltachatcoreexample/MainActivity.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.openxchange.deltachatcoreexample; - -import android.os.Bundle; -import io.flutter.app.FlutterActivity; -import io.flutter.plugins.GeneratedPluginRegistrant; - -public class MainActivity extends FlutterActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - GeneratedPluginRegistrant.registerWith(this); - } -} From dc3fbb2ca656e8dff8495b70916f8a8a8c35837c Mon Sep 17 00:00:00 2001 From: Boehrsi Date: Thu, 7 May 2020 11:05:45 +0200 Subject: [PATCH 05/16] Update to latest DCC API --- android/jni/dc_wrapper.c | 216 +++++++++++++++--- .../main/java/com/b44t/messenger/DcChat.java | 7 +- .../java/com/b44t/messenger/DcContact.java | 3 +- .../java/com/b44t/messenger/DcContext.java | 33 ++- .../java/com/b44t/messenger/DcProvider.java | 26 +++ 5 files changed, 236 insertions(+), 49 deletions(-) create mode 100644 android/src/main/java/com/b44t/messenger/DcProvider.java diff --git a/android/jni/dc_wrapper.c b/android/jni/dc_wrapper.c index 3db3b4d..b67c3eb 100644 --- a/android/jni/dc_wrapper.c +++ b/android/jni/dc_wrapper.c @@ -25,15 +25,50 @@ #include #include "../../delta_chat_core/deltachat-ffi/deltachat.h" +#include +#include static dc_msg_t* get_dc_msg(JNIEnv *env, jobject obj); + +// passing a NULL-jstring results in a NULL-ptr - this is needed by functions using eg. NULL for "delete" #define CHAR_REF(a) \ - const char* a##Ptr = (a)? (*env)->GetStringUTFChars(env, (a), 0) : NULL; // passing a NULL-jstring results in a NULL-ptr - this is needed by functions using eg. NULL for "delete" + char* a##Ptr = char_ref__(env, (a)); +static char* char_ref__(JNIEnv* env, jstring a) { + if (a==NULL) { + return NULL; + } + + /* we do not use the JNI functions GetStringUTFChars()/ReleaseStringUTFChars() + as they do not work on some older systems for code points >0xffff, eg. emojos. + as a workaround, we're calling back to java-land's String.getBytes() which works as expected */ + static jclass s_strCls = NULL; + static jmethodID s_getBytes = NULL; + static jclass s_strEncode = NULL; + if (s_getBytes==NULL) { + s_strCls = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "java/lang/String")); + s_getBytes = (*env)->GetMethodID(env, s_strCls, "getBytes", "(Ljava/lang/String;)[B"); + s_strEncode = (*env)->NewGlobalRef(env, (*env)->NewStringUTF(env, "UTF-8")); + } + + const jbyteArray stringJbytes = (jbyteArray)(*env)->CallObjectMethod(env, a, s_getBytes, s_strEncode); + const jsize length = (*env)->GetArrayLength(env, stringJbytes); + jbyte* pBytes = (*env)->GetByteArrayElements(env, stringJbytes, NULL); + if (pBytes==NULL) { + return NULL; + } + + char* cstr = strndup((const char*)pBytes, length); + + (*env)->ReleaseByteArrayElements(env, stringJbytes, pBytes, JNI_ABORT); + (*env)->DeleteLocalRef(env, stringJbytes); + + return cstr; +} #define CHAR_UNREF(a) \ - if(a) { (*env)->ReleaseStringUTFChars(env, (a), a##Ptr); } + free(a##Ptr); #define JSTRING_NEW(a) jstring_new__(env, (a)) static jstring jstring_new__(JNIEnv* env, const char* a) @@ -49,7 +84,7 @@ static jstring jstring_new__(JNIEnv* env, const char* a) static jclass s_strCls = NULL; static jmethodID s_strCtor = NULL; static jclass s_strEncode = NULL; - if (s_strCls==NULL) { + if (s_strCtor==NULL) { s_strCls = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "java/lang/String")); s_strCtor = (*env)->GetMethodID(env, s_strCls, "", "([BLjava/lang/String;)V"); s_strEncode = (*env)->NewGlobalRef(env, (*env)->NewStringUTF(env, "UTF-8")); @@ -57,7 +92,7 @@ static jstring jstring_new__(JNIEnv* env, const char* a) int a_bytes = strlen(a); jbyteArray array = (*env)->NewByteArray(env, a_bytes); - (*env)->SetByteArrayRegion(env, array, 0, a_bytes, a); + (*env)->SetByteArrayRegion(env, array, 0, a_bytes, (const jbyte*)a); jstring ret = (jstring) (*env)->NewObject(env, s_strCls, s_strCtor, array, s_strEncode); (*env)->DeleteLocalRef(env, array); /* we have to delete the reference as it is not returned to Java, AFAIK */ @@ -82,7 +117,7 @@ static jintArray dc_array2jintArray_n_unref(JNIEnv *env, dc_array_t* ca) if (ca) { if (icnt) { - uint32_t* ca_data = dc_array_get_raw(ca); + const uint32_t* ca_data = dc_array_get_raw(ca); if (sizeof(uint32_t)==sizeof(jint)) { (*env)->SetIntArrayRegion(env, ret, 0, icnt, (jint*)ca_data); } @@ -113,7 +148,7 @@ static uint32_t* jintArray2uint32Pointer(JNIEnv* env, jintArray ja, int* ret_icn int i, icnt = (*env)->GetArrayLength(env, ja); if (icnt > 0) { - const jint* temp = (*env)->GetIntArrayElements(env, ja, NULL); + jint* temp = (*env)->GetIntArrayElements(env, ja, NULL); if (temp) { ret = calloc(icnt, sizeof(uint32_t)); @@ -154,7 +189,7 @@ static dc_context_t* get_dc_context(JNIEnv *env, jobject obj) fid = (*env)->GetFieldID(env, cls, "contextCPtr", "J" /*Signature, J=long*/); } if (fid) { - return (dc_chat_t*)(*env)->GetLongField(env, obj, fid); + return (dc_context_t*)(*env)->GetLongField(env, obj, fid); } return NULL; } @@ -170,7 +205,7 @@ static uintptr_t s_context_callback_(dc_context_t* context, int event, uintptr_t return 0; /* may happen on startup */ } - (*jnicontext->jvm)->GetEnv(jnicontext->jvm, &env, JNI_VERSION_1_6); // as this function may be called from _any_ thread, we cannot use a static pointer to JNIEnv + (*jnicontext->jvm)->GetEnv(jnicontext->jvm, (void**)&env, JNI_VERSION_1_6); // as this function may be called from _any_ thread, we cannot use a static pointer to JNIEnv if (env==NULL) { return 0; /* may happen on startup */ } @@ -201,8 +236,13 @@ JNIEXPORT jlong Java_com_b44t_messenger_DcContext_createContextCPtr(JNIEnv *env, } -/* DcContext - open/configure/connect/fetch */ +JNIEXPORT void Java_com_b44t_messenger_DcContact_unrefContextCPtr(JNIEnv *env, jobject obj) +{ + dc_context_unref(get_dc_context(env, obj)); +} + +/* DcContext - open/configure/connect/fetch */ JNIEXPORT jint Java_com_b44t_messenger_DcContext_open(JNIEnv *env, jobject obj, jstring dbfile) { @@ -219,6 +259,23 @@ JNIEXPORT void Java_com_b44t_messenger_DcContext_close(JNIEnv *env, jobject obj) } +JNIEXPORT void Java_com_b44t_messenger_DcContext_setStockTranslation(JNIEnv *env, jobject obj, jint stock_id, jstring translation) +{ + CHAR_REF(translation); + dc_set_stock_translation(get_dc_context(env, obj), stock_id, translationPtr); + CHAR_UNREF(translation) +} + + +JNIEXPORT jboolean Java_com_b44t_messenger_DcContext_setConfigFromQr(JNIEnv *env, jobject obj, jstring qr) +{ + CHAR_REF(qr); + jboolean ret = dc_set_config_from_qr(get_dc_context(env, obj), qrPtr); + CHAR_UNREF(qr); + return ret; +} + + JNIEXPORT jstring Java_com_b44t_messenger_DcContext_getBlobdir(JNIEnv *env, jobject obj) { char* temp = dc_get_blobdir(get_dc_context(env, obj)); @@ -329,6 +386,7 @@ JNIEXPORT void Java_com_b44t_messenger_DcContext_performSmtpIdle(JNIEnv *env, jo dc_perform_smtp_idle(get_dc_context(env, obj)); } + JNIEXPORT void Java_com_b44t_messenger_DcContext_interruptSmtpIdle(JNIEnv *env, jobject obj) { dc_interrupt_smtp_idle(get_dc_context(env, obj)); @@ -444,7 +502,7 @@ JNIEXPORT jint Java_com_b44t_messenger_DcContext_getChatIdByContactId(JNIEnv *en JNIEXPORT void Java_com_b44t_messenger_DcContext_markseenMsgs(JNIEnv *env, jobject obj, jintArray msg_ids) { int msg_ids_cnt = 0; - const uint32_t* msg_ids_ptr = jintArray2uint32Pointer(env, msg_ids, &msg_ids_cnt); + uint32_t* msg_ids_ptr = jintArray2uint32Pointer(env, msg_ids, &msg_ids_cnt); dc_markseen_msgs(get_dc_context(env, obj), msg_ids_ptr, msg_ids_cnt); free(msg_ids_ptr); } @@ -468,9 +526,9 @@ JNIEXPORT void Java_com_b44t_messenger_DcContext_marknoticedContact(JNIEnv *env, } -JNIEXPORT void Java_com_b44t_messenger_DcContext_archiveChat(JNIEnv *env, jobject obj, jint chat_id, jint archive) +JNIEXPORT void Java_com_b44t_messenger_DcContext_setChatVisibility(JNIEnv *env, jobject obj, jint chat_id, jint visibility) { - dc_archive_chat(get_dc_context(env, obj), chat_id, archive); + dc_set_chat_visibility(get_dc_context(env, obj), chat_id, visibility); } @@ -558,6 +616,13 @@ JNIEXPORT jint Java_com_b44t_messenger_DcContext_getFreshMsgCount(JNIEnv *env, j } +JNIEXPORT jint Java_com_b44t_messenger_DcContext_estimateDeletionCount(JNIEnv *env, jobject obj, jboolean from_server, jlong seconds) +{ + return dc_estimate_deletion_cnt(get_dc_context(env, obj), from_server, seconds); +} + + + JNIEXPORT jlong Java_com_b44t_messenger_DcContext_getMsgCPtr(JNIEnv *env, jobject obj, jint id) { return (jlong)dc_get_msg(get_dc_context(env, obj), id); @@ -582,7 +647,7 @@ JNIEXPORT jstring Java_com_b44t_messenger_DcContext_getMsgInfo(JNIEnv *env, jobj JNIEXPORT void Java_com_b44t_messenger_DcContext_deleteMsgs(JNIEnv *env, jobject obj, jintArray msg_ids) { int msg_ids_cnt = 0; - const uint32_t* msg_ids_ptr = jintArray2uint32Pointer(env, msg_ids, &msg_ids_cnt); + uint32_t* msg_ids_ptr = jintArray2uint32Pointer(env, msg_ids, &msg_ids_cnt); dc_delete_msgs(get_dc_context(env, obj), msg_ids_ptr, msg_ids_cnt); free(msg_ids_ptr); } @@ -591,7 +656,7 @@ JNIEXPORT void Java_com_b44t_messenger_DcContext_deleteMsgs(JNIEnv *env, jobject JNIEXPORT void Java_com_b44t_messenger_DcContext_forwardMsgs(JNIEnv *env, jobject obj, jintArray msg_ids, jint chat_id) { int msg_ids_cnt = 0; - const uint32_t* msg_ids_ptr = jintArray2uint32Pointer(env, msg_ids, &msg_ids_cnt); + uint32_t* msg_ids_ptr = jintArray2uint32Pointer(env, msg_ids, &msg_ids_cnt); dc_forward_msgs(get_dc_context(env, obj), msg_ids_ptr, msg_ids_cnt, chat_id); free(msg_ids_ptr); } @@ -599,12 +664,11 @@ JNIEXPORT void Java_com_b44t_messenger_DcContext_forwardMsgs(JNIEnv *env, jobjec JNIEXPORT void Java_com_b44t_messenger_DcContext_starMsgs(JNIEnv *env, jobject obj, jintArray msg_ids, jint star) { int msg_ids_cnt = 0; - const uint32_t* msg_ids_ptr = jintArray2uint32Pointer(env, msg_ids, &msg_ids_cnt); + uint32_t* msg_ids_ptr = jintArray2uint32Pointer(env, msg_ids, &msg_ids_cnt); dc_star_msgs(get_dc_context(env, obj), msg_ids_ptr, msg_ids_cnt, star); free(msg_ids_ptr); } - JNIEXPORT jint Java_com_b44t_messenger_DcContext_prepareMsg(JNIEnv *env, jobject obj, jint chat_id, jobject msg) { return dc_prepare_msg(get_dc_context(env, obj), chat_id, get_dc_msg(env, msg)); @@ -626,6 +690,30 @@ JNIEXPORT jint Java_com_b44t_messenger_DcContext_sendTextMsg(JNIEnv *env, jobjec } +JNIEXPORT jint Java_com_b44t_messenger_DcContext_addDeviceMsg(JNIEnv *env, jobject obj, jstring label, jobject msg) +{ + CHAR_REF(label); + int msg_id = dc_add_device_msg(get_dc_context(env, obj), labelPtr, get_dc_msg(env, msg)); + CHAR_UNREF(label); + return msg_id; +} + + +JNIEXPORT jboolean Java_com_b44t_messenger_DcContext_wasDeviceMsgEverAdded(JNIEnv *env, jobject obj, jstring label) +{ + CHAR_REF(label); + jboolean ret = dc_was_device_msg_ever_added(get_dc_context(env, obj), labelPtr) != 0; + CHAR_UNREF(label); + return ret; +} + + +JNIEXPORT void Java_com_b44t_messenger_DcContext_updateDeviceChats(JNIEnv *env, jobject obj) +{ + dc_update_device_chats(get_dc_context(env, obj)); +} + + /* DcContext - handle config */ JNIEXPORT void Java_com_b44t_messenger_DcContext_setConfig(JNIEnv *env, jobject obj, jstring key, jstring value /*may be NULL*/) @@ -757,7 +845,7 @@ JNIEXPORT jstring Java_com_b44t_messenger_DcContext_imexHasBackup(JNIEnv *env, j JNIEXPORT void Java_com_b44t_messenger_DcContext_emptyServer(JNIEnv *env, jobject obj, jint flags) { - //dc_empty_server(get_dc_context(env, obj), flags); + dc_empty_server(get_dc_context(env, obj), flags); } @@ -799,6 +887,15 @@ JNIEXPORT void Java_com_b44t_messenger_DcContext_deleteAllLocations(JNIEnv *env, dc_delete_all_locations(get_dc_context(env, obj)); } + +JNIEXPORT jlong Java_com_b44t_messenger_DcContext_getProviderFromEmailCPtr(JNIEnv *env, jobject obj, jstring email) +{ + CHAR_REF(email); + jlong ret = (jlong)dc_provider_new_from_email(get_dc_context(env, obj), emailPtr); + CHAR_UNREF(email); + return ret; +} + /* DcContext - COI */ JNIEXPORT jint Java_com_b44t_messenger_DcContext_isCoiSupported(JNIEnv *env, jobject obj) @@ -1050,15 +1147,15 @@ JNIEXPORT jboolean Java_com_b44t_messenger_DcChat_isGroup(JNIEnv *env, jobject o } -JNIEXPORT jint Java_com_b44t_messenger_DcChat_getArchived(JNIEnv *env, jobject obj) +JNIEXPORT jint Java_com_b44t_messenger_DcChat_getVisibility(JNIEnv *env, jobject obj) { - return dc_chat_get_archived(get_dc_chat(env, obj)); + return dc_chat_get_visibility(get_dc_chat(env, obj)); } JNIEXPORT jstring Java_com_b44t_messenger_DcChat_getName(JNIEnv *env, jobject obj) { - const char* temp = dc_chat_get_name(get_dc_chat(env, obj)); + char* temp = dc_chat_get_name(get_dc_chat(env, obj)); jstring ret = JSTRING_NEW(temp); dc_str_unref(temp); return ret; @@ -1067,7 +1164,7 @@ JNIEXPORT jstring Java_com_b44t_messenger_DcChat_getName(JNIEnv *env, jobject ob JNIEXPORT jstring Java_com_b44t_messenger_DcChat_getProfileImage(JNIEnv *env, jobject obj) { - const char* temp = dc_chat_get_profile_image(get_dc_chat(env, obj)); + char* temp = dc_chat_get_profile_image(get_dc_chat(env, obj)); jstring ret = JSTRING_NEW(temp); dc_str_unref(temp); return ret; @@ -1098,6 +1195,12 @@ JNIEXPORT jboolean Java_com_b44t_messenger_DcChat_isDeviceTalk(JNIEnv *env, jobj } +JNIEXPORT jboolean Java_com_b44t_messenger_DcChat_canSend(JNIEnv *env, jobject obj) +{ + return dc_chat_can_send(get_dc_chat(env, obj))!=0; +} + + JNIEXPORT jboolean Java_com_b44t_messenger_DcChat_isVerified(JNIEnv *env, jobject obj) { return dc_chat_is_verified(get_dc_chat(env, obj))!=0; @@ -1430,7 +1533,7 @@ JNIEXPORT jint Java_com_b44t_messenger_DcContact_getId(JNIEnv *env, jobject obj) JNIEXPORT jstring Java_com_b44t_messenger_DcContact_getName(JNIEnv *env, jobject obj) { - const char* temp = dc_contact_get_name(get_dc_contact(env, obj)); + char* temp = dc_contact_get_name(get_dc_contact(env, obj)); jstring ret = JSTRING_NEW(temp); dc_str_unref(temp); return ret; @@ -1439,7 +1542,7 @@ JNIEXPORT jstring Java_com_b44t_messenger_DcContact_getName(JNIEnv *env, jobject JNIEXPORT jstring Java_com_b44t_messenger_DcContact_getDisplayName(JNIEnv *env, jobject obj) { - const char* temp = dc_contact_get_display_name(get_dc_contact(env, obj)); + char* temp = dc_contact_get_display_name(get_dc_contact(env, obj)); jstring ret = JSTRING_NEW(temp); dc_str_unref(temp); return ret; @@ -1448,7 +1551,7 @@ JNIEXPORT jstring Java_com_b44t_messenger_DcContact_getDisplayName(JNIEnv *env, JNIEXPORT jstring Java_com_b44t_messenger_DcContact_getFirstName(JNIEnv *env, jobject obj) { - const char* temp = dc_contact_get_first_name(get_dc_contact(env, obj)); + char* temp = dc_contact_get_first_name(get_dc_contact(env, obj)); jstring ret = JSTRING_NEW(temp); dc_str_unref(temp); return ret; @@ -1457,7 +1560,7 @@ JNIEXPORT jstring Java_com_b44t_messenger_DcContact_getFirstName(JNIEnv *env, jo JNIEXPORT jstring Java_com_b44t_messenger_DcContact_getAddr(JNIEnv *env, jobject obj) { - const char* temp = dc_contact_get_addr(get_dc_contact(env, obj)); + char* temp = dc_contact_get_addr(get_dc_contact(env, obj)); jstring ret = JSTRING_NEW(temp); dc_str_unref(temp); return ret; @@ -1466,7 +1569,7 @@ JNIEXPORT jstring Java_com_b44t_messenger_DcContact_getAddr(JNIEnv *env, jobject JNIEXPORT jstring Java_com_b44t_messenger_DcContact_getNameNAddr(JNIEnv *env, jobject obj) { - const char* temp = dc_contact_get_name_n_addr(get_dc_contact(env, obj)); + char* temp = dc_contact_get_name_n_addr(get_dc_contact(env, obj)); jstring ret = JSTRING_NEW(temp); dc_str_unref(temp); return ret; @@ -1475,7 +1578,7 @@ JNIEXPORT jstring Java_com_b44t_messenger_DcContact_getNameNAddr(JNIEnv *env, jo JNIEXPORT jstring Java_com_b44t_messenger_DcContact_getProfileImage(JNIEnv *env, jobject obj) { - const char* temp = dc_contact_get_profile_image(get_dc_contact(env, obj)); + char* temp = dc_contact_get_profile_image(get_dc_contact(env, obj)); jstring ret = JSTRING_NEW(temp); dc_str_unref(temp); return ret; @@ -1567,6 +1670,54 @@ JNIEXPORT void Java_com_b44t_messenger_DcLot_unrefLotCPtr(JNIEnv *env, jobject o } +/******************************************************************************* + * DcProvider + ******************************************************************************/ + + +static dc_provider_t* get_dc_provider(JNIEnv *env, jobject obj) +{ + static jfieldID fid = 0; + if (fid==0) { + jclass cls = (*env)->GetObjectClass(env, obj); + fid = (*env)->GetFieldID(env, cls, "providerCPtr", "J" /*Signature, J=long*/); + } + if (fid) { + return (dc_provider_t*)(*env)->GetLongField(env, obj, fid); + } + return NULL; +} + + +JNIEXPORT void Java_com_b44t_messenger_DcProvider_unrefProviderCPtr(JNIEnv *env, jobject obj) +{ + dc_provider_unref(get_dc_provider(env, obj)); +} + + +JNIEXPORT jint Java_com_b44t_messenger_DcProvider_getStatus(JNIEnv *env, jobject obj) +{ + return (jint)dc_provider_get_status(get_dc_provider(env, obj)); +} + + +JNIEXPORT jstring Java_com_b44t_messenger_DcProvider_getBeforeLoginHint(JNIEnv *env, jobject obj) +{ + char* temp = dc_provider_get_before_login_hint(get_dc_provider(env, obj)); + jstring ret = JSTRING_NEW(temp); + dc_str_unref(temp); + return ret; +} + + +JNIEXPORT jstring Java_com_b44t_messenger_DcProvider_getOverviewPage(JNIEnv *env, jobject obj) +{ + char* temp = dc_provider_get_overview_page(get_dc_provider(env, obj)); + jstring ret = JSTRING_NEW(temp); + dc_str_unref(temp); + return ret; +} + /******************************************************************************* * Tools ******************************************************************************/ @@ -1594,14 +1745,3 @@ JNIEXPORT jstring Java_com_b44t_messenger_DcContext_dataToString(JNIEnv *env, jc return JSTRING_NEW(cstring); } - -JNIEXPORT jlong Java_com_b44t_messenger_DcContext_stringToData(JNIEnv *env, jclass cls, jstring javaString) -{ - char* cstring = NULL; - if (javaString) { - CHAR_REF(javaString); - cstring = strdup(javaStringPtr); - CHAR_UNREF(javaString); - } - return (jlong)cstring; // the return value of stringToData() will be passed to c-land and free()'d there -} diff --git a/android/src/main/java/com/b44t/messenger/DcChat.java b/android/src/main/java/com/b44t/messenger/DcChat.java index de6c6fc..111f2a4 100644 --- a/android/src/main/java/com/b44t/messenger/DcChat.java +++ b/android/src/main/java/com/b44t/messenger/DcChat.java @@ -9,6 +9,10 @@ public class DcChat { public final static int DC_CHAT_ID_ALLDONE_HINT = 7; public final static int DC_CHAT_ID_LAST_SPECIAL = 9; + public final static int DC_CHAT_VISIBILITY_NORMAL = 0; + public final static int DC_CHAT_VISIBILITY_ARCHIVED = 1; + public final static int DC_CHAT_VISIBILITY_PINNED = 2; + public DcChat(long chatCPtr) { this.chatCPtr = chatCPtr; } @@ -21,13 +25,14 @@ public DcChat(long chatCPtr) { public native int getId (); public native boolean isGroup (); - public native int getArchived (); + public native int getVisibility (); public native String getName (); public native String getProfileImage (); public native int getColor (); public native boolean isUnpromoted (); public native boolean isSelfTalk (); public native boolean isDeviceTalk (); + public native boolean canSend (); public native boolean isVerified (); public native boolean isSendingLocations(); diff --git a/android/src/main/java/com/b44t/messenger/DcContact.java b/android/src/main/java/com/b44t/messenger/DcContact.java index a2468ec..07dc4d8 100644 --- a/android/src/main/java/com/b44t/messenger/DcContact.java +++ b/android/src/main/java/com/b44t/messenger/DcContact.java @@ -3,7 +3,8 @@ public class DcContact { public final static int DC_CONTACT_ID_SELF = 1; - public final static int DC_CONTACT_ID_DEVICE = 2; + public final static int DC_CONTACT_ID_INFO = 2; + public final static int DC_CONTACT_ID_DEVICE = 5; public final static int DC_CONTACT_ID_LAST_SPECIAL = 9; public final static int DC_CONTACT_ID_NEW_CONTACT = -1; // used by the UI, not valid to the core public final static int DC_CONTACT_ID_NEW_GROUP = -2; // - " - diff --git a/android/src/main/java/com/b44t/messenger/DcContext.java b/android/src/main/java/com/b44t/messenger/DcContext.java index 128dc0c..c8a690d 100644 --- a/android/src/main/java/com/b44t/messenger/DcContext.java +++ b/android/src/main/java/com/b44t/messenger/DcContext.java @@ -28,11 +28,6 @@ public class DcContext { public final static int DC_EVENT_IMEX_FILE_WRITTEN = 2052; public final static int DC_EVENT_SECUREJOIN_INVITER_PROGRESS = 2060; public final static int DC_EVENT_SECUREJOIN_JOINER_PROGRESS = 2061; - public final static int DC_EVENT_IS_OFFLINE = 2081; - public final static int DC_EVENT_GET_STRING = 2091; - public final static int DC_EVENT_GET_QUANTITIY_STRING = 2092; - public final static int DC_EVENT_HTTP_GET = 2100; - public final static int DC_EVENT_HTTP_POST = 2110; public final static int DC_IMEX_EXPORT_SELF_KEYS = 1; public final static int DC_IMEX_IMPORT_SELF_KEYS = 2; @@ -44,6 +39,7 @@ public class DcContext { public final static int DC_GCL_ARCHIVED_ONLY = 0x01; public final static int DC_GCL_NO_SPECIALS = 0x02; public final static int DC_GCL_ADD_ALLDONE_HINT = 0x04; + public final static int DC_GCL_FOR_FORWARDING = 0x08; public final static int DC_GCM_ADDDAYMARKER = 0x01; @@ -52,6 +48,7 @@ public class DcContext { public final static int DC_QR_FPR_OK = 210; public final static int DC_QR_FPR_MISMATCH = 220; public final static int DC_QR_FPR_WITHOUT_ADDR = 230; + public final static int DC_QR_ACCOUNT = 250; public final static int DC_QR_ADDR = 320; public final static int DC_QR_TEXT = 330; public final static int DC_QR_URL = 332; @@ -70,6 +67,9 @@ public class DcContext { public final static int DC_SHOW_EMAILS_ACCEPTED_CONTACTS = 1; public final static int DC_SHOW_EMAILS_ALL = 2; + public final static int DC_MEDIA_QUALITY_BALANCED = 0; + public final static int DC_MEDIA_QUALITY_WORSE = 1; + public final static int DC_EMPTY_MVBOX = 0x01; public final static int DC_EMPTY_INBOX = 0x02; @@ -78,8 +78,16 @@ public DcContext(String osName) { contextCPtr = createContextCPtr(osName); } + @Override + protected void finalize() throws Throwable { + super.finalize(); + unrefContextCPtr(); + contextCPtr = 0; + } + public native int open (String dbfile); public native void close (); + public native void setStockTranslation (int stockId, String translation); public native String getBlobdir (); public native void configure (); public native void stopOngoingProcess (); @@ -102,11 +110,12 @@ public DcContext(String osName) { public native void performSmtpJobs (); public native void performSmtpIdle (); - public native void interruptSmtpIdle (); + public native void interruptSmtpIdle (); public native void maybeNetwork (); public native void setConfig (String key, String value); public void setConfigInt (String key, int value) { setConfig(key, Integer.toString(value)); } + public native boolean setConfigFromQr (String qr); public native String getConfig (String key); public int getConfigInt (String key) { try{return Integer.parseInt(getConfig(key));} catch(Exception e) {} return 0; } @Deprecated public String getConfig (String key, String def) { return getConfig(key); } @@ -135,7 +144,7 @@ public DcContext(String osName) { public native void marknoticedChat (int chat_id); public native void marknoticedAllChats (); public native void marknoticedContact (int contact_id); - public native void archiveChat (int chat_id, int archive); + public native void setChatVisibility (int chat_id, int visibility); public native int getChatIdByContactId (int contact_id); public native int createChatByContactId(int contact_id); public native int createChatByMsgId (int msg_id); @@ -157,13 +166,16 @@ public DcContext(String osName) { public @NonNull DcMsg getMsg (int msg_id) { return new DcMsg(getMsgCPtr(msg_id)); } public native String getMsgInfo (int id); public native int getFreshMsgCount (int chat_id); + public native int estimateDeletionCount(boolean from_server, long seconds); public native void deleteMsgs (int msg_ids[]); public native void forwardMsgs (int msg_ids[], int chat_id); public native int prepareMsg (int chat_id, DcMsg msg); public native int sendMsg (int chat_id, DcMsg msg); public native int sendTextMsg (int chat_id, String text); public native void starMsgs (int msg_ids[], int star); - public native long checkQrCPtr (String qr); + public native int addDeviceMsg (String label, DcMsg msg); + public native boolean wasDeviceMsgEverAdded(String label); + public native void updateDeviceChats (); public @NonNull DcLot checkQr (String qr) { return new DcLot(checkQrCPtr(qr)); } public native String getSecurejoinQr (int chat_id); public native int joinSecurejoin (String qr); @@ -171,6 +183,7 @@ public DcContext(String osName) { public native boolean isSendingLocationsToChat(int chat_id); public @NonNull DcArray getLocations (int chat_id, int contact_id, long timestamp_start, long timestamp_end) { return new DcArray(getLocationsCPtr(chat_id, contact_id, timestamp_start, timestamp_end)); } public native void deleteAllLocations (); + public @Nullable DcProvider getProviderFromEmail (String email) { long cptr = getProviderFromEmailCPtr(email); return cptr!=0 ? new DcProvider(cptr) : null; } public native int isCoiSupported (); public native int isCoiEnabled (); public native int isWebPushSupported (); @@ -196,11 +209,11 @@ public long handleEvent(int event, long data1, long data2) { public native static boolean data1IsString(int event); public native static boolean data2IsString(int event); public native static String dataToString (long data); - public native static long stringToData (String str); // working with raw c-data private long contextCPtr; // CAVE: the name is referenced in the JNI private native long createContextCPtr(String osName); + private native void unrefContextCPtr (); public native long createMsgCPtr (int viewtype); private native long getChatlistCPtr (int listflags, String query, int queryId); private native long getChatCPtr (int chat_id); @@ -208,4 +221,6 @@ public long handleEvent(int event, long data1, long data2) { private native long getDraftCPtr (int id); private native long getContactCPtr (int id); private native long getLocationsCPtr (int chat_id, int contact_id, long timestamp_start, long timestamp_end); + private native long checkQrCPtr (String qr); + private native long getProviderFromEmailCPtr (String addr); } diff --git a/android/src/main/java/com/b44t/messenger/DcProvider.java b/android/src/main/java/com/b44t/messenger/DcProvider.java new file mode 100644 index 0000000..09941cf --- /dev/null +++ b/android/src/main/java/com/b44t/messenger/DcProvider.java @@ -0,0 +1,26 @@ +package com.b44t.messenger; + +public class DcProvider { + + public final static int DC_PROVIDER_STATUS_OK = 1; + public final static int DC_PROVIDER_STATUS_PREPARATION = 2; + public final static int DC_PROVIDER_STATUS_BROKEN = 3; + + public DcProvider(long providerCPtr) { + this.providerCPtr = providerCPtr; + } + + @Override protected void finalize() throws Throwable { + super.finalize(); + unrefProviderCPtr(); + providerCPtr = 0; + } + + public native int getStatus (); + public native String getBeforeLoginHint (); + public native String getOverviewPage (); + + // working with raw c-data + private long providerCPtr; // CAVE: the name is referenced in the JNI + private native void unrefProviderCPtr(); +} From ea244784ea59933dfe00095dac57bb711d416974 Mon Sep 17 00:00:00 2001 From: Boehrsi Date: Thu, 7 May 2020 11:40:56 +0200 Subject: [PATCH 06/16] Adjust wrappers to new DCC API --- .../deltachatcore/handlers/ChatCallHandler.java | 10 +++++----- .../deltachatcore/handlers/EventChannelHandler.java | 5 ----- example/android/gradle.properties | 1 - lib/src/chat.dart | 6 +++--- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/android/src/main/java/com/openxchange/deltachatcore/handlers/ChatCallHandler.java b/android/src/main/java/com/openxchange/deltachatcore/handlers/ChatCallHandler.java index 966daad..5804a3c 100644 --- a/android/src/main/java/com/openxchange/deltachatcore/handlers/ChatCallHandler.java +++ b/android/src/main/java/com/openxchange/deltachatcore/handlers/ChatCallHandler.java @@ -52,7 +52,7 @@ public class ChatCallHandler extends AbstractCallHandler { private static final String METHOD_CHAT_GET_ID = "chat_getId"; private static final String METHOD_CHAT_IS_GROUP = "chat_isGroup"; - private static final String METHOD_CHAT_GET_ARCHIVED = "chat_getArchived"; + private static final String METHOD_CHAT_GET_VISIBILITY = "chat_getVisibility"; private static final String METHOD_CHAT_GET_COLOR = "chat_getColor"; private static final String METHOD_CHAT_GET_NAME = "chat_getName"; private static final String METHOD_CHAT_GET_PROFILE_IMAGE = "chat_getProfileImage"; @@ -77,8 +77,8 @@ public void handleCall(MethodCall methodCall, MethodChannel.Result result) { case METHOD_CHAT_IS_GROUP: isGroup(methodCall, result); break; - case METHOD_CHAT_GET_ARCHIVED: - getArchived(methodCall, result); + case METHOD_CHAT_GET_VISIBILITY: + getVisibility(methodCall, result); break; case METHOD_CHAT_GET_COLOR: getColor(methodCall, result); @@ -151,13 +151,13 @@ private void getName(MethodCall methodCall, MethodChannel.Result result) { result.success(chat.getName()); } - private void getArchived(MethodCall methodCall, MethodChannel.Result result) { + private void getVisibility(MethodCall methodCall, MethodChannel.Result result) { DcChat chat = getChat(methodCall, result); if (chat == null) { resultErrorGeneric(methodCall, result); return; } - result.success(chat.getArchived()); + result.success(chat.getVisibility()); } private void getColor(MethodCall methodCall, MethodChannel.Result result) { diff --git a/android/src/main/java/com/openxchange/deltachatcore/handlers/EventChannelHandler.java b/android/src/main/java/com/openxchange/deltachatcore/handlers/EventChannelHandler.java index 93b94e9..ccc3c4d 100644 --- a/android/src/main/java/com/openxchange/deltachatcore/handlers/EventChannelHandler.java +++ b/android/src/main/java/com/openxchange/deltachatcore/handlers/EventChannelHandler.java @@ -76,11 +76,6 @@ public class EventChannelHandler implements EventChannel.StreamHandler { DcContext.DC_EVENT_IMEX_FILE_WRITTEN, DcContext.DC_EVENT_SECUREJOIN_INVITER_PROGRESS, DcContext.DC_EVENT_SECUREJOIN_JOINER_PROGRESS, - DcContext.DC_EVENT_IS_OFFLINE, - DcContext.DC_EVENT_GET_STRING, - DcContext.DC_EVENT_GET_QUANTITIY_STRING, - DcContext.DC_EVENT_HTTP_GET, - DcContext.DC_EVENT_HTTP_POST, DcContext.DC_EVENT_MISSING_KEY, COI_EVENT_SET_METADATA_DONE, COI_EVENT_WEB_PUSH_SUBSCRIPTION); diff --git a/example/android/gradle.properties b/example/android/gradle.properties index a673820..94adc3a 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1,4 +1,3 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true -android.enableR8=true diff --git a/lib/src/chat.dart b/lib/src/chat.dart index f7e0518..1ee1e8b 100644 --- a/lib/src/chat.dart +++ b/lib/src/chat.dart @@ -50,7 +50,7 @@ class Chat extends Base { static const String methodChatGetId = "chat_getId"; static const String methodChatIsGroup = "chat_isGroup"; - static const String methodChatGetArchived = "chat_getArchived"; + static const String methodChatGetVisibility = "chat_getVisibility"; static const String methodChatGetColor = "chat_getColor"; static const String methodChatGetName = "chat_getName"; static const String methodChatGetProfileImage = "chat_getProfileImage"; @@ -84,8 +84,8 @@ class Chat extends Base { return await loadAndGetValue(methodChatIsGroup); } - Future getArchived() async { - return await loadAndGetValue(methodChatGetArchived); + Future getVisibility() async { + return await loadAndGetValue(methodChatGetVisibility); } Future getColor() async { From 075eeb6ed2817a058c43540fed20a79927869b72 Mon Sep 17 00:00:00 2001 From: Boehrsi Date: Thu, 7 May 2020 13:20:52 +0200 Subject: [PATCH 07/16] Add new start / stop logic analog to the DCC Android solution Add minimalSetup Adjust logging Cleanup gradle.properties Update main.dart --- .../deltachatcore/DeltaChatCorePlugin.java | 24 +- .../NativeInteractionManager.java | 306 +++++++----------- .../com/openxchange/deltachatcore/Utils.java | 8 + example/android/gradle.properties | 1 + example/lib/main.dart | 2 + 5 files changed, 151 insertions(+), 190 deletions(-) diff --git a/android/src/main/java/com/openxchange/deltachatcore/DeltaChatCorePlugin.java b/android/src/main/java/com/openxchange/deltachatcore/DeltaChatCorePlugin.java index adbc659..a9d26e2 100644 --- a/android/src/main/java/com/openxchange/deltachatcore/DeltaChatCorePlugin.java +++ b/android/src/main/java/com/openxchange/deltachatcore/DeltaChatCorePlugin.java @@ -67,11 +67,11 @@ import io.flutter.plugin.common.MethodChannel.MethodCallHandler; import io.flutter.plugin.common.MethodChannel.Result; -import static android.util.Log.DEBUG; +import static android.util.Log.INFO; import static com.openxchange.deltachatcore.Utils.logEventAndDelegate; public class DeltaChatCorePlugin implements MethodCallHandler, FlutterPlugin { - static final String TAG = "coi"; + static final String TAG = "coi-plugin"; private static final String LIBRARY_NAME = "native-utils"; private static final String CHANNEL_DELTA_CHAT_CORE = "deltaChatCore"; @@ -90,6 +90,8 @@ public class DeltaChatCorePlugin implements MethodCallHandler, FlutterPlugin { private static final String ARGUMENT_REMOVE_CACHE_IDENTIFIER = "removeCacheIdentifier"; private static final String ARGUMENT_DB_NAME = "dbName"; + private static final String ARGUMENT_MINIMAL_SETUP = "minimalSetup"; + private static final String CACHE_IDENTIFIER_CHAT = "chat"; private static final String CACHE_IDENTIFIER_CHAT_LIST = "chatList"; @@ -122,7 +124,7 @@ public DeltaChatCorePlugin() { @Override public void onAttachedToEngine(FlutterPluginBinding binding) { Log.d("dboehrs" + this.hashCode(), "onAttachedToEngine:"); - logEventAndDelegate(eventChannelHandler, DEBUG, TAG, "Attaching plugin via v2 embedding"); + logEventAndDelegate(eventChannelHandler, INFO, TAG, "Attaching plugin via v2 embedding"); context = binding.getApplicationContext(); messenger = binding.getBinaryMessenger(); methodChannel = new MethodChannel(messenger, CHANNEL_DELTA_CHAT_CORE); @@ -132,7 +134,7 @@ public void onAttachedToEngine(FlutterPluginBinding binding) { @Override public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { Log.d("dboehrs" + this.hashCode(), "onDetachedFromEngine: "); - logEventAndDelegate(eventChannelHandler, DEBUG, TAG, "Detaching plugin via v2 embedding"); + logEventAndDelegate(eventChannelHandler, INFO, TAG, "Detaching plugin via v2 embedding"); context = null; messenger = null; if (methodChannel != null) { @@ -214,25 +216,32 @@ private void handleBaseCalls(MethodCall methodCall, Result result) { } private void init(MethodCall methodCall, Result result) { + Boolean minimalSetup = methodCall.argument(ARGUMENT_MINIMAL_SETUP); + if (minimalSetup == null ) { + throw new IllegalArgumentException("No setup type defined (minimal vs. full setup, exiting."); + } + logEventAndDelegate(eventChannelHandler, INFO, TAG, "Init started, with minimal setup = " + minimalSetup); String dbName = methodCall.argument(ARGUMENT_DB_NAME); if (dbName == null || dbName.isEmpty()) { throw new IllegalArgumentException("No database name given, exiting."); } System.loadLibrary(LIBRARY_NAME); eventChannelHandler = new EventChannelHandler(messenger); - nativeInteractionManager = new NativeInteractionManager(context, dbName, eventChannelHandler); + nativeInteractionManager = new NativeInteractionManager(context, dbName, minimalSetup, eventChannelHandler); contextCallHandler = new ContextCallHandler(nativeInteractionManager, contactCache, messageCache, chatCache); chatListCallHandler = new ChatListCallHandler(nativeInteractionManager, chatCache); messageCallHandler = new MessageCallHandler(nativeInteractionManager, contextCallHandler); contactCallHandler = new ContactCallHandler(nativeInteractionManager, contextCallHandler); chatCallHandler = new ChatCallHandler(nativeInteractionManager, contextCallHandler); - logEventAndDelegate(eventChannelHandler, DEBUG, TAG, nativeInteractionManager.getInfo()); + logEventAndDelegate(eventChannelHandler, INFO, TAG, nativeInteractionManager.getInfo()); result.success(nativeInteractionManager.getDbPath()); + logEventAndDelegate(eventChannelHandler, INFO, TAG, "Init finished"); } private void tearDown(Result result) { + logEventAndDelegate(eventChannelHandler, INFO, TAG, "Teardown started"); Log.d("dboehrs" + this.hashCode(), "tearDown: "); - nativeInteractionManager.stop(); + nativeInteractionManager.tearDown(); nativeInteractionManager = null; contextCallHandler = null; chatListCallHandler = null; @@ -247,6 +256,7 @@ private void tearDown(Result result) { chatCache.clear(); contactCache.clear(); messageCache.clear(); + logEventAndDelegate(eventChannelHandler, INFO, TAG, "Teardown finished"); } private void handleContextCalls(MethodCall methodCall, Result result) { diff --git a/android/src/main/java/com/openxchange/deltachatcore/NativeInteractionManager.java b/android/src/main/java/com/openxchange/deltachatcore/NativeInteractionManager.java index fd3d6fc..3466540 100644 --- a/android/src/main/java/com/openxchange/deltachatcore/NativeInteractionManager.java +++ b/android/src/main/java/com/openxchange/deltachatcore/NativeInteractionManager.java @@ -18,7 +18,6 @@ import androidx.annotation.RequiresApi; -import static android.util.Log.DEBUG; import static android.util.Log.ERROR; import static android.util.Log.INFO; import static android.util.Log.WARN; @@ -35,53 +34,71 @@ public class NativeInteractionManager extends DcContext { private final long wakeLockTimeout = 10 * 60 * 1000L; /*10 minutes*/ private final String dbPath; + private final boolean minimalSetup; private final EventChannelHandler eventChannelHandler; private final Object threadsSynchronized = new Object(); - private final Object imapThreadSynchronized = new Object(); - private final Object mvboxThreadSynchronized = new Object(); - private final Object sentBoxThreadSynchronized = new Object(); - private final Object smtpThreadSynchronized = new Object(); - private Thread imapThread = null; + private final Object loopsSynchronized = new Object(); + + // IMAP thread for the inbox folder + private Thread inboxThread = null; private PowerManager.WakeLock imapWakeLock = null; + private int inboxLoops = 0; + + // IMAP thread for the move-to folder (Deltachat or coi/chats) private Thread mvboxThread = null; private PowerManager.WakeLock mvboxWakeLock = null; + private int mvboxLoops = 0; + + // IMAP thread for the sent folder private Thread sentBoxThread = null; private PowerManager.WakeLock sentBoxWakeLock = null; + private int sentBoxLoops = 0; + + // SMTP thread for sending messages private Thread smtpThread = null; private PowerManager.WakeLock smtpWakeLock = null; - private boolean imapThreadStarted; - private boolean mvboxThreadStarted; - private boolean sentBoxThreadStarted; - private boolean smtpThreadStarted; + private int smtpLoops = 0; + + // Only relevant if minimalSetup = false, as if minimalSetup = true no threads are used at all + private boolean threadsRunning = true; - NativeInteractionManager(Context context, String dbName, EventChannelHandler eventChannelHandler) { + NativeInteractionManager(Context context, String dbName, boolean minimalSetup, EventChannelHandler eventChannelHandler) { super("Android " + BuildConfig.VERSION_NAME); this.eventChannelHandler = eventChannelHandler; + this.minimalSetup = minimalSetup; File databaseFile = new File(context.getFilesDir(), dbName); dbPath = databaseFile.getAbsolutePath(); - open(databaseFile.getAbsolutePath()); - try { - PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + init(context, databaseFile); + } - imapWakeLock = Objects.requireNonNull(powerManager).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, COI_IMAP_WAKE_LOCK); - imapWakeLock.setReferenceCounted(false); // if the idle-thread is killed for any reasons, it is better not to rely on reference counting + private void init(Context context, File databaseFile) { + logEventAndDelegate(eventChannelHandler, INFO, TAG, "Opening database"); + open(databaseFile.getAbsolutePath()); + logEventAndDelegate(eventChannelHandler, INFO, TAG, "Database opened"); + if (!minimalSetup) { + try { + PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - mvboxWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, COI_MVBOX_WAKE_LOCK); - mvboxWakeLock.setReferenceCounted(false); // if the idle-thread is killed for any reasons, it is better not to rely on reference counting + imapWakeLock = Objects.requireNonNull(powerManager).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, COI_IMAP_WAKE_LOCK); + imapWakeLock.setReferenceCounted(false); // if the idle-thread is killed for any reasons, it is better not to rely on reference counting - sentBoxWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, COI_SENT_BOX_WAKE_LOCK); - sentBoxWakeLock.setReferenceCounted(false); // if the idle-thread is killed for any reasons, it is better not to rely on reference counting + mvboxWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, COI_MVBOX_WAKE_LOCK); + mvboxWakeLock.setReferenceCounted(false); // if the idle-thread is killed for any reasons, it is better not to rely on reference counting - smtpWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, COI_SMTP_WAKE_LOCK); - smtpWakeLock.setReferenceCounted(false); // if the idle-thread is killed for any reasons, it is better not to rely on reference counting - } catch (Exception e) { - logEventAndDelegate(eventChannelHandler, ERROR, TAG, "Cannot create wakeLocks"); + sentBoxWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, COI_SENT_BOX_WAKE_LOCK); + sentBoxWakeLock.setReferenceCounted(false); // if the idle-thread is killed for any reasons, it is better not to rely on reference counting + + smtpWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, COI_SMTP_WAKE_LOCK); + smtpWakeLock.setReferenceCounted(false); // if the idle-thread is killed for any reasons, it is better not to rely on reference counting + } catch (Exception e) { + logEventAndDelegate(eventChannelHandler, ERROR, TAG, "Cannot create wakeLocks"); + } + startThreads(INTERRUPT_IDLE); + setupConnectivityObserver(context); } - start(); - setupConnectivityObserver(context); } @Override @@ -130,90 +147,48 @@ private void delegateError(int eventId, long data1, String error) { eventChannelHandler.handleEvent(eventId, data1Object, error); } - private void start() { - logEventAndDelegate(eventChannelHandler, DEBUG, TAG, "Starting threads"); - startThreads(INTERRUPT_IDLE); - waitForThreadsRunning(); - } - private void startThreads(@SuppressWarnings("SameParameterValue") int flags) { + logEventAndDelegate(eventChannelHandler, INFO, TAG, "Starting / reusing threads"); synchronized (threadsSynchronized) { + if (inboxThread == null || !inboxThread.isAlive()) { - if (imapThread == null || !imapThread.isAlive()) { - - synchronized (imapThreadSynchronized) { - imapThreadStarted = false; - } - - imapThread = new Thread(() -> { - // raise the starting condition - // after acquiring a wakelock so that the process is not terminated. - // as imapWakeLock is not reference counted that would result in a wakelock-gap is not needed here. - imapWakeLock.acquire(wakeLockTimeout); - synchronized (imapThreadSynchronized) { - imapThreadStarted = true; - imapThreadSynchronized.notifyAll(); - } - - logEventAndDelegate(eventChannelHandler, INFO, TAG, "###################### IMAP-Thread " + Thread.currentThread().getId() + " started. ######################"); - - - while (true) { - if (Thread.interrupted()) { - synchronized (imapThreadSynchronized) { - imapThreadStarted = false; - imapThreadSynchronized.notifyAll(); - } - logEventAndDelegate(eventChannelHandler, INFO, TAG, "###################### IMAP-Thread " + Thread.currentThread().getId() + " stopped. ######################"); - return; - } + inboxThread = new Thread(() -> { + logEventAndDelegate(eventChannelHandler, INFO, TAG, "INBOX-Thread " + Thread.currentThread().getId() + " started."); + while (threadsRunning) { imapWakeLock.acquire(wakeLockTimeout); performImapJobs(); performImapFetch(); imapWakeLock.release(); + synchronized (loopsSynchronized) { + inboxLoops++; + } performImapIdle(); } - }, "imapThread"); - imapThread.setPriority(Thread.NORM_PRIORITY); - imapThread.start(); + logEventAndDelegate(eventChannelHandler, INFO, TAG, "INBOX-Thread " + Thread.currentThread().getId() + " stopped."); + }, "inboxThread"); + inboxThread.setPriority(Thread.NORM_PRIORITY); + inboxThread.start(); } else { if ((flags & INTERRUPT_IDLE) != 0) { interruptImapIdle(); } } - if (mvboxThread == null || !mvboxThread.isAlive()) { - synchronized (mvboxThreadSynchronized) { - mvboxThreadStarted = false; - } - mvboxThread = new Thread(() -> { - mvboxWakeLock.acquire(wakeLockTimeout); - synchronized (mvboxThreadSynchronized) { - mvboxThreadStarted = true; - mvboxThreadSynchronized.notifyAll(); - } - - logEventAndDelegate(eventChannelHandler, INFO, TAG, "###################### MVBOX-Thread " + Thread.currentThread().getId() + " started. ######################"); - - - while (true) { - if (Thread.interrupted()) { - synchronized (mvboxThreadSynchronized) { - mvboxThreadStarted = false; - mvboxThreadSynchronized.notifyAll(); - } - logEventAndDelegate(eventChannelHandler, INFO, TAG, "###################### MVBOX-Thread " + Thread.currentThread().getId() + " stopped. ######################"); - return; - } + logEventAndDelegate(eventChannelHandler, INFO, TAG, "MVBOX-Thread " + Thread.currentThread().getId() + " started."); + while (threadsRunning) { mvboxWakeLock.acquire(wakeLockTimeout); performMvboxJobs(); performMvboxFetch(); mvboxWakeLock.release(); + synchronized (loopsSynchronized) { + mvboxLoops++; + } performMvboxIdle(); } + logEventAndDelegate(eventChannelHandler, INFO, TAG, "MVBOX-Thread " + Thread.currentThread().getId() + " stopped."); }, "mvboxThread"); mvboxThread.setPriority(Thread.NORM_PRIORITY); mvboxThread.start(); @@ -223,38 +198,21 @@ private void startThreads(@SuppressWarnings("SameParameterValue") int flags) { } } - if (sentBoxThread == null || !sentBoxThread.isAlive()) { - synchronized (sentBoxThreadSynchronized) { - sentBoxThreadStarted = false; - } - sentBoxThread = new Thread(() -> { - sentBoxWakeLock.acquire(wakeLockTimeout); - synchronized (sentBoxThreadSynchronized) { - sentBoxThreadStarted = true; - sentBoxThreadSynchronized.notifyAll(); - } - - logEventAndDelegate(eventChannelHandler, INFO, TAG, "###################### SENTBOX-Thread " + Thread.currentThread().getId() + " started. ######################"); - - - while (true) { - if (Thread.interrupted()) { - synchronized (sentBoxThreadSynchronized) { - sentBoxThreadStarted = false; - sentBoxThreadSynchronized.notifyAll(); - } - logEventAndDelegate(eventChannelHandler, INFO, TAG, "###################### SENTBOX-Thread " + Thread.currentThread().getId() + " stopped. ######################"); - return; - } + logEventAndDelegate(eventChannelHandler, INFO, TAG, "SENTBOX-Thread " + Thread.currentThread().getId() + " started."); + while (threadsRunning) { sentBoxWakeLock.acquire(wakeLockTimeout); performSentboxJobs(); performSentboxFetch(); sentBoxWakeLock.release(); + synchronized (loopsSynchronized) { + sentBoxLoops++; + } performSentboxIdle(); } + logEventAndDelegate(eventChannelHandler, INFO, TAG, "SENTBOX-Thread " + Thread.currentThread().getId() + " stopped."); }, "sentBoxThread"); sentBoxThread.setPriority(Thread.NORM_PRIORITY - 1); sentBoxThread.start(); @@ -264,102 +222,84 @@ private void startThreads(@SuppressWarnings("SameParameterValue") int flags) { } } - if (smtpThread == null || !smtpThread.isAlive()) { - - synchronized (smtpThreadSynchronized) { - smtpThreadStarted = false; - } - smtpThread = new Thread(() -> { - smtpWakeLock.acquire(wakeLockTimeout); - synchronized (smtpThreadSynchronized) { - smtpThreadStarted = true; - smtpThreadSynchronized.notifyAll(); - } - - logEventAndDelegate(eventChannelHandler, INFO, TAG, "###################### SMTP-Thread " + Thread.currentThread().getId() + " started. ######################"); - - - while (true) { - if (Thread.interrupted()) { - synchronized (smtpThreadSynchronized) { - smtpThreadStarted = false; - smtpThreadSynchronized.notifyAll(); - } - logEventAndDelegate(eventChannelHandler, INFO, TAG, "###################### SMTP-Thread " + Thread.currentThread().getId() + " stopped. ######################"); - return; - } + logEventAndDelegate(eventChannelHandler, INFO, TAG, "SMTP-Thread " + Thread.currentThread().getId() + " started."); + while (threadsRunning) { smtpWakeLock.acquire(wakeLockTimeout); performSmtpJobs(); smtpWakeLock.release(); + synchronized (loopsSynchronized) { + smtpLoops++; + } performSmtpIdle(); } + logEventAndDelegate(eventChannelHandler, INFO, TAG, "SMTP-Thread " + Thread.currentThread().getId() + " stopped."); }, "smtpThread"); smtpThread.setPriority(Thread.MAX_PRIORITY); smtpThread.start(); } } + waitForThreadsExecutedOnce(); + logEventAndDelegate(eventChannelHandler, INFO, TAG, "Threads started / reused"); } - private void waitForThreadsRunning() { - try { - synchronized (imapThreadSynchronized) { - while (!imapThreadStarted) { - imapThreadSynchronized.wait(); + private void waitForThreadsExecutedOnce() { + while (true) { + synchronized (loopsSynchronized) { + if (inboxLoops > 0 && mvboxLoops > 0 && sentBoxLoops > 0 && smtpLoops > 0) { + break; } } - - synchronized (mvboxThreadSynchronized) { - while (!mvboxThreadStarted) { - mvboxThreadSynchronized.wait(); - } - } - - synchronized (sentBoxThreadSynchronized) { - while (!sentBoxThreadStarted) { - sentBoxThreadSynchronized.wait(); - } - } - - synchronized (smtpThreadSynchronized) { - while (!smtpThreadStarted) { - smtpThreadSynchronized.wait(); - } - } - } catch (Exception e) { - e.printStackTrace(); + Utils.sleep(500); } } - void stop() { - logEventAndDelegate(eventChannelHandler, DEBUG, TAG, "Stopping threads"); - stopThreads(); - interruptImapIdle(); - interruptMvboxIdle(); - interruptSentboxIdle(); - interruptSmtpIdle(); - imapThread = null; - mvboxThread = null; - sentBoxThread = null; - smtpThread = null; - logEventAndDelegate(eventChannelHandler, DEBUG, TAG, "Closing database"); + void tearDown() { + if (!minimalSetup) { + logEventAndDelegate(eventChannelHandler, INFO, TAG, "Stopping threads"); + stopThreads(); + logEventAndDelegate(eventChannelHandler, INFO, TAG, "Threads stopped"); + } + logEventAndDelegate(eventChannelHandler, INFO, TAG, "Closing database"); close(); + logEventAndDelegate(eventChannelHandler, INFO, TAG, "Database closed"); } private void stopThreads() { - if (imapThread != null) { - imapThread.interrupt(); - } - if (mvboxThread != null) { - mvboxThread.interrupt(); - } - if (sentBoxThread != null) { - sentBoxThread.interrupt(); - } - if (smtpThread != null) { - smtpThread.interrupt(); + threadsRunning = false; + synchronized (threadsSynchronized) { + do { + // in theory, interrupting once outside the loop should be sufficient, + // but there are some corner cases, see https://github.com/deltachat/deltachat-core-rust/issues/925 + if (inboxThread != null && inboxThread.isAlive()) { + interruptImapIdle(); + } + if (mvboxThread != null && mvboxThread.isAlive()) { + interruptMvboxIdle(); + } + if (sentBoxThread != null && sentBoxThread.isAlive()) { + interruptSentboxIdle(); + } + if (smtpThread != null && smtpThread.isAlive()) { + interruptSmtpIdle(); + } + + Utils.sleep(300); + + } while ((inboxThread != null && inboxThread.isAlive()) + || (mvboxThread != null && mvboxThread.isAlive()) + || (sentBoxThread != null && sentBoxThread.isAlive()) + || (smtpThread != null && smtpThread.isAlive())); } + inboxThread = null; + mvboxThread = null; + sentBoxThread = null; + smtpThread = null; + inboxLoops = 0; + mvboxLoops = 0; + sentBoxLoops = 0; + smtpLoops = 0; } private void setupConnectivityObserver(Context context) { @@ -387,8 +327,8 @@ public void onReceive(Context context, Intent intent) { } private void startOnNetworkAvailable() { - logEventAndDelegate(eventChannelHandler, INFO, TAG, "###################### Network is connected again. ######################"); - start(); + logEventAndDelegate(eventChannelHandler, INFO, TAG, "Network is connected again."); + startThreads(INTERRUPT_IDLE); } @RequiresApi(api = Build.VERSION_CODES.N) diff --git a/android/src/main/java/com/openxchange/deltachatcore/Utils.java b/android/src/main/java/com/openxchange/deltachatcore/Utils.java index 73af6c5..33ef17f 100644 --- a/android/src/main/java/com/openxchange/deltachatcore/Utils.java +++ b/android/src/main/java/com/openxchange/deltachatcore/Utils.java @@ -71,6 +71,14 @@ public static void runOnBackground(final @NonNull Runnable runnable) { AsyncTask.THREAD_POOL_EXECUTOR.execute(runnable); } + public static void sleep(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + throw new AssertionError(e); + } + } + public static void logEventAndDelegate(@Nullable EventChannelHandler eventChannelHandler, int logLevel, String tag, String message) { boolean shouldDelegate = eventChannelHandler != null; diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 94adc3a..a673820 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1,3 +1,4 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true +android.enableR8=true diff --git a/example/lib/main.dart b/example/lib/main.dart index 4074f89..8d8ede3 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -156,9 +156,11 @@ class _MyAppState extends State { var chatCntAfterDeletingChat = await chatList.getChatCnt(); await chatList.tearDown(); _addListItem(text: "deleteChat", assertion: 0, result: chatCntAfterDeletingChat); + await Future.delayed(const Duration(seconds: 1)); core.tearDownAsync(); var dbFile = File(core.dbPath); dbFile.deleteSync(); + _addListItem(text: "Teardown and cleanup down"); } } on PlatformException { throw StateError("Test suite failed, forbidden state entered"); From ac55ea6d3a027e8087d3880d519e7b4984e6e0bf Mon Sep 17 00:00:00 2001 From: "daniel.boehrs" Date: Wed, 2 Oct 2019 15:34:41 +0200 Subject: [PATCH 08/16] OT-396 decrypt message in memory --- android/jni/dc_wrapper.c | 15 ++++++++++ .../java/com/b44t/messenger/DcContext.java | 1 + .../handlers/AbstractCallHandler.java | 2 ++ .../handlers/ContextCallHandler.java | 28 +++++++++++++++++++ lib/src/base.dart | 2 ++ lib/src/context.dart | 7 +++++ 6 files changed, 55 insertions(+) diff --git a/android/jni/dc_wrapper.c b/android/jni/dc_wrapper.c index b67c3eb..9462765 100644 --- a/android/jni/dc_wrapper.c +++ b/android/jni/dc_wrapper.c @@ -961,6 +961,21 @@ JNIEXPORT jint Java_com_b44t_messenger_DcContext_isCoiMessageFilterEnabled(JNIEn return (jint)dc_get_coi_message_filter(get_dc_context(env, obj)); } +JNIEXPORT jstring Java_com_b44t_messenger_DcContext_decryptMessageInMemory(JNIEnv *env, jobject obj, jstring contentType, jstring content, jstring senderAddr) +{ + CHAR_REF(contentType); + CHAR_REF(content); + CHAR_REF(senderAddr); + int partCount; + char* temp = dc_decrypt_message_in_memory(get_dc_context(env, obj), contentTypePtr, contentPtr, senderAddrPtr, 0, &partCount); + jstring ret = JSTRING_NEW(temp); + free(temp); + CHAR_UNREF(contentType); + CHAR_UNREF(content); + CHAR_UNREF(senderAddr); + return ret; +} + /******************************************************************************* * DcArray ******************************************************************************/ diff --git a/android/src/main/java/com/b44t/messenger/DcContext.java b/android/src/main/java/com/b44t/messenger/DcContext.java index c8a690d..bc9cb6b 100644 --- a/android/src/main/java/com/b44t/messenger/DcContext.java +++ b/android/src/main/java/com/b44t/messenger/DcContext.java @@ -194,6 +194,7 @@ protected void finalize() throws Throwable { public native void setCoiMessageFilter (int mode, int id); public native int isCoiMessageFilterEnabled(); public native void validateWebPush (String uid, String msg, int id); + public native String decryptMessageInMemory (String contentType, String content, String senderAddress); /** * @return true if at least one chat has location streaming enabled diff --git a/android/src/main/java/com/openxchange/deltachatcore/handlers/AbstractCallHandler.java b/android/src/main/java/com/openxchange/deltachatcore/handlers/AbstractCallHandler.java index 99ce3ef..4ba268c 100644 --- a/android/src/main/java/com/openxchange/deltachatcore/handlers/AbstractCallHandler.java +++ b/android/src/main/java/com/openxchange/deltachatcore/handlers/AbstractCallHandler.java @@ -82,6 +82,8 @@ abstract public class AbstractCallHandler { static final String ARGUMENT_UID = "uid"; static final String ARGUMENT_MODE = "mode"; static final String ARGUMENT_ENABLE = "enable"; + static final String ARGUMENT_CONTENT = "content"; + static final String ARGUMENT_CONTENT_TYPE = "contentType"; static final String ARGUMENT_MIME_TYPE = "mimeType"; static final String ARGUMENT_DURATION = "duration"; static final String ARGUMENT_DIR = "dir"; diff --git a/android/src/main/java/com/openxchange/deltachatcore/handlers/ContextCallHandler.java b/android/src/main/java/com/openxchange/deltachatcore/handlers/ContextCallHandler.java index 70be4f1..aa33243 100644 --- a/android/src/main/java/com/openxchange/deltachatcore/handlers/ContextCallHandler.java +++ b/android/src/main/java/com/openxchange/deltachatcore/handlers/ContextCallHandler.java @@ -117,6 +117,7 @@ public class ContextCallHandler extends com.openxchange.deltachatcore.handlers.A private static final String METHOD_RETRY_SENDING_PENDING_MESSAGES = "context_retrySendingPendingMessages"; private static final String METHOD_GET_CONTACT_ID_BY_ADDRESS = "context_getContactIdByAddress"; private static final String METHOD_GET_NEXT_MEDIA = "context_getNextMedia"; + private static final String METHOD_DECRYPT_IN_MEMORY = "context_decryptInMemory"; private static final String TYPE_INT = "int"; private static final String TYPE_STRING = "String"; @@ -297,6 +298,9 @@ public void handleCall(MethodCall methodCall, MethodChannel.Result result) { case METHOD_VALIDATE_WEB_PUSH: validateWebPush(methodCall, result); break; + case METHOD_DECRYPT_IN_MEMORY: + decryptMessageInMemory(methodCall, result); + break; case METHOD_GET_MESSAGE_INFO: getMessageInfo(methodCall, result); break; @@ -1194,4 +1198,28 @@ private void retrySendingPendingMessages(MethodChannel.Result result) { dcContext.maybeNetwork(); result.success(null); } + + private void decryptMessageInMemory(MethodCall methodCall, MethodChannel.Result result) { + if (!hasArgumentKeys(methodCall, ARGUMENT_CONTENT_TYPE, ARGUMENT_CONTENT, ARGUMENT_ADDRESS)) { + resultErrorArgumentMissing(result); + return; + } + String contentType = methodCall.argument(ARGUMENT_CONTENT_TYPE); + if (contentType == null || contentType.isEmpty()) { + resultErrorArgumentMissingValue(result); + return; + } + String content = methodCall.argument(ARGUMENT_CONTENT); + if (content == null || content.isEmpty()) { + resultErrorArgumentMissingValue(result); + return; + } + String senderAddress = methodCall.argument(ARGUMENT_ADDRESS); + if (senderAddress == null || senderAddress.isEmpty()) { + resultErrorArgumentMissingValue(result); + return; + } + String plainText = dcContext.decryptMessageInMemory(contentType, content, senderAddress); + result.success(plainText); + } } \ No newline at end of file diff --git a/lib/src/base.dart b/lib/src/base.dart index def4add..ccc670f 100644 --- a/lib/src/base.dart +++ b/lib/src/base.dart @@ -72,6 +72,8 @@ abstract class Base { static const String argumentUid = "uid"; static const String argumentMode = "mode"; static const String argumentEnable = "enable"; + static const String argumentContentType = "contentType"; + static const String argumentContent = "content"; static const String argumentMimeType = "mimeType"; static const String argumentDuration = "duration"; static const String argumentDir = "dir"; diff --git a/lib/src/context.dart b/lib/src/context.dart index 53142d5..6b53a83 100644 --- a/lib/src/context.dart +++ b/lib/src/context.dart @@ -105,6 +105,7 @@ class Context { static const String methodRetrySendingPendingMessages = "context_retrySendingPendingMessages"; static const String methodGetContactIdByAddress = "context_getContactIdByAddress"; static const String methodGetNextMedia = "context_getNextMedia"; + static const String methodDecryptInMemory = "context_decryptInMemory"; static const String configAddress = "addr"; static const String configMailServer = "mail_server"; @@ -403,6 +404,10 @@ class Context { return await core.invokeMethod(methodGetNextMedia, getNextMediaArguments(messageId, dir, messageTypeOne, messageTypeTwo, messageTypeThree)); } + Future decryptInMemory(String contentType, String content, String senderAddress) async { + return await core.invokeMethod(methodDecryptInMemory, getDecryptInMemoryArguments(contentType, content, senderAddress)); + } + Map getKeyArguments(String key) => {Base.argumentKey: key}; Map getConfigArguments(String type, String key, value) => @@ -484,4 +489,6 @@ class Context { Base.argumentMessageTypeTwo: messageTypeTwo, Base.argumentMessageTypeThree: messageTypeThree }; + + Map getDecryptInMemoryArguments(String contentType, String content, String senderAddress) => {Base.argumentContentType: contentType, Base.argumentContent: content, Base.argumentAddress: senderAddress}; } From be1a1f67a8a0414b1c5fbb7348f707fe9bb77655 Mon Sep 17 00:00:00 2001 From: "daniel.boehrs" Date: Fri, 8 May 2020 16:52:07 +0200 Subject: [PATCH 09/16] Update core + update background fetch logic --- .../deltachatcore/handlers/ContextCallHandler.java | 8 ++++---- example/android/gradle.properties | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/android/src/main/java/com/openxchange/deltachatcore/handlers/ContextCallHandler.java b/android/src/main/java/com/openxchange/deltachatcore/handlers/ContextCallHandler.java index aa33243..14c2c03 100644 --- a/android/src/main/java/com/openxchange/deltachatcore/handlers/ContextCallHandler.java +++ b/android/src/main/java/com/openxchange/deltachatcore/handlers/ContextCallHandler.java @@ -42,8 +42,6 @@ package com.openxchange.deltachatcore.handlers; -import android.util.Log; - import com.b44t.messenger.DcChat; import com.b44t.messenger.DcChatlist; import com.b44t.messenger.DcContact; @@ -996,8 +994,10 @@ private void setChatName(MethodCall methodCall, MethodChannel.Result result) { } private void interruptIdleForIncomingMessages(MethodChannel.Result result) { - dcContext.interruptImapIdle(); - dcContext.interruptMvboxIdle(); + dcContext.performImapJobs(); + dcContext.performImapFetch(); + dcContext.performMvboxJobs(); + dcContext.performMvboxFetch(); result.success(null); } diff --git a/example/android/gradle.properties b/example/android/gradle.properties index a673820..94adc3a 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1,4 +1,3 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true -android.enableR8=true From b7d843d0f27135d80503458f40273e54b88b4288 Mon Sep 17 00:00:00 2001 From: Frank Gregor Date: Thu, 28 May 2020 11:37:12 +0200 Subject: [PATCH 10/16] 501-iOS: minor changes, adding struct MethodChannel --- ios/Classes/Helper/MethodChannel.swift | 53 ++++++++++++++++++++++ ios/Classes/SwiftDeltaChatCorePlugin.swift | 2 +- 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 ios/Classes/Helper/MethodChannel.swift diff --git a/ios/Classes/Helper/MethodChannel.swift b/ios/Classes/Helper/MethodChannel.swift new file mode 100644 index 0000000..0168c10 --- /dev/null +++ b/ios/Classes/Helper/MethodChannel.swift @@ -0,0 +1,53 @@ +/* + * OPEN-XCHANGE legal information + * + * All intellectual property rights in the Software are protected by + * international copyright laws. + * + * + * In some countries OX, OX Open-Xchange and open xchange + * as well as the corresponding Logos OX Open-Xchange and OX are registered + * trademarks of the OX Software GmbH group of companies. + * The use of the Logos is not covered by the Mozilla Public License 2.0 (MPL 2.0). + * Instead, you are allowed to use these Logos according to the terms and + * conditions of the Creative Commons License, Version 2.5, Attribution, + * Non-commercial, ShareAlike, and the interpretation of the term + * Non-commercial applicable to the aforementioned license is published + * on the web site https://www.open-xchange.com/terms-and-conditions/. + * + * Please make sure that third-party modules and libraries are used + * according to their respective licenses. + * + * Any modifications to this package must retain all copyright notices + * of the original copyright holder(s) for the original code used. + * + * After any such modifications, the original and derivative code shall remain + * under the copyright of the copyright holder(s) and/or original author(s) as stated here: + * https://www.open-xchange.com/legal/. The contributing author shall be + * given Attribution for the derivative code and a license granting use. + * + * Copyright (C) 2016-2020 OX Software GmbH + * Mail: info@open-xchange.com + * + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the Mozilla Public License 2.0 + * for more details. + */ + +import Foundation + +struct MethodChannel {} + +extension MethodChannel { + + struct DeltaChat { + static let Core = "deltaChatCore" + } + +} diff --git a/ios/Classes/SwiftDeltaChatCorePlugin.swift b/ios/Classes/SwiftDeltaChatCorePlugin.swift index bf3a0b4..698cd97 100644 --- a/ios/Classes/SwiftDeltaChatCorePlugin.swift +++ b/ios/Classes/SwiftDeltaChatCorePlugin.swift @@ -85,7 +85,7 @@ public class SwiftDeltaChatCorePlugin: NSObject, FlutterPlugin { // This is our entry point public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "deltaChatCore", binaryMessenger: registrar.messenger()) + let channel = FlutterMethodChannel(name: MethodChannel.DeltaChat.Core, binaryMessenger: registrar.messenger()) let delegate = SwiftDeltaChatCorePlugin(registrar: registrar) registrar.addMethodCallDelegate(delegate, channel: channel) } From 28e2d787ca1a160c35cfca1d6ca3e0efb62830ae Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 2 Jun 2020 08:40:04 +0200 Subject: [PATCH 11/16] Chat id returning draft (#87) Co-authored-by: Sergey Kitov --- android/jni/dc_wrapper.c | 16 +++++++--------- .../java/com/b44t/messenger/ChatIdWrapper.java | 5 +++++ .../main/java/com/b44t/messenger/DcContext.java | 2 +- .../handlers/ContextCallHandler.java | 8 +++++++- 4 files changed, 20 insertions(+), 11 deletions(-) create mode 100644 android/src/main/java/com/b44t/messenger/ChatIdWrapper.java diff --git a/android/jni/dc_wrapper.c b/android/jni/dc_wrapper.c index 9462765..d085d8c 100644 --- a/android/jni/dc_wrapper.c +++ b/android/jni/dc_wrapper.c @@ -616,12 +616,6 @@ JNIEXPORT jint Java_com_b44t_messenger_DcContext_getFreshMsgCount(JNIEnv *env, j } -JNIEXPORT jint Java_com_b44t_messenger_DcContext_estimateDeletionCount(JNIEnv *env, jobject obj, jboolean from_server, jlong seconds) -{ - return dc_estimate_deletion_cnt(get_dc_context(env, obj), from_server, seconds); -} - - JNIEXPORT jlong Java_com_b44t_messenger_DcContext_getMsgCPtr(JNIEnv *env, jobject obj, jint id) { @@ -961,14 +955,18 @@ JNIEXPORT jint Java_com_b44t_messenger_DcContext_isCoiMessageFilterEnabled(JNIEn return (jint)dc_get_coi_message_filter(get_dc_context(env, obj)); } -JNIEXPORT jstring Java_com_b44t_messenger_DcContext_decryptMessageInMemory(JNIEnv *env, jobject obj, jstring contentType, jstring content, jstring senderAddr) +JNIEXPORT jstring Java_com_b44t_messenger_DcContext_decryptMessageInMemory(JNIEnv *env, jobject obj, jstring contentType, jstring content, jstring senderAddr, jobject chatIdWrapper) { CHAR_REF(contentType); CHAR_REF(content); CHAR_REF(senderAddr); int partCount; - char* temp = dc_decrypt_message_in_memory(get_dc_context(env, obj), contentTypePtr, contentPtr, senderAddrPtr, 0, &partCount); - jstring ret = JSTRING_NEW(temp); + int chat_id; + char* temp = dc_decrypt_message_in_memory(get_dc_context(env, obj), contentTypePtr, contentPtr, senderAddrPtr, 0, &partCount, &chat_id); + jclass cls = (*env)->GetObjectClass(env, chatIdWrapper); + jfieldID fid = (*env)->GetFieldID(env,cls,"chatId","I"); + (*env)->SetIntField(env,chatIdWrapper,fid,chat_id); + jstring ret = JSTRING_NEW(temp); free(temp); CHAR_UNREF(contentType); CHAR_UNREF(content); diff --git a/android/src/main/java/com/b44t/messenger/ChatIdWrapper.java b/android/src/main/java/com/b44t/messenger/ChatIdWrapper.java new file mode 100644 index 0000000..8de575d --- /dev/null +++ b/android/src/main/java/com/b44t/messenger/ChatIdWrapper.java @@ -0,0 +1,5 @@ +package com.b44t.messenger; + +public class ChatIdWrapper { + public int chatId; +} diff --git a/android/src/main/java/com/b44t/messenger/DcContext.java b/android/src/main/java/com/b44t/messenger/DcContext.java index bc9cb6b..f96c610 100644 --- a/android/src/main/java/com/b44t/messenger/DcContext.java +++ b/android/src/main/java/com/b44t/messenger/DcContext.java @@ -194,7 +194,7 @@ protected void finalize() throws Throwable { public native void setCoiMessageFilter (int mode, int id); public native int isCoiMessageFilterEnabled(); public native void validateWebPush (String uid, String msg, int id); - public native String decryptMessageInMemory (String contentType, String content, String senderAddress); + public native String decryptMessageInMemory (String contentType, String content, String senderAddress, ChatIdWrapper chatIdWrapper); /** * @return true if at least one chat has location streaming enabled diff --git a/android/src/main/java/com/openxchange/deltachatcore/handlers/ContextCallHandler.java b/android/src/main/java/com/openxchange/deltachatcore/handlers/ContextCallHandler.java index 14c2c03..706e1ba 100644 --- a/android/src/main/java/com/openxchange/deltachatcore/handlers/ContextCallHandler.java +++ b/android/src/main/java/com/openxchange/deltachatcore/handlers/ContextCallHandler.java @@ -42,6 +42,9 @@ package com.openxchange.deltachatcore.handlers; +import android.util.Log; + +import com.b44t.messenger.ChatIdWrapper; import com.b44t.messenger.DcChat; import com.b44t.messenger.DcChatlist; import com.b44t.messenger.DcContact; @@ -1200,6 +1203,7 @@ private void retrySendingPendingMessages(MethodChannel.Result result) { } private void decryptMessageInMemory(MethodCall methodCall, MethodChannel.Result result) { + Log.d("dboehrs", "decryptMessageInMemory: started"); if (!hasArgumentKeys(methodCall, ARGUMENT_CONTENT_TYPE, ARGUMENT_CONTENT, ARGUMENT_ADDRESS)) { resultErrorArgumentMissing(result); return; @@ -1219,7 +1223,9 @@ private void decryptMessageInMemory(MethodCall methodCall, MethodChannel.Result resultErrorArgumentMissingValue(result); return; } - String plainText = dcContext.decryptMessageInMemory(contentType, content, senderAddress); + ChatIdWrapper chatIdWrapper = new ChatIdWrapper(); + String plainText = dcContext.decryptMessageInMemory(contentType, content, senderAddress, chatIdWrapper); + Log.d("dboehrs", "decryptMessageInMemory: finished with" + chatIdWrapper.chatId); result.success(plainText); } } \ No newline at end of file From d820e3ae4c26949e338f78c95c0abb26ef1d4401 Mon Sep 17 00:00:00 2001 From: Boehrsi Date: Tue, 2 Jun 2020 15:35:44 +0200 Subject: [PATCH 12/16] Fix inMemoryDecryption --- .../deltachatcore/DeltaChatCorePlugin.java | 4 -- .../handlers/ContextCallHandler.java | 9 ++- lib/delta_chat_core.dart | 1 + lib/src/context.dart | 2 +- lib/src/types/decrypted_chat_message.dart | 57 +++++++++++++++++++ 5 files changed, 63 insertions(+), 10 deletions(-) create mode 100644 lib/src/types/decrypted_chat_message.dart diff --git a/android/src/main/java/com/openxchange/deltachatcore/DeltaChatCorePlugin.java b/android/src/main/java/com/openxchange/deltachatcore/DeltaChatCorePlugin.java index a9d26e2..86f2de4 100644 --- a/android/src/main/java/com/openxchange/deltachatcore/DeltaChatCorePlugin.java +++ b/android/src/main/java/com/openxchange/deltachatcore/DeltaChatCorePlugin.java @@ -44,7 +44,6 @@ import android.content.Context; -import android.util.Log; import com.b44t.messenger.DcChat; import com.b44t.messenger.DcContact; @@ -123,7 +122,6 @@ public DeltaChatCorePlugin() { @Override public void onAttachedToEngine(FlutterPluginBinding binding) { - Log.d("dboehrs" + this.hashCode(), "onAttachedToEngine:"); logEventAndDelegate(eventChannelHandler, INFO, TAG, "Attaching plugin via v2 embedding"); context = binding.getApplicationContext(); messenger = binding.getBinaryMessenger(); @@ -133,7 +131,6 @@ public void onAttachedToEngine(FlutterPluginBinding binding) { @Override public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { - Log.d("dboehrs" + this.hashCode(), "onDetachedFromEngine: "); logEventAndDelegate(eventChannelHandler, INFO, TAG, "Detaching plugin via v2 embedding"); context = null; messenger = null; @@ -240,7 +237,6 @@ private void init(MethodCall methodCall, Result result) { private void tearDown(Result result) { logEventAndDelegate(eventChannelHandler, INFO, TAG, "Teardown started"); - Log.d("dboehrs" + this.hashCode(), "tearDown: "); nativeInteractionManager.tearDown(); nativeInteractionManager = null; contextCallHandler = null; diff --git a/android/src/main/java/com/openxchange/deltachatcore/handlers/ContextCallHandler.java b/android/src/main/java/com/openxchange/deltachatcore/handlers/ContextCallHandler.java index 706e1ba..08bab5a 100644 --- a/android/src/main/java/com/openxchange/deltachatcore/handlers/ContextCallHandler.java +++ b/android/src/main/java/com/openxchange/deltachatcore/handlers/ContextCallHandler.java @@ -42,8 +42,6 @@ package com.openxchange.deltachatcore.handlers; -import android.util.Log; - import com.b44t.messenger.ChatIdWrapper; import com.b44t.messenger.DcChat; import com.b44t.messenger.DcChatlist; @@ -54,6 +52,8 @@ import com.openxchange.deltachatcore.IdCache; import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; @@ -1203,7 +1203,6 @@ private void retrySendingPendingMessages(MethodChannel.Result result) { } private void decryptMessageInMemory(MethodCall methodCall, MethodChannel.Result result) { - Log.d("dboehrs", "decryptMessageInMemory: started"); if (!hasArgumentKeys(methodCall, ARGUMENT_CONTENT_TYPE, ARGUMENT_CONTENT, ARGUMENT_ADDRESS)) { resultErrorArgumentMissing(result); return; @@ -1225,7 +1224,7 @@ private void decryptMessageInMemory(MethodCall methodCall, MethodChannel.Result } ChatIdWrapper chatIdWrapper = new ChatIdWrapper(); String plainText = dcContext.decryptMessageInMemory(contentType, content, senderAddress, chatIdWrapper); - Log.d("dboehrs", "decryptMessageInMemory: finished with" + chatIdWrapper.chatId); - result.success(plainText); + List data = Arrays.asList(chatIdWrapper.chatId, plainText); + result.success(data); } } \ No newline at end of file diff --git a/lib/delta_chat_core.dart b/lib/delta_chat_core.dart index d1845ac..9003046 100644 --- a/lib/delta_chat_core.dart +++ b/lib/delta_chat_core.dart @@ -51,6 +51,7 @@ import 'package:logging/logging.dart'; export 'package:delta_chat_core/src/types/chat_summary.dart'; export 'package:delta_chat_core/src/types/event.dart'; export 'package:delta_chat_core/src/types/qrcode_result.dart'; +export 'package:delta_chat_core/src/types/decrypted_chat_message.dart'; export 'src/base.dart'; export 'src/chat.dart'; diff --git a/lib/src/context.dart b/lib/src/context.dart index 6b53a83..6571ff2 100644 --- a/lib/src/context.dart +++ b/lib/src/context.dart @@ -404,7 +404,7 @@ class Context { return await core.invokeMethod(methodGetNextMedia, getNextMediaArguments(messageId, dir, messageTypeOne, messageTypeTwo, messageTypeThree)); } - Future decryptInMemory(String contentType, String content, String senderAddress) async { + Future> decryptInMemory(String contentType, String content, String senderAddress) async { return await core.invokeMethod(methodDecryptInMemory, getDecryptInMemoryArguments(contentType, content, senderAddress)); } diff --git a/lib/src/types/decrypted_chat_message.dart b/lib/src/types/decrypted_chat_message.dart new file mode 100644 index 0000000..86a21cc --- /dev/null +++ b/lib/src/types/decrypted_chat_message.dart @@ -0,0 +1,57 @@ +/* + * OPEN-XCHANGE legal information + * + * All intellectual property rights in the Software are protected by + * international copyright laws. + * + * + * In some countries OX, OX Open-Xchange and open xchange + * as well as the corresponding Logos OX Open-Xchange and OX are registered + * trademarks of the OX Software GmbH group of companies. + * The use of the Logos is not covered by the Mozilla Public License 2.0 (MPL 2.0). + * Instead, you are allowed to use these Logos according to the terms and + * conditions of the Creative Commons License, Version 2.5, Attribution, + * Non-commercial, ShareAlike, and the interpretation of the term + * Non-commercial applicable to the aforementioned license is published + * on the web site https://www.open-xchange.com/terms-and-conditions/. + * + * Please make sure that third-party modules and libraries are used + * according to their respective licenses. + * + * Any modifications to this package must retain all copyright notices + * of the original copyright holder(s) for the original code used. + * + * After any such modifications, the original and derivative code shall remain + * under the copyright of the copyright holder(s) and/or original author(s) as stated here: + * https://www.open-xchange.com/legal/. The contributing author shall be + * given Attribution for the derivative code and a license granting use. + * + * Copyright (C) 2016-2020 OX Software GmbH + * Mail: info@open-xchange.com + * + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the Mozilla Public License 2.0 + * for more details. + */ + +const indexChatId = 0; +const indexContent = 1; + +class DecryptedChatMessage { + int chatId; + String content; + + DecryptedChatMessage.fromMethodChannel(List data) { + if (data is! List) { + throw ArgumentError("Given data is no List, can't create DecryptedPushMessage object"); + } + chatId = data[indexChatId]; + content = data[indexContent]; + } +} From 16d955f1cada9b83dea862686b9470576cc38bb7 Mon Sep 17 00:00:00 2001 From: Frank Gregor Date: Fri, 5 Jun 2020 12:04:00 +0200 Subject: [PATCH 13/16] adopts changes regarding renaming of 'getArchived' to 'getVisibility' --- ios/Classes/DCWrapper/DcChat.swift | 8 ++++---- ios/Classes/Handler/ChatCallHandler.swift | 12 ++++++------ ios/Classes/Helper/Method.swift | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ios/Classes/DCWrapper/DcChat.swift b/ios/Classes/DCWrapper/DcChat.swift index 15ad22e..60fd43e 100644 --- a/ios/Classes/DCWrapper/DcChat.swift +++ b/ios/Classes/DCWrapper/DcChat.swift @@ -71,6 +71,10 @@ class DcChat { var type: Int32 { return dc_chat_get_type(chatPointer) } + + var visibility: Int32 { + return dc_chat_get_visibility(chatPointer) + } var chatType: ChatType { return ChatType(rawValue: type) ?? .group // group as fallback - shouldn't get here @@ -115,10 +119,6 @@ class DcChat { return nil }() -// var archived: Int32 { -// return dc_chat_get_archived(chatPointer) -// } - var color: UInt32 { return dc_chat_get_color(chatPointer) } diff --git a/ios/Classes/Handler/ChatCallHandler.swift b/ios/Classes/Handler/ChatCallHandler.swift index 1144b09..1970be3 100644 --- a/ios/Classes/Handler/ChatCallHandler.swift +++ b/ios/Classes/Handler/ChatCallHandler.swift @@ -63,8 +63,8 @@ class ChatCallHandler: MethodCallHandling { case Method.Chat.IS_GROUP: isGroup(methodCall: call, result: result) break - case Method.Chat.GET_ARCHIVED: -// getArchived(methodCall: call, result: result) + case Method.Chat.GET_VISIBILITY: + getVisibility(methodCall: call, result: result) break case Method.Chat.GET_COLOR: getColor(methodCall: call, result: result) @@ -113,10 +113,10 @@ class ChatCallHandler: MethodCallHandling { result(chat.name) } -// private func getArchived(methodCall: FlutterMethodCall, result: FlutterResult) { -// let chat = getChat(methodCall: methodCall, result: result) -// result(NSNumber(value: chat.archived)) -// } + private func getVisibility(methodCall: FlutterMethodCall, result: FlutterResult) { + let chat = getChat(methodCall: methodCall, result: result) + result(NSNumber(value: chat.visibility)) + } private func getColor(methodCall: FlutterMethodCall, result: FlutterResult) { let chat = getChat(methodCall: methodCall, result: result) diff --git a/ios/Classes/Helper/Method.swift b/ios/Classes/Helper/Method.swift index 62cb825..18c4097 100644 --- a/ios/Classes/Helper/Method.swift +++ b/ios/Classes/Helper/Method.swift @@ -76,7 +76,7 @@ extension Method { struct Chat { static let GET_ID = "chat_getId" static let IS_GROUP = "chat_isGroup" - static let GET_ARCHIVED = "chat_getArchived" + static let GET_VISIBILITY = "chat_getVisibility" static let GET_COLOR = "chat_getColor" static let GET_NAME = "chat_getName" static let GET_PROFILE_IMAGE = "chat_getProfileImage" From 98a685a00797a608231b43bdd0b6671db042e9ec Mon Sep 17 00:00:00 2001 From: Frank Gregor Date: Fri, 5 Jun 2020 12:17:56 +0200 Subject: [PATCH 14/16] Removes unused 'base_systemInfo' call handling --- ios/Classes/Helper/Method.swift | 1 - ios/Classes/SwiftDeltaChatCorePlugin.swift | 6 ------ 2 files changed, 7 deletions(-) diff --git a/ios/Classes/Helper/Method.swift b/ios/Classes/Helper/Method.swift index 18c4097..571db3f 100644 --- a/ios/Classes/Helper/Method.swift +++ b/ios/Classes/Helper/Method.swift @@ -64,7 +64,6 @@ extension Method { struct Base { static let INIT = "base_init" - static let SYSTEM_INFO = "base_systemInfo" static let CORE_LISTENER = "base_coreListener" static let LOGOUT = "base_logout" } diff --git a/ios/Classes/SwiftDeltaChatCorePlugin.swift b/ios/Classes/SwiftDeltaChatCorePlugin.swift index 698cd97..90edd49 100644 --- a/ios/Classes/SwiftDeltaChatCorePlugin.swift +++ b/ios/Classes/SwiftDeltaChatCorePlugin.swift @@ -123,8 +123,6 @@ public class SwiftDeltaChatCorePlugin: NSObject, FlutterPlugin { switch (call.method) { case Method.Base.INIT: baseInit(result: result) - case Method.Base.SYSTEM_INFO: - systemInfo(result: result) case Method.Base.LOGOUT: logout(result: result) default: @@ -170,10 +168,6 @@ public class SwiftDeltaChatCorePlugin: NSObject, FlutterPlugin { Utils.logEventAndDelegate(logLevel: SwiftyBeaver.Level.error, message: "Couldn't open user database at path: \(dcContext.userDatabasePath)") result(DCPluginError.couldNotOpenDataBase()) } - - fileprivate func systemInfo(result: FlutterResult) { - result(UIApplication.version) - } func logout(result: FlutterResult) { let application = UIApplication.shared From 2bc78727d9b46b056a9c27999b2ebac6a98128be Mon Sep 17 00:00:00 2001 From: Frank Gregor Date: Fri, 5 Jun 2020 15:11:57 +0200 Subject: [PATCH 15/16] Adds changes regarding 'minimalSetup' and 'tearDown' --- ios/Classes/DCWrapper/DCContext.swift | 12 +-- ios/Classes/DCWrapper/DCEventHandler.swift | 1 - .../ErrorHandling/DcContextError.swift | 12 +-- ios/Classes/Handler/ChatListCallHandler.swift | 5 ++ ios/Classes/Handler/ContextCallHandler.swift | 19 +++-- ios/Classes/Handler/EventChannelHandler.swift | 5 ++ .../{Arguments.swift => Argument.swift} | 1 + ios/Classes/Helper/Method.swift | 1 + ios/Classes/SwiftDeltaChatCorePlugin.swift | 84 ++++++++++++++----- 9 files changed, 95 insertions(+), 45 deletions(-) rename ios/Classes/Helper/{Arguments.swift => Argument.swift} (98%) diff --git a/ios/Classes/DCWrapper/DCContext.swift b/ios/Classes/DCWrapper/DCContext.swift index c66faba..47fb313 100644 --- a/ios/Classes/DCWrapper/DCContext.swift +++ b/ios/Classes/DCWrapper/DCContext.swift @@ -61,6 +61,10 @@ class DcContext { deinit { dc_context_unref(DcContext.contextPointer) } + + func teardown() { + dc_close(DcContext.contextPointer) + } // MARK: - User Database @@ -131,10 +135,6 @@ class DcContext { dc_delete_chat(DcContext.contextPointer, UInt32(chatId)) } -// func archiveChat(chatId: UInt32, archive: Bool) { -// dc_archive_chat(DcContext.contextPointer, UInt32(chatId), Int32(archive ? 1 : 0)) -// } - func createChatByMessageId(messageId: UInt32) -> DcChat { let chatId = dc_create_chat_by_msg_id(DcContext.contextPointer, messageId) return DcChat(id: chatId) @@ -281,7 +281,7 @@ class DcContext { func sendAttachment(fromPath path: String, withType type: Int32, mimeType: String?, text: String?, duration: Int32?, forChatId chatId: UInt32) throws -> UInt32 { guard Int(type).isValidAttachmentType else { - throw DcContextError(kind: .wrongAttachmentType(type)) + throw DcContextError.wrongAttachmentType(type) } let msg = DcMsg(type: type) @@ -297,7 +297,7 @@ class DcContext { switch type { case DC_MSG_IMAGE, DC_MSG_GIF: guard let image = UIImage(contentsOfFile: path) else { - throw DcContextError(kind: .missingImageAtPath(path)) + throw DcContextError.missingImageAtPath(path) } let pixelSize = image.sizeInPixel diff --git a/ios/Classes/DCWrapper/DCEventHandler.swift b/ios/Classes/DCWrapper/DCEventHandler.swift index 43e02cc..7e83018 100644 --- a/ios/Classes/DCWrapper/DCEventHandler.swift +++ b/ios/Classes/DCWrapper/DCEventHandler.swift @@ -59,7 +59,6 @@ class DCEventHandler { func start(_ completion: (() -> Void)? = nil) { if state == .running { return } - state = .running DispatchQueue.global(qos: .background).async { diff --git a/ios/Classes/DCWrapper/ErrorHandling/DcContextError.swift b/ios/Classes/DCWrapper/ErrorHandling/DcContextError.swift index ef417ec..857d059 100644 --- a/ios/Classes/DCWrapper/ErrorHandling/DcContextError.swift +++ b/ios/Classes/DCWrapper/ErrorHandling/DcContextError.swift @@ -42,13 +42,7 @@ import Foundation -struct DcContextError: Error { - - enum ErrorKind { - case missingImageAtPath(String) - case wrongAttachmentType(Int32) - } - - let kind: ErrorKind - +enum DcContextError: Error { + case missingImageAtPath(String) + case wrongAttachmentType(Int32) } diff --git a/ios/Classes/Handler/ChatListCallHandler.swift b/ios/Classes/Handler/ChatListCallHandler.swift index 2a7f648..f0345ed 100644 --- a/ios/Classes/Handler/ChatListCallHandler.swift +++ b/ios/Classes/Handler/ChatListCallHandler.swift @@ -58,6 +58,11 @@ class ChatListCallHandler: MethodCallHandling { self.context = context self.chatCache = chatCache } + + deinit { + chatCache.clear() + chatListCache.clear() + } // MARK: - Protocol MethodCallHandling diff --git a/ios/Classes/Handler/ContextCallHandler.swift b/ios/Classes/Handler/ContextCallHandler.swift index 017bf98..2af90ce 100644 --- a/ios/Classes/Handler/ContextCallHandler.swift +++ b/ios/Classes/Handler/ContextCallHandler.swift @@ -56,6 +56,12 @@ class ContextCallHandler: MethodCallHandling { self.messageCache = messageCache self.chatCache = chatCache } + + deinit { + contactCache.clear() + messageCache.clear() + chatCache.clear() + } // MARK: - Protocol MethodCallHandling @@ -454,7 +460,7 @@ class ContextCallHandler: MethodCallHandling { let chatId = methodCall.intValue(for: Argument.CHAT_ID, result: result) let type = methodCall.intValue(for: Argument.TYPE, result: result) let text = methodCall.stringValue(for: Argument.TEXT, result: result) - var mimeType = methodCall.stringValue(for: Argument.MIME_TYPE, result: result) + let mimeType = methodCall.stringValue(for: Argument.MIME_TYPE, result: result) let duration = methodCall.intValue(for: Argument.DURATION, result: result) guard let path = methodCall.stringValue(for: Argument.PATH, result: result) else { @@ -466,12 +472,12 @@ class ContextCallHandler: MethodCallHandling { let messageId = try context.sendAttachment(fromPath: path, withType: type, mimeType: mimeType, text: text, duration: duration, forChatId: UInt32(chatId)) result(NSNumber(value: messageId)) - } catch DcContextError.ErrorKind.missingImageAtPath(let path) { - Utils.logEventAndDelegate(logLevel: SwiftyBeaver.Level.error, message: "Can't find image at given path: \(path)") - } catch DcContextError.ErrorKind.wrongAttachmentType(let type) { - Utils.logEventAndDelegate(logLevel: SwiftyBeaver.Level.error, message: "Wrong attachment type given: \(type)!") + } catch DcContextError.missingImageAtPath(let path) { + Utils.logEventAndDelegate(logLevel: .error, message: "Can't find image at given path: \(path)") + } catch DcContextError.wrongAttachmentType(let type) { + Utils.logEventAndDelegate(logLevel: .error, message: "Wrong attachment type given: \(type)!") } catch { - Utils.logEventAndDelegate(logLevel: SwiftyBeaver.Level.error, message: "Unhandled error: \(error)") + Utils.logEventAndDelegate(logLevel: .error, message: "Unhandled error: \(error)") } } @@ -601,7 +607,6 @@ class ContextCallHandler: MethodCallHandling { let star = methodCall.intValue(for: Argument.VALUE, result: result) context.starMessages(messageIds: msgIds, star: star) -// _ = msgIds.map { loadAndCacheChatMessage(with: $0, update: true) } result(nil) } diff --git a/ios/Classes/Handler/EventChannelHandler.swift b/ios/Classes/Handler/EventChannelHandler.swift index 49d8333..967ebe5 100644 --- a/ios/Classes/Handler/EventChannelHandler.swift +++ b/ios/Classes/Handler/EventChannelHandler.swift @@ -87,6 +87,11 @@ class EventChannelHandler: NSObject, FlutterStreamHandler { override init() { super.init() } + + deinit { + self.eventChannel.setStreamHandler(nil) + self.eventChannel = nil + } // MARK: - FlutterStreamHandler diff --git a/ios/Classes/Helper/Arguments.swift b/ios/Classes/Helper/Argument.swift similarity index 98% rename from ios/Classes/Helper/Arguments.swift rename to ios/Classes/Helper/Argument.swift index 88713e9..7dd1cf3 100644 --- a/ios/Classes/Helper/Arguments.swift +++ b/ios/Classes/Helper/Argument.swift @@ -80,6 +80,7 @@ struct Argument { static let MESSAGE_TYPE_ONE = "messageTypeOne" static let MESSAGE_TYPE_TWO = "messageTypeTwo" static let MESSAGE_TYPE_THREE = "messageTypeThree" + static let MINIMAL_SETUP = "minimalSetup" struct Error { static let MISSING = "1" diff --git a/ios/Classes/Helper/Method.swift b/ios/Classes/Helper/Method.swift index 571db3f..f19dcd7 100644 --- a/ios/Classes/Helper/Method.swift +++ b/ios/Classes/Helper/Method.swift @@ -66,6 +66,7 @@ extension Method { static let INIT = "base_init" static let CORE_LISTENER = "base_coreListener" static let LOGOUT = "base_logout" + static let TEARDOWN = "base_tearDown" } } diff --git a/ios/Classes/SwiftDeltaChatCorePlugin.swift b/ios/Classes/SwiftDeltaChatCorePlugin.swift index 90edd49..9dea318 100644 --- a/ios/Classes/SwiftDeltaChatCorePlugin.swift +++ b/ios/Classes/SwiftDeltaChatCorePlugin.swift @@ -50,18 +50,19 @@ public class SwiftDeltaChatCorePlugin: NSObject, FlutterPlugin { fileprivate let registrar: FlutterPluginRegistrar! - fileprivate let dcContext: DcContext! - fileprivate let dcEventHandler: DCEventHandler! + fileprivate var dcContext: DcContext! + fileprivate var dcEventHandler: DCEventHandler! - fileprivate let chatCache: IdCache = IdCache() - fileprivate let contactCache: IdCache = IdCache() - fileprivate let messageCache: IdCache = IdCache() + fileprivate var chatCache: IdCache = IdCache() + fileprivate var contactCache: IdCache = IdCache() + fileprivate var messageCache: IdCache = IdCache() - fileprivate let chatCallHandler: ChatCallHandler! - fileprivate let chatListCallHandler: ChatListCallHandler! - fileprivate let contactCallHandler: ContactCallHandler! - fileprivate let contextCallHandler: ContextCallHandler! - fileprivate let messageCallHandler: MessageCallHandler! + fileprivate var chatCallHandler: ChatCallHandler! + fileprivate var chatListCallHandler: ChatListCallHandler! + fileprivate var contactCallHandler: ContactCallHandler! + fileprivate var contextCallHandler: ContextCallHandler! + fileprivate var messageCallHandler: MessageCallHandler! + fileprivate var eventChannelHandler: EventChannelHandler! // MARK: - Initialization @@ -77,23 +78,29 @@ public class SwiftDeltaChatCorePlugin: NSObject, FlutterPlugin { self.contactCallHandler = ContactCallHandler(context: dcContext, contextCallHandler: contextCallHandler) self.messageCallHandler = MessageCallHandler(context: dcContext, contextCallHandler: contextCallHandler) - let ech = EventChannelHandler.sharedInstance - ech.messenger = registrar.messenger() + self.eventChannelHandler = EventChannelHandler.sharedInstance + self.eventChannelHandler.messenger = registrar.messenger() super.init() } + + // MARK: - FlutterPlugin // This is our entry point public static func register(with registrar: FlutterPluginRegistrar) { + Utils.logEventAndDelegate(logLevel: .info, message: "Attaching plugin via v2 embedding") let channel = FlutterMethodChannel(name: MethodChannel.DeltaChat.Core, binaryMessenger: registrar.messenger()) let delegate = SwiftDeltaChatCorePlugin(registrar: registrar) registrar.addMethodCallDelegate(delegate, channel: channel) } - - // MARK: - FlutterPlugin + + public func detachFromEngine(for registrar: FlutterPluginRegistrar) { + Utils.logEventAndDelegate(logLevel: .info, message: "Detaching plugin via v2 embedding") + teardown() + } public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - Utils.logEventAndDelegate(logLevel: SwiftyBeaver.Level.debug, message: "Dart MethodCall: \(call.method)") + Utils.logEventAndDelegate(logLevel: .debug, message: "Dart MethodCall: \(call.method)") if call.contains(key: Argument.REMOVE_CACHE_IDENTIFIER) { removeFromCache(with: call, result: result) @@ -113,7 +120,7 @@ public class SwiftDeltaChatCorePlugin: NSObject, FlutterPlugin { case Method.Prefix.MSG: messageCallHandler.handle(call, result: result) default: - Utils.logEventAndDelegate(logLevel: SwiftyBeaver.Level.debug, message: "Failing for \(call.method)") + Utils.logEventAndDelegate(logLevel: .debug, message: "Failing for \(call.method)") result(FlutterMethodNotImplemented) } @@ -122,11 +129,16 @@ public class SwiftDeltaChatCorePlugin: NSObject, FlutterPlugin { func handleBaseCalls(with call: FlutterMethodCall, result: FlutterResult) { switch (call.method) { case Method.Base.INIT: - baseInit(result: result) + baseInit(with: call, result: result) case Method.Base.LOGOUT: + teardown() logout(result: result) + result(nil) + case Method.Base.TEARDOWN: + teardown() + result(nil) default: - Utils.logEventAndDelegate(logLevel: SwiftyBeaver.Level.error, message: "Failing for \(call.method)") + Utils.logEventAndDelegate(logLevel: .error, message: "Failing for \(call.method)") result(FlutterMethodNotImplemented) } } @@ -146,7 +158,7 @@ public class SwiftDeltaChatCorePlugin: NSObject, FlutterPlugin { _ = chatCache.removeValue(for: id) case .chatMessage: if let msg = messageCache.removeValue(for: id) { - Utils.logEventAndDelegate(logLevel: SwiftyBeaver.Level.info, message: "removed message: \(msg.type)") + Utils.logEventAndDelegate(logLevel: .info, message: "removed message: \(msg.type)") } case .contact: _ = contactCache.removeValue(for: id) @@ -158,14 +170,21 @@ public class SwiftDeltaChatCorePlugin: NSObject, FlutterPlugin { // MARK: - Handle Base Calls - fileprivate func baseInit(result: FlutterResult) { + fileprivate func baseInit(with call: FlutterMethodCall, result: FlutterResult) { + let minimalSetup = call.boolValue(for: Argument.MINIMAL_SETUP, result: result) + + Utils.logEventAndDelegate(logLevel: .info, message: "Init started, with minimal setup: \(minimalSetup ? "YES": "NO")") + if dcContext.openUserDataBase() { - dcEventHandler.start() + if !minimalSetup { + dcEventHandler.start() + } _ = dcContext.getCoreInfo() result(dcContext.userDatabasePath) return } - Utils.logEventAndDelegate(logLevel: SwiftyBeaver.Level.error, message: "Couldn't open user database at path: \(dcContext.userDatabasePath)") + + Utils.logEventAndDelegate(logLevel: .error, message: "Couldn't open user database at path: \(dcContext.userDatabasePath)") result(DCPluginError.couldNotOpenDataBase()) } @@ -179,5 +198,26 @@ public class SwiftDeltaChatCorePlugin: NSObject, FlutterPlugin { result(nil) } } + + func teardown() { + Utils.logEventAndDelegate(logLevel: .info, message: "Teardown started") + + dcContext.teardown() + dcContext = nil + dcEventHandler.stop() + dcEventHandler = nil + contextCallHandler = nil + chatCallHandler = nil + chatListCallHandler = nil + contactCallHandler = nil + messageCallHandler = nil + eventChannelHandler = nil + + chatCache.clear() + contactCache.clear() + messageCache.clear() + + Utils.logEventAndDelegate(logLevel: .info, message: "Teardown finished") + } } From c54cc0a06185b5d7f02f7bc0a369d9dafbd028f9 Mon Sep 17 00:00:00 2001 From: Frank Gregor Date: Fri, 5 Jun 2020 15:53:38 +0200 Subject: [PATCH 16/16] Adds incomplete(!) implementation for 'decryptMessageInMemory` --- ios/Classes/DCWrapper/DCChatIdWrapper.swift | 48 ++++++++++++++++++++ ios/Classes/DCWrapper/DCContext.swift | 18 ++++++++ ios/Classes/Handler/ContextCallHandler.swift | 19 ++++++++ ios/Classes/Helper/Argument.swift | 2 + ios/Classes/Helper/Method.swift | 1 + 5 files changed, 88 insertions(+) create mode 100644 ios/Classes/DCWrapper/DCChatIdWrapper.swift diff --git a/ios/Classes/DCWrapper/DCChatIdWrapper.swift b/ios/Classes/DCWrapper/DCChatIdWrapper.swift new file mode 100644 index 0000000..34d2929 --- /dev/null +++ b/ios/Classes/DCWrapper/DCChatIdWrapper.swift @@ -0,0 +1,48 @@ +/* + * OPEN-XCHANGE legal information + * + * All intellectual property rights in the Software are protected by + * international copyright laws. + * + * + * In some countries OX, OX Open-Xchange and open xchange + * as well as the corresponding Logos OX Open-Xchange and OX are registered + * trademarks of the OX Software GmbH group of companies. + * The use of the Logos is not covered by the Mozilla Public License 2.0 (MPL 2.0). + * Instead, you are allowed to use these Logos according to the terms and + * conditions of the Creative Commons License, Version 2.5, Attribution, + * Non-commercial, ShareAlike, and the interpretation of the term + * Non-commercial applicable to the aforementioned license is published + * on the web site https://www.open-xchange.com/terms-and-conditions/. + * + * Please make sure that third-party modules and libraries are used + * according to their respective licenses. + * + * Any modifications to this package must retain all copyright notices + * of the original copyright holder(s) for the original code used. + * + * After any such modifications, the original and derivative code shall remain + * under the copyright of the copyright holder(s) and/or original author(s) as stated here: + * https://www.open-xchange.com/legal/. The contributing author shall be + * given Attribution for the derivative code and a license granting use. + * + * Copyright (C) 2016-2020 OX Software GmbH + * Mail: info@open-xchange.com + * + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the Mozilla Public License 2.0 + * for more details. + */ + +import Foundation + +class DcChatIdWrapper { + var chatId: Int32 = 0 + var totalNumberOfParts: Int32 = 0 +} diff --git a/ios/Classes/DCWrapper/DCContext.swift b/ios/Classes/DCWrapper/DCContext.swift index 47fb313..1476d24 100644 --- a/ios/Classes/DCWrapper/DCContext.swift +++ b/ios/Classes/DCWrapper/DCContext.swift @@ -357,6 +357,24 @@ class DcContext { func getNextMedia(messageId: UInt32, dir: Int32, msgTypeOne: Int32, msgTypeTwo: Int32, msgTypeThree: Int32) -> UInt32 { return dc_get_next_media(DcContext.contextPointer, messageId, dir, msgTypeOne, msgTypeTwo, msgTypeThree) } + + func decryptMessageInMemory(contentType: String, content: String, senderAddress: String, chatIdWrapper: DcChatIdWrapper) -> String? { +// var totalNumberOfParts: UnsafeMutablePointer? +// var chatId: UnsafeMutablePointer? +// +// guard let cMessage = dc_decrypt_message_in_memory( +// DcContext.contextPointer, +// contentType, +// content, +// senderAddress, +// 0, +// totalNumberOfParts, +// chatId) else { +// return nil +// } +// return String(cString: cMessage) + return nil + } // MARK: - General diff --git a/ios/Classes/Handler/ContextCallHandler.swift b/ios/Classes/Handler/ContextCallHandler.swift index 2af90ce..3fbe98b 100644 --- a/ios/Classes/Handler/ContextCallHandler.swift +++ b/ios/Classes/Handler/ContextCallHandler.swift @@ -181,6 +181,8 @@ class ContextCallHandler: MethodCallHandling { getContactIdByAddress(methodCall: call, result: result) case Method.Context.GET_NEXT_MEDIA: getNextMedia(methodCall: call, result: result) + case Method.Context.DECRYPT_IN_MEMORY: + decryptMessageInMemory(methodCall: call, result: result) default: Utils.logEventAndDelegate(logLevel: SwiftyBeaver.Level.error, message: "Context: Failing for \(call.method)") result(FlutterMethodNotImplemented) @@ -366,6 +368,23 @@ class ContextCallHandler: MethodCallHandling { result(nextMessageId) } + + fileprivate func decryptMessageInMemory(methodCall: FlutterMethodCall, result: FlutterResult) { +// if !methodCall.contains(keys: [Argument.CONTENT, Argument.CONTENT_TYPE, Argument.ADDRESS]) { +// Method.Error.missingArgument("'\(Argument.CONTENT)', '\(Argument.CONTENT_TYPE)' or '\(Argument.ADDRESS)'", result: result) +// return +// } +// +// guard let content = methodCall.stringValue(for: Argument.CONTENT, result: result), +// let contentType = methodCall.stringValue(for: Argument.CONTENT_TYPE, result: result), +// let address = methodCall.stringValue(for: Argument.ADDRESS, result: result) else { +// Method.Error.missingArgumentValues(for: [Argument.CONTENT, Argument.CONTENT_TYPE, Argument.ADDRESS], result: result) +// return +// } + + //TODO: Finish Implementation!!! +// context.decryptMessageInMemory(contentType: contentType, content: content, senderAddress: address, chatIdWrapper: <#T##DcChatIdWrapper#>) + } // MARK: - Chat Related diff --git a/ios/Classes/Helper/Argument.swift b/ios/Classes/Helper/Argument.swift index 7dd1cf3..887b878 100644 --- a/ios/Classes/Helper/Argument.swift +++ b/ios/Classes/Helper/Argument.swift @@ -81,6 +81,8 @@ struct Argument { static let MESSAGE_TYPE_TWO = "messageTypeTwo" static let MESSAGE_TYPE_THREE = "messageTypeThree" static let MINIMAL_SETUP = "minimalSetup" + static let CONTENT = "content" + static let CONTENT_TYPE = "contentType" struct Error { static let MISSING = "1" diff --git a/ios/Classes/Helper/Method.swift b/ios/Classes/Helper/Method.swift index f19dcd7..0cc3aa1 100644 --- a/ios/Classes/Helper/Method.swift +++ b/ios/Classes/Helper/Method.swift @@ -219,6 +219,7 @@ extension Method { static let GET_CONTACT_ID_BY_ADDRESS = "context_getContactIdByAddress" static let GET_NEXT_MEDIA = "context_getNextMedia" static let CLOSE = "context_close" + static let DECRYPT_IN_MEMORY = "context_decryptInMemory" } }