Flutter: How to wait for android platform channel function to complete and return the result - flutter

Creating a platform channel on Android for File upload using AWS SDK.
Now I want to wait for the upload to complete in the background and return the status of the result.
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == "uploadToAWS") {
new DoUpload().execute();
// how to await here ?
result.success(true);
} else {
result.notImplemented()
}
}

Instead of waiting there, remove "result.success(true);" statement from there and simply assign "result" object to your own object of type "MethodChannel.Result".
Then use it to execute its "success", "error" or "notImplemented" method from anywhere as shown in below example.
public class Activity extends FlutterActivity {
private MethodChannel.Result myResult;
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
.setMethodCallHandler(
(call, result) -> {
myResult = result;
}
);
private void myCustomMethod {
myResult.success(yourData);
}
}
Now you can use these myResult.success(), myResult.error() methods from anywhere within class.

Related

What is the proper way to launch app using alarm manager?

I need to implement a flutter alarm app that opens through the alarm manager.
It seems that there is the way to do it using android_intent and android_alarm_manager_plus.
I was looking for the right way to create such functionality, but this option does not work:
await AndroidAlarmManager.oneShot(
Duration(seconds: 2), 1, setAlarm);
void setAlarm()async{
AndroidIntent intent =
AndroidIntent(action: 'action_view', package: 'com.example.test_alarm');
await intent.launch().catchError((e) {
print(e.toString());
});
}
Debug console gives this:
I/IntentSender( 4886): Cannot resolve explicit intent - ignoring package
V/IntentSender( 4886): Sending intent Intent { act=android.intent.action.VIEW (has extras) }
try using https://pub.dev/packages/app_launcher
void setAlarm() async{
await AppLauncher.openApp(
androidApplicationId: "com.example.test_alarm",
);
}
I think your code is not working because the setAlarm() function will be run from Dart Background Isolate. So you need to modify the AlarmBroadcastReceiver.java code of plugin like below.
public class AlarmBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
PowerManager powerManager = (PowerManager)
context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK |
PowerManager.ACQUIRE_CAUSES_WAKEUP |
PowerManager.ON_AFTER_RELEASE, "AlarmBroadcastReceiver:My wakelock");
Intent startIntent = context
.getPackageManager()
.getLaunchIntentForPackage(context.getPackageName());
if (startIntent != null)
startIntent.setFlags(
Intent.FLAG_ACTIVITY_REORDER_TO_FRONT |
Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
);
wakeLock.acquire(3 * 60 * 1000L /*3 minutes*/);
context.startActivity(startIntent);
AlarmService.enqueueAlarmProcessing(context, intent);
wakeLock.release();
}
}
If you want to launch your app on a lock screen, need to add codes at the MainActivity. Here is an example code.
class MainActivity : FlutterActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// ...
turnScreenOnAndKeyguardOff()
}
private fun turnScreenOnAndKeyguardOff() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setShowWhenLocked(true)
setTurnScreenOn(true)
window.addFlags(WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON)
} else {
window.addFlags(
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED // deprecated api 27
or WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD // deprecated api 26
or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON // deprecated api 27
or WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
)
}
val keyguardMgr = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
keyguardMgr.requestDismissKeyguard(this, null)
}
}
}

Having trouble figuring out how to place code into kotlin and use a method channel to send functions from flutter

My MainActivity.kt:
class MainActivity : FlutterActivity() {
private val CHANNEL = "flutter.native/helper"
private var mapId: Int? = null
override fun onCreate(savedInstanceState: Bundle?, PersistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, PersistentState)
GeneratedPluginRegistrant.registerWith(FlutterEngine(this));
}
override fun configureFlutterEngine(#NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
MethodChannel(flutterEngine.dartExecutor, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == "KML") {
result.success(getKMLResource());
} else {
result.notImplemented();
}
}
}
private fun getKMLResource(): Int {
return R.raw.borders;
}
}
I'm trying to insert the below call, but I get a errors every time.
override fun onMethodCall(call: MethodCall, result: Result) {
when (call.method) {
"showToast" -> {
val text = call.argument<String>("text") // hello world
showToast(text)
}
}
}
Errors:
'onMethodCall' overrides nothing
One type argument expected for class Result<out T>
Unresolved reference: showToast
Here's my flutter portion:
Future<void> printSomething(GoogleMapController mapController) async {
const MethodChannel channel = MethodChannel('flutter.native/helper');
channel.invokeMethod('showToast', {'text': 'hello world'});
}
I have no idea how to incorporate this into my kotlin code properly, and there aren't many examples out there. At least none that I could find. My ultimate goal is to be able to manipulate private fun getKMLResource() and set return as a directory I select from my flutter app.
Anybody know how to do this? Please help. Thank you.
Here's my add kml flutter function:
Here's how I select my .kml:
Future<void> addKml(GoogleMapController mapController) async {
const MethodChannel channel = MethodChannel('flutter.native/helper');
try {
int kmlResourceId = await channel.invokeMethod('KML');
return mapController.channel.invokeMethod("map#addKML", <String, dynamic>{
'resourceId': kmlResourceId,
});
} on PlatformException catch (e) {
throw 'Unable to plot map: ${e.message}';
}
}
First of all, you gotta follow these documentations:
Writing custom platform-specific code
Supporting the new Android plugins APIs
Error:
'onMethodCall' overrides nothing
Means that you are trying override something that does not exist
In this situation, you have to implement missing classes that are found in the docs tutorial:
ActivityAware, FlutterPlugin, MethodChannel.MethodCallHandler
On:
MethodChannel.MethodCallHandler
You can override (Kotlin snippet):
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result)
That should fix your problem.
ActivityAware and FlutterPlugin are essentials for the new flutter upgrades and you can efficiently fix memory leaks with them.
class BubbleOverlayPlugin : ActivityAware, FlutterPlugin, MethodChannel.MethodCallHandler {
private var activity: Activity? = null
private var channel: MethodChannel? = null
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
//here lies the platform methods calls
}
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
channel?.setMethodCallHandler(null)
//release resources
}
override fun onAttachedToEngine(#NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, channelName)
channel?.setMethodCallHandler(this)
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = binding.activity
}
override fun onDetachedFromActivity() {
//release resources
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
activity = binding.activity
}
override fun onDetachedFromActivityForConfigChanges() {
//release resources
}
}
You can check my source code from the plugin bubble_overlay, It is using the last docs recommendations.
Repo: bubble_overlay repo

Channel Invokemethod from native android not invoking the flutter method

Basically I'm invoking a payment sdk in native android(kotlin) using platform channel. SDK is initialised successfully. After payment transaction I will receive the payment status in a kotlin file which actually extends sdk class files (not an activity). From this callback method I need to pass the status to the flutter dart code. Everything runs successfully without any error but while passing the payment status flutter method is not invoked.
Flutter dart code.
#override
void initState() {
// TODO: implement initState
super.initState();
platform.setMethodCallHandler(_handlePaymentResponse); // To call from native android after payment response.
}
// Invoked from Native Android after payment response.
Future<dynamic> _handlePaymentResponse(MethodCall call) async {
print('_handlePaymentResponse method called');
switch(call.method) {
case "message":
debugPrint('From Native====' + call.arguments);
return new Future.value("");
}
}
// On button click this method will be invoked.
Future<void> _initializeSDK() async {
print('Token $token');
try {
await platform.invokeMethod('callPaymentSDK');
} on PlatformException catch (e) {
print("Failed to initialize SDK : '${e.message}'.");
}
}
Native Android : Paymentstatus.kt
class PaymentResponse() : LibraryPaymentStatusProtocol, Parcelable {
//val channel = MethodChannel(flutterView, MainActivity.CHANNEL)
var backgroundFlutterView: FlutterNativeView? = null
override fun paymentStatus(p0: String?, p1: Activity?) {
if (p1 != null) {
Handler(Looper.getMainLooper()).post {
Log.v("PaymentResponse", "Main thread called")
backgroundFlutterView = FlutterNativeView(p1, true)
val channel = MethodChannel(backgroundFlutterView, MainActivity.CHANNEL)
channel.invokeMethod("message", p0); // Invoking Flutter method
}
}
}
}
I ran in to the same issue, and in the end the solution that worked for me (not sure if this is the ideal approach) was to store a reference to the FlutterEngine inside my MainActivity like so:
class MainActivity: FlutterActivity() {
companion object {
var flutterEngineInstance: FlutterEngine? = null
}
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
flutterEngineInstance = flutterEngine
}
}
In my code I would then just invoke a method like this:
MethodChannel(
MainActivity.flutterEngineInstance.dartExecutor.binaryMessenger,
"com.example"
).invokeMethod("method", mapOf())
For my case i didn't receive data from android native to flutter when i called channel.invokeMethod from OnCreate() in MainActivity: FlutterActivity()
so i added channel.invokeMethod( ... ) inside onStart() method, its worked.
also you can use:
runOnUiThread { channel.invokeMethod( ... ) }
or
Handler(Looper.getMainLooper()).post { channel.invokeMethod( ... ) }

Android Room with RXJava2; onNext() of emitter is not properly triggered

I am switching from async tasks to rxjava2 and have some issues with my code tests.
I have a room table of elements that have a certain monetary amount. On a usercontrol that is called DisplayCurrentBudget, a sum of all amounts should be displayed. This number must refresh everytime a new element is inserted. I tackled the requirement in two ways, but both produce the same result: My code does not care if the database is updated, it only updates when the fragment is recreated (onCreateView).
My first attempt was this:
//RxJava2 Test
Observable<ItemS> ItemObservable = Observable.create( emitter -> {
try {
List<ItemS> movies = oStandardModel.getItemsVanilla();
for (ItemS movie : movies) {
emitter.onNext(movie);
}
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
});
DisposableObserver<ItemS> disposable = ItemObservable.
subscribeOn(Schedulers.io()).
observeOn(AndroidSchedulers.mainThread()).
subscribeWith(new DisposableObserver<ItemS>() {
public List<ItemS> BadFeelingAboutThis = new ArrayList<ItemS>();
#Override
public void onNext(ItemS movie) {
// Access your Movie object here
BadFeelingAboutThis.add(movie);
}
#Override
public void onError(Throwable e) {
// Show the user that an error has occurred
}
#Override
public void onComplete() {
// Show the user that the operation is complete
oBinding.DisplayCurrentBudget.setText(Manager.GetBigSum(BadFeelingAboutThis).toString());
}
});
I already was uncomfortable with that code. My second attempt produces the exact same result:
Observable<BigDecimal> ItemObservable2 = Observable.create( emitter -> {
try {
BigDecimal mySum = oStandardModel.getWholeBudget();
emitter.onNext(mySum);
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
});
DisposableObserver<BigDecimal> disposable = ItemObservable2.
subscribeOn(Schedulers.io()).
observeOn(AndroidSchedulers.mainThread()).
subscribeWith(new DisposableObserver<BigDecimal>() {
#Override
public void onNext(BigDecimal sum) {
// Access your Movie object here
oBinding.DisplayCurrentBudget.setText(sum.toString());
}
#Override
public void onError(Throwable e) {
// Show the user that an error has occurred
}
#Override
public void onComplete() {
// Show the user that the operation is complete
}
});
Any obvious issues with my code?
Thanks for reading, much appreciate it!
Edit:
I was asked what Manager.GetBigSum does, it actually does not do much. It only adds BigDecimal-Values of an Item list.
public static BigDecimal GetBigSum(List<ItemS> ListP){
List<BigDecimal> bigDList = ListP.stream().map(ItemS::get_dAmount).collect(Collectors.toList());
return bigDList.stream()
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
Further, I simplified the query. But it still does not care about DB updates, only about fragment recreation:
Single.fromCallable(() -> oStandardModel.getItemsVanilla())
.map(Manager::GetBigSum)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
e -> oBinding.DisplayCurrentBudget.setText(e.toString())
);
Your rx logic has no error. That should be internal error in your getWholeBudget.
But why you write rx so complex?
For your case, you can just write:
Single.fromCallable(() -> oStandardModel.getItemsVanilla())
.map(Manager::GetBigSum)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
e -> oBinding.DisplayCurrentBudget.setText(sum.toString()),
e -> log.error(e));
I solved it this way:
oStandardModel.getItemJointCatLive().observe(this, new Observer<List<ItemJointCat>>() {
#Override
public void onChanged(#Nullable final List<ItemJointCat> oItemSP) {
Single.fromCallable(() -> oStandardModel.getWholeBudget())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
e -> oBinding.DisplayCurrentBudget.setText(e.toString())
);
}
});
My mistake was that I assumed RXjava2 does not need an onchanged event...now i just use onchanged event of livedata observer to trigger a simple rxjava2 query.
Do you think there is anything wrong with that approach?

compose with vertx for sequential code

I have two operations step_1() and step_2() and want to execute step_2() AFTER step_1().
With normal java this would be:
step_1();
step_2();
With vertx I have to use vertx-compose(). Am I right?
According to https://groups.google.com/forum/#!topic/vertx/FuvlPLpoGOA, I dont need Futures for sequential code.
"If you want to do each request sequencially you dont need futures."
So how can I do that without using futures?
I dont know, if this matters: My Vertx from which this code is executed is a "Worker"-Verticle.
#Override
public void start(Future<Void> fut) throws IOException {
Future<Void> step_1 = Future.future();
step_1.compose(res -> {
// If the future succeeded
Future<Void> step_2 = step_1();
step_2.compose(res2 -> {
step_2();
}, Future.future().setHandler(handler -> {
// If the future failed
}));
//I dont need that
}, Future.future().setHandler(handler -> {
// If the future failed
}));
}
public void step_1(){
..
}
public void step_2(){
..
}
Is this the right and shortest (!) way?
Below is an example of chaining of Future, I have made the example very trivial nonetheless it showcases the concept.
#RunWith(VertxUnitRunner.class)
public class Chaining {
private Vertx vertx = Vertx.vertx();
#Test
public void futures_chaining(TestContext context) throws Exception {
Async async = context.async();
firstOperation()
.compose((outcome) -> {
System.out.println(outcome);
return secondOperation();
})
.compose(outcome -> {
System.out.println(outcome);
/*
For stopping unit test we are returning this future
for production use-case this would be Future.succeededFuture
or Future.failedFuture depending on your method outcomes
*/
return Future.future(handle -> async.complete());
});
}
private Future<String> firstOperation() {
Future<String> future = Future.future();
vertx.setTimer(1000, delay -> future.complete("First Operation Complete"));
return future;
}
private Future<String> secondOperation() {
Future<String> future = Future.future();
vertx.setTimer(1000, delay -> future.complete("Second Operation Complete"));
return future;
}
}
"If you want to do each request sequencially you dont need futures."
No, it's not. In asynchronous frameworks like Vert.x, input/output operations are non-blocking. It means, that if you call few asynchronous operations, they'll start working simultaneously. And if you want to do few requests sequentially, then you should use futures or callbacks to execute new request only after previous one finished successfully.
Check this code with futures, newer version with RxJava 2 and article about project.
#Override
public Future<Optional<Todo>> getCertain(String todoID) {
Future<Optional<Todo>> result = Future.future();
redis.hget(Constants.REDIS_TODO_KEY, todoID, res -> {
if (res.succeeded()) {
result.complete(Optional.ofNullable(
res.result() == null ? null : new Todo(res.result())));
} else
result.fail(res.cause());
});
return result;
}
#Override
public Future<Todo> update(String todoId, Todo newTodo) {
return this.getCertain(todoId).compose(old -> {
if (old.isPresent()) {
Todo fnTodo = old.get().merge(newTodo);
return this.insert(fnTodo)
.map(r -> r ? fnTodo : null);
} else {
return Future.succeededFuture();
}
});
}
RxJava exists specifically to compose async events: http://vertx.io/docs/vertx-rx/java/
Assuming both step_1() and step_1() aren't designed to return results (i.e. they effectively return void) then you could change them to return Observable or Single and chain them together similar to this:
step_1().doOnSuccess(this::step_2()).subscribe(/* control resumes here */);
RxJava (or rather, reactive programming in general) takes a little bit to wrap your head around it, but I would strongly recommend using it if you're planning to chain together async operations.
Pass step_2 as argument to step_1
#Override
public void start(Future<Void> fut) throws IOException {
step_1(step_2);
}
private void step_1(Runnable function){
someAsynccall("some-arg", response -> {
function.run();
}).end();
}
private void step_2(){
// do something
}