From a99a00de309f2ca9d1ca4f4ad9f08a9a0bdf0766 Mon Sep 17 00:00:00 2001 From: erinn Date: Fri, 28 May 2021 15:56:45 -0700 Subject: [PATCH] WIP: partial android service migration --- android/app/build.gradle | 29 +++++ android/app/src/main/AndroidManifest.xml | 2 + .../kotlin/im/cwtch/flwtch/FlwtchWorker.kt | 122 ++++++++++++++++++ .../kotlin/im/cwtch/flwtch/MainActivity.kt | 37 +++--- pubspec.lock | 4 +- 5 files changed, 177 insertions(+), 17 deletions(-) create mode 100644 android/app/src/main/kotlin/im/cwtch/flwtch/FlwtchWorker.kt diff --git a/android/app/build.gradle b/android/app/build.gradle index 4b49db23..2786507c 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -69,6 +69,15 @@ android { signingConfig signingConfigs.release } } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8.toString() + } } flutter { @@ -82,4 +91,24 @@ dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2" implementation "com.airbnb.android:lottie:3.5.0" implementation "com.android.support.constraint:constraint-layout:2.0.4" + + // WorkManager + + // (Java only) + //implementation("androidx.work:work-runtime:$work_version") + + // Kotlin + coroutines + implementation("androidx.work:work-runtime-ktx:2.5.0") + + // optional - RxJava2 support + //implementation("androidx.work:work-rxjava2:$work_version") + + // optional - GCMNetworkManager support + //implementation("androidx.work:work-gcm:$work_version") + + // optional - Test helpers + //androidTestImplementation("androidx.work:work-testing:$work_version") + + // optional - Multiprocess support + implementation "androidx.work:work-multiprocess:2.5.0" } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 43ed2836..1162d408 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -42,4 +42,6 @@ + + diff --git a/android/app/src/main/kotlin/im/cwtch/flwtch/FlwtchWorker.kt b/android/app/src/main/kotlin/im/cwtch/flwtch/FlwtchWorker.kt new file mode 100644 index 00000000..b14af9f4 --- /dev/null +++ b/android/app/src/main/kotlin/im/cwtch/flwtch/FlwtchWorker.kt @@ -0,0 +1,122 @@ +package im.cwtch.flwtch + +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import android.graphics.Color +import android.os.Build +import android.util.Log + +import androidx.annotation.RequiresApi +import androidx.core.app.NotificationCompat +import androidx.work.CoroutineWorker +import androidx.work.Data +import androidx.work.ForegroundInfo +import androidx.work.WorkerParameters +import androidx.work.WorkManager + +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 org.json.JSONObject + +import cwtch.Cwtch + +class FlwtchWorker(context: Context, parameters: WorkerParameters) : + CoroutineWorker(context, parameters) { + private val CWTCH_EVENTBUS = "test.flutter.dev/eventBus" + + private val notificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as + NotificationManager + + override suspend fun doWork(): Result { + val appDir = inputData.getString(KEY_APP_DIR) + ?: return Result.failure() + val torPath = inputData.getString(KEY_TOR_PATH) + ?: return Result.failure() + // Mark the Worker as important + val progress = "Trying to do a Flwtch" + setForeground(createForegroundInfo(progress)) + download(appDir, torPath) + return Result.success() + } + + private suspend fun download(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) + } + } + } + } + + // Creates an instance of ForegroundInfo which can be used to update the + // ongoing notification. + private fun createForegroundInfo(progress: String): ForegroundInfo { + val id = "flwtch" + val title = "Flwtch" + val cancel = "Nevermind"//todo: translate + // This PendingIntent can be used to cancel the worker + val intent = WorkManager.getInstance(applicationContext) + .createCancelPendingIntent(getId()) + + val channelId = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createNotificationChannel(id, id) + } 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 notification = NotificationCompat.Builder(applicationContext, id) + .setContentTitle(title) + .setTicker(title) + .setContentText(progress) + .setSmallIcon(R.mipmap.knott) + .setOngoing(true) + // Add the cancel action to the notification which can + // be used to cancel the worker + .addAction(android.R.drawable.ic_delete, cancel, intent) + .build() + + return ForegroundInfo(101, notification) + } + + + @RequiresApi(Build.VERSION_CODES.O) + private fun createNotificationChannel(channelId: String, channelName: String): String{ + val chan = NotificationChannel(channelId, + channelName, NotificationManager.IMPORTANCE_NONE) + chan.lightColor = Color.BLUE + chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE + notificationManager.createNotificationChannel(chan) + return channelId + } + + companion object { + const val KEY_APP_DIR = "KEY_APP_DIR" + const val KEY_TOR_PATH = "KEY_TOR_PATH" + } + + class AppbusEvent(json: String) : JSONObject(json) { + val EventType = this.optString("EventType") + val EventID = this.optString("EventID") + val Data = this.optString("Data") + } +} diff --git a/android/app/src/main/kotlin/im/cwtch/flwtch/MainActivity.kt b/android/app/src/main/kotlin/im/cwtch/flwtch/MainActivity.kt index c82a32ea..dbac7d8d 100644 --- a/android/app/src/main/kotlin/im/cwtch/flwtch/MainActivity.kt +++ b/android/app/src/main/kotlin/im/cwtch/flwtch/MainActivity.kt @@ -7,6 +7,11 @@ import android.os.Bundle import android.os.Looper import android.util.Log +import androidx.work.Data +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.WorkManager +import androidx.work.WorkRequest + import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch @@ -44,8 +49,6 @@ class MainActivity: FlutterActivity() { // 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) } - - } private fun handleAppInfo(@NonNull call: MethodCall, @NonNull result: Result) { @@ -70,21 +73,25 @@ class MainActivity: FlutterActivity() { val appDir = (call.argument("appDir") as? String) ?: ""; val torPath = (call.argument("torPath") as? String) ?: "tor"; Log.i("MainActivity.kt", " appDir: '" + appDir + "' torPath: '" + torPath + "'") - Cwtch.startCwtch(appDir, torPath) + + //Cwtch.startCwtch(appDir, torPath)// todo + val data: Data = Data.Builder().putString(FlwtchWorker.KEY_APP_DIR, appDir).putString(FlwtchWorker.KEY_TOR_PATH, torPath).build() + val uploadWorkRequest: WorkRequest = OneTimeWorkRequestBuilder().setInputData(data).build() + WorkManager.getInstance(this).enqueue(uploadWorkRequest) // seperate coroutine to poll event bus and send to dart - val eventbus_chan = MethodChannel(flutterEngine?.dartExecutor?.binaryMessenger, CWTCH_EVENTBUS) - Log.i("MainActivity.kt", "got event chan: " + eventbus_chan + " launching corouting...") - GlobalScope.launch(Dispatchers.IO) { - while(true) { - val evt = AppbusEvent(Cwtch.getAppBusEvent()) - Log.i("MainActivity.kt", "got appbusEvent: " + evt) - launch(Dispatchers.Main) { - //todo: this elides evt.EventID which may be needed at some point? - eventbus_chan.invokeMethod(evt.EventType, evt.Data) - } - } - } +// val eventbus_chan = MethodChannel(flutterEngine?.dartExecutor?.binaryMessenger, CWTCH_EVENTBUS) +// Log.i("MainActivity.kt", "got event chan: " + eventbus_chan + " launching corouting...") +// GlobalScope.launch(Dispatchers.IO) { +// while(true) { +// val evt = AppbusEvent(Cwtch.getAppBusEvent()) +// Log.i("MainActivity.kt", "got appbusEvent: " + evt) +// launch(Dispatchers.Main) { +// //todo: this elides evt.EventID which may be needed at some point? +// eventbus_chan.invokeMethod(evt.EventType, evt.Data) +// } +// } +// } } "SelectProfile" -> { val onion = (call.argument("profile") as? String) ?: ""; diff --git a/pubspec.lock b/pubspec.lock index 8c01490f..011cd945 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -14,7 +14,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.6.1" + version: "2.5.0" boolean_selector: dependency: transitive description: @@ -371,7 +371,7 @@ packages: name: vm_service url: "https://pub.dartlang.org" source: hosted - version: "6.2.0" + version: "6.1.0+1" webdriver: dependency: transitive description: