android service and notification support take one. ACTION
This commit is contained in:
parent
89507a8aa6
commit
9c69275fe6
|
@ -114,6 +114,7 @@ dependencies {
|
|||
|
||||
// end of workmanager deps
|
||||
|
||||
// ipc
|
||||
implementation "io.reactivex:rxkotlin:1.x.y"
|
||||
// needed to prevent a ListenableFuture dependency conflict/bug
|
||||
// see https://github.com/google/ExoPlayer/issues/7905#issuecomment-692637059
|
||||
implementation 'com.google.guava:guava:any'
|
||||
}
|
||||
|
|
|
@ -45,4 +45,7 @@
|
|||
|
||||
<!--Needed to run in background (lol)-->
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<!--Meeded to check if activity is foregrounded or if messages from the service should be queued-->
|
||||
<uses-permission android:name="android.permission.GET_TASKS" />
|
||||
</manifest>
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
package im.cwtch.flwtch
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.*
|
||||
import android.content.Context
|
||||
import android.content.Context.ACTIVITY_SERVICE
|
||||
import android.content.Intent
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import androidx.work.*
|
||||
import cwtch.Cwtch
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import io.flutter.FlutterInjector
|
||||
import org.json.JSONObject
|
||||
|
||||
|
||||
|
@ -27,6 +25,9 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
|
|||
context.getSystemService(Context.NOTIFICATION_SERVICE) as
|
||||
NotificationManager
|
||||
|
||||
private var notificationID: MutableMap<String, Int> = mutableMapOf()
|
||||
private var notificationIDnext: Int = 1;
|
||||
|
||||
override suspend fun doWork(): Result {
|
||||
val method = inputData.getString(KEY_METHOD)
|
||||
?: return Result.failure()
|
||||
|
@ -36,38 +37,96 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
|
|||
// Mark the Worker as important
|
||||
val progress = "Trying to do a Flwtch"
|
||||
setForeground(createForegroundInfo(progress))
|
||||
handleCwtch(method, args)
|
||||
return Result.success()
|
||||
return handleCwtch(method, args)
|
||||
}
|
||||
|
||||
private suspend fun handleCwtch(method: String, args: String) {
|
||||
var a = JSONObject(args);
|
||||
private fun getNotificationID(profile: String, contact: String): Int {
|
||||
val k = "$profile $contact"
|
||||
if (!notificationID.containsKey(k)) {
|
||||
notificationID[k] = notificationIDnext++
|
||||
}
|
||||
return notificationID[k] ?: -1
|
||||
}
|
||||
|
||||
private suspend fun handleCwtch(method: String, args: String): Result {
|
||||
val a = JSONObject(args);
|
||||
when (method) {
|
||||
"Start" -> {
|
||||
Log.i("FlwtchWorker.kt", "handleAppInfo Start")
|
||||
val appDir = (a.get("appDir") as? String) ?: "";
|
||||
val torPath = (a.get("torPath") as? String) ?: "tor";
|
||||
Log.i("FlwtchWorker.kt", " appDir: '" + appDir + "' torPath: '" + torPath + "'")
|
||||
val appDir = (a.get("appDir") as? String) ?: ""
|
||||
val torPath = (a.get("torPath") as? String) ?: "tor"
|
||||
Log.i("FlwtchWorker.kt", "appDir: '$appDir' torPath: '$torPath'")
|
||||
|
||||
Cwtch.startCwtch(appDir, torPath)
|
||||
if (Cwtch.startCwtch(appDir, torPath) != 0.toByte()) return Result.failure()
|
||||
|
||||
// infinite coroutine :)
|
||||
while(true) {
|
||||
val evt = MainActivity.AppbusEvent(Cwtch.getAppBusEvent())
|
||||
Log.i("FlwtchWorker.kt", "got appbusEvent: " + evt)
|
||||
//Log.i("FlwtchWorker.kt", "got appbusEvent: " + evt)
|
||||
if (isStopped) {
|
||||
Log.i("FlwtchWorker.kt", "COROUTINEWORKER DOT ISSTOPPED TRUE OH MY")
|
||||
}
|
||||
//todo: this elides evt.EventID which may be needed at some point?
|
||||
val data = Data.Builder()
|
||||
.putString("EventType", evt.EventType)
|
||||
.putString("Data", evt.Data)
|
||||
.putString("EventID", evt.EventID)
|
||||
|
||||
if (evt.EventType == "NewMessageFromPeer") {
|
||||
val data = JSONObject(evt.Data)
|
||||
|
||||
// val appProcesses: List<ActivityManager.RunningAppProcessInfo> = (applicationContext.getSystemService(ACTIVITY_SERVICE) as ActivityManager).runningAppProcesses
|
||||
// for (appProcess in appProcesses) {
|
||||
// if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
|
||||
// Log.i("Foreground App", appProcess.processName)
|
||||
// }
|
||||
// }
|
||||
val channelId =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
createMessageNotificationChannel(data.getString("RemotePeer"), data.getString("RemotePeer"))
|
||||
} else {
|
||||
// If earlier version channel ID is not used
|
||||
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
|
||||
""
|
||||
}
|
||||
|
||||
val loader = FlutterInjector.instance().flutterLoader()
|
||||
val key = loader.getLookupKeyForAsset("assets/"+data.getString("Picture"))//"assets/profiles/001-centaur.png")
|
||||
val fh = applicationContext.assets.open(key)
|
||||
|
||||
val clickIntent = Intent(applicationContext, MainActivity::class.java).also { intent ->
|
||||
intent.setAction(Intent.ACTION_RUN)//("im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS")
|
||||
//intent.setClassName("im.cwtch.flwtch", "MainActivity")
|
||||
intent.putExtra("EventType", "NotificationClicked")
|
||||
intent.putExtra("ProfileOnion", data.getString("ProfileOnion"))
|
||||
intent.putExtra("RemotePeer", data.getString("RemotePeer"))
|
||||
}
|
||||
|
||||
val newNotification = NotificationCompat.Builder(applicationContext, channelId)
|
||||
.setContentTitle(data.getString("Nick"))
|
||||
.setContentText("New message")
|
||||
.setLargeIcon(BitmapFactory.decodeStream(fh))
|
||||
.setSmallIcon(R.mipmap.knott)
|
||||
.setContentIntent(PendingIntent.getActivity(applicationContext, 1, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT))
|
||||
.setAutoCancel(true)
|
||||
.build()
|
||||
setProgress(data)
|
||||
Thread.sleep(200)
|
||||
notificationManager.notify(getNotificationID(data.getString("ProfileOnion"), data.getString("RemotePeer")), newNotification)
|
||||
}
|
||||
//todo: this elides evt.EventID which may be needed at some point?
|
||||
// val data = Data.Builder()
|
||||
// .putString("EventType", evt.EventType)
|
||||
// .putString("Data", evt.Data)
|
||||
// .putString("EventID", evt.EventID)
|
||||
// .build()
|
||||
//setProgress(data)//progress can only hold a single undelivered value so it's possible for observers to miss rapidfire updates
|
||||
//Thread.sleep(200)//this is a kludge to make it mostly-work until a proper channel is implemented
|
||||
Intent().also { intent ->
|
||||
intent.setAction("im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS")
|
||||
intent.putExtra("EventType", evt.EventType)
|
||||
intent.putExtra("Data", evt.Data)
|
||||
intent.putExtra("EventID", evt.EventID)
|
||||
LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
"ReconnectCwtchForeground" -> {
|
||||
Cwtch.reconnectCwtchForeground()
|
||||
}
|
||||
"SelectProfile" -> {
|
||||
val onion = (a.get("profile") as? String) ?: "";
|
||||
Cwtch.selectProfile(onion)
|
||||
|
@ -87,7 +146,7 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
|
|||
"NumMessages" -> {
|
||||
val profile = (a.get("profile") as? String) ?: "";
|
||||
val handle = (a.get("contact") as? String) ?: "";
|
||||
Result.success(Data.Builder().putLong("result", Cwtch.numMessages(profile, handle)).build())
|
||||
return Result.success(Data.Builder().putLong("result", Cwtch.numMessages(profile, handle)).build())
|
||||
}
|
||||
"GetMessage" -> {
|
||||
//Log.i("MainActivivity.kt", (a.get("index")));
|
||||
|
@ -100,14 +159,14 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
|
|||
val handle = (a.get("contact") as? String) ?: "";
|
||||
val indexI = a.getInt("index") ?: 0;
|
||||
Log.i("FlwtchWorker.kt", "indexI = " + indexI)
|
||||
Result.success(Data.Builder().putString("result", Cwtch.getMessage(profile, handle, indexI.toLong())).build())
|
||||
return Result.success(Data.Builder().putString("result", Cwtch.getMessage(profile, handle, indexI.toLong())).build())
|
||||
}
|
||||
"GetMessages" -> {
|
||||
val profile = (a.get("profile") as? String) ?: "";
|
||||
val handle = (a.get("contact") as? String) ?: "";
|
||||
val start = (a.get("start") as? Long) ?: 0;
|
||||
val end = (a.get("end") as? Long) ?: 0;
|
||||
Result.success(Data.Builder().putString("result", Cwtch.getMessages(profile, handle, start, end)).build())
|
||||
return Result.success(Data.Builder().putString("result", Cwtch.getMessages(profile, handle, start, end)).build())
|
||||
}
|
||||
"AcceptContact" -> {
|
||||
val profile = (a.get("ProfileOnion") as? String) ?: "";
|
||||
|
@ -177,26 +236,9 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
|
|||
val groupHandle = (a.get("groupHandle") as? String) ?: "";
|
||||
Cwtch.rejectInvite(profile, groupHandle);
|
||||
}
|
||||
else -> Result.failure()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun launchCwtch(appDir: String, torPath: String) {
|
||||
Cwtch.startCwtch(appDir, torPath)
|
||||
// seperate coroutine to poll event bus and send to dart
|
||||
//Log.i("FlwtchWorker.kt", "got event chan: " + eventbus_chan + " launching corouting...")
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
while(true) {
|
||||
val evt = AppbusEvent(Cwtch.getAppBusEvent())
|
||||
Log.i("FlwtchWorker.kt", "got appbusEvent: " + evt)
|
||||
launch(Dispatchers.Main) {
|
||||
//todo: this elides evt.EventID which may be needed at some point?
|
||||
val flutterEngine = FlutterEngine(applicationContext)
|
||||
val eventbus_chan = MethodChannel(flutterEngine?.dartExecutor?.binaryMessenger, CWTCH_EVENTBUS)
|
||||
eventbus_chan.invokeMethod(evt.EventType, evt.Data)
|
||||
}
|
||||
}
|
||||
else -> return Result.failure()
|
||||
}
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
// Creates an instance of ForegroundInfo which can be used to update the
|
||||
|
@ -243,6 +285,17 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
|
|||
return channelId
|
||||
}
|
||||
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
private fun createMessageNotificationChannel(channelId: String, channelName: String): String{
|
||||
val chan = NotificationChannel(channelId,
|
||||
channelName, NotificationManager.IMPORTANCE_HIGH)
|
||||
chan.lightColor = Color.MAGENTA
|
||||
chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
|
||||
notificationManager.createNotificationChannel(chan)
|
||||
return channelId
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val KEY_METHOD = "KEY_METHOD"
|
||||
const val KEY_ARGS = "KEY_ARGS"
|
||||
|
|
|
@ -1,12 +1,20 @@
|
|||
package im.cwtch.flwtch
|
||||
|
||||
import SplashView
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import androidx.annotation.NonNull
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||
import androidx.work.*
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
@ -27,6 +35,8 @@ import kotlin.concurrent.thread
|
|||
|
||||
import org.json.JSONObject
|
||||
import java.io.File
|
||||
import java.time.Duration
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class MainActivity: FlutterActivity() {
|
||||
|
||||
|
@ -42,11 +52,32 @@ class MainActivity: FlutterActivity() {
|
|||
// Channel to send eventbus events on
|
||||
private val CWTCH_EVENTBUS = "test.flutter.dev/eventBus"
|
||||
|
||||
// Channel to trigger contactview when an external notification is clicked
|
||||
private val CHANNEL_NOTIF_CLICK = "im.cwtch.flwtch/notificationClickHandler"
|
||||
|
||||
private var methodChan: MethodChannel? = null
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
if (methodChan == null || intent.extras == null) return
|
||||
if (!intent.extras!!.containsKey("ProfileOnion") || !intent.extras!!.containsKey("RemotePeer")) {
|
||||
Log.i("onNewIntent", "got intent with no onions")
|
||||
return
|
||||
}
|
||||
val profile = intent.extras!!.getString("ProfileOnion")
|
||||
val handle = intent.extras!!.getString("RemotePeer")
|
||||
val mappo = mapOf("ProfileOnion" to profile, "RemotePeer" to handle)
|
||||
Log.i("MainActivity.kt", "onNewIntent($profile, $handle)")
|
||||
val j = JSONObject(mappo)
|
||||
methodChan!!.invokeMethod("NotificationClicked", j.toString())
|
||||
}
|
||||
|
||||
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
// Note: this methods are invoked on the main thread.
|
||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL_APP_INFO).setMethodCallHandler { call, result -> handleAppInfo(call, result) }
|
||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL_CWTCH).setMethodCallHandler { call, result -> handleCwtch(call, result) }
|
||||
methodChan = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL_NOTIF_CLICK)
|
||||
}
|
||||
|
||||
private fun handleAppInfo(@NonNull call: MethodCall, @NonNull result: Result) {
|
||||
|
@ -61,49 +92,72 @@ class MainActivity: FlutterActivity() {
|
|||
val ainfo = this.applicationContext.packageManager.getApplicationInfo(
|
||||
"im.cwtch.flwtch", // Must be app name
|
||||
PackageManager.GET_SHARED_LIBRARY_FILES)
|
||||
|
||||
return ainfo.nativeLibraryDir
|
||||
}
|
||||
|
||||
// receives messages from the ForegroundService (which provides, ironically enough, the backend)
|
||||
private fun handleCwtch(@NonNull call: MethodCall, @NonNull result: Result) {
|
||||
var method = call.method
|
||||
val argmap: Map<String, String> = call.arguments as Map<String, String>
|
||||
val data: Data = Data.Builder().putString(FlwtchWorker.KEY_METHOD, call.method).putString(FlwtchWorker.KEY_ARGS, JSONObject(argmap).toString()).build()
|
||||
var tag = ""
|
||||
if (call.method == "Start") {
|
||||
tag = "cwtchEventBus"
|
||||
}
|
||||
val workRequest: WorkRequest = OneTimeWorkRequestBuilder<FlwtchWorker>().setInputData(data).addTag(tag).build()
|
||||
WorkManager.getInstance(this).enqueue(workRequest)
|
||||
if (call.method == "Start") {
|
||||
val eventbus_chan = MethodChannel(flutterEngine?.dartExecutor?.binaryMessenger, CWTCH_EVENTBUS)
|
||||
WorkManager.getInstance(applicationContext)
|
||||
// requestId is the WorkRequest id
|
||||
.getWorkInfosByTagLiveData("cwtchEventBus")
|
||||
.observe(this, Observer<List<WorkInfo>> { listOfWorkInfo ->
|
||||
if (listOfWorkInfo.isNullOrEmpty()) {
|
||||
return@Observer
|
||||
}
|
||||
|
||||
for (workInfo in listOfWorkInfo) {
|
||||
if (workInfo != null) {
|
||||
val progress = workInfo.progress
|
||||
val eventType = progress.getString("EventType") ?: ""
|
||||
val eventData = progress.getString("Data")
|
||||
val output = progress.keyValueMap.toString()
|
||||
try {
|
||||
if (eventType != "") {
|
||||
Log.i("MainActivity.kt", "got event $output $eventType $eventData")
|
||||
eventbus_chan.invokeMethod(eventType, eventData)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.i("MainActivity.kt", "event bus exception")
|
||||
}
|
||||
} else {
|
||||
Log.i("MainActivity.kt", "got null workInfo")
|
||||
}
|
||||
}
|
||||
})
|
||||
// the frontend calls Start every time it fires up, but we don't want to *actually* call Cwtch.Start()
|
||||
// in case the ForegroundService is still running. in both cases, however, we *do* want to re-register
|
||||
// the eventbus listener.
|
||||
if (call.method == "Start") {
|
||||
val workerTag = "cwtchEventBusWorker"
|
||||
val uniqueTag = argmap["torPath"] ?: "nullEventBus"
|
||||
|
||||
// note: because the ForegroundService is specified as UniquePeriodicWork, it can't actually get
|
||||
// accidentally duplicated. however, we still need to manually check if it's running or not, so
|
||||
// that we can divert this method call to ReconnectCwtchForeground instead if so.
|
||||
val works = WorkManager.getInstance(this).getWorkInfosByTag(workerTag).get()
|
||||
var alreadyRunning = false
|
||||
for (workInfo in works) {
|
||||
if (workInfo.tags.contains(uniqueTag)) {
|
||||
if (workInfo.state == WorkInfo.State.RUNNING || workInfo.state == WorkInfo.State.ENQUEUED) {
|
||||
alreadyRunning = true
|
||||
}
|
||||
} else {
|
||||
WorkManager.getInstance(this).cancelWorkById(workInfo.id)
|
||||
}
|
||||
}
|
||||
|
||||
// register our eventbus listener. note that we observe any/all work according to its tag, which
|
||||
// results in an implicit "reconnection" to old service threads even after frontend restarts
|
||||
val mc = MethodChannel(flutterEngine?.dartExecutor?.binaryMessenger, CWTCH_EVENTBUS)
|
||||
val filter = IntentFilter("im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS")
|
||||
LocalBroadcastManager.getInstance(applicationContext).registerReceiver(MyBroadcastReceiver(mc), filter)
|
||||
|
||||
if (alreadyRunning) {
|
||||
Log.i("MainActivity.kt", "diverting Start -> Reconnect")
|
||||
method = "ReconnectCwtchForeground"
|
||||
} else {
|
||||
Log.i("MainActivity.kt", "Start() launching foregroundservice")
|
||||
// this is where the eventbus ForegroundService gets launched. WorkManager should keep it alive after this
|
||||
val data: Data = Data.Builder().putString(FlwtchWorker.KEY_METHOD, call.method).putString(FlwtchWorker.KEY_ARGS, JSONObject(argmap).toString()).build()
|
||||
// 15 minutes is the shortest interval you can request
|
||||
val workRequest = PeriodicWorkRequestBuilder<FlwtchWorker>(15, TimeUnit.MINUTES).setInputData(data).addTag(workerTag).addTag(uniqueTag).build()
|
||||
val workResult = WorkManager.getInstance(this).enqueueUniquePeriodicWork("req_$uniqueTag", ExistingPeriodicWorkPolicy.KEEP, workRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// ...otherwise fallthru to a normal ffi method call (and return the result using the result callback)
|
||||
val data: Data = Data.Builder().putString(FlwtchWorker.KEY_METHOD, method).putString(FlwtchWorker.KEY_ARGS, JSONObject(argmap).toString()).build()
|
||||
val workRequest = OneTimeWorkRequestBuilder<FlwtchWorker>().setInputData(data).build()
|
||||
val workResult = WorkManager.getInstance(this).enqueue(workRequest)
|
||||
WorkManager.getInstance(applicationContext).getWorkInfoByIdLiveData(workRequest.id).observe(
|
||||
this, Observer { workInfo ->
|
||||
if (workInfo.state == WorkInfo.State.SUCCEEDED) {
|
||||
val res = workInfo.outputData.keyValueMap.toString()
|
||||
//Log.i("MainActivity.kt", "method $method returned SUCCESS($res)")
|
||||
result.success(workInfo.outputData.getString("result"))
|
||||
} else {
|
||||
val idk = workInfo.state.toString()
|
||||
//Log.i("MainActivity.kt", "method $method returned $idk")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// source: https://web.archive.org/web/20210203022531/https://stackoverflow.com/questions/41928803/how-to-parse-json-in-kotlin/50468095
|
||||
|
@ -125,4 +179,23 @@ class MainActivity: FlutterActivity() {
|
|||
val EventID = this.optString("EventID")
|
||||
val Data = this.optString("Data")
|
||||
}
|
||||
|
||||
class MyBroadcastReceiver(mc: MethodChannel) : BroadcastReceiver() {
|
||||
val eventBus: MethodChannel = mc
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
// StringBuilder().apply {
|
||||
// append("Action: ${intent.action}\n")
|
||||
// append("URI: ${intent.toUri(Intent.URI_INTENT_SCHEME)}\n")
|
||||
// toString().also { log ->
|
||||
// Log.d("MyBroadcastReceiver", log)
|
||||
// }
|
||||
// }
|
||||
val evtType = intent.getStringExtra("EventType") ?: ""
|
||||
val evtData = intent.getStringExtra("Data") ?: ""
|
||||
//val evtID = intent.getStringExtra("EventID") ?: ""//todo?
|
||||
eventBus.invokeMethod(evtType, evtData)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
abstract class Cwtch {
|
||||
// ignore: non_constant_identifier_names
|
||||
Future<void> Start();
|
||||
Future<dynamic> Start();
|
||||
// ignore: non_constant_identifier_names
|
||||
Future<void> ReconnectCwtchForeground();
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
void SelectProfile(String onion);
|
||||
|
|
|
@ -15,8 +15,8 @@ import '../config.dart';
|
|||
/// Cwtch API ///
|
||||
/////////////////////
|
||||
|
||||
typedef start_cwtch_function = Void Function(Pointer<Utf8> str, Int32 length, Pointer<Utf8> str2, Int32 length2);
|
||||
typedef StartCwtchFn = void Function(Pointer<Utf8> dir, int len, Pointer<Utf8> tor, int torLen);
|
||||
typedef start_cwtch_function = Int8 Function(Pointer<Utf8> str, Int32 length, Pointer<Utf8> str2, Int32 length2);
|
||||
typedef StartCwtchFn = int Function(Pointer<Utf8> dir, int len, Pointer<Utf8> tor, int torLen);
|
||||
|
||||
typedef void_from_string_string_function = Void Function(Pointer<Utf8>, Int32, Pointer<Utf8>, Int32);
|
||||
typedef VoidFromStringStringFn = void Function(Pointer<Utf8>, int, Pointer<Utf8>, int);
|
||||
|
@ -76,7 +76,7 @@ class CwtchFfi implements Cwtch {
|
|||
}
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
Future<void> Start() async {
|
||||
Future<dynamic> Start() async {
|
||||
String home = "";
|
||||
String bundledTor = "";
|
||||
Map<String, String> envVars = Platform.environment;
|
||||
|
@ -109,6 +109,14 @@ class CwtchFfi implements Cwtch {
|
|||
});
|
||||
}
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
Future<void> ReconnectCwtchForeground() async {
|
||||
var reconnectCwtch = library.lookup<NativeFunction<Void Function()>>("c_ReconnectCwtchForeground");
|
||||
// ignore: non_constant_identifier_names
|
||||
final ReconnectCwtchForeground = reconnectCwtch.asFunction<void Function()>();
|
||||
ReconnectCwtchForeground();
|
||||
}
|
||||
|
||||
// Called on object being disposed to (presumably on app close) to close the isolate that's listening to libcwtch-go events
|
||||
@override
|
||||
void dispose() {
|
||||
|
|
|
@ -43,7 +43,7 @@ class CwtchGomobile implements Cwtch {
|
|||
}
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
Future<void> Start() async {
|
||||
Future<dynamic> Start() async {
|
||||
print("gomobile.dart: Start()...");
|
||||
var cwtchDir = path.join((await androidHomeDirectory).path, ".cwtch");
|
||||
if (EnvironmentConfig.BUILD_VER == dev_version) {
|
||||
|
@ -51,7 +51,13 @@ class CwtchGomobile implements Cwtch {
|
|||
}
|
||||
String torPath = path.join(await androidLibraryDir, "libtor.so");
|
||||
print("gomobile.dart: Start invokeMethod Start($cwtchDir, $torPath)...");
|
||||
cwtchPlatform.invokeMethod("Start", {"appDir": cwtchDir, "torPath": torPath});
|
||||
return cwtchPlatform.invokeMethod("Start", {"appDir": cwtchDir, "torPath": torPath});
|
||||
}
|
||||
|
||||
@override
|
||||
// ignore: non_constant_identifier_names
|
||||
Future<void> ReconnectCwtchForeground() async {
|
||||
cwtchPlatform.invokeMethod("ReconnectCwtchForeground", {});
|
||||
}
|
||||
|
||||
// Handle libcwtch-go events (received via kotlin) and dispatch to the cwtchNotifier
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:cwtch/notification_manager.dart';
|
||||
import 'package:cwtch/views/messageview.dart';
|
||||
import 'package:cwtch/widgets/rightshiftfixer.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cwtch/cwtch/ffi.dart';
|
||||
|
@ -8,6 +11,7 @@ import 'package:cwtch/errorHandler.dart';
|
|||
import 'package:cwtch/settings.dart';
|
||||
import 'package:cwtch/torstatus.dart';
|
||||
import 'package:cwtch/views/triplecolview.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'cwtch/cwtch.dart';
|
||||
import 'cwtch/cwtchNotifier.dart';
|
||||
|
@ -44,6 +48,8 @@ class FlwtchState extends State<Flwtch> {
|
|||
var columns = [1]; // default or 'single column' mode
|
||||
//var columns = [1, 1, 2];
|
||||
late ProfileListState profs;
|
||||
final MethodChannel notificationClickChannel = MethodChannel('im.cwtch.flwtch/notificationClickHandler');
|
||||
final GlobalKey<NavigatorState> navKey = GlobalKey<NavigatorState>();
|
||||
|
||||
@override
|
||||
initState() {
|
||||
|
@ -51,6 +57,7 @@ class FlwtchState extends State<Flwtch> {
|
|||
cwtchInit = false;
|
||||
|
||||
profs = ProfileListState();
|
||||
notificationClickChannel.setMethodCallHandler(_externalNotificationClicked);
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, NullNotificationsManager());
|
||||
|
@ -63,10 +70,9 @@ class FlwtchState extends State<Flwtch> {
|
|||
cwtch = CwtchFfi(cwtchNotifier);
|
||||
}
|
||||
|
||||
cwtch.Start().then((val) {
|
||||
setState(() {
|
||||
cwtchInit = true;
|
||||
});
|
||||
cwtch.Start();
|
||||
setState(() {
|
||||
cwtchInit = true;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -92,13 +98,12 @@ class FlwtchState extends State<Flwtch> {
|
|||
return Consumer<Settings>(
|
||||
builder: (context, settings, child) => MaterialApp(
|
||||
key: Key('app'),
|
||||
navigatorKey: navKey,
|
||||
locale: settings.locale,
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
title: 'Cwtch',
|
||||
theme: mkThemeData(settings),
|
||||
// from dan: home: cwtchInit == true ? ProfileMgrView(cwtch) : SplashView(),
|
||||
// from erinn: home: columns.length == 3 ? TripleColumnView() : ProfileMgrView(),
|
||||
home: cwtchInit == true ? (columns.length == 3 ? TripleColumnView() : ShiftRightFixer(child: ProfileMgrView())) : SplashView(),
|
||||
),
|
||||
);
|
||||
|
@ -106,6 +111,26 @@ class FlwtchState extends State<Flwtch> {
|
|||
);
|
||||
}
|
||||
|
||||
Future<void> _externalNotificationClicked(MethodCall call) async {
|
||||
var args = jsonDecode(call.arguments);
|
||||
var profile = profs.getProfile(args["ProfileOnion"])!;
|
||||
var contact = profile.contactList.getContact(args["RemotePeer"])!;
|
||||
contact.unreadMessages = 0;
|
||||
navKey.currentState?.push(
|
||||
MaterialPageRoute<void>(
|
||||
builder: (BuildContext builderContext) {
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider.value(value: profile),
|
||||
ChangeNotifierProvider.value(value: contact),
|
||||
],
|
||||
builder: (context, child) => MessageView(),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
cwtch.dispose();
|
||||
|
|
|
@ -426,6 +426,7 @@ class MessageState extends ChangeNotifier {
|
|||
void tryLoad(BuildContext context) {
|
||||
Provider.of<FlwtchState>(context, listen: false).cwtch.GetMessage(profileOnion, contactHandle, messageIndex).then((jsonMessage) {
|
||||
try {
|
||||
print("debug messageJson $jsonMessage");
|
||||
dynamic messageWrapper = jsonDecode(jsonMessage);
|
||||
if (messageWrapper['Message'] == null || messageWrapper['Message'] == '' || messageWrapper['Message'] == '{}') {
|
||||
this._senderOnion = profileOnion;
|
||||
|
|
Loading…
Reference in New Issue