1

I have an alarm reminder that shows notification with extra data. What I want to do is send that extra data via intent from my notification to my main activity, and then open the app and call a specific method (location info) in that activity. That method will open a new activity, based on the locationId I have passed.

It works great when my app is running in the background, but when it is killed, ie when the app is completely closed, it opens the app's main activity, but does not call the method.

My notification code used to send data with intent:

Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("fromNotification", true);
intent.putExtra("locationId", locationId);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP 
              | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 111, intent, 
              PendingIntent.FLAG_UPDATE_CURRENT);

And this is how I call my mehtod:

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    setIntent(intent);
    if (getIntent().hasExtra("fromNotification")) {
        String locationId = getIntent().getStringExtra("locationId");
        CallFetchLocationInfoFromMapsFragment(locationId);
    }
}

I have also tried adding this at the end of my main activity's onCreate, and this should have been the solution, but it did nothing:

if (getIntent().hasExtra("fromNotification")) {
    String locationId = getIntent().getStringExtra("locationId");
    CallFetchLocationInfoFromMapsFragment(locationId);
}

I tried a lot more solutions (including this, this, this, this, this, this, this, and many more), but couldn't make it work.

Manifest (just the main activity part):

<application
    android:name="com.weekendcoder.kemo.hotels.app.AppController"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:screenOrientation="portrait"
        android:theme="@style/AppTheme.NoActionBar"
        android:launchMode="singleTask">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    ...
</application>

Here is the description of my ideal workflow - if I click on the notification it should open my MainActivity, that has a maps fragment. After everything is loaded on my map, I would want to start a new activity from my main activity's map - I would like to open locationDetails activity using the id I received from the notification. Since the debugging stops as soon as I close the app I couldn't pinpoint the issue, but I have tried adding this:

if (getIntent().hasExtra("fromNotification")) 
{
    Toast.makeText(MainActivity.this, "Notification!", Toast.LENGTH_LONG).show();
}

at the beginning of my onCreate and it did display the toast. I can't use this condition on the beginning of my onCreate since the map is not loaded and set up, so I added it at the end of my onCreate. In this case it is never called.

I could make it work by opening that new activity directly from the notifications, but that is not my ideal solution. I would like to be able to call any method in activity from notification when the app is closed.

I have also tried adding this:

if (getIntent().hasExtra("fromNotification")) {
    String locationId = getIntent().getStringExtra("locationId ");
    CallFetchLocationInfoFromMapsFragment(locationId);
    Toast.makeText(MainActivity.this, "Notification id: " locationId, Toast.LENGTH_LONG).show();
}else{
    Toast.makeText(MainActivity.this, "No notification", Toast.LENGTH_LONG).show();
}

at the end of my onCreate just to see if I can get this value to display, but i keep getting "No notification" toast.

When I put the same if condition at the start of my onCreate it crashes:

08-14 18:06:03.319 18591-18591/com.weekendcoder.kemo.hotels D/SQLiteHandler: Fetching notification settings from sqlite db
08-14 18:06:03.329 18591-18591/com.weekendcoder.kemo.hotels W/Settings: Setting airplane_mode_on has moved from android.provider.Settings.System to android.provider.Settings.Global, returning read-only value.
08-14 18:06:03.384 18591-18591/com.weekendcoder.kemo.hotels D/AndroidRuntime: Shutting down VM
08-14 18:06:03.384 18591-18591/com.weekendcoder.kemo.hotels W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0x41cdc700)
08-14 18:06:03.389 18591-18591/com.weekendcoder.kemo.hotels E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.weekendcoder.kemo.hotels/com.weekendcoder.kemo.hotels.MainActivity}: java.lang.NullPointerException
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2295)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2349)
    at android.app.ActivityThread.access$700(ActivityThread.java:159)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1316)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:176)
    at android.app.ActivityThread.main(ActivityThread.java:5419)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:525)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1046)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:862)
    at dalvik.system.NativeStart.main(Native Method)
 Caused by: java.lang.NullPointerException
    at com.weekendcoder.kemo.hotels.sliderfragments.MapsFragment.fetchlocationDetails(MapsFragment.java:545)
    at com.weekendcoder.kemo.hotels.MainActivity.CallFetchlocationDetailsFromMapsFragment(MainActivity.java:661)
    at com.weekendcoder.kemo.hotels.MainActivity.onCreate(MainActivity.java:324)
    at android.app.Activity.performCreate(Activity.java:5372)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1104)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2257)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2349) 
    at android.app.ActivityThread.access$700(ActivityThread.java:159) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1316) 
    at android.os.Handler.dispatchMessage(Handler.java:99) 
    at android.os.Looper.loop(Looper.java:176) 
    at android.app.ActivityThread.main(ActivityThread.java:5419) 
    at java.lang.reflect.Method.invokeNative(Native Method) 
    at java.lang.reflect.Method.invoke(Method.java:525) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1046) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:862) 
    at dalvik.system.NativeStart.main(Native Method) 

EDIT:

There is a possible solution that works, but I am not happy with it and would like to find something smarter/safer. The solution would be to get the id at the beginning and then add this:

if (locationId != null) {
    final Handler handler = new Handler();
    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            //Do something after 100ms
            CallFetchLocationInfoFromMapsFragment(locationId);
        }
    }, 5000);
}

That would allow my app to wait for 5 seconds and then call the method after all the other methods executed. This works, but I am not sure that it is the best way to go on with this, some devices might be slower. It would probably be better to find a way to check if onCreated completely finished executing and then call this method.

Banana
  • 2,255
  • 7
  • 31
  • 52
  • 1
    You faced this problem because once app is not running and user click on notification It will redirect to activity's on create method you cannot get call on onNewIntent. You can store data on shared pref and then retrieve that on onCreate – Jinesh Chhichhiya Aug 13 '18 at 13:13
  • @JineshChhichhiya I know I cannot use onNewIntent in this case (when the app is not running), but I should be able to add that code (method call) in my onCreate, and it should work when the app is closed. – Banana Aug 13 '18 at 13:18
  • post your manifest – David Wasser Aug 14 '18 at 16:18
  • @DavidWasser I posted the part related to my main activity, I can post more if you want, but my manifest has about 200 lines. – Banana Aug 14 '18 at 16:29
  • Please remove the special launch mode and see if that helps. Once you've done that, tapping the notification when your app is not running should launch the app and create a new instance of `MainActivity`. In `onCreate()` please add some debug logging to see if the `Intent` has any extras and output them. Tell me what you find. – David Wasser Aug 14 '18 at 17:40
  • @DavidWasser Tried it, but got the same results. I tried adding this condition at the beginning of my onCreate, but the app crashed (I added my log). There are quite a few methods I added to setup my main activity before I can call this method with location id, so in this case I guess all the necessary methods were not loaded. And when I put it at the end of my onCreate it never gets called. – Banana Aug 14 '18 at 19:41
  • If the condition is true at the beginning of `onCreate()` but not at the end, you are doing something in `onCreate()` that breaks this. Please post the complete code of `onCreate()` – David Wasser Aug 14 '18 at 20:54
  • And please add debug logging (as I suggested) to see if the `Intent` has any extras and output them. – David Wasser Aug 14 '18 at 20:54
  • It is also likely that you can't do what you want to do in `onCreate()` because your `Fragment` is not set up yet. You might need to just remember what you want to do (start location details) in `onCreate()` but delay actually doing it until `onResume()` or ater you've setup your `Fragment`s or whatever. – David Wasser Aug 14 '18 at 20:57
  • @DavidWasser Hey David, I believe that was the issue, my maps fragment was not set up. I found a hack - I put a delay for now, but I am not happy with it. I have added that piece of code in my question. This works with the delay, but I don't think it is the best solution (some devices might be very slow.) – Banana Aug 14 '18 at 21:00
  • 1
    Without going through all your code, you could try one of the following: 1) just post to the handler without the delay. This will run after `onCreate()` is finished. 2) Run the code in `onResume()` or post to the handler (without delay) in `onResume()`. – David Wasser Aug 14 '18 at 21:18
  • @DavidWasser Thanks for your replies David. They helped me find the issue. I have a main activity that is too complicated, and I will have to sit down one weekend and really do a thorough refactoring. The main issue is that I have a maps fragment that, based on a certain locations, sends lat and lon via listener to my other fragment, and it can sometimes be called twice (I overcomplicated that). The main thing is this - it works without a special launch mode, and the issue was with **fragments not being set up before I called the method.** If you post this answer I will accept it. Thanks again – Banana Aug 15 '18 at 17:35
  • Happy that I was able to help you solve your problem. I'm sure you've learned something from the experience. I've created an answer that summarizes that main points. – David Wasser Aug 15 '18 at 19:47
  • 1
    @DavidWasser You were really helpful, also your previous answers to similar questions helped :) – Banana Aug 15 '18 at 20:11

2 Answers2

1

I think you need this

if (getIntent().hasExtra("fromNotification")) {
    String locationId = getIntent().getStringExtra("locationId");
    CallFetchLocationInfoFromMapsFragment(locationId);
}

together with setting the singleTask launch mode for your activity in manifest

<activity android:name=".YourActivity"
        android:launchMode="singleTask">
    ...
</activity>
nsndvd
  • 770
  • 7
  • 21
  • @nsdvd I have put that if condition at the end of my onCreate, but it did not work. I will try it later today and let you know if it works, but I think I might have already tried singleTask mode. I will let you know once I test it again today. Thanks. – Banana Aug 14 '18 at 07:54
1

During a discussion in comments (above) you indicated that you were using launchMode="singleTask". This is a special launch mode and should not be necessary for you app. It usually creates more problems than it solves. Please remove the special launch mode and see if that helps. Once you've done that, tapping the notification when your app is not running should launch the app and create a new instance of MainActivity.

It is also likely that you can't do what you want to do in onCreate() because your Fragment is not set up yet. You might need to just remember what you want to do (start location details) in onCreate() but delay actually doing it until onResume() or ater you've setup your Fragments or whatever.

David Wasser
  • 85,616
  • 15
  • 182
  • 239