flutter - Invoking method from android activity - flutter

I tried to app to app communication on Flutter - Android.
First, Android app(A) bound Flutter service which Android Service(B).
Then A call to B and B start activity.
Second, activity invoke method on Flutter using MethodChannel.
But I faced the Exception.
How to invoke flutter method on Android activity that start with Android service.
Exception
E/flutter (12950): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: MissingPluginException(No implementation found for method getDatabasesPath on channel com.tekartik.sqflite)
E/flutter (12950): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: MissingPluginException(No implementation found for method getAll on channel plugins.flutter.io/shared_preferences)
MyService.kt
class MyService : Service() {
private lateinit var mMessenger: Messenger
internal class IncomingHandler(context: Context,
private val applicationContext: Context = context.applicationContext
) : Handler() {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when (msg.what) {
WHAT_REQ_VP -> {
applicationContext.startActivity(Intent(applicationContext,
TempActivity::class.java)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
}
}
}
}
override fun onBind(intent: Intent): IBinder {
mMessenger = Messenger(IncomingHandler(this))
return mMessenger.binder
}
}
TempActivity.kt
class TempActivity : FlutterActivity() {
private lateinit var channel: MethodChannel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_temp)
val backgroundFlutterView = FlutterNativeView(baseContext, true)
channel = MethodChannel(backgroundFlutterView, "com.example.test")
GeneratedPluginRegistrant.registerWith(this)
channel.setMethodCallHandler { call, result -> run {
when (call.method) {
"result" -> {
print(call.argument<String>("result"))
}
else -> {}
}
} }
findViewById<Button>(R.id.btn_confirm).setOnClickListener {
val arguments = HashMap<String, String>()
arguments["message"] = "From Android"
channel.invokeMethod("test", arguments)
}
}
}
temp.dart
import 'package:flutter/services.dart';
class Temp {
static final _singleton = Temp._internal();
static const MethodChannel _channel = const MethodChannel('com.example.test');
factory Temp() {
return _singleton;
}
Temp._internal() {
_channel.setMethodCallHandler(_handleMethod);
}
_handleMethod(MethodCall call) {
switch (call.method) {
case 'test':
print('_handleMethod: ${call.method}');
_channel.invokeMethod('result', 'Result from flutter client');
}
}
}

Related

Cannot invoke method - Method Channel in Flutter

I'm trying to write platform specific code in Flutter. First of all, created a method to obtain battery temperature inside MainActivity.kt
This is my full code in MainActivity.kt file:
package com.xyz.zyx
import android.os.BatteryManager;
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.EventChannel
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.Registrar
class MainActivity: FlutterFragmentActivity() {
// ...
private var batteryManager: BatteryManager? = null
private var methodChannel: MethodChannel? = null
private var temperatureChannel: EventChannel? = null
override fun configureFlutterEngine(#NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "battery").setMethodCallHandler {
call, result ->
if (call.method == "getBatteryTemperature") {
val batteryTemperature = getBatteryTemperature()
if (batteryTemperature != -1) {
result.success(batteryTemperature)
} else {
result.error("UNAVAILABLE", "Battery temperature not available.", null)
}
} else {
result.notImplemented()
}
}
}
private fun getBatteryTemperature(): Int {
val batteryStatus: Intent? =
IntentFilter(Intent.ACTION_BATTERY_CHANGED).let { ifilter ->
this.registerReceiver(null, ifilter)
val batteryTemperature: Int
val batteryManager =
getSystemService(Context.BATTERY_SERVICE) as BatteryManager
batteryTemperature = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -99)
return batteryTemperature
}
}
}
This is how I call my platform specific code from Flutter side:
static const platform = MethodChannel('battery');
Future<void> _getBatteryTemp() async {
String batteryLevel;
try {
var result = await platform.invokeMethod('getBatteryTemperature');
batteryLevel = 'Battery temperature at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
print(batteryLevel);
}
Lastly, this is the error on Flutter app running on an Android device. It throws an MissingPluginException even though there is a method called getBatteryTemperature, it says there isn't.
E/flutter (25427): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: MissingPluginException(No implementation found for method getBatteryTemperature on channel battery)
E/flutter (25427): #0 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:294:7)
Is there something did I miss to implement in my code? Why does it not work?

Get data from Google Fit History Client when the app is in background or killed

I am working on a flutter plugin to calculate total steps in interval using Google Fit History Client (Fitness.getHistoryClient) which requires flutter activity and I want to call some of its methods in the background. I am using workmanager 0.5.0 to schedule a background job.
class MyPlugin: FlutterPlugin, MethodCallHandler, ActivityAware, PluginRegistry.ActivityResultListener {
private lateinit var channel : MethodChannel
private val TAG = "MY TAG"
private var activity: Activity? = null
private var mResult: Result? = null
private lateinit var context: Context
override fun onAttachedToEngine(#NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "my_channel_name")
channel.setMethodCallHandler(this)
context = flutterPluginBinding.applicationContext
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"getTotalStepsInInterval" -> getTotalStepsInInterval(call, result)
else -> result.notImplemented()
}
}
private fun getTotalStepsInInterval(call: MethodCall, result: Result){
mResult = result
if (activity == null) {
mResult?.success(-1)
return
}
// val readRequest = ...
Fitness.getHistoryClient(activity!!, getGoogleAccount())
.readData(readRequest)
.addOnSuccessListener { response ->
var totalSteps = 0
// Calculate total steps from response
mResult?.success(totalSteps)
}
.addOnFailureListener { }
}
override fun onDetachedFromEngine(#NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
if (channel == null) {
return
}
binding.addActivityResultListener(this)
activity = binding.activity
}
// onDetachedFromActivityForConfigChanges + onReattachedToActivityForConfigChanges + onDetachedFromActivity
}
I am able to get the total steps when I invoke this method from a function in dart.
MyPlugin myPlugin = MyPlugin();
int res = await myPlugin.getTotalStepsInInterval();
print(res);
Activity is null when a method is invoked from the callback dispatcher of workmanager.
void callbackDispatcher() async {
workmanager.executeTask((task, inputData) async {
print('Running - Callback Dispatcher');
try {
MyPlugin myPlugin = MyPlugin();
int res = await myPlugin.getTotalStepsInInterval();
print(res);
} catch (err) {
throw Exception(err);
}
return Future.value(true);
});
}
// Output -------> -1
Since subscription is active using Recording API, I can fetch the data using readData when the user comes back to the app but I want to get the data at a specific time (For eg: 5:00 PM which I can schedule using workmanager) even when the app is not running. Duplicate Question
Still not possible?

i am trying to use method channel to access camera and take photo , but how can i get the file from method channel respons

package com.example.unit_testing
import android.content.Intent
import android.graphics.Bitmap
import android.provider.MediaStore
import android.view.WindowManager.LayoutParams
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodChannel
class MainActivity : FlutterActivity() {
private val METHOD_CHANNEL_NAME = "com.jinto/method"
private var methodChannel: MethodChannel? = null
private val cameraRequest = 1888
var imageView: Bitmap? = null
override fun configureFlutterEngine(#NonNull flutterEngine: FlutterEngine) {
window.addFlags(LayoutParams.FLAG_SECURE)
super.configureFlutterEngine(flutterEngine)
// Setup Channels
setupChannels(flutterEngine.dartExecutor.binaryMessenger)
}
override fun onDestroy() {
teardownChannels()
super.onDestroy()
}
private fun setupChannels(messenger: BinaryMessenger) {
methodChannel = MethodChannel(messenger, METHOD_CHANNEL_NAME)
val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
methodChannel!!.setMethodCallHandler { call, result ->
if (call.method == "cameraOn") {
startActivityForResult(cameraIntent, cameraRequest)
result.success(imageView)
} else {
result.notImplemented()
}
}
}
private fun teardownChannels() {
methodChannel!!.setMethodCallHandler(null)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == cameraRequest && resultCode == RESULT_OK) {
val imageBitmap = data?.extras?.get("data") as Bitmap
println("wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww${imageBitmap.width}")
imageView = imageBitmap
}
}
}
if we pass the bitmap(imageView) it won't work , initially it retune nothing , after the camera service started not getting any data
how can i convert the bitmap into file and return it, can you help me to solve this problem,
This is my code to get data from platform side,
Future<void> _checkAvailability() async {
try {
var apiImage = await methodChannel.invokeMethod("cameraOn");
print('$apiImage');
} on PlatformException catch (e) {
print('error');
print(e);
}
}

Service Binder object casting issue during service connection

I have an issue in service connection issue in that line var binder:TwentyFourHoursService.LocalBinder = service as TwentyFourHoursService.LocalBinder and could not find solution:
override fun onServiceConnected(
className: ComponentName,
service: IBinder
) { // cast the IBinder and get MyService instance
var binder:TwentyFourHoursService.LocalBinder = service as TwentyFourHoursService.LocalBinder
myService = binder.getService()
bound = true
// serviceCallbacks =this as ServiceCallbacks
myService!!.setCallbacks(mActivity) // register
}
Here is my service:
class TwentyFourHoursService : Service() {
private val mHandler: Handler = Handler() //run on another Thread to avoid crash
private var mTimer: Timer? = null //timer handling
// Binder given to clients
private val binder: IBinder = LocalBinder()
override fun onBind(intent: Intent): IBinder {
// throw UnsupportedOperationException("Not yet implemented")
return binder
}
override fun onCreate() {
if (mTimer != null) // Cancel if already existed
mTimer!!.cancel() else mTimer = Timer() //recreate new
mTimer!!.scheduleAtFixedRate(
TimeDisplay(),
0,
notify
) //Schedule task
//Timer().scheduleAtFixedRate(TimeDisplay(),0, notify)
}
fun setCallbacks(callbacks: ServiceCallbacks?) {
serviceCallbacks = callbacks
}
override fun onDestroy() {
super.onDestroy()
mTimer!!.cancel() //For Cancel Timer
Toast.makeText(this, "Service is Destroyed", Toast.LENGTH_SHORT).show()
}
//class TimeDisplay for handling task
internal inner class TimeDisplay : TimerTask() {
override fun run() { // run on another thread
mHandler.post(Runnable {
// display toast
/* if (serviceCallbacks!=null) {
serviceCallbacks!!.doSomething()
}*/
// Reload current fragment
// Reload current fragment
// startActivity(Intent(applicationContext, FitnessSlideMenuScreen::class.java))
// rFitnessSlideMenuScreen().displaySelectedFragment(HomeFragment())
Toast.makeText(applicationContext, "Service is running", Toast.LENGTH_SHORT).show()
})
}
}
Error:
***java.lang.ClassCastException: android.os.BinderProxy cannot be cast to com.example.beyahfitness.service.TwentyFourHoursService$LocalBinder***
The issue as I realised was as a result of the way had declared my service in Manifest,
<service
android:name=".MyService"
android:enabled="true"
android:process=":MyService" >
When I try getting the service at;
override fun onServiceConnected(name: ComponentName,service: IBinder)
I kept getting this error; java.lang.ClassCastException: android.os.BinderProxy cannot be cast to com.demo.MyService$LocalBinder
I resolved to refactoring my Service as below;
<service
android:name=".MyService" >
This apparently solves my problem, hope it helps you too

Interface analog in Swift for callbacks realization

Please, help me to make interface in Swift to use it for callbacks purposes.
Example in Java:
public interface ErrorListener {
void onError(String errorMsg);
}
class SomeUiClass implements ErrorListener {
public SomeUiClass () {
SomeWorkingClass s = new SomeWorkingClass();
s.setErrorListener(this);
s.doSomething(true);
}
#Override
void onError(String errorMsg) {
System.out.println("Error msg: " + errorMsg);
}
}
class SomeWorkingClass {
ErrorListener listener;
void setErrorListener (ErrorListener listener) {
this.listener = listener;
}
void doSomething (boolean withError) {
if (withError) listener.onError("Test error");
}
}
//....
SomeUiClass ui = new SomeUiClass(); // prints: Error msg: Test error
So, I tried to use protocol in Swift for this purpose, but I didn't understand, how to use it properly.
It would look like this in swift
protocol ErrorListener {
func onError(errorMsg:String)
}
class SomeUiClass : UIViewController , ErrorListener {
func onError(errorMsg:String)
print("Error msg: ", errorMsg)
}
}
class SomeWorkingClass : UIViewController{
weak var listener:ErrorListener?
func setErrorListener (listener:ErrorListener) {
self.listener = listener
}
}
let ui = SomeUiClass() // prints: Error msg: Test error