6

I have a little Android application in which I specify my application directly and do some application-wide setup in the ApplicationSubclass' onCreate, but I am getting the following error (Note, I know about FLAG_ACTIVITY_NEW_TASK):

Caused by: android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity  context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
    at android.app.ContextImpl.startActivity(ContextImpl.java:644)
    at android.content.ContextWrapper.startActivity(ContextWrapper.java:258)
    at somePart.ofA.nameSpace.ApplicationSubclass.sendNotificationEmail(ApplicationSubclass.java:186)

I am calling this sendNotificationEmail when some exceptions are thrown and I catch them, so that the user of the app can send in a little e-mail with information on the exception so that I can more easily fix anything that might arise or guide them on fixing their issue.

Here is some of the relevant code:

manifest.xml:

<application android:label="@string/app_name" android:icon="@drawable/icon" android:name=".ApplicationSubclass">
// ... some stuff, including all of my app's activities
</application>

the ApplicationSubclass is defined as:

public class ApplicationSubclass extends Application {
   // Multiple methods, including an override of onCreate

  public void sendNotificationEmail(String emailBody) {
      Intent emailIntent = new Intent(Intent.ACTION_SEND);
      emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      emailIntent.setType("text/html");
      emailIntent.putExtra(Intent.EXTRA_EMAIL, notificationRecipients);
      emailIntent.putExtra(Intent.EXTRA_SUBJECT, "MyAppName Error");
      emailIntent.putExtra(Intent.EXTRA_TEXT, emailBody);
      try {
          startActivity(Intent.createChooser(emailIntent, "An error has occurred! Send an error report?"));
      } catch (ActivityNotFoundException e) {
          // If there is nothing that can send a text/html MIME type
          e.printStackTrace();
      }
   }
}

I'd like to know why this is happening. I read some documentation, I looked for some answers on StackOverflow and the Internet at large. It seems to me that I should be fine since I am setting FLAG_ACTIVITY_NEW_TASK, however that's clearly not the case!

Is it simply that I may not even attempt to do this from the Application's context? Must I be within a Activity in my application when I attempt this? It would be sort of annoying to need to re-engineer around this, and being able to send exception e-mails would be quite useful!

EDIT: Rich asked a good question, and that's why the heck would I want to be doing this in the first place? The answer is simply that there is data being loaded in the Application that could fail, and I'd like the user to be able to send some of that failure data when this occurs.

The reason this is in the Application and not the activity is that there are application state variables I don't want to lose every time someone rotates the device. When I found out about the fact that onPause and onCreate are called when you rotate (for Activies) I refactored this code out into the Application (which works great!). I had actually tried the solution to send these to onConfigurationChanged and Override that method in my activity, but that's messy and for whatever reason despite putting android:configChanges="keyboardHidden|orientation" in my manifest it wasn't correctly working for me. I'm sure I could pursue this option, but I'd rather leave things as they are (it's cleaner) and get the ability for someone to e-mail!

Kevek
  • 2,534
  • 5
  • 18
  • 29
  • the question is, why do you want to fire an intent like this outside the scope of an activity? – Rich Oct 12 '11 at 20:42
  • @Rich because there are various variables I am loading within the Application on startup. I want these to stick around, and I don't want to lose them every time someone rotates the device. When I found out about the fact that onPause and onCreate are called when you rotate (for Activies) I refactored this code out into the Application (which works great!) but there's some loading that could result in an error and I'd love to be able to have the user send a e-mail of the stack trace should this occur! – Kevek Oct 12 '11 at 20:45
  • If you have vars in the Application class, you can retrieve the Application class from within any Activity with getApplicationContext. Since you are sub-classing it you will need to cast the return value of that method. You can use this as a repository for stuff you don't want to lose when the activity goes out of scope. – Rich Oct 12 '11 at 20:53
  • @Rich yes, that's exactly what I'm doing. And it's working happily. The problem is what happens if whatever method is loading data for those variables throws an exception *in* *the* *ApplicationSubclass*, and I'd like to capture that failure information and prompt a user to send that off to me so that I can better diagnose what went wrong, remotely? -- In this case I'd need to be able to launch the Activity with the emailIntent from within the ApplicationSubclass – Kevek Oct 12 '11 at 20:59
  • Two possible ideas: 1) Send the required data in a broadcast intent. Derive your activities from a common base like you do your Application and reg/unreg the broadcast receiver in the base class and also have the launch intent logic in the base class (that way you're always in the scope of the currently active Activity). 2) Save the information to a persistent storage place, and the next time an Activity onCreates itself (again requiring Activity-derived base class), launch the intent with data loaded from persistent storage. I prefer option 1, but both would work. – Rich Oct 13 '11 at 00:02
  • Option 3...instead of emailing it, post it to a web service behind the user's back in a background thread...doesn't require any interaction or knowledge on the user's part – Rich Oct 13 '11 at 00:04
  • @Rich while those are all good options, there are reasons I would like to avoid each. **1)** I would like to be able to capture application-level exceptions, as those are the most difficult to diagnose remotely. I'm not *entirely* *sure* this is necessary, so this option may work. I'll look into it! **2)** This is not data that can or should be saved in a persistent place, it may involve sensitive information I cannot save. **3)** The connection to web server requires authentication, if that fails I'd like to know. E-mail is the better solution for this case! – Kevek Oct 13 '11 at 13:37
  • By persistent, I meant beyond the Activity scope and not necessarily to a permanent place like the device or SD card. In-memory persistence such as a crash info wrapper class owned by the Application subclass is an example. It doesn't persist beyond the life cycle of the app and is not in a place where other apps or rooted users can get to it. I personally use a ServiceLocator to hold static references to my singletons like that, but you can just make it a member of the Application instance. – Rich Oct 13 '11 at 13:48

2 Answers2

17

Ah! I figured out what I did, it's quite simple! I was setting the FLAG_ACTIVITY_NEW_TASK on the wrong intent! Silly me, I missed that Intent.createChooser(...,...) will return a new Intent, so you must set the flag on the chooser Intent rather than on the ACTION_SEND Intent.

Not all that confusing when you think about it, and I can't believe I overlooked that!

So if anyone ever does what I did, here you go:

public void sendNotificationEmail(String emailBody) {
        Intent emailIntent = new Intent(Intent.ACTION_SEND);
        emailIntent.setType("text/html");
        emailIntent.putExtra(Intent.EXTRA_EMAIL, notificationRecipients);
        emailIntent.putExtra(Intent.EXTRA_SUBJECT, "MyAppName Error");
        emailIntent.putExtra(Intent.EXTRA_TEXT, emailBody);
        Intent emailChooser = Intent.createChooser(emailIntent, "An error has occurred! Send an error report?");
        emailChooser.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        try {
            startActivity(emailChooser);
        } catch (ActivityNotFoundException e) {
            // If there is nothing that can send a text/html MIME type
            e.printStackTrace();
        }
    }
Kevek
  • 2,534
  • 5
  • 18
  • 29
  • Does launching this activity (`emailChooser`) overrides the activity registered in your manifest listening to the intent filter action `MAIN`, category `LAUNCHER`??? thanks! – Throoze Jun 26 '14 at 16:52
6

I have launch an activity(to relogin the user when loss the session) from my class which subclass from Application as follow:

public boolean relogin(Activity act) {      
    Intent intent = new Intent(act,ActivityLogin.class);    
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);     
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
    startActivity(intent);              
    act.finish();// here i finish my current activity
}
bplpu
  • 464
  • 4
  • 21
Dayerman
  • 3,973
  • 6
  • 38
  • 54
  • I added the additional flag that you have, but it was to no avail. It didn't fix the issue I'm seeing. (Though I don't understand why, as I'm led to believe that having the FLAG_ACTIVITY_NEW_TASK set should allow me to launch an Activity from anywhere...) – Kevek Oct 12 '11 at 22:25
  • 2
    Shouldn't this be setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TOP) ? – tf.alves Dec 02 '14 at 13:05
  • I had to finish my current activity using act.finish(); or my app crashed with following error. java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState – Ajith M A Sep 20 '18 at 11:29