2021-06-24 23:10:45 +00:00
package im.cwtch.flwtch
import SplashView
2022-04-12 22:03:21 +00:00
import android.annotation.TargetApi
2021-06-24 23:10:45 +00:00
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
2022-04-12 22:03:21 +00:00
import android.net.Uri
import android.os.PowerManager
import android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
2021-06-24 23:10:45 +00:00
import android.util.Log
import android.view.Window
2022-04-13 21:53:44 +00:00
import android.view.WindowManager
2022-04-12 22:03:21 +00:00
import androidx.annotation.NonNull
2021-06-24 23:10:45 +00:00
import androidx.lifecycle.Observer
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.work.*
2022-04-12 22:03:21 +00:00
import cwtch.Cwtch
2021-06-24 23:10:45 +00:00
import io.flutter.embedding.android.FlutterActivity
2022-04-12 22:03:21 +00:00
import io.flutter.embedding.android.SplashScreen
2021-06-24 23:10:45 +00:00
import io.flutter.embedding.engine.FlutterEngine
2022-04-12 22:03:21 +00:00
import io.flutter.plugin.common.ErrorLogResult
2021-06-24 23:10:45 +00:00
import io.flutter.plugin.common.MethodCall
2022-04-12 22:03:21 +00:00
import io.flutter.plugin.common.MethodChannel
2021-06-24 23:10:45 +00:00
import io.flutter.plugin.common.MethodChannel.Result
import org.json.JSONObject
2021-12-15 01:13:13 +00:00
import java.nio.file.Files
import java.nio.file.Paths
2022-04-12 22:03:21 +00:00
import java.util.concurrent.TimeUnit
2022-03-23 23:08:19 +00:00
2021-06-24 23:10:45 +00:00
class MainActivity : FlutterActivity ( ) {
override fun provideSplashScreen ( ) : SplashScreen ? = SplashView ( )
2022-04-12 22:03:21 +00:00
2021-06-24 23:10:45 +00:00
// Channel to get app info
private val CHANNEL _APP _INFO = " test.flutter.dev/applicationInfo "
private val CALL _APP _INFO = " getNativeLibDir "
2022-04-13 01:06:48 +00:00
private val ANDROID _SETTINGS _CHANNEL _NAME = " androidSettings "
private val ANDROID _SETTINGS _CHANGE _NAME = " androidSettingsChanged "
private var andoidSettingsChangeChannel : MethodChannel ? = null
2022-04-12 22:03:21 +00:00
private val CALL _ASK _BATTERY _EXEMPTION = " requestBatteryExemption "
private val CALL _IS _BATTERY _EXEMPT = " isBatteryExempt "
2021-06-24 23:10:45 +00:00
// Channel to get cwtch api calls on
private val CHANNEL _CWTCH = " cwtch "
// Channel to send eventbus events on
private val CWTCH _EVENTBUS = " test.flutter.dev/eventBus "
2021-06-25 00:59:54 +00:00
// Channels to trigger actions when an external notification is clicked
2021-06-24 23:10:45 +00:00
private val CHANNEL _NOTIF _CLICK = " im.cwtch.flwtch/notificationClickHandler "
2021-06-25 00:59:54 +00:00
private val CHANNEL _SHUTDOWN _CLICK = " im.cwtch.flwtch/shutdownClickHandler "
2021-06-24 23:10:45 +00:00
2022-04-06 01:36:18 +00:00
private val TAG : String = " MainActivity.kt "
2021-06-24 23:10:45 +00:00
// WorkManager tag applied to all Start() infinite coroutines
val WORKER _TAG = " cwtchEventBusWorker "
private var myReceiver : MyBroadcastReceiver ? = null
2021-06-25 00:59:54 +00:00
private var notificationClickChannel : MethodChannel ? = null
private var shutdownClickChannel : MethodChannel ? = null
2021-06-24 23:10:45 +00:00
2021-09-27 19:53:21 +00:00
// "Download to..." prompt extra arguments
2021-10-01 19:38:06 +00:00
private val FILEPICKER _REQUEST _CODE = 234
2021-12-14 23:50:08 +00:00
private val PREVIEW _EXPORT _REQUEST _CODE = 235
2022-03-10 21:29:28 +00:00
private val PROFILE _EXPORT _REQUEST _CODE = 236
2022-04-12 22:03:21 +00:00
private val REQUEST _DOZE _WHITELISTING _CODE : Int = 9
2021-09-27 19:53:21 +00:00
private var dlToProfile = " "
private var dlToHandle = " "
private var dlToFileKey = " "
2021-12-14 23:50:08 +00:00
private var exportFromPath = " "
2021-09-27 19:53:21 +00:00
2022-04-13 21:53:44 +00:00
override fun onCreate ( savedInstanceState : android . os . Bundle ? ) {
super . onCreate ( savedInstanceState )
window . setFlags ( WindowManager . LayoutParams . FLAG _SECURE , WindowManager . LayoutParams . FLAG _SECURE )
// Todo: when we support SDK 31
// hideOverlay()
}
/ *
@TargetApi ( 31 )
fun hideOverlay ( ) {
window . setHideOverlayWindows ( true ) ;
}
* /
2021-09-30 00:16:00 +00:00
// handles clicks received from outside the app (ie, notifications)
2021-06-24 23:10:45 +00:00
override fun onNewIntent ( intent : Intent ) {
super . onNewIntent ( intent )
2021-06-25 00:59:54 +00:00
if ( notificationClickChannel == null || intent . extras == null ) return
if ( intent . extras !! . getString ( " EventType " ) == " NotificationClicked " ) {
2021-06-29 18:49:29 +00:00
if ( !in tent . extras !! . containsKey ( " ProfileOnion " ) || !in tent . extras !! . containsKey ( " Handle " ) ) {
2021-06-25 00:59:54 +00:00
Log . i ( " onNewIntent " , " got notification clicked intent with no onions " )
return
}
val profile = intent . extras !! . getString ( " ProfileOnion " )
2021-06-29 18:49:29 +00:00
val handle = intent . extras !! . getString ( " Handle " )
val mappo = mapOf ( " ProfileOnion " to profile , " Handle " to handle )
2021-06-25 00:59:54 +00:00
val j = JSONObject ( mappo )
notificationClickChannel !! . invokeMethod ( " NotificationClicked " , j . toString ( ) )
} else if ( intent . extras !! . getString ( " EventType " ) == " ShutdownClicked " ) {
shutdownClickChannel !! . invokeMethod ( " ShutdownClicked " , " " )
} else {
print ( " warning: received intent with unknown method; ignoring " )
2021-06-24 23:10:45 +00:00
}
}
2021-09-30 00:16:00 +00:00
// handles return values from the system file picker
2021-09-27 19:53:21 +00:00
override fun onActivityResult ( requestCode : Int , result : Int , intent : Intent ? ) {
2021-10-01 19:38:06 +00:00
super . onActivityResult ( requestCode , result , intent ) ;
2022-04-13 01:06:48 +00:00
// has null intent and data
if ( requestCode == REQUEST _DOZE _WHITELISTING _CODE ) {
// 0 == "battery optimized" (still)
// -1 == "no battery optimization" (exempt!)
andoidSettingsChangeChannel !! . invokeMethod ( " powerExemptionChange " , result == - 1 )
return ;
}
2021-09-27 19:53:21 +00:00
if ( intent == null || intent !! . getData ( ) == null ) {
2022-04-12 22:03:21 +00:00
Log . i ( TAG , " user canceled activity " ) ;
2021-09-27 19:53:21 +00:00
return ;
}
2021-10-01 19:38:06 +00:00
if ( requestCode == FILEPICKER _REQUEST _CODE ) {
val filePath = intent !! . getData ( ) . toString ( ) ;
val manifestPath = StringBuilder ( ) . append ( this . applicationContext . cacheDir ) . append ( " / " ) . append ( this . dlToFileKey ) . toString ( ) ;
handleCwtch ( MethodCall ( " DownloadFile " , mapOf (
" ProfileOnion " to this . dlToProfile ,
" handle " to this . dlToHandle ,
" filepath " to filePath ,
" manifestpath " to manifestPath ,
" filekey " to this . dlToFileKey
) ) , ErrorLogResult ( " " ) ) ; //placeholder; this Result is never actually invoked
2021-12-14 23:50:08 +00:00
} else if ( requestCode == PREVIEW _EXPORT _REQUEST _CODE ) {
val targetPath = intent !! . getData ( ) . toString ( )
2021-12-15 01:13:13 +00:00
val sourcePath = Paths . get ( this . exportFromPath ) ;
val targetUri = Uri . parse ( targetPath ) ;
val os = this . applicationContext . getContentResolver ( ) . openOutputStream ( targetUri ) ;
val bytesWritten = Files . copy ( sourcePath , os ) ;
Log . d ( " MainActivity:PREVIEW_EXPORT " , " copied " + bytesWritten . toString ( ) + " bytes " ) ;
if ( bytesWritten != 0L ) {
os ?. flush ( ) ;
os ?. close ( ) ;
//Files.delete(sourcePath);
}
2022-03-10 21:29:28 +00:00
} else if ( requestCode == PROFILE _EXPORT _REQUEST _CODE ) {
val targetPath = intent !! . getData ( ) . toString ( )
val srcFile = StringBuilder ( ) . append ( this . applicationContext . cacheDir ) . append ( " / " ) . append ( this . exportFromPath ) . toString ( ) ;
Log . i ( " MainActivity:PREVIEW_EXPORT " , " exporting previewed file " + srcFile ) ;
val sourcePath = Paths . get ( srcFile ) ;
val targetUri = Uri . parse ( targetPath ) ;
val os = this . applicationContext . getContentResolver ( ) . openOutputStream ( targetUri ) ;
val bytesWritten = Files . copy ( sourcePath , os ) ;
Log . d ( " MainActivity:PREVIEW_EXPORT " , " copied " + bytesWritten . toString ( ) + " bytes " ) ;
if ( bytesWritten != 0L ) {
os ?. flush ( ) ;
os ?. close ( ) ;
//Files.delete(sourcePath);
}
2021-10-01 19:38:06 +00:00
}
2021-09-27 19:53:21 +00:00
}
2021-06-24 23:10:45 +00:00
override fun configureFlutterEngine ( @NonNull flutterEngine : FlutterEngine ) {
super . configureFlutterEngine ( flutterEngine )
// Note: this methods are invoked on the main thread.
//note to self: ask someone if this does anything ^ea
requestWindowFeature ( Window . FEATURE _NO _TITLE )
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 ) }
2022-04-13 01:06:48 +00:00
MethodChannel ( flutterEngine . dartExecutor . binaryMessenger , ANDROID _SETTINGS _CHANNEL _NAME ) . setMethodCallHandler { call , result -> handleAndroidSettings ( call , result ) }
2021-06-25 00:59:54 +00:00
notificationClickChannel = MethodChannel ( flutterEngine . dartExecutor . binaryMessenger , CHANNEL _NOTIF _CLICK )
shutdownClickChannel = MethodChannel ( flutterEngine . dartExecutor . binaryMessenger , CHANNEL _SHUTDOWN _CLICK )
2022-04-13 01:06:48 +00:00
andoidSettingsChangeChannel = MethodChannel ( flutterEngine . dartExecutor . binaryMessenger , ANDROID _SETTINGS _CHANGE _NAME )
2021-06-24 23:10:45 +00:00
}
2022-03-11 00:10:28 +00:00
// MethodChannel CHANNEL_APP_INFO handler (Flutter Channel for requests for Android environment info)
2021-06-24 23:10:45 +00:00
private fun handleAppInfo ( @NonNull call : MethodCall , @NonNull result : Result ) {
when ( call . method ) {
CALL _APP _INFO -> result . success ( getNativeLibDir ( ) )
?: result . error ( " Unavailable " , " nativeLibDir not available " , null ) ;
2022-04-13 01:06:48 +00:00
else -> result . notImplemented ( )
}
}
// MethodChannel ANDROID_SETTINGS_CHANNEL_NAME handler (Flutter Channel for requests for Android settings)
// Called from lib/view/globalsettingsview.dart
private fun handleAndroidSettings ( @NonNull call : MethodCall , @NonNull result : Result ) {
when ( call . method ) {
CALL _IS _BATTERY _EXEMPT -> result . success ( checkIgnoreBatteryOpt ( ) ?: false ) ;
CALL _ASK _BATTERY _EXEMPTION -> { requestBatteryExemption ( ) ; result . success ( null ) ; }
2021-06-24 23:10:45 +00:00
else -> result . notImplemented ( )
}
}
2022-04-12 22:03:21 +00:00
@TargetApi ( 23 )
private fun checkIgnoreBatteryOpt ( ) : Boolean {
val powerManager = getSystemService ( Context . POWER _SERVICE ) as PowerManager
return powerManager . isIgnoringBatteryOptimizations ( this . packageName ) ?: false ;
}
@TargetApi ( 23 )
private fun requestBatteryExemption ( ) {
val i = Intent ( )
i . action = ACTION _REQUEST _IGNORE _BATTERY _OPTIMIZATIONS
i . data = Uri . parse ( " package: " + this . packageName )
startActivityForResult ( i , REQUEST _DOZE _WHITELISTING _CODE ) ;
}
2021-06-24 23:10:45 +00:00
private fun getNativeLibDir ( ) : String {
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
2022-03-23 23:08:19 +00:00
// todo change usage patern to match that in FlwtchWorker
// Unsafe for anything using int args, causes access time attempt to cast to string which will fail
2021-06-24 23:10:45 +00:00
val argmap : Map < String , String > = call . arguments as Map < String , String >
// 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.
2022-03-23 23:08:19 +00:00
when ( call . method ) {
" Start " -> {
val uniqueTag = argmap [ " torPath " ] ?: " nullEventBus "
2021-06-24 23:10:45 +00:00
2022-03-23 23:08:19 +00:00
// 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 ( WORKER _TAG ) . get ( )
for ( workInfo in works ) {
WorkManager . getInstance ( this ) . cancelWorkById ( workInfo . id )
}
WorkManager . getInstance ( this ) . pruneWork ( )
2021-06-24 23:10:45 +00:00
2022-03-23 23:08:19 +00:00
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 ( WORKER _TAG ) . addTag ( uniqueTag ) . build ( )
WorkManager . getInstance ( this ) . enqueueUniquePeriodicWork ( " req_ $uniqueTag " , ExistingPeriodicWorkPolicy . REPLACE , workRequest )
2021-09-27 19:53:21 +00:00
}
2022-03-23 23:08:19 +00:00
" CreateDownloadableFile " -> {
this . dlToProfile = argmap [ " ProfileOnion " ] ?: " "
this . dlToHandle = argmap [ " handle " ] ?: " "
val suggestedName = argmap [ " filename " ] ?: " filename.ext "
this . dlToFileKey = argmap [ " filekey " ] ?: " "
val intent = Intent ( Intent . ACTION _CREATE _DOCUMENT ) . apply {
addCategory ( Intent . CATEGORY _OPENABLE )
type = " application/octet-stream "
putExtra ( Intent . EXTRA _TITLE , suggestedName )
}
startActivityForResult ( intent , FILEPICKER _REQUEST _CODE )
2021-12-14 23:50:08 +00:00
}
2022-03-23 23:08:19 +00:00
" ExportPreviewedFile " -> {
this . exportFromPath = argmap [ " Path " ] ?: " "
val suggestion = argmap [ " FileName " ] ?: " filename.ext "
var imgType = " jpeg "
if ( suggestion . endsWith ( " png " ) ) {
imgType = " png "
} else if ( suggestion . endsWith ( " webp " ) ) {
imgType = " webp "
} else if ( suggestion . endsWith ( " bmp " ) ) {
imgType = " bmp "
} else if ( suggestion . endsWith ( " gif " ) ) {
imgType = " gif "
}
val intent = Intent ( Intent . ACTION _CREATE _DOCUMENT ) . apply {
addCategory ( Intent . CATEGORY _OPENABLE )
type = " image/ " + imgType
putExtra ( Intent . EXTRA _TITLE , suggestion )
}
startActivityForResult ( intent , PREVIEW _EXPORT _REQUEST _CODE )
2021-12-14 23:50:08 +00:00
}
2022-03-23 23:08:19 +00:00
" ExportProfile " -> {
this . exportFromPath = argmap [ " file " ] ?: " "
val intent = Intent ( Intent . ACTION _CREATE _DOCUMENT ) . apply {
addCategory ( Intent . CATEGORY _OPENABLE )
type = " application/gzip "
putExtra ( Intent . EXTRA _TITLE , argmap [ " file " ] )
}
startActivityForResult ( intent , PROFILE _EXPORT _REQUEST _CODE )
2022-03-10 21:29:28 +00:00
}
2022-03-23 23:08:19 +00:00
" GetMessages " -> {
Log . d ( " MainActivity.kt " , " Cwtch GetMessages " )
2021-06-24 23:10:45 +00:00
2022-03-23 23:08:19 +00:00
val profile = argmap [ " ProfileOnion " ] ?: " "
val conversation : Int = call . argument ( " conversation " ) ?: 0
val indexI : Int = call . argument ( " index " ) ?: 0
val count : Int = call . argument ( " count " ) ?: 1
result . success ( Cwtch . getMessages ( profile , conversation . toLong ( ) , indexI . toLong ( ) , count . toLong ( ) ) )
2022-04-20 01:34:22 +00:00
return
2022-03-23 23:08:19 +00:00
}
" SendMessage " -> {
val profile : String = call . argument ( " ProfileOnion " ) ?: " "
val conversation : Int = call . argument ( " conversation " ) ?: 0
val message : String = call . argument ( " message " ) ?: " "
result . success ( Cwtch . sendMessage ( profile , conversation . toLong ( ) , message ) )
2022-04-20 01:34:22 +00:00
return
2021-06-24 23:10:45 +00:00
}
2022-03-23 23:08:19 +00:00
" SendInvitation " -> {
val profile : String = call . argument ( " ProfileOnion " ) ?: " "
val conversation : Int = call . argument ( " conversation " ) ?: 0
val target : Int = call . argument ( " target " ) ?: 0
result . success ( Cwtch . sendInvitation ( profile , conversation . toLong ( ) , target . toLong ( ) ) )
2022-04-20 01:34:22 +00:00
return
2022-03-23 23:08:19 +00:00
}
" ShareFile " -> {
val profile : String = call . argument ( " ProfileOnion " ) ?: " "
val conversation : Int = call . argument ( " conversation " ) ?: 0
val filepath : String = call . argument ( " filepath " ) ?: " "
result . success ( Cwtch . shareFile ( profile , conversation . toLong ( ) , filepath ) )
2022-04-20 01:34:22 +00:00
return
2022-03-23 23:08:19 +00:00
}
2022-04-06 01:36:18 +00:00
" CreateProfile " -> {
val nick : String = call . argument ( " nick " ) ?: " "
val pass : String = call . argument ( " pass " ) ?: " "
Cwtch . createProfile ( nick , pass )
}
" LoadProfiles " -> {
val pass : String = call . argument ( " pass " ) ?: " "
Cwtch . loadProfiles ( pass )
}
" ChangePassword " -> {
val profile : String = call . argument ( " ProfileOnion " ) ?: " "
val pass : String = call . argument ( " OldPass " ) ?: " "
val passNew : String = call . argument ( " NewPass " ) ?: " "
val passNew2 : String = call . argument ( " NewPassAgain " ) ?: " "
Cwtch . changePassword ( profile , pass , passNew , passNew2 )
}
" GetMessage " -> {
val profile : String = call . argument ( " ProfileOnion " ) ?: " "
val conversation : Int = call . argument ( " conversation " ) ?: 0
val indexI : Int = call . argument ( " index " ) ?: 0
2022-04-27 04:29:56 +00:00
result . success ( Cwtch . getMessage ( profile , conversation . toLong ( ) , indexI . toLong ( ) ) )
2022-04-20 01:34:22 +00:00
return
2022-04-06 01:36:18 +00:00
}
" GetMessageByID " -> {
val profile : String = call . argument ( " ProfileOnion " ) ?: " "
val conversation : Int = call . argument ( " conversation " ) ?: 0
val id : Int = call . argument ( " id " ) ?: 0
2022-04-27 04:29:56 +00:00
result . success ( Cwtch . getMessageByID ( profile , conversation . toLong ( ) , id . toLong ( ) ) )
2022-04-20 01:34:22 +00:00
return
2022-04-06 01:36:18 +00:00
}
" GetMessageByContentHash " -> {
val profile : String = call . argument ( " ProfileOnion " ) ?: " "
val conversation : Int = call . argument ( " conversation " ) ?: 0
val contentHash : String = call . argument ( " contentHash " ) ?: " "
2022-04-27 04:29:56 +00:00
result . success ( Cwtch . getMessagesByContentHash ( profile , conversation . toLong ( ) , contentHash ) )
2022-04-20 01:34:22 +00:00
return
2022-04-06 01:36:18 +00:00
}
" SetMessageAttribute " -> {
val profile : String = call . argument ( " ProfileOnion " ) ?: " "
val conversation : Int = call . argument ( " conversation " ) ?: 0
val channel : Int = call . argument ( " Chanenl " ) ?: 0
val midx : Int = call . argument ( " Message " ) ?: 0
val key : String = call . argument ( " key " ) ?: " "
val value : String = call . argument ( " value " ) ?: " "
Cwtch . setMessageAttribute ( profile , conversation . toLong ( ) , channel . toLong ( ) , midx . toLong ( ) , key , value )
}
" AcceptConversation " -> {
val profile : String = call . argument ( " ProfileOnion " ) ?: " "
val conversation : Int = call . argument ( " conversation " ) ?: 0
Cwtch . acceptConversation ( profile , conversation . toLong ( ) )
}
" BlockContact " -> {
val profile : String = call . argument ( " ProfileOnion " ) ?: " "
val conversation : Int = call . argument ( " conversation " ) ?: 0
Cwtch . blockContact ( profile , conversation . toLong ( ) )
}
" UnblockContact " -> {
val profile : String = call . argument ( " ProfileOnion " ) ?: " "
val conversation : Int = call . argument ( " conversation " ) ?: 0
Cwtch . unblockContact ( profile , conversation . toLong ( ) )
}
" DownloadFile " -> {
val profile : String = call . argument ( " ProfileOnion " ) ?: " "
val conversation : Int = call . argument ( " conversation " ) ?: 0
val filepath : String = call . argument ( " filepath " ) ?: " "
val manifestpath : String = call . argument ( " manifestpath " ) ?: " "
val filekey : String = call . argument ( " filekey " ) ?: " "
// FIXME: Prevent spurious calls by Intent
if ( profile != " " ) {
Cwtch . downloadFile ( profile , conversation . toLong ( ) , filepath , manifestpath , filekey )
}
}
" CheckDownloadStatus " -> {
val profile : String = call . argument ( " ProfileOnion " ) ?: " "
val fileKey : String = call . argument ( " fileKey " ) ?: " "
Cwtch . checkDownloadStatus ( profile , fileKey )
}
" VerifyOrResumeDownload " -> {
val profile : String = call . argument ( " ProfileOnion " ) ?: " "
val conversation : Int = call . argument ( " conversation " ) ?: 0
val fileKey : String = call . argument ( " fileKey " ) ?: " "
Cwtch . verifyOrResumeDownload ( profile , conversation . toLong ( ) , fileKey )
}
" SendProfileEvent " -> {
val onion : String = call . argument ( " onion " ) ?: " "
val jsonEvent : String = call . argument ( " jsonEvent " ) ?: " "
Cwtch . sendProfileEvent ( onion , jsonEvent )
}
" SendAppEvent " -> {
val jsonEvent : String = call . argument ( " jsonEvent " ) ?: " "
Cwtch . sendAppEvent ( jsonEvent )
}
" ResetTor " -> {
Cwtch . resetTor ( )
}
" ImportBundle " -> {
val profile : String = call . argument ( " ProfileOnion " ) ?: " "
val bundle : String = call . argument ( " bundle " ) ?: " "
Cwtch . importBundle ( profile , bundle )
}
" CreateGroup " -> {
val profile : String = call . argument ( " ProfileOnion " ) ?: " "
val server : String = call . argument ( " server " ) ?: " "
val groupName : String = call . argument ( " groupName " ) ?: " "
Cwtch . createGroup ( profile , server , groupName )
}
" DeleteProfile " -> {
val profile : String = call . argument ( " ProfileOnion " ) ?: " "
val pass : String = call . argument ( " pass " ) ?: " "
Cwtch . deleteProfile ( profile , pass )
}
" ArchiveConversation " -> {
val profile : String = call . argument ( " ProfileOnion " ) ?: " "
val conversation : Int = call . argument ( " conversation " ) ?: 0
Cwtch . archiveConversation ( profile , conversation . toLong ( ) )
}
" DeleteConversation " -> {
val profile : String = call . argument ( " ProfileOnion " ) ?: " "
val conversation : Int = call . argument ( " conversation " ) ?: 0
Cwtch . deleteContact ( profile , conversation . toLong ( ) )
}
" SetProfileAttribute " -> {
val profile : String = call . argument ( " ProfileOnion " ) ?: " "
val key : String = call . argument ( " Key " ) ?: " "
val v : String = call . argument ( " Val " ) ?: " "
Cwtch . setProfileAttribute ( profile , key , v )
}
" SetConversationAttribute " -> {
val profile : String = call . argument ( " ProfileOnion " ) ?: " "
val conversation : Int = call . argument ( " conversation " ) ?: 0
val key : String = call . argument ( " Key " ) ?: " "
val v : String = call . argument ( " Val " ) ?: " "
Cwtch . setConversationAttribute ( profile , conversation . toLong ( ) , key , v )
}
" LoadServers " -> {
val password : String = call . argument ( " Password " ) ?: " "
Cwtch . loadServers ( password )
}
" CreateServer " -> {
val password : String = call . argument ( " Password " ) ?: " "
val desc : String = call . argument ( " Description " ) ?: " "
val autostart : Boolean = call . argument ( " Autostart " ) ?: false
Cwtch . createServer ( password , desc , autostart )
}
" DeleteServer " -> {
val serverOnion : String = call . argument ( " ServerOnion " ) ?: " "
val password : String = call . argument ( " Password " ) ?: " "
Cwtch . deleteServer ( serverOnion , password )
}
" LaunchServers " -> {
Cwtch . launchServers ( )
}
" LaunchServer " -> {
val serverOnion : String = call . argument ( " ServerOnion " ) ?: " "
Cwtch . launchServer ( serverOnion )
}
" StopServer " -> {
val serverOnion : String = call . argument ( " ServerOnion " ) ?: " "
Cwtch . stopServer ( serverOnion )
}
" StopServers " -> {
Cwtch . stopServers ( )
}
" DestroyServers " -> {
Cwtch . destroyServers ( )
}
" SetServerAttribute " -> {
val serverOnion : String = call . argument ( " ServerOnion " ) ?: " "
val key : String = call . argument ( " Key " ) ?: " "
val v : String = call . argument ( " Val " ) ?: " "
Cwtch . setServerAttribute ( serverOnion , key , v )
}
" ExportProfile " -> {
val profileOnion : String = call . argument ( " ProfileOnion " ) ?: " "
val file : String = StringBuilder ( ) . append ( this . applicationContext . cacheDir ) . append ( " / " ) . append ( call . argument ( " file " ) ?: " " ) . toString ( )
Log . i ( " FlwtchWorker " , " constructing exported file " + file ) ;
Cwtch . exportProfile ( profileOnion , file )
}
" ImportProfile " -> {
val file : String = call . argument ( " file " ) ?: " "
val pass : String = call . argument ( " pass " ) ?: " "
Data . Builder ( ) . putString ( " result " , Cwtch . importProfile ( file , pass ) ) . build ( )
}
" ReconnectCwtchForeground " -> {
Cwtch . reconnectCwtchForeground ( )
}
" Shutdown " -> {
Cwtch . shutdownCwtch ( ) ;
}
2022-03-23 23:08:19 +00:00
else -> {
// ...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 ( )
WorkManager . getInstance ( this ) . enqueue ( workRequest )
WorkManager . getInstance ( applicationContext ) . getWorkInfoByIdLiveData ( workRequest . id ) . observe (
this , Observer { workInfo ->
if ( workInfo != null && workInfo . state == WorkInfo . State . SUCCEEDED ) {
val res = workInfo . outputData . keyValueMap . toString ( )
result . success ( workInfo . outputData . getString ( " result " ) )
}
}
)
2022-04-20 01:34:22 +00:00
return
2022-03-23 23:08:19 +00:00
}
}
2022-04-20 01:34:22 +00:00
result . success ( null )
2021-06-24 23:10:45 +00:00
}
// using onresume/onstop for broadcastreceiver because of extended discussion on https://stackoverflow.com/questions/7439041/how-to-unregister-broadcastreceiver
override fun onResume ( ) {
super . onResume ( )
Log . i ( " MainActivity.kt " , " onResume " )
if ( myReceiver == null ) {
Log . i ( " MainActivity.kt " , " onResume registering local broadcast receiver / event bus forwarder " )
val mc = MethodChannel ( flutterEngine ?. dartExecutor ?. binaryMessenger , CWTCH _EVENTBUS )
val filter = IntentFilter ( " im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS " )
myReceiver = MyBroadcastReceiver ( mc )
LocalBroadcastManager . getInstance ( applicationContext ) . registerReceiver ( myReceiver !! , filter )
}
// ReconnectCwtchForeground which will resync counters and settings...
// We need to do this here because after a "pause" flutter is still running
// but we might have lost sync with the background process...
Log . i ( " MainActivity.kt " , " Call ReconnectCwtchForeground " )
2022-04-12 22:03:21 +00:00
Cwtch . reconnectCwtchForeground ( )
2021-06-24 23:10:45 +00:00
}
override fun onStop ( ) {
super . onStop ( )
Log . i ( " MainActivity.kt " , " onStop " )
if ( myReceiver != null ) {
LocalBroadcastManager . getInstance ( applicationContext ) . unregisterReceiver ( myReceiver !! ) ;
myReceiver = null ;
}
}
override fun onDestroy ( ) {
super . onDestroy ( )
Log . i ( " MainActivity.kt " , " onDestroy - cancelling all WORKER_TAG and pruning old work " )
WorkManager . getInstance ( this ) . cancelAllWorkByTag ( WORKER _TAG )
WorkManager . getInstance ( this ) . pruneWork ( )
}
class AppbusEvent ( json : String ) : JSONObject ( json ) {
val EventType = this . optString ( " EventType " )
val EventID = this . optString ( " EventID " )
val Data = this . optString ( " Data " )
}
// MainActivity.MyBroadcastReceiver receives events from the Cwtch service via im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS Android local broadcast intents
// then it forwards them to the flutter ui engine using the CWTCH_EVENTBUS methodchannel
class MyBroadcastReceiver ( mc : MethodChannel ) : BroadcastReceiver ( ) {
val eventBus : MethodChannel = mc
override fun onReceive ( context : Context , intent : Intent ) {
val evtType = intent . getStringExtra ( " EventType " ) ?: " "
val evtData = intent . getStringExtra ( " Data " ) ?: " "
//val evtID = intent.getStringExtra("EventID") ?: ""//todo?
eventBus . invokeMethod ( evtType , evtData )
}
}
}