{ "id": "117643", "key": "TIMOB-14674", "fields": { "issuetype": { "id": "1", "description": "A problem which impairs or prevents the functions of the product.", "name": "Bug", "subtask": false }, "project": { "id": "10153", "key": "TIMOB", "name": "Titanium SDK/CLI", "projectCategory": { "id": "10100", "description": "Titanium and related SDKs used in application development", "name": "Client" } }, "fixVersions": [ { "id": "15597", "description": "2013 Sprint 17", "name": "2013 Sprint 17", "archived": true, "released": true, "releaseDate": "2013-08-23" }, { "id": "15599", "description": "2013 Sprint 17 API", "name": "2013 Sprint 17 API", "archived": true, "released": true, "releaseDate": "2013-08-23" }, { "id": "15693", "description": "2013 Sprint 19 API", "name": "2013 Sprint 19 API", "archived": true, "released": true, "releaseDate": "2013-09-20" } ], "resolution": { "id": "7", "description": "", "name": "Invalid" }, "resolutiondate": "2013-09-13T22:06:03.000+0000", "created": "2013-07-26T14:17:10.000+0000", "priority": { "name": "High", "id": "2" }, "labels": [], "versions": [], "issuelinks": [ { "id": "31863", "type": { "id": "10003", "name": "Relates", "inward": "relates to", "outward": "relates to" }, "outwardIssue": { "id": "119960", "key": "TIMOB-15180", "fields": { "summary": "Android: Implement an event \"taskremoved\" when Service.onTaskRemoved() is called", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "priority": { "name": "High", "id": "2" }, "issuetype": { "id": "2", "description": "A new feature of the product, which has yet to be developed.", "name": "New Feature", "subtask": false } } } } ], "assignee": { "name": "pwang", "key": "pwang", "displayName": "Ping Wang", "active": true, "timeZone": "America/Los_Angeles" }, "updated": "2017-03-22T21:54:21.000+0000", "status": { "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", "name": "Closed", "id": "6", "statusCategory": { "id": 3, "key": "done", "colorName": "green", "name": "Done" } }, "components": [ { "id": "10202", "name": "Android", "description": "Android Platform" } ], "description": "h2. Problem description\r\nWhen 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:\r\n\r\n1) the application has 2 features: download files and play music files\r\n2) when playing a file, if the app goes in background, a local notification will show the current play status\r\n3) while a file is downloading, a local notification will show the status\r\n4) 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)\r\n5) 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.\r\n\r\nh2. Code tested\r\nThe following code has been tested:\r\n\r\n{code:javascript}\r\nvar tabgroup = Ti.UI.createTabGroup();\r\n\r\nvar win = Ti.UI.createWindow({\r\n\tbackgroundColor : 'black',\r\n\tlayout : 'vertical'\r\n});\r\n\r\nvar btn = Ti.UI.createButton({\r\n\ttitle : 'Notify'\r\n});\r\nbtn.addEventListener('click', function(e) {\r\n\t// Intent object to launch the application\r\n\tvar intent = Ti.Android.createIntent({\r\n\t\tflags : Ti.Android.FLAG_UPDATE_CURRENT,\r\n\t\tclassName : 'com.appcelerator.davide.testapp.TestappActivity'\r\n\t});\r\n\tintent.addCategory(Ti.Android.CATEGORY_LAUNCHER);\r\n\tintent.putExtra(Ti.Android.EXTRA_TEXT, \"notification\");\r\n\r\n\t// Create a PendingIntent to tie together the Activity and Intent\r\n\tvar pending = Titanium.Android.createPendingIntent({\r\n\t\tintent : intent,\r\n\t\tflags : Titanium.Android.FLAG_UPDATE_CURRENT\r\n\t});\r\n\r\n\t// Create the notification\r\n\tvar notification = Titanium.Android.createNotification({\r\n\t\tcontentTitle : 'Something Happened',\r\n\t\tcontentText : 'Click to return to the application.',\r\n\t\tcontentIntent : pending\r\n\t});\r\n\t// Send the notification.\r\n\tTitanium.Android.NotificationManager.notify(1, notification);\r\n});\r\n\r\nvar btn2 = Ti.UI.createButton({\r\n\ttitle : 'Play sound'\r\n});\r\nbtn2.addEventListener('click', function(e) {\r\n\tvar music = Ti.Media.createAudioPlayer({\r\n\t\turl : \"/somesong.mp3\",\r\n\t\tautoplay : true,\r\n\t\tallowBackground : true\r\n\t});\r\n\tmusic.play();\r\n});\r\n\r\nwin.add(btn);\r\nwin.add(btn2);\r\n\r\nvar tab = Ti.UI.createTab({\r\n\twindow : win,\r\n\ttitle : 'Tab 1'\r\n});\r\n\r\ntabgroup.addTab(tab);\r\ntabgroup.open();\r\n\r\ntabgroup.addEventListener('open', function() {\r\n\ttabgroup.getActivity().addEventListener('resume', function(e) {\r\n\t\t//var intent = e.source.intent;\r\n\t\tvar activity = Ti.Android.currentActivity;\r\n\t\tvar intent = activity ? activity.intent : null;\r\n\r\n\t\tif (intent) {\r\n\t\t\tTi.API.info(\"intent: \" + JSON.stringify(intent));\r\n\t\t\tvar notification = false;\r\n\t\t\tif (intent.hasExtra(Ti.Android.EXTRA_TEXT) && (intent.getStringExtra(Ti.Android.EXTRA_TEXT) == \"notification\")) {\r\n\t\t\t\tnotification = true;\r\n\t\t\t\tintent.putExtra(Ti.Android.EXTRA_TEXT, \"\");\r\n\t\t\t\t// remove the EXTRA_TEXT once captured\r\n\t\t\t}\r\n\r\n\t\t\tif (notification == true) {\r\n\t\t\t\t// operations to be performed when the app is open from the notification\r\n\t\t\t\talert('Coming from a notification');\r\n\t\t\t} else {\r\n\t\t\t\talert('Just resumed the app');\r\n\t\t\t}\r\n\t\t}\r\n\t});\r\n});\r\n{code}\r\n\r\nThe 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.", "attachment": [], "flagged": false, "summary": "Android: avoid code re-execution when opening app from a local notification", "creator": { "name": "dcassenti", "key": "dcassenti", "displayName": "Davide Cassenti", "active": true, "timeZone": "Europe/Berlin" }, "subtasks": [], "reporter": { "name": "dcassenti", "key": "dcassenti", "displayName": "Davide Cassenti", "active": true, "timeZone": "Europe/Berlin" }, "environment": "Android 4.2\r\nSDK 3.1.x", "comment": { "comments": [ { "id": "266705", "author": { "name": "pwang", "key": "pwang", "displayName": "Ping Wang", "active": true, "timeZone": "America/Los_Angeles" }, "body": "[~dcassenti], the code you attached is not runnable. When I click the 'Notify' button, it crashes with the runtime error:\r\n{code}\r\nE/TiExceptionHandler(12812): (main) [40,145835] ----- Titanium Javascript Runtime Error -----\r\nE/TiExceptionHandler(12812): (main) [0,145835] - In app.js:13,29\r\nE/TiExceptionHandler(12812): (main) [0,145835] - Message: Uncaught Error: Missing class for name: com.appcelerator.davide.apptest.ApptestActivity\r\nE/TiExceptionHandler(12812): (main) [0,145835] - Source: var intent = Ti.Android.createIntent({\r\n{code}\r\nIs ApptestActivity your main activity? What do you mean by 'cannot get the handle to the \"music\" element'?\r\nI 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.", "updateAuthor": { "name": "pwang", "key": "pwang", "displayName": "Ping Wang", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2013-08-15T17:53:29.000+0000", "updated": "2013-08-15T20:06:13.000+0000" }, { "id": "266958", "author": { "name": "pwang", "key": "pwang", "displayName": "Ping Wang", "active": true, "timeZone": "America/Los_Angeles" }, "body": "[~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 \n{code}\nflags : Ti.Android.FLAG_ACTIVITY_SINGLE_TOP\nclassName : 'org.appcelerator.titanium.TiActivity'\n{code} and remove line 17, the test case will work as expected.\n\nBy 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\". \n\nPlease 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.\n\nIn 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.\n\nResolve the ticket as Invalid.", "updateAuthor": { "name": "pwang", "key": "pwang", "displayName": "Ping Wang", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2013-08-16T21:44:03.000+0000", "updated": "2013-08-16T21:44:03.000+0000" }, { "id": "267146", "author": { "name": "pwang", "key": "pwang", "displayName": "Ping Wang", "active": true, "timeZone": "America/Los_Angeles" }, "body": "[~dcassenti], I already explained the alert message in my last comment.", "updateAuthor": { "name": "pwang", "key": "pwang", "displayName": "Ping Wang", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2013-08-19T17:10:49.000+0000", "updated": "2013-08-19T17:10:49.000+0000" }, { "id": "269289", "author": { "name": "ingo", "key": "ingo", "displayName": "Ingo Muschenetz", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Sounds like there are still some unanswered issues here.", "updateAuthor": { "name": "ingo", "key": "ingo", "displayName": "Ingo Muschenetz", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2013-09-03T16:39:23.000+0000", "updated": "2013-09-03T16:39:23.000+0000" }, { "id": "271122", "author": { "name": "pwang", "key": "pwang", "displayName": "Ping Wang", "active": true, "timeZone": "America/Los_Angeles" }, "body": "Here are several posts discussing \"What Exactly Happens When You Swipe An Android App From the Recent Apps List\":\nhttp://www.howtogeek.com/169549/what-exactly-happens-when-you-swipe-an-android-app-from-the-recent-apps-list/\nhttp://lifehacker.com/what-happens-when-you-remove-an-app-from-androids-mult-1179868228\nBasically, 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.\nIn 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.\nThe 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.", "updateAuthor": { "name": "pwang", "key": "pwang", "displayName": "Ping Wang", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2013-09-13T22:05:06.000+0000", "updated": "2013-09-13T22:05:06.000+0000" }, { "id": "271135", "author": { "name": "pwang", "key": "pwang", "displayName": "Ping Wang", "active": true, "timeZone": "America/Los_Angeles" }, "body": "[~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.\r\n{code}\r\nvar intent = Titanium.Android.createServiceIntent( { url: 'service.js' } );\r\nTi.Android.startService(intent);\r\n{code}\r\nand in the service.js, cancel all the notifications inside the event listener for the \"taskremoved\" event, eg.\r\n{code}\r\nvar service = Titanium.Android.currentService;\r\nvar intent = service.intent;\r\n\r\nservice.addEventListener(\"taskremoved\", function(){\r\n\tTi.Android.NotificationManager.cancelAll();\r\n\tTi.Android.stopService(intent);\r\n});\r\n\r\nTitanium.API.info(\"Hello World! I am a Service.\");\r\n{code}", "updateAuthor": { "name": "pwang", "key": "pwang", "displayName": "Ping Wang", "active": true, "timeZone": "America/Los_Angeles" }, "created": "2013-09-13T22:47:35.000+0000", "updated": "2013-09-13T22:48:33.000+0000" }, { "id": "415059", "author": { "name": "lmorris", "key": "lmorris", "displayName": "Lee Morris", "active": false, "timeZone": "America/Los_Angeles" }, "body": "Closing ticket as invalid with reference to previous comments.", "updateAuthor": { "name": "lmorris", "key": "lmorris", "displayName": "Lee Morris", "active": false, "timeZone": "America/Los_Angeles" }, "created": "2017-03-22T21:54:21.000+0000", "updated": "2017-03-22T21:54:21.000+0000" } ], "maxResults": 22, "total": 22, "startAt": 0 } } }