Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-14674] Android: avoid code re-execution when opening app from a local notification

GitHub Issuen/a
TypeBug
PriorityHigh
StatusClosed
ResolutionInvalid
Resolution Date2013-09-13T22:06:03.000+0000
Affected Version/sn/a
Fix Version/s2013 Sprint 17, 2013 Sprint 17 API, 2013 Sprint 19 API
ComponentsAndroid
Labelsn/a
ReporterDavide Cassenti
AssigneePing Wang
Created2013-07-26T14:17:10.000+0000
Updated2017-03-22T21:54:21.000+0000

Description

Problem description

When an application goes into background, and a local notification is shown, there is need to be able to click the notification and reopen the app without re-executing its app.js code. The use case is this: 1) the application has 2 features: download files and play music files 2) when playing a file, if the app goes in background, a local notification will show the current play status 3) while a file is downloading, a local notification will show the status 4) when the user clicks on the notification, a specific page is open (e.g. the details of the download if you click the download notification, or the music details if you click the playing notification) 5) as the app reopens from background, the user needs to be able to control the variables, e.g. the audio player, stop the download etc.

Code tested

The following code has been tested:
var tabgroup = Ti.UI.createTabGroup();

var win = Ti.UI.createWindow({
	backgroundColor : 'black',
	layout : 'vertical'
});

var btn = Ti.UI.createButton({
	title : 'Notify'
});
btn.addEventListener('click', function(e) {
	// Intent object to launch the application
	var intent = Ti.Android.createIntent({
		flags : Ti.Android.FLAG_UPDATE_CURRENT,
		className : 'com.appcelerator.davide.testapp.TestappActivity'
	});
	intent.addCategory(Ti.Android.CATEGORY_LAUNCHER);
	intent.putExtra(Ti.Android.EXTRA_TEXT, "notification");

	// Create a PendingIntent to tie together the Activity and Intent
	var pending = Titanium.Android.createPendingIntent({
		intent : intent,
		flags : Titanium.Android.FLAG_UPDATE_CURRENT
	});

	// Create the notification
	var notification = Titanium.Android.createNotification({
		contentTitle : 'Something Happened',
		contentText : 'Click to return to the application.',
		contentIntent : pending
	});
	// Send the notification.
	Titanium.Android.NotificationManager.notify(1, notification);
});

var btn2 = Ti.UI.createButton({
	title : 'Play sound'
});
btn2.addEventListener('click', function(e) {
	var music = Ti.Media.createAudioPlayer({
		url : "/somesong.mp3",
		autoplay : true,
		allowBackground : true
	});
	music.play();
});

win.add(btn);
win.add(btn2);

var tab = Ti.UI.createTab({
	window : win,
	title : 'Tab 1'
});

tabgroup.addTab(tab);
tabgroup.open();

tabgroup.addEventListener('open', function() {
	tabgroup.getActivity().addEventListener('resume', function(e) {
		//var intent = e.source.intent;
		var activity = Ti.Android.currentActivity;
		var intent = activity ? activity.intent : null;

		if (intent) {
			Ti.API.info("intent: " + JSON.stringify(intent));
			var notification = false;
			if (intent.hasExtra(Ti.Android.EXTRA_TEXT) && (intent.getStringExtra(Ti.Android.EXTRA_TEXT) == "notification")) {
				notification = true;
				intent.putExtra(Ti.Android.EXTRA_TEXT, "");
				// remove the EXTRA_TEXT once captured
			}

			if (notification == true) {
				// operations to be performed when the app is open from the notification
				alert('Coming from a notification');
			} else {
				alert('Just resumed the app');
			}
		}
	});
});
The problem here, is that the user comes back to the app and the code is re-executed; this means there is no more way to control the music player. If the player is outside the button callback, the audio is re-created and plays again when reopening the app.

Comments

  1. Ping Wang 2013-08-15

    [~dcassenti], the code you attached is not runnable. When I click the 'Notify' button, it crashes with the runtime error:
       E/TiExceptionHandler(12812): (main) [40,145835] ----- Titanium Javascript Runtime Error -----
       E/TiExceptionHandler(12812): (main) [0,145835] - In app.js:13,29
       E/TiExceptionHandler(12812): (main) [0,145835] - Message: Uncaught Error: Missing class for name: com.appcelerator.davide.apptest.ApptestActivity
       E/TiExceptionHandler(12812): (main) [0,145835] - Source:     var intent = Ti.Android.createIntent({
       
    Is ApptestActivity your main activity? What do you mean by 'cannot get the handle to the "music" element'? I tested the code. After clicking the 'Play music' and 'Notify' button, I backgrounded the app. And then I clicked the notification, the app was launched, the music continued playing and the alert "Coming from a notification" showed. Not sure what is the issue you described.
  2. Ping Wang 2013-08-16

    [~dcassenti], in you test case, you actually want to bring the tabgroup activity to front. But tabgroup activity which is 'org.appcelerator.titanium.TiActivity' is not the same as the launch activity which is 'com.appcelerator.davidecassenti.apptest.ApptestActivity'. So if you modify line 14 and 15 to
       flags : Ti.Android.FLAG_ACTIVITY_SINGLE_TOP
       className : 'org.appcelerator.titanium.TiActivity'
       
    and remove line 17, the test case will work as expected. By setting the flag as [FLAG_ACTIVITY_SINGLE_TOP](http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_SINGLE_TOP), it will not start a new instance of the tabgroup activity every time clicking the local notification but just bring the running activity to front. Therefore, even we go back to the app by clicking the notification we still see the alert "Just resumed the app". Please take a look at those [activity flags](http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_BROUGHT_TO_FRONT) and play it a little bit and you will find different flags give you different results. In addition, since the activities for all the heavyweight windows and tabgroups have the same java class name "org.appcelerator.titanium.TiActivity", if there are several HW windows / tabgroup, I don't think you can bring one specific window/tabgroup which is not running at the top of the stack to front. And even you can, I don't think that's a good idea because it may re-order the activities in the stack and may cause some unexpected behavior/errors (refer to [this doc](http://developer.android.com/guide/components/tasks-and-back-stack.html) for more details). Therefore, if you want to start a specific activity after clicking the notification, I suggest to use the [url](http://docs.appcelerator.com/titanium/latest/#!/api/Titanium.Android.Intent-property-url) property when you create an intent. Resolve the ticket as Invalid.
  3. Ping Wang 2013-08-19

    [~dcassenti], I already explained the alert message in my last comment.
  4. Ingo Muschenetz 2013-09-03

    Sounds like there are still some unanswered issues here.
  5. Ping Wang 2013-09-13

    Here are several posts discussing "What Exactly Happens When You Swipe An Android App From the Recent Apps List": http://www.howtogeek.com/169549/what-exactly-happens-when-you-swipe-an-android-app-from-the-recent-apps-list/ http://lifehacker.com/what-happens-when-you-remove-an-app-from-androids-mult-1179868228 Basically, swiping app away from the recent app list will kill any background or empty processes of the application but it won't stop the running services or remove the notifications from the status bar. In Davide's example, swiping the app away from the recent app list actually kills the root activity of the application, so the notification cannot launch the jsActivity successfully. The solution is to remove the notifications when the app is swiped away from the recent apps list. Filed a new ticket for that feature TIMOB-15180. Resolved this ticket.
  6. Ping Wang 2013-09-13

    [~dcassenti], I already submitted a PR https://github.com/appcelerator/titanium_mobile/pull/4687 for TIMOB-15180. Once that is merged, you can add a service in the app.js, eg.
       var intent = Titanium.Android.createServiceIntent( { url: 'service.js' } );
       Ti.Android.startService(intent);
       
    and in the service.js, cancel all the notifications inside the event listener for the "taskremoved" event, eg.
       var service = Titanium.Android.currentService;
       var intent = service.intent;
       
       service.addEventListener("taskremoved", function(){
       	Ti.Android.NotificationManager.cancelAll();
       	Ti.Android.stopService(intent);
       });
       
       Titanium.API.info("Hello World!  I am a Service.");
       
  7. Lee Morris 2017-03-22

    Closing ticket as invalid with reference to previous comments.

JSON Source