How to get back data from activity to React-native which is called by intent - react-native

I want to get data back from a activity which i called from react native module
#ReactMethod
public void callNewActivity(String code, Callback runner){
Activity activity = getCurrentActivity();
Intent intent = new Intent(activity, novaActivity.class);
activity.startActivity(intent);
}
i want to invoke runner callback with a string param which will came "novaActivity"

Related

Push notifications with back stack (Pending Intents, Kotlin)

I'm facing a problem with passing the launch URL from one activity to another, without creating a new Intent for my MainActivity.
I have a webview, which is work with OneSignal push notifications. I wanted to modify the grouping notifications content.
If there's a way to get the result I want (modifying notifications group layout for OneSignal) That would be awesome. I'll simply use the One Signal default action and that would be the best solution for me.
If I have to implement it on that way:
https://developer.android.com/training/notify-user/group
The problem is, when I start a new child activity of the MainActivity, I don't use the "StartActivity / StartActivityForResults" functions.
This is the extension of OSRemoteNotificationReceivedHandler (OneSignal class)
It's outside of my MainActivity class.
class NotificationServiceExtension : OSRemoteNotificationReceivedHandler {
#RequiresApi(Build.VERSION_CODES.N)
override fun remoteNotificationReceived(
context: Context,
notificationReceivedEvent: OSNotificationReceivedEvent
) {
val notification = notificationReceivedEvent.notification
val bigText = Html.fromHtml(notification.body, FROM_HTML_MODE_LEGACY).toString()
var smallText = Html.fromHtml(notification.additionalData["cleantitle"] as String, FROM_HTML_MODE_LEGACY).toString()
val summaryStatistics = Html.fromHtml(notification.additionalData["setSummaryText"] as String, FROM_HTML_MODE_LEGACY).toString()
if (smallText == "test") {
smallText = Html.fromHtml(notification.additionalData["smalltitle"] as String, FROM_HTML_MODE_LEGACY).toString()
}
else{
val name = Html.fromHtml(notification.additionalData["text"] as String, FROM_HTML_MODE_LEGACY).toString()
smallText += " from $name"
}
val smallContent = RemoteViews("com.webviewapp.mywebviewapp", R.layout.small_layout_notification)
val sum = RemoteViews("com.webviewapp.mywebviewapp", R.layout.summary_layout_notification)
val bigContent = RemoteViews("com.webviewapp.mywebviewapp", R.layout.large_notification_layout)
bigContent.setTextViewText(R.id.notification_title, smallText)
bigContent.setTextViewText(R.id.notification_content, bigText)
smallContent.setTextViewText(R.id.notification_title, smallText)
sum.setTextViewText(R.id.notification_title, summaryStatistics)
notificationReceivedEvent.complete(null)
var bp: Bitmap? = null
try {
bp =Picasso.get().load(notification.largeIcon).get()
smallContent.setImageViewBitmap(R.id.noti_pic, bp)
bigContent.setImageViewBitmap(R.id.noti_pic, bp)
}
catch(e:Exception){
print(e)
}
try {
val fid = notification.additionalData["fid"] as String
notificationId = fid.toInt()
}
catch(e:java.lang.Exception){
notificationId += Date().time.toInt()
}
val notificationOpenActivity = Intent(context.applicationContext, MainActivity::class.java)
.putExtra("launchURL", notification.additionalData["pushURL"] as String)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
val resultPendingIntent: PendingIntent? = TaskStackBuilder.create(context.applicationContext).run {
// Add the intent, which inflates the back stack
addNextIntentWithParentStack(notificationOpenActivity)
// Get the PendingIntent containing the entire back stack
getPendingIntent(0,
PendingIntent.FLAG_UPDATE_CURRENT)
}
val receivedNotification = NotificationCompat.Builder(context.applicationContext, NOTIFICATION_GROUP)
.setSmallIcon(R.drawable.myIcon)
.setColor(ContextCompat.getColor(context.applicationContext,R.color.blue_primary))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setStyle(NotificationCompat.DecoratedCustomViewStyle()) // to expand button
.setAutoCancel(true)
.setVibrate(longArrayOf(500, 500, 500))
.setCustomBigContentView(bigContent)
.setCustomContentView(smallContent)
.setCustomHeadsUpContentView(sum)
.setChannelId(NOTIFICATION_CHANNEL)
.setGroup(NOTIFICATION_GROUP)
.setGroupSummary(false)
.setContentIntent(resultPendingIntent)
.build()
val summary = NotificationCompat.Builder(context.applicationContext, NOTIFICATION_GROUP)
.setSmallIcon(R.drawable.myIcon)
.setColor(ContextCompat.getColor(context.applicationContext,R.color.blue_primary))
//.setContentTitle(summaryStatistics.toString())
.setContentTitle(summaryStatistics)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setStyle(NotificationCompat.InboxStyle()
.setBigContentTitle(summaryStatistics)
.addLine(summaryStatistics)
.setSummaryText(summaryStatistics))
.setAutoCancel(true)
.setChannelId(NOTIFICATION_CHANNEL)
.setGroup(NOTIFICATION_GROUP)
.setGroupSummary(true)
.build()
NotificationManagerCompat.from(context.applicationContext).apply {
notify(notificationId, receivedNotification)
notify(SUMMARY_ID, summary)
}
}
}
And the notifications work good as I want. The problem is, How do I pass from the child activity the extra parameter to it's parent if I didn't create that child from the parent? I simply want to load it's URL into my webview, but also keep the back stack.
Also, how can I make sure I don't create multiple MainActivity if I won't use that child?
Thanks in advance.
Notification Back Stack
Android's documentation page "Start an Activity from a Notification" covers the back stack use case under the "Regular activity" suggestion.
Regular activity
This is an activity that exists as a part of your app's normal UX flow. So when the user arrives in the activity from the notification, the new task should include a complete back stack, allowing them to press Back and navigate up the app hierarchy.
I see you are using the addNextIntentWithParentStack method on TaskStackBuilder already in your code so looks like you may have already followed that page.
However there is one thing wrong with Google's docs here, the requestCode sent to getPendingIntent should be a unique value for your app.
Example:
getPendingIntent(
1234, // NOTE: Change this to a unique requestCode for your app
PendingIntent.FLAG_UPDATE_CURRENT
)
I have filed an docs issue with Google on this.
Lastly, since I didn't see this in your question make sure you have correctly added android:parentActivityName to your Activity in your AndroidManifest.xml per Android's "Define your app's Activity hierarchy"
OneSignal Details
Notification Tracking
Note that calling notificationReceivedEvent.complete(null) means OneSignal won't know anything about your notification you're displaying with NotificationManagerCompat. This changes a few things:
Click counts won't be sent to OneSignal
Notification won't be restored. (notifications are automatically cleaned when the app is "force stopped", device is rebooted, or app is updated)
Notification Groups
OneSignal can already do the grouping and summary you have in your code. Just set the "Group Key" on the dashboard, or android_group if you are sending the notification with the REST API.
Back stack
OneSignal doesn't allow you to control the back stack, it simply just always resumes that app and leaves the back stack un-effected. However you can disable this default behavior with com.onesignal.NotificationOpened.DEFAULT in your AndroidManifest.xml and use your own startActivity from the OneSignal.setNotificationOpenedHandler.

How to inform a Flux that I have an item ready to publish?

I am trying to make a class that would take incoming user events, process them and then pass the result to whoever subscribed to it:
class EventProcessor
{
val flux: Flux<Result>
fun onUserEvent1(e : Event)
{
val result = process(e)
// Notify flux that I have a new result
}
fun onUserEvent2(e : Event)
{
val result = process(e)
// Notify flux that I have a new result
}
fun process(e : Event): Result
{
...
}
}
Then the client code can subscribe to EventProcessor::flux and get notified each time a user event has been successfully processed.
However, I do not know how to do this. I tried to construct the flux with the Flux::generate function like this:
class EventProcessor
{
private var sink: SynchronousSink<Result>? = null
val flux: Flux<Result> = Flux.generate{ sink = it }
fun onUserEvent1(e : Event)
{
val result = process(e)
sink?.next(result)
}
fun onUserEvent2(e : Event)
{
val result = process(e)
sink?.next(result)
}
....
}
But this does not work, since I am supposed to immediately call next on the SynchronousSink<Result> passed to me in Flux::generate. I cannot store the sink as in the example:
reactor.core.Exceptions$ErrorCallbackNotImplemented:
java.lang.IllegalStateException: The generator didn't call any of the
SynchronousSink method
I was also thinking about the Flux::merge and Flux::concat methods, but these are static and they create a new Flux. I just want to push things into the existing flux, such that whoever holds it, gets notified.
Based on my limited understanding of the reactive types, this is supposed to be a common use case. Yet I find it very difficult to actually implement it. This brings me to a suspicion that I am missing something crucial or that I am using the library in an odd way, in which it was not intended to be used. If this is the case, any advice is warmly welcome.

Only one callback may be registered to a function in a native module react native error

I recently added an android native module to my which listens on timezone and time changed broadcasts from the system and allows the app to perform some operations. The native module looks like this
public class TimezoneHandlerModule extends ReactContextBaseJavaModule {
private final Context context;
private final TimezoneChangeBroadcastReceiver timezoneChangeBroadcastReceiver;
private Callback onTimezoneChangeCallback;
public TimezoneHandlerModule(ReactApplicationContext reactContext) {
super(reactContext);
this.context = reactContext;
this.timezoneChangeBroadcastReceiver = new TimezoneChangeBroadcastReceiver();
}
private void registerForTimezoneChangeHandler() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_TIME_CHANGED);
intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
getReactApplicationContext().registerReceiver(timezoneChangeBroadcastReceiver, intentFilter);
}
private void unregisterTimezoneChangeHandler() {
getReactApplicationContext().unregisterReceiver(timezoneChangeBroadcastReceiver);
}
public void setOnTimezoneChangeCallback(Callback onTimezoneChangeCallback) {
this.onTimezoneChangeCallback = onTimezoneChangeCallback;
}
/**
* #return the name of this module. This will be the name used to {#code require()} this module
* from javascript.
*/
#Override
public String getName() {
return "TimezoneHandler";
}
#ReactMethod
public void start(Callback onChange) {
Log.d(getName(), "Starting the timezone change handler");
this.registerForTimezoneChangeHandler();
this.setOnTimezoneChangeCallback(onChange);
}
#ReactMethod
public void stop() {
Log.d(getName(), "Stopping the timezone change handler");
this.unregisterTimezoneChangeHandler();
}
private class TimezoneChangeBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d(getName(), "Received broadcast for timezone/time change " + intent.getAction());
final String action = intent.getAction();
if (action.equals(Intent.ACTION_TIME_CHANGED) || action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
TimezoneHandlerModule.this.onTimezoneChangeCallback.invoke();
}
}
}
}
Two react methods are exposed start and stop. start takes a function as a parameter which is invoked whenever a broadcast for timezone changed or time changed is received. After hooking up the native module and starting the app in emulator, I opened Settings and change the timezone and I can see that the relevant logs are printed.
11-24 17:07:21.837 1597-1597/com.xyz D/TimezoneHandler: Received broadcast for timezone/time change
11-24 17:07:21.837 1597-1907/com.xyz I/ReactNativeJS: Detected timezone change
When I change the timezone again, I see below error in the logcat output
1-24 17:22:42.356 1597-1597/com.galarmapp D/TimezoneHandler: Received broadcast for timezone/time change
11-24 17:22:42.365 1597-1907/com.galarmapp E/ReactNativeJS: The callback start() exists in module TimezoneHandler, but only one callback may be registered to a function in a native module.
11-24 17:22:42.367 1597-1908/com.galarmapp E/unknown:React: The callback start() exists in module TimezoneHandler, but only one callback may be registered to a function in a native module., stack:
__invokeCallback#12814:10
<unknown>#12685:24
guard#12604:3
invokeCallbackAndReturnFlushedQueue#12684:6
From the error message, it seems as if I am trying to attach a separate callback to the start function but I am not doing any such thing. I am calling the start method in the componentWillMount of the top level component and have confirmed that it is not called twice. I see that other people have also seen this error while trying different things but still don't understand the reason behind the problem.
Please share if you have any insights.
According to the documentation http://facebook.github.io/react-native/docs/native-modules-android.html#callbacks - "A native module is supposed to invoke its callback only once. It can, however, store the callback and invoke it later." Once you have done invoke() on the callback, you cannot use it again.
This particular use case of time zone change is better solved by sending events to javascript. See this documentation http://facebook.github.io/react-native/docs/native-modules-android.html#sending-events-to-javascript

Open Android activity automatically on receiving notification

I have to launch the app on receiving notification. The following piece of code works fine when the app is killed and notification is received (i.e the code inside if condition). But when the app is running in foreground or background, multiple instances of the activity gets created(i.e snippet in else condition). It's not the MainActivity that has to be launched on receiving the notification, instead it's some other activity containing the broadcast Receiver.
I have added the following lines in the onMessage of GCMintentService class.
if (currentPackage.equalsIgnoreCase(context.getPackageName()
.toString())) {
broadcastMessage(context, message);
} else {
Intent mIntent = new Intent(context, MainActivity.class);
mIntent.setAction(Intent.ACTION_MAIN);
mIntent.addCategory(Intent.CATEGORY_LAUNCHER);
mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(mIntent);
}
In the activity, under onReceive method of BroadcastReceiver, i am starting the activity again.
private final BroadcastReceiver mHandleMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
startActivity(getIntent().setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT));
}
};
I also had this requirement in one of my application. We can achieve it if we call
Intent mIntent = new Intent(context, MainActivity.class);
mIntent.setAction(Intent.ACTION_MAIN);
mIntent.addCategory(Intent.CATEGORY_LAUNCHER);
mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(mIntent);
broadcastMessage(context, message);
In the main activity use the following in the broadcast receiver that would receive the broadcasted message above.
WakeLock wakeLock = null;
KeyguardManager kgMgr = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
boolean locked = kgMgr.inKeyguardRestrictedInputMode();
PowerManager pm = (PowerManager) context
.getSystemService(Context.POWER_SERVICE);
if (!pm.isScreenOn()) {
wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK
| PowerManager.ACQUIRE_CAUSES_WAKEUP, "MyWakeLock");
wakeLock.acquire();
}
if (locked) {
Window mWindow = getWindow();
mWindow.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
}
Personally I feel that this is not the best of the answers and also best of the ideas to open the app directly when received a notification as there will be many functions like onCreate onResume, will be triggered automatically, spoil the users work if they are in a really important work by opening another app directly, also we need to put a lot of flags or use any other method to manage the flow of the application, when user open the app, app comes from background, app opened due notification and all such cases. Avoid it as it spoils the whole user experience.

Titanium module - life cycle events not called

I am building a Titanium module for the Android platform and I want to use the life cycle events of the module (i.e. onDestroy, onPause, etc). I tried to use them by overriding these life cycle events in the module class like this:
#Kroll.module(name="custom", id="vub.ac.be.custom")
public class CustomModule extends KrollModule {
private static final String TAG = "customModule";
#Kroll.onAppCreate
public static void onAppCreate(TiApplication app) {
}
private void destroyServices(){
//...
}
#Override
public void onStop(Activity activity) {
Log.d(TAG, "STOPPING");
destroyServices();
super.onStop(activity);
}
#Override
public void onPause(Activity activity) {
Log.d(TAG, "[MODULE LIFECYCLE EVENT] pause");
super.onPause(activity);
}
#Override
public void onResume(Activity activity) {
Log.d(TAG, "[MODULE LIFECYCLE EVENT] resume");
super.onResume(activity);
}
#Override
public void onDestroy(Activity activity) {
Log.d(TAG, "[MODULE LIFECYCLE EVENT] destroy");
destroyService();
super.onDestroy(activity);
}
}
but when I opening and closing the application, these life cycle events are never called. Does anybody know how to use them, because only if I can use them I will be able to build the module I want. Thanks
Could this be the origin of my problems: inline link moddevguide
https://github.com/appcelerator/titanium_modules/blob/master/moddevguide/mobile/android/src/ti/moddevguide/ModdevguideModule.java
on line 72 they describe the following:
// Lifecycle
// NOTES:
//
// 1. Modules are created in the root context
// 2. Using navBarHidden (or fullscreen or modal) causes the window, when opened, to run in a new Android Activity.
// 3. The root context/activity will be stopped when a new activity is launched
// 4. Lifecycle notifications will NOT be received while the root activity is stopped.
I run the module in an application that uses navBarHidden, so as described a new android activity wil be started and the root activity is stopped. Whenever the root activity is stopped, the lifecycle notifications are received. Can anyone confirm this and does anybody know how to solve this? thanks
What version of SDK are you using? On 3.3.0 lifecycle callbacks is calling regardless to navigation bar hidden.