Implementing a flutter plugin to wake screen when app is asleep - flutter

I am trying to turn on a screen once an alarm fires. I am using this
[package][1] for the alarm. However, after time doing research I have not come any closer to achieving waking the screen on alarm firing.
The solutions either use deprecated APIs or are only specific to code within some contexts like activities and fragments, which is not the case for a flutter plugin.
Here is what I have tried
-Implementing ActivityAware interface but the onAttachedToActivity() never seems to fire
-Changing activity settings of my main activity in android manifes
android:showWhenLocked="true"
android:turnScreenOn="true"
I am currently trying to get an activity within the plugin to add flags to it to turn the screen on
if (Build.VERSION.SDK_INT < 27)
activity!!.window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
else
activity!!.setTurnScreenOn(true)
The problem is the activity variable is always null because for some reason, onAttachedToActivity doesn't run.
I have created an activity within the native code but I don't know how to get a reference to that activity and use it to turn the flags on. Remember, my screen is turned off so this is possibly what is complicating the matter.
Here is where I create the activity:
private fun startMainActivity(context: Context) {
val pm = context.packageManager
val intent = pm.getLaunchIntentForPackage(context.packageName)
context.startActivity(intent)
turnScreenON()
}
How can I create this plugin to wake the screen when an alarm fires?
Here is my code for the android kotlin side:
package com.example.wake_screen
import android.app.Activity
import android.content.Context
import android.os.Build
import android.util.Log
import android.view.WindowManager
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
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
/** WakeScreenPlugin */
class WakeScreenPlugin: ActivityAware, FlutterPlugin, MethodCallHandler{
private lateinit var channel : MethodChannel
private lateinit var mContext: Context
private var activity: Activity? = null
override fun onAttachedToEngine(#NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "wake_screen")
channel.setMethodCallHandler(this)
mContext = flutterPluginBinding.applicationContext
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = binding.activity
Log.d("We are attached",binding.lifecycle.toString())
}
override fun onDetachedFromActivity() {
//release resources
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
activity = binding.activity
}
override fun onDetachedFromActivityForConfigChanges() {
//release resources
}
override fun onMethodCall(#NonNull call: MethodCall, #NonNull result: Result) {
if (call.method == "getPlatformVersion") {
startMainActivity(mContext)
result.success("Android ${android.os.Build.VERSION.RELEASE}")
}
else {
result.notImplemented()
}
}
private fun turnScreenON(){
if (Build.VERSION.SDK_INT < 27)
activity!!.window.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
else
activity!!.setTurnScreenOn(true)
}
private fun startMainActivity(context: Context) {
val pm = context.packageManager
val intent = pm.getLaunchIntentForPackage(context.packageName)
context.startActivity(intent)
turnScreenON()
}
override fun onDetachedFromEngine(#NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}
Here's my code on the flutter side:
//Provide instruction to wake device after duration
void setAlarm() async {
await AndroidAlarmManager.oneShot(
const Duration(seconds: _duration),
0,
wakeTheScreen,
wakeup: true,
allowWhileIdle: true,
);
}
//Function invoking platform code
static Future<void> wakeTheScreen() async {
String? version = await WakeScreen().getPlatformVersion();
print(version);
}
In summary, How do I wake the screen up using a flutter plugin that needs an activity to work
[1]: https://pub.dev/packages/android_alarm_manager_plus

Related

Unhandled Exception: MissingPluginException(No implementation found for method...)

I have an existing Flutter app that I use to prototype and test ideas before adding the idea to a project. I need a custom plugin to track location in the background for a project so I added plugin related code into the normal app project.
I am targeting android for a start. I have a Dart class representing the plugin that creates a method a channel to communicate with the platform code. On the platform side, I have created a class that extends FlutterPlugin
However, when I run the app and Dart native code calls methods on the Android side using the method channel, I get Unhandled Exception: MissingPluginException.
Here's the code
Dart code
class GeofencePlugin {
final MethodChannel _channel =
const MethodChannel('marcel/geofencing_plugin');
Future<bool> init() async {
//callbackDispatcher is a top level function that acts as entry point for background isolate
final callback = PluginUtilities.getCallbackHandle(callbackDispatcher);
await _channel
.invokeMethod('GeofencingPlugin.initialiseService', <dynamic>[callback!.toRawHandle()]);
return true;
}
Future<bool> registerGeofence(GeofenceRegion region) async {
return true;
}
Future<bool> removeGeofence(GeofenceRegion region) async {
return true;
}
}
Android code
class GeofencingPlugin : ActivityAware, FlutterPlugin, MethodChannel.MethodCallHandler {
private var mContext : Context? = null
private var mActivity : Activity? = null
private val geofencePendingIntent: PendingIntent by lazy {
val intent = Intent(mContext, GeofenceBroadcastReceiver::class.java)
PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
}
companion object {
#JvmStatic
private val TAG = "MARK_TAG"
#JvmStatic
val SHARED_PREFERENCES_KEY = "com.example.flutter_playground.geofencing"
#JvmStatic
val CALLBACK_DISPATCHER_HANDLE_KEY = "callback_dispatch_handler"
#JvmStatic
private fun initialiseService(context: Context, args: ArrayList<*>?) {
val callbackHandle = args!![0] as Long
context.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE)
.edit()
.putLong(CALLBACK_DISPATCHER_HANDLE_KEY, callbackHandle)
.commit()
}
}
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
mContext = binding.applicationContext
val channel = MethodChannel(binding.binaryMessenger, "marcel/geofencing_plugin")
channel.setMethodCallHandler(this)
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
mContext = null
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
mActivity = binding.activity
}
override fun onDetachedFromActivity() {
mActivity = null
}
override fun onDetachedFromActivityForConfigChanges() {
mActivity = null
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
mActivity = binding.activity
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
val args = call.arguments<ArrayList<*>>()
when(call.method) {
"GeofencingPlugin.initialiseService" -> {
initialiseService(mContext!!, args)
setupGeo()
result.success(true)
}
else -> result.notImplemented()
}
}
private fun setupGeo(){
val geofencingClient = mContext!!.getGeofenceClient()
val fence = Geofence.Builder()
.setRequestId("Mark")
.setCircularRegion(46.5422,14.4011,500f)
.setExpirationDuration(600000)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT)
.build()
val request = GeofencingRequest.Builder().apply {
setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_DWELL)
addGeofences(listOf(fence))
}.build()
geofencingClient.addGeofences(request, geofencePendingIntent)?.run {
addOnSuccessListener {
Toast.makeText(mContext,"Hahaha", Toast.LENGTH_LONG).show()
}
addOnFailureListener {
Log.d(TAG,it.message?:"Eoo")
Toast.makeText(mContext,"Heeerrrh", Toast.LENGTH_LONG).show()
}
}
}
}
I tried the following
Manually adding the android plugin class to the GeneratedPluginRegistrant.java file
Using flutterEngine.plugins.add('my plugin') in the MainActivity's onCreate method.
After digging into the pubsec.yaml of other plugins, I found out in order to have my app's plugin recognised by flutter I have to add the following to my pubsec.yaml under the flutter section:
plugin:
platforms:
android:
package: com.example.flutter_playground.geofencing
pluginClass: GeofencingPlugin

How to make a FlutterActivity be a MethodCallHandler?

I made my FlutterActivity MainActivity a MethodCallHandler:
class MainActivity: FlutterActivity(), MethodCallHandler {
so I implemented onMethodCall.
However, to be able to process method calls, still something is missing. This is how it's done in a Flutter plugin:
class MyPlugin: FlutterPlugin {
private lateinit var channel : MethodChannel
private val LOG_TAG: String = "MyPlugin.kt"
override fun onAttachedToEngine(#NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "myplugin")
channel.setMethodCallHandler(this)
}
How can I make my MainActivity be the actual MethodCallHandler?
configureFlutterEngine provides binding between Dart and native.
class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(#NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "<your_channel>").setMethodCallHandler {
call, result ->
// TODO
}
}
...

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

Native Dialog for location setting on Flutter

Is there a way to implement a Dialog for Location setting like the image below which gets triggered when app requires GPS location and doesn't find. Hitting OK will right away turn on the system GPS. This seems more convenient for users instead of taking them to location and manually turn on.
Is it possible to implement such thing in Flutter?
Expanded View of dialog:
Credits to Rajesh, as answered here. The plugin lets you add this native dialog for a quick location setting.
The implementation is quite simple as this:
import 'package:location/location.dart';
var location = Location();
Future _checkGps() async {
if(!await location.serviceEnabled()){
location.requestService();
}
}
#override
void initState() {
super.initState();
_checkGps();
}
In Kotlin, try this code:
class HomeActivity : AppCompatActivity() {
private val requestLocation = 199
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableLoc()
}
private fun enableLoc() {
val mLocationRequest = LocationRequest.create()
mLocationRequest.interval = 10000
mLocationRequest.fastestInterval = 5000
// mLocationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
val builder = LocationSettingsRequest.Builder()
.addLocationRequest(mLocationRequest)
val client = LocationServices.getSettingsClient(this)
val task =
client.checkLocationSettings(builder.build())
task.addOnSuccessListener(this) {
// All location settings are satisfied. The client can initialize
// location requests here.
// ...
}
task.addOnFailureListener(this) { e ->
if (e is ResolvableApiException) {
// Location settings are not satisfied, but this can be fixed
// by showing the user a dialog.
try {
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
e.startResolutionForResult(
this,
requestLocation
)
} catch (sendEx: SendIntentException) {
// Ignore the error.
}
}
}
}
}

Cannot access 'TAG': it is invisible (private in supertype) in 'AppCompatActivity'

I am a beginner in android programming. I'm currently using android studio 3.2.1.
I am trying to monitor the different states of an android activity in log. I have written the code shown below, but I keep receiving the error message:
Cannot access 'TAG': it is invisible (private in supertype) in 'AppCompatActivity'.
Even after searching, I cannot figure the error. Can some one help?
Code:
package com.cooperation.bestech.test1
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.util.Log;
class MainActivity : AppCompatActivity() {
private static final String TAG = "MyMessage";
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.i(TAG, "onCreate");
}
override fun onStart() {
super.onStart()
Log.i(TAG, "onStart");
}
override fun onPause() {
super.onPause()
Log.i(TAG, "onPause");
}
override fun onResume() {
super.onResume()
Log.i(TAG, "onResume");
}
override fun onStop() {
super.onStop()
Log.i(TAG, "onStop");
}
override fun onRestart() {
super.onRestart()
Log.i(TAG, "onRestart");
}
override fun onDestroy() {
super.onDestroy()
Log.i(TAG, "onDestroy");
}
override fun onSaveInstanceState(outState: Bundle?) {
super.onSaveInstanceState(outState)
Log.i(TAG, "onSaveInstanceState");
}
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
super.onRestoreInstanceState(savedInstanceState)
Log.i(TAG, "onRestoreInstanceState");
}
}
Since the original question is written in Kotlin (from comment), ignore the request of "launch Java project instead of the default Kotlin" may be a better solution.
And, applying the correct way to declare and assign a variable, your code segment should be like this:
class MainActivity : AppCompatActivity() {
val TAG = "MyMessage"
import android.content.ContentValues.TAG
Seems you are using Kotlin.So you need to define TAG in kotlin way:
In Kotlin constants located in the companion object:
class MyClass {
companion object {
private val TAG = "ClassName"
}
}