is it possible to stream mjpeg video from native Kotlin to Flutter? - flutter

I have video stream in mjpeg format, so I have read and implmented a lot of ways to work with it, but anything of these ways don't meet my requirments (also I was using webView packages, but now we should implement changing screen oreintation feature and webView dosen't have such thing, so I am using Hero animation to make it works, but in that case my urls with video stream open twice. It dosen't work because it has limitation: only one user can watch stream, in other cases stream will be closed).
So I have found the way to play mjepeg video using Kotlin, and read bout MethodChannel in Flutter, I implemented almost everything as it was said in docs, but it still dosen't work.
Native code:
class MainActivity: FlutterActivity() {
private val channel = "com.example.my_app"
private var view: MjpegView? = null
override fun configureFlutterEngine(#NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, channel).setMethodCallHandler {
call, result ->
if(call.method == "Video") {
view = findViewById(R.id.mjpegid)
view!!.isAdjustHeight = true
view!!.mode1 = MjpegView.MODE_FIT_WIDTH
view!!.setUrl("http://videos/videos/LiveViewCamera")
view!!.isRecycleBitmap1 = true
result.success("it successed!")
}
else {
result.notImplemented()
}
}
}
}
in Flutter:
final channel = MethodChannel('com.example.my_app');
String videos = '';
Future getVideoFromNative() async {
videos = await channel.invokeMethod('Video');
print(videos);
print('are you hehe?');
setState(() {});
}
Got this error:
E/MethodChannel#com.example.rosysk_nano_cleaned(17621): Failed to handle method call
E/MethodChannel#com.example.rosysk_nano_cleaned(17621): java.lang.NullPointerException
When I open it in pure Kotlin app, my video and code works perfectly fine

Related

Flutter - how to cancel a SSE Stream

Hey I cant figure out how to cancel an SSE Stream in my flutter app.
I am using this package to use SSE in my app: https://pub.dev/packages/sse_channel
I feel like it has a major lack in the documentation.
The SSE works fine just don't know how to stop it with a function.
Im using this to start the SSE
channel = SseChannel.connect(Uri.parse(dotenv.env['BASE_URL'] + '/sse/rn-updates'));
and
channel.stream.listen((message) { code... and some other stuff }
that part works but I don't know how to close it.
Tried stuff like this
static void cancelStream() {
if (channel != null)
channel.sink.close()
}
You should create a global var in your class
StreamSubscription? stream;
and assign it to this variable
stream = channel.stream.listen((message) { code... and some other stuff }
and you can use this code to cancel stream
stream?.cancel();

How to Display Camera Preview in .NET MAUI?

Currently, what I did is to access the camera and use it to take a picture, but this isn't exactly what I want to accomplish.
I want to activate and display front camera preview on my content page, and then take a picture by pressing a button control.
I've searched around and I can't seem to find any solution. In Xamarin, there's CameraView from Xamarin Community Toolkit that can be used for camera stream, but from the looks of it, CameraView hasn't been implemented in .NET MAUI Community Toolkit. Is there any other way to do this? Thank you.
This is the code for the button event
private async void takephoto(object sender, EventArgs e)
{
if (MediaPicker.Default.IsCaptureSupported)
{
FileResult photo = await MediaPicker.Default.CapturePhotoAsync();
if (photo != null)
{
// save the file into local storage
string localFilePath = Path.Combine(FileSystem.CacheDirectory, photo.FileName);
using Stream sourceStream = await photo.OpenReadAsync();
Stream stream = await photo.OpenReadAsync();
using FileStream localFileStream = File.OpenWrite(localFilePath);
await sourceStream.CopyToAsync(localFileStream);
DisplayPhoto.Source = ImageSource.FromStream(() =>
{
return stream;
});
}
}
}

How to retrieve logcat in Flutter?

How do you get the output written to logcat back into the Flutter app that caused it? Or simpler asked: How to read logcat in Flutter?
The problem is this:
The app uses a stack of Android plugins to communicate with some custom hardware through Bluetooth. Those Android plugins write extensively to logcat. Now, for debugging, it would be very helpful to be able to read all the messages the App (including native plugins) has written to logcat. Question is, is this somehow possible?
How would you tackle that?
Check out the plugin called logcat on pub.dev.
Sadly, it seems to be no longer maintained and isn't updated for null safety.
But you can check out the source code here and see how the plugin gets access to the android logcat.
Because the logcat is a native thing, you'll have to use a MethodChannel to call a Java/Kotlin function:
// define MethodChannel
final platform = const MethodChannel('app.channel.logcat');
// call native method
logs = await platform.invokeMethod('execLogcat');
And the native part:
public class LogcatPlugin implements MethodCallHandler {
public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), "app.channel.logcat");
channel.setMethodCallHandler(new LogcatPlugin());
}
#Override
public void onMethodCall(MethodCall call, Result result) {
if (call.method.equals("execLogcat")) {
String logs = getLogs();
if (logs != null) {
result.success(logs);
} else {
result.error("UNAVAILABLE", "logs not available.", null);
}
} else {
result.notImplemented();
}
}
String getLogs() {
try {
Process process = Runtime.getRuntime().exec("logcat -d");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder log = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
log.append(line);
}
return log.toString();
} catch (IOException e) {
return "EXCEPTION" + e.toString();
}
}
}
The code samples are from github.com/pharshdev/logcat.
Maybe you can just fork the git repo and migrate it to null safety if needed.
Check the plugin called logcat_monitor on pub.dev.
Its biggest advantage over the other logcat plugin is that it allows continuous monitoring of logcat messages.
Follows a screenshot example:
how to use
Add the dependencies:
dependencies:
logcat_monitor: ^0.0.4
Create a function to consume the logcat messages
void _mylistenStream(dynamic value) {
if (value is String) {
_logBuffer.writeln(value);
}
}
Register your function as a listener to get logs then use it in anyway within your app.
LogcatMonitor.addListen(_mylistenStream);
Start the logcat monitor passing the filter parameters as defined in logcat tool.
await LogcatMonitor.startMonitor("*.*");

Flutter: how can I permanently register a sensor (and never unregister it?)

TL;DR how can I have an Android sensor permanently running/active/registered for my app, even if I close it?
Objective:
I'm making a Flutter application that counts your steps using the pedometer package,
which uses the built-in sensor TYPE_STEP_COUNTER of Android,
which returns the # of steps taken since last boot (iOS). On Android, any steps taken before installing the app are not counted.
How I implemented it:
When the app is actively running in the foreground, each step causes
a myStepCount to increment by 1.
In all other cases (phone locked, went to home-screen, closed the app...), the android TYPE_STEP_COUNTER sensor should
still be running in the background, and once I open my app again, the
difference between new stepCount and last saved stepCount (saved
using shared_prefs) will be calculated and added to myStepCount.
Important:
The TYPE_STEP_COUNTER sensor must be permanently running/stay registered in the background, even after I lock my phone, go to the home-screen, or close the app...
Observations:
On my Samsung Galaxy A02s, my app works perfectly fine, as it it supposed to
(as described above). That is because on that phone I also have the
Google Fit app installed, which tracks your steps 24/7 (so the
TYPE_STEP_COUNTER sensor is permanently registered).
On my Samsung Galaxy S7, my app does not work as it's supposed to.
myStepCount gets incremented when I take steps while the app is
running in the foreground. But steps taken while the app is closed
will NOT be added to myStepCount once I open the app again.
Note: I don't have any other step-counting-apps like Google Fit on this phone.
Conclusion:
I need to find a way to register the TYPE_STEP_COUNTER sensor from my Flutter app, and keep it registered even after I close the app.
2 Attempted (but unsuccessful) Solutions:
1st Attempt:
Calling Native Android Code from my Flutter Code to register the sensor
This is my main.dart file (with the unimportant parts left out for simplicity):
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(App());
}
class App extends StatefulWidget {
#override
_AppState createState() => _AppState();
}
class _AppState extends State<App> with WidgetsBindingObserver {
#override
void initState() {
super.initState();
if (Platform.isAndroid) {
_activateStepCounterSensor();
} else if (Platform.isIOS) {
//TODO check if anything is needed to to here
}
}
void _activateStepCounterSensor() async {
MethodChannel _stepCounterChannel = MethodChannel('com.cedricds.wanderapp/stepCounter'); //convention
dynamic result = await _stepCounterChannel.invokeMethod('activateStepCounterSensor');
switch (result) {
case "success":
//The following line gets printed when I run the flutter app on my Samsung Galaxy S7:
print('_activateStepCounterSensor(): successfully registered step counter sensor for android');
break;
case "error":
print('_activateStepCounterSensor(): failed to register step counter sensor (not available) for android');
//TODO display errorpage (because app is completely useless in this case)
break;
default:
print('_activateStepCounterSensor(): unknown result: $result');
break;
}
}
//build() and other lifecycle-methods and helper methods: not important for this question
}
This is my MainActivity.kt file:
package com.cedricds.wanderapp
import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.util.Log
import android.widget.Toast
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity2: FlutterActivity(), SensorEventListener {
private val STEP_COUNTER_CHANNEL = "com.cedricds.wanderapp/stepCounter";
private lateinit var channel: MethodChannel
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, STEP_COUNTER_CHANNEL)
channel.setMethodCallHandler { call, result ->
when(call.method){ //this is like switch-case statement in Java
"activateStepCounterSensor" -> {
activateStepCounterSensor(result)
}
}
}
}
private var sensorManager : SensorManager?=null
private var sensor: Sensor ?= null
private fun activateStepCounterSensor(result: MethodChannel.Result) {
//This line gets printed when I run the flutter app, so the method gets called successfully:
Log.d("Android", "Native Android: activateStepCounterSensor()")
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensor = sensorManager?.getDefaultSensor(Sensor.TYPE_STEP_COUNTER)
if (sensor == null) {
Toast.makeText(this, "missing hardware.", Toast.LENGTH_LONG).show()
result.error("error", "error", "error")
} else {
sensorManager?.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL)
//This line gets printed:
Log.d("Android", "Native Android: registered TYPE_STEP_COUNTER")
//and never unregister that listener
result.success("success")
}
}
override fun onSensorChanged(p0: SensorEvent?) {}
override fun onAccuracyChanged(p0: Sensor?, p1: Int) {}
}
Despite the few print(...) and Log.d(...) being printed in the console as expected, the app doesn't work how I expected it to work. When I exit the app, walk for example 50 steps, then open the app again, those 50 steps are missing. It seems the sensor is being unregistered somewhere.
2nd Attempt:
Modifying the pedometer package's code by removing unregisterListener(...):
The only changes I did to the file were 2 Log.d(...) statements and more importantly, commenting out a specific line of code.
modified SensorStreamHandler.kt from the pedometer package:
package com.example.pedometer
import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.os.Looper
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.EventChannel
import android.os.Handler
import android.util.Log
class SensorStreamHandler() : EventChannel.StreamHandler {
private var sensorEventListener: SensorEventListener? = null
private var sensorManager: SensorManager? = null
private var sensor: Sensor? = null
private lateinit var context: Context
private lateinit var sensorName: String
private lateinit var flutterPluginBinding: FlutterPlugin.FlutterPluginBinding
constructor(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding, sensorType: Int) : this() {
this.context = flutterPluginBinding.applicationContext
this.sensorName = if (sensorType == Sensor.TYPE_STEP_COUNTER) "StepCount" else "StepDetection"
sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensor = sensorManager!!.getDefaultSensor(sensorType)
this.flutterPluginBinding = flutterPluginBinding
}
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
Log.d("Pedometer", "Native Android: onListen()")
if (sensor == null) {
events!!.error("1", "$sensorName not available",
"$sensorName is not available on this device");
} else {
sensorEventListener = sensorEventListener(events!!);
sensorManager!!.registerListener(sensorEventListener, sensor, SensorManager.SENSOR_DELAY_NORMAL);
}
}
override fun onCancel(arguments: Any?) {
Log.d("Pedometer", "Native Android: onCancel()")
//The only change I did: commenting out the following line:
// sensorManager!!.unregisterListener(sensorEventListener);
}
}
This also did not solve my problem. So if someone knows how I can permanently register the TYPE_STEP_COUNTER sensor in my flutter app, please let me know.
Update:
I've contacted one of the developers of the pedometer package, and he suggested me to use flutter_foreground_service (which is developed by the same team/company as pedometer). It works.
But I would still find it interesting, if there is another way (maybe similar to my 2 failed attempts).

How to call a platform code from an isolate in flutter?

I know these libraries flutter_isolate and isolate_handler exist that support doing these but I couldn't find any method to call platform specific codes from inside of them. Can someone show any example of how it's done ?
My answer might not answer to your question directly but I was facing the same situation.
I was working on this to run a computing intensive task avoiding UI lagging. After a long research, no, you cannot run Platform from other isolates rather than the main one.
Instead of making and running the native code on another isolate, let's make the native code run in background instead with TaskQueue.
Channels and platform threading
The example code in the above link is using onAttachedToEngine, you can check it out. However, I was using is configureFlutterEngine so I have to figure out a little bit until I found a solution that I need binaryMessenger to make it working. Luckily it can called from flutterEngine too!
This is the example code of when using configureFlutterEngine
class MainActivity: FlutterFragmentActivity() {
val MOBILE_SDK_CHANNEL = "com.example.app/flutter"
override fun configureFlutterEngine(#NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
super.configureFlutterEngine(flutterEngine)
val taskQueue = flutterEngine.dartExecutor.binaryMessenger.makeBackgroundTaskQueue()
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, MOBILE_SDK_CHANNEL,
StandardMethodCodec.INSTANCE, taskQueue).setMethodCallHandler { call, result ->
// TODO : Do implementation here
when (call.method) {
"example_method" -> {
// success
// result.success(data)
}
else -> result.notImplemented()
}
}
}
}
With taskQueue the native code will be ready to run in background.
Future<void> getData() async {
if (Platform.isAndroid) {
const platform = MethodChannel("com.example.app/flutter");
var data = await platform.invokeMethod("example_method");
// data is ready to use and non-blocked the UI thread
return data
}
}
The native code now runs in non-blocking manner, no more UI lag. :)