title: Properly start Activity from Notification regardless of app state
body: <p>I have an app with a splash screen Activity, followed by a main Activity. The splash screen loads stuff (database, etc.) before starting the main Activity. From this main Activity the user can navigate to multiple other child Activities and back. Some of the child Activities are started using <code>startActivityForResult()</code>, others just <code>startActivity()</code>.</p>
  
  <p>The Activity hierarchy are as depicted below.  </p>
  
  <pre><code>|                    Child A (startActivityForResult)
  |                   /
  |--&gt; Splash --&gt; Main -- Child B (startActivityForResult)
  |      ^            \
  |      |             Child C (startActivity)
  |       \
  |        This Activity is currently skipped if a Notification is started
  |        while the app is not running or in the background.
  </code></pre>
  
  <p>I need to achieve the following behavior <strong>when clicking a Notification</strong>:</p>
  
  <ol>
  <li><strong>The state in the Activity must be maintained</strong>, since the user has selected some recipes to create a shopping list. If a new Activity is started, I believe the state will be lost.</li>
  <li>If the app is in the Main Activity, bring that to the front and let me know in code that I arrived from a Notification.</li>
  <li>If the app is in a child Activity started with <code>startActivityForResult()</code>, I need to add data to an Intent before going back to the Main Activity so that it can catch the result properly.</li>
  <li>If the app is in a child Activity started with <code>startActivity()</code> I just need to go back since there is nothing else to do (this currently works).</li>
  <li>If the app is not in the background, nor the foreground (i.e. it is <em>not</em> running) I must start the Main Activity and also know that I arrived from a Notification, so that I can set up things that are not set up yet, since the Splash Activity is skipped in this case in my current setup.</li>
  </ol>
  
  <p>I have tried lots of various suggestions here on SO and elsewhere, but I have not been able to successfully get the behavior described above. I have also tried reading the <a href="http://developer.android.com/guide/topics/ui/notifiers/notifications.html">documentation</a> without becoming a lot wiser, just a little. My current situation for the cases above when clicking my Notification is:</p>
  
  <ol>
  <li>I arrive in the Main Activity in <code>onNewIntent()</code>. I do not arrive here if the app is not running (or in the background). This seems to be expected and desired behavior.</li>
  <li>I am not able to catch that I am coming from a Notification in any child Activities, thus I am not able to properly call <code>setResult()</code> in those Activities. <strong>How should I do this?</strong></li>
  <li>This currently works, since the Notification just closes the child Activity, which is ok.</li>
  <li>I am able to get the Notification Intent in <code>onCreate()</code> by using <code>getIntent()</code> and <code>Intent.getBooleanExtra()</code> with a boolean set in the Notification. I should thus be able to make it work, but I am not sure that this is the best way. <strong>What is the preferred way of doing this?</strong></li>
  </ol>
  
  <h2>Current code</h2>
  
  <p><strong>Creating Notification:</strong></p>
  
  <p>The Notification is created when an HTTP request inside a Service returns some data.</p>
  
  <pre><code>NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
          .setSmallIcon(getNotificationIcon())
          .setAutoCancel(true)
          .setColor(ContextCompat.getColor(context, R.color.my_brown))
          .setContentTitle(getNotificationTitle(newRecipeNames))
          .setContentText(getContentText(newRecipeNames))
          .setStyle(new NotificationCompat.BigTextStyle().bigText("foo"));
  
  Intent notifyIntent = new Intent(context, MainActivity.class);
  notifyIntent.setAction(Intent.ACTION_MAIN);
  notifyIntent.addCategory(Intent.CATEGORY_LAUNCHER);
  
  notifyIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
  
  /* Add a thing to let MainActivity know that we came from a Notification. */
  notifyIntent.putExtra("intent_bool", true);
  
  PendingIntent notifyPendingIntent = PendingIntent.getActivity(context, 0, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
  builder.setContentIntent(notifyPendingIntent);
  
  NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
  notificationManager.notify(111, builder.build());
  </code></pre>
  
  <p><strong>MainActivity.java:</strong></p>
  
  <pre><code>@Override
  protected void onCreate(Bundle savedInstanceState)
  {
      Intent intent = getIntent();
      if (intent.getBooleanExtra("intent_bool", false))
      {
          // We arrive here if the app was not running, as described in point 4 above.
      }
  
      ...
  }
  
  @Override
  protected void onActivityResult(int requestCode, int resultCode, Intent data)
  {
      switch (requestCode)
      {
          case CHILD_A:
              // Intent data is null here when starting from Notification. We will thus crash and burn if using it. Normally data has values when closing CHILD_A properly.
              // This is bullet point 2 above.
              break;
  
          case CHILD_B:
              // Same as CHILD_A
              break;
      }
  
      ...
  }
  
  @Override
  protected void onNewIntent(Intent intent)
  {
      super.onNewIntent(intent);
      boolean arrivedFromNotification = intent.getBooleanExtra("intent_bool", false);
      // arrivedFromNotification is true, but onNewIntent is only called if the app is already running.
      // This is bullet point 1 above.
      // Do stuff with Intent.
      ... 
  }
  </code></pre>
  
  <p><strong>Inside a child Activity started with <code>startActivityForResult()</code>:</strong></p>
  
  <pre><code>@Override
  protected void onNewIntent(Intent intent)
  {
      // This point is never reached when opening a Notification while in the child Activity.
      super.onNewIntent(intent);
  }
  
  @Override
  public void onBackPressed()
  {
      // This point is never reached when opening a Notification while in the child Activity.
  
      Intent resultIntent = getResultIntent();
      setResult(Activity.RESULT_OK, resultIntent);
  
      // NOTE! super.onBackPressed() *must* be called after setResult().
      super.onBackPressed();
      this.finish();
  }
  
  private Intent getResultIntent()
  {
      int recipeCount = getRecipeCount();
      Recipe recipe   = getRecipe();
  
      Intent recipeIntent = new Intent();
      recipeIntent.putExtra(INTENT_RECIPE_COUNT, recipeCount);
      recipeIntent.putExtra(INTENT_RECIPE, recipe);
  
      return recipeIntent;
  }
  </code></pre>
  
  <p><strong>AndroidManifest.xml:</strong></p>
  
  <pre><code>&lt;application
      android:allowBackup="true"
      android:icon="@mipmap/my_launcher_icon"
      android:label="@string/my_app_name"
      android:theme="@style/MyTheme"
      android:name="com.mycompany.myapp.MyApplication" &gt;
  
      &lt;activity
          android:name="com.mycompany.myapp.activities.SplashActivity"
          android:screenOrientation="portrait" &gt;
          &lt;intent-filter&gt;
              &lt;action android:name="android.intent.action.MAIN" /&gt;
              &lt;category android:name="android.intent.category.LAUNCHER" /&gt;
          &lt;/intent-filter&gt;
      &lt;/activity&gt;
  
      &lt;activity
          android:name="com.mycompany.myapp.activities.MainActivity"
          android:label="@string/my_app_name"
          android:screenOrientation="portrait"
          android:windowSoftInputMode="adjustPan" &gt;
      &lt;/activity&gt;
  
      &lt;activity
          android:name="com.mycompany.myapp.activities.ChildActivityA"
          android:label="@string/foo"
          android:parentActivityName="com.mycompany.myapp.activities.MainActivity"
          android:screenOrientation="portrait"
          android:windowSoftInputMode="adjustPan" &gt;
          &lt;meta-data
              android:name="android.support.PARENT_ACTIVITY"
              android:value="com.mycompany.myapp.activities.MainActivity" &gt;
          &lt;/meta-data&gt;
      &lt;/activity&gt;
  
      &lt;activity
          android:name="com.mycompany.myapp.activities.ChildActivityB"
          android:label="@string/foo"
          android:parentActivityName="com.mycompany.myapp.activities.MainActivity"
          android:screenOrientation="portrait" &gt;
          &lt;meta-data
              android:name="android.support.PARENT_ACTIVITY"
              android:value="com.mycompany.myapp.activities.MainActivity" &gt;
          &lt;/meta-data&gt;
      &lt;/activity&gt;
  
      ...
  &lt;/manifest&gt;
  </code></pre>