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)
}
}
}
Related
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
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( ... ) }
I like to initialize MoPub SDK in Android and continue loading banners and interstitials only if SDK init is complete and successful.
What can be a good way to accomplish this?
create a variable listner :
private SdkInitializationListener Listner2;
then put this code inside your oncreate :
final SdkConfiguration.Builder configBuilder = new SdkConfiguration.Builder("24534e1901884e398f1253216226017e");
configBuilder.withLogLevel(DEBUG);
Listner2 = new SdkInitializationListener() {
#Override
public void onInitializationFinished() {
Log.d("MoPub", "SDK initialized");
mInterstitial.load();
}
};
MoPub.initializeSdk(this, configBuilder.build(), Listner2);
The code above load test interstitial if SDK init is complete and successful.
Very easy to accomplish with Android's new Livedata objects. There objects are observable and you can receive their changes wherever you want in your app.
Technology and patterns used:
Livedata
Singleton pattern
MoPub SDK 5.11.1
I will show you how by code snippets
Singleton pattern
public class MoPubSdk {
private static MoPubSdk INSTANCE;
public static MoPubSdk getInstance(Activity activity) {
if(MoPubSdk.INSTANCE == null) {
MoPubSdk.INSTANCE = new MoPubSdk(activity);
}
return MoPubSdk.INSTANCE;
}
}
Constructor with SdkInitializationListener function
Note the MutableLiveData object that changes it's value to true (isInitialized) when the listener is called and the MoPub SDK is ready to call ads. Also see the showConsentIfNeeded function which you can see in the next code block. Just comment the function if not needed.
public class MoPubSdk {
private final MutableLiveData<Boolean> isMoPubSdkInitialized = new MutableLiveData<>();
private Activity mActivity;
private MoPubSdk(Activity activity) {
this.mActivity = activity;
SdkConfiguration sdkConfiguration = new SdkConfiguration.Builder("ANYadunitID")
.withLogLevel(BuildConfig.DEBUG ? MoPubLog.LogLevel.DEBUG : MoPubLog.LogLevel.NONE)
.build();
MoPub.initializeSdk(activity, sdkConfiguration, initSdkListener());
}
private SdkInitializationListener initSdkListener() {
return new SdkInitializationListener() {
#Override
public void onInitializationFinished() {
/* MoPub SDK initialized.
Check if you should show the consent dialog here, and make your ad requests. */
Log.d("MoPub", "SDK initialized");
isMoPubSdkInitialized.setValue(true);
showConsentIfNeeded();
}
};
}
public LiveData<Boolean> isMoPubSdkInitialized() {
return isMoPubSdkInitialized;
}
}
Optional consent Dialog
private void showConsentIfNeeded() {
PersonalInfoManager mPersonalInfoManager = MoPub.getPersonalInformationManager();
Log.d("customeee", "Can collect pers information? "+MoPub.canCollectPersonalInformation()
+ ".\nShould show consent dialog? "+mPersonalInfoManager.shouldShowConsentDialog());
if(!MoPub.canCollectPersonalInformation()) {
if(mPersonalInfoManager.shouldShowConsentDialog()) {
mPersonalInfoManager.loadConsentDialog(new ConsentDialogListener() {
#Override
public void onConsentDialogLoaded() {
mPersonalInfoManager.showConsentDialog();
}
#Override
public void onConsentDialogLoadFailed(#NonNull MoPubErrorCode moPubErrorCode) {
MoPubLog.i("Consent dialog failed to load.");
}
});
}
}
}
So much for initialization. Now let's call the MoPub SDK from an activity and continue with banner loading.
public class MyActivity extends Activity {
private MoPubSdk moPubSdk;
protected void onCreate(final Bundle savedInstanceState) {
moPubSdk = MoPubSdk.getInstance(this);
moPubSdk.isMoPubSdkInitialized().observe(this, new Observer<Boolean>() {
#Override
public void onChanged(Boolean aBoolean) {
if (aBoolean)
//Init your banner here.
}
});
}
}
The LiveData is easy to explain. First time the MoPub SDK initialization begins the Livedata object is false. It takes some time to init, on success the value switches to true and the observer gets called, you can begin banner loading.
On switching activities during init or call MoPubSdk#getInstance another time, the value is already true and gets passed directly on #observe call and you init your banner straight away.
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.
I am trying to integrate my Hybrid Mobile App (Inonic + Cordova) with hockey App
but the problem is Hockey App is support Native apps (According to my info). So is there any guide available for that?
Hybrid App integration with Hockey app.
When I try to follow hockey app integration with android platform (hybrid app) it also said me to add some code in main activity so where i can find this
Main activity is inside Android platform... cordova/platforms/android/src/...
Put in onCreate method the Register...
There also some plugins for help in this task like https://github.com/peutetre/cordova-plugin-hockeyapp
Take into account that a lot of crash JavaScript problems do not crash in native world it would be helpful to use additional way to communicate controlled errors for example the saveException method, try to expose this by plugin into javascript, it will let store context information error: http://hockeyapp.net/help/sdk/android/3.0.1/net/hockeyapp/android/ExceptionHandler.html
I have tested the solution for Android only in a fork of the previous mentioned plugin:
https://github.com/m-alcu/cordova-plugin-hockeyapp
There are several actions available but yo only need to use "start" and "saveException" for controlled errors to be send to hockeyapps.
hockeyapp.js:
var exec = require('cordova/exec');
var hockeyapp = {
start:function(success, failure, token) {
exec(success, failure, "HockeyApp", "start", [ token ]);
},
feedback:function(success, failure) {
exec(success, failure, "HockeyApp", "feedback", []);
},
saveException:function(success, failure, description) {
exec(success, failure, "HockeyApp", "saveException", [ description ]);
}
};
module.exports = hockeyapp;
hockeyapp.java:
package com.zengularity.cordova.hockeyapp;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.json.JSONArray;
import android.widget.Toast;
import static net.hockeyapp.android.ExceptionHandler.saveException;
import net.hockeyapp.android.FeedbackManager;
import net.hockeyapp.android.CrashManager;
import net.hockeyapp.android.CrashManagerListener;
public class HockeyApp extends CordovaPlugin {
public static boolean initialized = false;
public static String token;
public static String description;
#Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
if (action.equals("start")) {
token = args.optString(0);
CrashManager.register(cordova.getActivity(), token, null);
initialized = true;
callbackContext.success();
return true;
} else if(action.equals("feedback")) {
token = args.optString(0);
FeedbackManager.register(cordova.getActivity(), token, null);
cordova.getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
FeedbackManager.showFeedbackActivity(cordova.getActivity());
}
});
callbackContext.success();
return true;
} else if(action.equals("saveException")) {
description = args.optString(0);
if(initialized) {
Toast toast = Toast.makeText(cordova.getActivity(), "problem", Toast.LENGTH_SHORT);
toast.show();
cordova.getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
Exception e = new Exception("Send problem");
saveException(e, new CrashManagerListener() {
public String getDescription() {
return description;
}
});
}
});
callbackContext.success();
return true;
} else {
callbackContext.error("cordova hockeyapp plugin not initialized, call start() first");
return false;
}
}
else {
return false;
}
}
}
example of use this plugin in a hellowold example (index.js):
var app = {
// Application Constructor
initialize: function() {
this.bindEvents();
},
// Bind Event Listeners
//
// Bind any events that are required on startup. Common events are:
// 'load', 'deviceready', 'offline', and 'online'.
bindEvents: function() {
document.addEventListener('deviceready', this.onDeviceReady, false);
},
// deviceready Event Handler
//
// The scope of 'this' is the event. In order to call the 'receivedEvent'
// function, we must explicitly call 'app.receivedEvent(...);'
onDeviceReady: function() {
app.receivedEvent('deviceready');
},
// Update DOM on a Received Event
receivedEvent: function(id) {
var parentElement = document.getElementById(id);
var listeningElement = parentElement.querySelector('.listening');
var receivedElement = parentElement.querySelector('.received');
listeningElement.setAttribute('style', 'display:none;');
receivedElement.setAttribute('style', 'display:block;');
console.log('Received Event: ' + id);
hockeyapp.start(
function() { alert('hockeyapp initialised'); },
function(msg) { alert(msg); },
'< your APP ID >');
hockeyapp.saveException(
function() { alert('hockeyapp saveException'); },
function(msg) { alert(msg); },
'Something wrong has happened: bla bla bla...');
}
};
app.initialize();
Hockey stores these controlled exceptions in the file directory of the app and asks to send it the next time user opens app: