Open Unity application using notification in statusbar

Hi,
I am trying to create Unity application that shows notification in statusbar. The application should be launched by tap on notification.

But I got the problem with implementation of such functionality. Sometime application is not launched. It looks like that:

  1. User executes application from launcher.
  2. User presses HOME button (onPause, onStope handlers of application activity are executed)
  3. User opens status bar and clicks on application notification.
  4. The application is not executed (there are some errors in the log, see below).
  5. Now user opens status bar second time and clicks on application notification again.
  6. The application is launched correctly.

The error (step 4) in the log looks like that:

02-07 11:14:02.425: I/ActivityManager(444): START {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x3400c000 Htcflg=0x1 cmp=com.example.u2aTest/.MyMainActivity bnds=[0,392][720,520] u=0} from pid -1
02-07 11:14:02.455: I/StatusBar(587): notificationClick intent:Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x3400c000 Htcflg=0x1 cmp=com.example.u2aTest/.MyMainActivity } intentFlag:1
02-07 11:14:02.465: I/Unity(24826): onDestroy
02-07 11:14:02.470: D/Unity(24826): onDetachedFromWindow
02-07 11:14:02.490: D/Process(24826): killProcess, pid=24826
02-07 11:14:02.495: D/Process(24826): dalvik.system.VMStack.getThreadStackTrace(Native Method)
02-07 11:14:02.495: D/Process(24826): java.lang.Thread.getStackTrace(Thread.java:599)
02-07 11:14:02.495: D/Process(24826): android.os.Process.killProcess(Process.java:944)
02-07 11:14:02.495: D/Process(24826): com.unity3d.player.UnityPlayer.kill(Unknown Source)
02-07 11:14:02.495: D/Process(24826): com.unity3d.player.UnityPlayer.quit(Unknown Source)
02-07 11:14:02.495: D/Process(24826): com.unity3d.player.UnityPlayerActivity.onDestroy(Unknown Source)
02-07 11:14:02.495: D/Process(24826): android.app.Activity.performDestroy(Activity.java:5255)
02-07 11:14:02.495: D/Process(24826): android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1135)
02-07 11:14:02.495: D/Process(24826): android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3761)
02-07 11:14:02.495: D/Process(24826): android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3811)
02-07 11:14:02.495: D/Process(24826): android.app.ActivityThread.access$1200(ActivityThread.java:151)
02-07 11:14:02.495: D/Process(24826): android.app.ActivityThread$H.handleMessage(ActivityThread.java:1384)
02-07 11:14:02.500: D/Process(24826): android.os.Handler.dispatchMessage(Handler.java:99)
02-07 11:14:02.500: D/Process(24826): android.os.Looper.loop(Looper.java:155)
02-07 11:14:02.500: D/Process(24826): android.app.ActivityThread.main(ActivityThread.java:5485)
02-07 11:14:02.500: D/Process(24826): java.lang.reflect.Method.invokeNative(Native Method)
02-07 11:14:02.500: D/Process(24826): java.lang.reflect.Method.invoke(Method.java:511)
02-07 11:14:02.500: D/Process(24826): com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1028)
02-07 11:14:02.500: D/Process(24826): com.android.internal.os.ZygoteInit.main(ZygoteInit.java:795)
02-07 11:14:02.500: D/Process(24826): dalvik.system.NativeStart.main(Native Method)
02-07 11:14:02.530: D/InputManager(444): setFocusedApplication Exception: java.lang.NullPointerException
02-07 11:14:02.530: I/WindowState(444): WIN DEATH: Window{41c7ae60 com.example.u2aTest/com.example.u2aTest.MyMainActivity paused=true}
02-07 11:14:02.530: I/ActivityManager(444): Recipient 24826
02-07 11:14:02.530: I/ActivityManager(444): Process com.example.u2aTest (pid 24826) has died.
02-07 11:14:02.530: W/ActivityManager(444): Force removing ActivityRecord{41a33e88 com.example.u2aTest/.MyMainActivity}: app died, no saved state

The log for correct behavior (step 6) looks differently:

02-07 11:15:11.870: I/ActivityManager(444): START {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x3400c000 Htcflg=0x1 cmp=com.example.u2aTest/.MyMainActivity bnds=[0,392][720,520] u=0} from pid -1
02-07 11:15:11.890: I/StatusBar(587): notificationClick intent:Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x3400c000 Htcflg=0x1 cmp=com.example.u2aTest/.MyMainActivity } intentFlag:1
02-07 11:15:11.915: I/ActivityManager(444): Start proc com.example.u2aTest for activity com.example.u2aTest/.MyMainActivity: pid=25141 uid=10034 gids={1015, 3003, 1028}
02-07 11:15:12.010: I/ActivityManager(444): Recipient 24972
02-07 11:15:12.185: D/PhoneStatusBar(587): disable: < expand icons alerts ticker* system_info back home recent clock >
02-07 11:15:12.195: W/dalvikvm(587): disableGcForExternalAlloc: false
02-07 11:15:16.140: I/Unity(25141): cpuarch.GetCpuFeatures() = 254
02-07 11:15:16.140: I/Unity(25141): cpuarch.GetTotalMemory() = 971
02-07 11:15:16.145: I/Unity(25141): gles_mode = 2 (integer)
02-07 11:15:16.145: I/Unity(25141): splash_mode = 0 (integer)
02-07 11:15:16.145: I/Unity(25141): hide_status_bar = True (bool)
02-07 11:15:16.145: I/Unity(25141): 32bit_display = False (bool)
02-07 11:15:16.145: I/Unity(25141): 24bit_depth = False (bool)
02-07 11:15:16.145: I/Unity(25141): default_aa = 0 (integer)
02-07 11:15:16.145: I/Unity(25141): useObb = False (bool)
02-07 11:15:16.145: I/Unity(25141): development_player = True (bool)
02-07 11:15:16.365: E/(25141): file /data/data/com.nvidia.NvCPLSvc/files/driverlist.txt: not found!
02-07 11:15:16.365: I/(25141): Attempting to load EGL implementation /system/lib//egl/libEGL_tegra_impl
02-07 11:15:16.380: I/(25141): Loaded EGL implementation /system/lib//egl/libEGL_tegra_impl
02-07 11:15:16.400: I/Unity(25141): onResume
02-07 11:15:16.400: I/Unity(25141): windowFocusChanged: true
02-07 11:15:16.425: D/Unity(25141): surfaces: attach glview
02-07 11:15:16.455: I/com.example.u2aTest(25141): MyMainActivity.onCreate
02-07 11:15:16.500: I/Unity(25141): onResume
02-07 11:15:16.500: I/com.example.u2aTest(25141): MyMainActivity.onResume

I have written to Unity support and got answer:
“this is not a unity issue. Try the forums for help. Some hints. Add android:launchMode=“singleTask” to the manifest. Fix your launch flags on the Intent.”

But actually I have tried to use all possible variants of launchMode with different combinations of Intent flags with no results - errors in logs are not changed. I have tried: no flags, FLAG_ACTIVITY_NEW_TASK, FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK, FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP. Intent has some (other flags), but I suspect they are not applicable in this case.

I have created test android application that uses android-plugin as a library project and calls exactly same code - everything works fine.

According to log the problem is here:

I/Unity(23208): onDestroy
D/Unity(23208): onDetachedFromWindow
com.unity3d.player.UnityPlayer.kill(Unknown Source)            
com.unity3d.player.UnityPlayer.quit(Unknown Source)            
com.unity3d.player.UnityPlayerActivity.onDestroy(Unknown Source)

WIN DEATH: Window{420aa7b8 com.example.u2aTest/com.example.u2aTest.MyMainActivity paused=true}
Process com.example.u2aTest (pid 10205) has died.

The process is died and new instance of application is not launched…

Has anybody any idea how to fix/workaround the problem?

P.s. I have created simple empty project, that could demonstrate the problem:
http://code.google.com/p/dvsrc/downloads/detail?name=status_bar_notification_launch.7z

This is manifest of test application:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.mobilityflow.w3d"
    android:versionCode="2"
    android:versionName="1.0">

    <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="15" />
	
 		<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.STORAGE" />
   	<uses-permission android:name="android.permission.INTERNET" /> 
		<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
	
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />	
		<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
		<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
	
    <application android:name="com.example.u2aTest.Kernel" android:icon="@drawable/app_icon" android:label="@string/app_name">
      	<activity android:name=".MyMainActivity" 
				android:label="@string/app_name" 
				android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen"
				android:launchMode="singleTop" 
		>
			<intent-filter>
      			<action android:name="android.intent.action.MAIN" />
      			<category android:name="android.intent.category.LAUNCHER" />
      		</intent-filter>
      	</activity>
>
    </application>
	
</manifest>

This is a code to create notification:

public static void showNotification(Context context) {
        final Intent result_intent = new Intent(context, MyMainActivity.class);
        result_intent.setAction(Intent.ACTION_MAIN);
        result_intent.addCategory(Intent.CATEGORY_LAUNCHER);
        result_intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        //result_intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        //result_intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);                
		
		TaskStackBuilder stack_builder = TaskStackBuilder.create(context);
		stack_builder.addParentStack(MyMainActivity.class);
		stack_builder.addNextIntent(result_intent);
		
		PendingIntent pending_intent = stack_builder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
		
		android.support.v4.app.NotificationCompat.Builder builder = new android.support.v4.app.NotificationCompat.Builder(context);

		Resources res = context.getResources();
		builder.setContentIntent(pending_intent)
			.setSmallIcon(R.drawable.ic_launcher)
			.setLargeIcon(BitmapFactory.decodeResource(res, R.drawable.ic_launcher))
		    .setTicker("test")
		    .setWhen(System.currentTimeMillis())
		    .setAutoCancel(false)
		    .setContentTitle("title")
		    .setContentInfo("cinfo")
		    .setContentText("ctext");
		Notification n = builder.build();
        n.flags = Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;   

		NotificationManager nm = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
		nm.notify(NOTIF_ID, n);		
	}

P.s.s. I have found description for the similar issue for xamarin application:
https://github.com/mono/MonoGame/issues/1126
The problem was “This was an infinite loop on the texture reloading that I was doing when the android activity resumed from the lock screen or prior-apps screen.”. But this solution is not applicable in my case…

Ok, saying that this is not a unity issue might be a bit of a stretch - since in process relaunching should imo be possible. We are working on support for that but that support is not what you are looking for.

Even if we ‘fix’ destroy() that means that your game will still be killed and relaunched, and I’m guessing this is not what you want. You probably only want to put the game in focus - in the same way as if you would have pressed the icon in the android launcher. I have never tried to implement a notification that brings an Activity to front my self. But this has to be where your issue is. I mean resuming a unity activity using the launcher works fine, right? I’m not sure if it’s the flags that are wrong, or something else done by the notification, but something clearly differs between your notification and the android app switcher/launcher.

Are you sure that your test activity doesn’t receive a destroy() call before it is resumed? Also, I would probably use launchMode=“singleTask” instead of singleTop - you don’t want to have multiple unity instances.

That’s right, resuming a unity activity using the launcher works without any problems. I agree, there is a unknown difference between launching activity through notification and through launcher. I haven’t found info about it neither in the documentation nor in forums. But you are right - this difference is not connected with unity (destroy is called in test non-unity application too). I will try to ask and clear this question on stackoverflow (Update: see http://stackoverflow.com/questions/15217463/launch-application-from-statusbar-without-re-creation)

Fortunately, I am not developing game, I am developing weather application. Users strongly want to view weather forecast in statusbar… And relaunch with killing and recreation application instance is workable option for me.

About calling destroy(). Yes, destroy() is called before resuming (for unknown reasons). I have added additional comments to code of test application:

public class MyMainActivity extends UnityPlayerActivity {
 @Override protected void onDestroy() {
 try {
    Kernel.logInfo("MyMainActivity.onDestroy1");
    super.onDestroy();
    Kernel.logInfo("MyMainActivity.onDestroy2");
 } catch (Exception ex) {
   Kernel.logInfo(ex.toString());
 }
 Kernel.logInfo("MyMainActivity.onDestroy3");
}

Result log is:

I/com.example.u2aTest(5321): MyMainActivity.onDestroy1
I/Unity(5321): onDestroy
D/Unity(5321): onDetachedFromWindow
D/Process(5321): killProcess, pid=5321
D/Process(5321): dalvik.system.VMStack.getThreadStackTrace(Native Method)
D/Process(5321): java.lang.Thread.getStackTrace(Thread.java:599)
D/Process(5321): android.os.Process.killProcess(Process.java:944)
D/Process(5321): com.unity3d.player.UnityPlayer.kill(Unknown Source)
D/Process(5321): com.unity3d.player.UnityPlayer.quit(Unknown Source)
D/Process(5321): com.unity3d.player.UnityPlayerActivity.onDestroy(Unknown Source)
D/Process(5321): com.example.u2aTest.MyMainActivity.onDestroy(MyMainActivity.java:36)
D/Process(5321): android.app.Activity.performDestroy(Activity.java:5255)
D/Process(5321): android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1135)
D/Process(5321): android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3761)
D/Process(5321): android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3811)
D/Process(5321): android.app.ActivityThread.access$1200(ActivityThread.java:151)
D/Process(5321): android.app.ActivityThread$H.handleMessage(ActivityThread.java:1384)
D/Process(5321): android.os.Handler.dispatchMessage(Handler.java:99)
D/Process(5321): android.os.Looper.loop(Looper.java:155)
D/Process(5321): android.app.ActivityThread.main(ActivityThread.java:5485)
D/Process(5321): java.lang.reflect.Method.invokeNative(Native Method)
D/Process(5321): java.lang.reflect.Method.invoke(Method.java:511)
D/Process(5321): com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1028)
D/Process(5321): com.android.internal.os.ZygoteInit.main(ZygoteInit.java:795)
D/Process(5321): dalvik.system.NativeStart.main(Native Method)
D/ViewRootImpl(444): @@@- disable SystemServer HW acceleration
I/WindowState(444): WIN DEATH: Window{41fda828 com.example.u2aTest/com.example.u2aTest.MyMainActivity paused=true}

There are no “onDestroy2” , “onDestroy3” messages or text of exception in the log. If I comment “super.onDestroy()” then application is relaunched without problems. But I suspect, commenting onDestroy is far from ideal solution :slight_smile:

P.s. launchMode =“singleTask” is used in real project.

Thanks a lot for your help. Hope, the problem with relaunching will be found.

Unfortunately we (untiy) don’t support activity re-creation within the same process in the current version. I would very much like to see this in a future version of unity, but this is not an easy problem to solve :wink: That said, it seems you found a solution to your problem on stack overflow.