I have a problem with appcelerator, regarding to android background service.
The service starts, and when I press the home button, the service is still running, and also when I click the back button.
But when I remove my app from the recent apps list(pressing long home button) , the service stops.
This is my code for the call service:
var SECONDS = 6;
// every 10 minutes
var intent = Titanium.Android.createServiceIntent({
url : 'myservice.js'
});
intent.putExtra('interval', SECONDS * 1000);
intent.putExtra('message_to_echo', num);
//in millimeter
var service = Titanium.Android.createService(intent);
service.addEventListener('resume', function(e) {
// num++;
// alert(num);
});
service.start();
And this is the file service code:
var service = Titanium.Android.currentService;
var intent = service.intent;
var message = intent.getStringExtra("message_to_echo");
var intent = Ti.Android.createIntent({
flags : Ti.Android.FLAG_ACTIVITY_CLEAR_TOP | Ti.Android.FLAG_ACTIVITY_SINGLE_TOP,
// Substitute the correct classname for your application
className : 'com.mappa.angeli.MappaActivity',
});
intent.addCategory(Ti.Android.CATEGORY_LAUNCHER);
// 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({
// icon is passed as an Android resource ID -- see Ti.App.Android.R.
// icon: Ti.App.Android.R.drawable.my_icon,
contentTitle: 'Something Happened',
contentText : 'Click to return to the application.',
// tickerText: 'Our app made a notification!',
defaults:Titanium.Android.NotificationManager.DEFAULT_SOUND,
contentIntent: pending
});
// Send the notification.
Titanium.Android.NotificationManager.notify(0, notification);
How can I run the service continously, such as whats app or something like that?
Please help me, it's very important.
Thanks in advance!!!
First let me give you a little perspective about your questions so that you'll be able to understand the logic behind.
My Understanding about your Question and Conclusion
What you are asking for is not possible even in the native app, specifically if you are using the background services.
Why it is doing so?
Because the application is no longer in service anymore. The app is
forcefully removed from the background and hence the OS stops all its
services running in the background to clear the Memory.
What most of the default players do to play music is the use the foreground service and show a notification that the app is running and is processing something in the background (in this case playing the songs).
Options that you could look for
I would suggest you to add up 'push notification service' to your app rather than going in for 'background service'. You will get the notification even if your app is not in the background (same as it is in WhatsApp).
You could follow the below link to implement the GCM push services for android:
http://www.tidev.io/2013/12/20/using-gcm-for-android-push-notifications-with-acs/
Hope this helps.
Good Luck, Cheers
Related
I've been trying to implement Notifications in my app without effectively succeeding. The following are my observations:
Requirements:
After scheduling a Notification it has to be repeated on a monthly basis.
The notification will be inflated with some model object data, so the object needs to be passed to the notification builder.
How I tackled the problem so far:
I created a NotificationScheduler object that basically creates a PendingIntent for the notification to be fired.
object NotificationScheduler {
fun scheduleNotification(context: Context, model: Model, cal: Calendar) {
cal.set(Calendar.HOUR_OF_DAY, 11)
val notificationIntent = Intent(context, NotificationPublisher::class.java)
notificationIntent.putExtra("model", Utils.parcelToBytes(model))
val pendingIntent = PendingIntent.getBroadcast(context, 1, notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT)
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.set(AlarmManager.RTC_WAKEUP, cal.timeInMillis, pendingIntent)
}
}
Then I have a NotificationPublisher class that should show the notification and set the next one by calling NotificationScheduler again.
class NotificationPublisher: BroadcastReceiver() {
var model: Model? = null
override fun onReceive(context: Context?, intent: Intent?) {
val modelBytes = intent?.getByteArrayExtra("model")
if (modelBytes != null) {
model = Utils.bytesToParcel(modelBytes, Model.CREATOR)
}
val notificationBuilder = NotificationBuilder(context!!, model!!)
val notification = notificationBuilder.buildNotification()
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(1, notification)
//schedules the next notification
NotificationScheduler.scheduleNotification(context, model!!, Calendar.getInstance())
}
}
Where the NotificationBuilder class just takes the context and the model and builds the notification
class NotificationBuilder(val context: Context, val model: Model) {
fun buildNotification(): Notification {
generateNotificationChannel(context)
val builder = NotificationCompat.Builder(context, "ID")
.setSmallIcon(R.drawable.sub)
.setContentTitle(model.title)
.setPriority(NotificationCompat.PRIORITY_HIGH)
return builder.build()
}
private fun generateNotificationChannel(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = "Test"
val description = "description"
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel("ID", name, importance)
channel.description = description
val notificationManager = context.getSystemService(NotificationManager::class.java)
notificationManager?.createNotificationChannel(channel)
}
}
My problem with this approach:
It works if I purposely display the notification as soon as it is created, but I get a NullPointerException whenever the user, for example, reboots his device or kills the process. While debugging I found out that the data I store in the notificationIntent in NotificationScheduler, once the device is rebooted, isn't available anymore in the onReceive() of the NotificationPublisher and therefore will generate a NPE.
I think I am doing something conceptually wrong with this approach, and I'd like you to help me figure out how to achieve the above mentioned requirements.
Well.., I am not going in details of your programming problems and will not help you to find the error.. But i have read your question keenly neatly and got the problem and its solution too..!! It is a bit theoretical.. But as i can see your programming skills are good .. so you just needs the direction and concept clearance.
Here we go :
It works if I purposely display the notification as soon as it is created, but I get a NullPointerException whenever the user, for example, reboots his device or kills the process.
This is the main cause of the problem. Whenever system reboots or whenever system kills the app.
What causing it...?
Whenever system is rebooted "All the alarms set by AlarmManager are no more preserved and needs to be re-scheduled again by the application"
What is work around then...?
Maintain a table of scheduled alarms. As soon as system fires ON_BOOT_COMPLETE re-schedule all again.
Most of the devices in market not triggering my ON_BOOT_COMPLETE receiver..!!
Yes most of the devices now a days are keeping only major companies services like google, facebook, whatsapp, twitter, instagram and rest all others are discarded at boot time.
Why mobile manufacturers does this?
As it is a business and it also improves performance and speed of the overall device. And if they do not allow said companies services on reboot .., then no one will take such phones..!!
Any solution for this..?
Use firebase job dispatcher. Let Google play services handle your service.
I hope it helps you and correct me if i am wrong or if need any further clarification over the issue. Thanks
EDIT 2
I would suggest create a job schedular job via your mainactivity context and let it handle google play services which will start executing job on every reboot. That job should call a foreground service with a proper notification. And via that foreground service again call to an intentservice which will re-schedule all the alarms by reading from Sqlite table one by one.
Edit : 3
Even if you follow these all paradigms suggested here... There is another problem you will notice..
Google play not starting My foreground service on reboot..!!
Yes..., As Post Marshmallow and modified ROMs ( including stock android and mobile manufacturers modified OS like OxygenOs, FunTouchOs, ColorOs,.. This list never ends as well) As battery is optimised and no private apps processes can come in memory on reboot..!!
Now is there any way i can tackle this problem too...?
Yes .. Show user a bold and bright alert :
If You want to perform this app normally,
you must stop optimising battery for this
app from settings..!!
That what the ways we can tackle these many problems.. Yes i
understand it has no well documentation from google and no sample codes too, Still in case if you require any help regarding implementing any sub-point I will help you in implementing these all sub-points as well
Thanks you as it helped you
I'm an Android app developer, and is developing an instant message app. The app has a notification problem on Android Oreo system, which shows persistent notification "App is running in the background" and cannot be cleared, and it's OK on system before Android Oreo.
Screenshot: The phone shows persistent notification App is running in the background
I find some discussion, such as Nexus Help Forum about this question, but it doesn't work in my phone's settings.
I want to know how to hide this notification programmatically and the app also can receive message instantly because it's an instant message app.
Any help is very appreciated.
The app has a notification problem on Android Oreo system, which shows persistent notification "App is running in the background" and cannot be cleared, and it's OK on system before Android Oreo.
You used startForeground() with a minimum-importance Notification.
I want to know how to hide this notification programmatically
Use startForeground() with a Notification that has higher than minimum importance. Or, do not use startForeground().
I find some install message apps such as WeChat, Facebook doesn't have this problem on Android Oreo
They are not using foreground services, presumably. For example, they might be using Firebase Cloud Messaging (FCM).
Before we talk about how to get rid of it, however, let’s talk about why it’s there in the first place.
Basically, in previous versions of Android, there was no real way of knowing if an app was running in the background doing a bunch of stuff it’s not supposed to be doing. In most scenarios, these misbehaving apps would wreak havoc on the battery by keeping the system awake—these are called “wakelocks.” In laymen’s terms, it was keeping the system from sleeping. That’s bad.
With Oreo, Google is calling out developers that let their apps do this sort of thing with the new notification. Essentially, if an app is running in the background and chewing up battery life, this new notification will tell you.
NOTE: There are a few legitimate scenarios where an app will continuously run in the background, like the VPN service running. Often, however, apps are running in the background unjustifiably.
It’s worth noting, though, removing the notification does not solve the issue. Period. There’s a reason this notification exists, and getting rid of it will do nothing to solve the underlying issue. You’ll either need to change a setting within the app or uninstall it altogether.
As long as you understand that and still want to remove it, let’s do this thing.
Because this is a relatively crucial system setting, there’s no way within Oreo itself to remove it. That makes sense.
But like with most things, the developer community has found a way to remove it, and developer iboalali released an app to do just that. It’s actually just called “Hide ‘running in the background’ Notification,” which is about as straightforward as an app name could ever be. Go ahead and give it an install.
Without root, there is no way to actually prevent Android System from displaying the persistent “app is running in the background” notification in Android 8.0 Oreo. Looking at the source code for the ForegroundServiceController, its implementation, and the ForegroundServiceDialog doesn’t really reveal anything we can take advantage of. Programatically nothing have been found so far.
Here's a Blog post that can help you
First, you must have the NotificationListenerService implementation. Second, in this service (after onListenerConnected callback), check the active ongoing notifications with packageName called 'android'. And check this notification's title is your app name or text value is 'App is running in the background' and snooze it.
public class NLService extends NotificationListenerService {
#Override
public void onNotificationRemoved(StatusBarNotification sbn) {}
#Override
public void onListenerConnected() {
super.onListenerConnected();
checkOngoingNotification();
}
#Override
public void onNotificationPosted(StatusBarNotification sbn){
if(sbn.isOngoing()) {
checkOngoingNotification();
return;
}
}
private void checkOngoingNotification() {
StatusBarNotification[] activeNotifications = getActiveNotifications();
Log.i("NLService", "Active notifications size : " + activeNotifications.length);
for (StatusBarNotification statusBarNotification : activeNotifications) {
Log.i("NLService", "notification package : " + statusBarNotification.getPackageName());
Log.i("NLService", "notification id : " + statusBarNotification.getId());
Log.i("NLService", "notification key : " + statusBarNotification.getKey());
Log.i("NLService", "isOngoing : " + statusBarNotification.isOngoing());
Log.i("NLService", "isClearable : " + statusBarNotification.isClearable());
Log.i("NLService", "groupKey : " + statusBarNotification.getGroupKey());
Notification notification = statusBarNotification.getNotification();
CharSequence title = notification.extras.getCharSequence(Notification.EXTRA_TITLE);
CharSequence text = notification.extras.getCharSequence(Notification.EXTRA_TEXT);
if (title != null && text != null) {
Log.i("NLService", "title : " + title);
Log.i("NLService", "text : " + text);
if (statusBarNotification.getPackageName().equals("android") &&
(title.toString().contains("Your App Name") || text.toString().contains("App is running"))) {
long snoozLong = 60000L * 60L * 24L * 20L;
this.snoozeNotification(statusBarNotification.getKey(), snoozLong);
Log.i("NLService", "Snoozed notification : " + title);
}
}
}
}
It turns out startForeground() with channel's IMPORTANCE_MIN is not only one source of the notification. If you call startForeground() and give it notification without setSmallIcon() result will be the same.
https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/am/ServiceRecord.java#816
Also you could find in logs something like that:
ActivityManager: Attempted to start a foreground service (ComponentInfo{com.example.app/com.example.app.ExampleService}) with a broken notification (no icon: Notification(channel=channel_example pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x40 color=0x00000000 vis=PRIVATE))
I work in Ionic Application, where I need to open a specific page in the app when users tap on notification,
Code
push.on('notification', function(data) {
window.plugins.toast.showShortTop('You have received a new Application!');
window.location.hash = 'home/contactus';
});
Its work fine when an application start open first time. But the problem is that when application open and that time user get any notification application redirect to 'home/contactus' page every time.
Please Help.
Unfortunately, the push plugin raises the notification event when the notification is received and again when it's clicked.
Your best bet is to assign and track the ids of notifications sent to the app:
var receivedNotifications={};
push.on('notification', function(data) {
if(!receivedNotifications[id]){
receivedNotifications[id]=true;
return;
}
window.plugins.toast.showShortTop('You have received a new Application!');
window.location.hash = 'home/contactus';
});
Consider this scenario. I have an ionic / angular app and I am using the the ngcordova plugin for push notifications. Now let us say a push notification arrives when the app is in the background. User, views the notification in the App drawer and then clicks the notification for further information. I would like the user to navigate to a particular path using $location.path(). How can I tap into the notification click event?
Ok. Turns out that there is no need to tap into the notification click event.
You could check if the app is in foreground using:
notification.foreground
so, if
if(notification.foreground) {
//do something for the case where user is using the app.
$popup('A notification just arrived');
} else {
//do something for the case where user is not using the app.
$location.path('/tab/somepage');
}
I answered a similar question some days ago. Here's the link
What i did was when the notification arrives to the app i save some data from the playload to the localStorage and then in the resume event of the app i read this var from localStorage and change the state of the app and passing this var to the url i want.
When the app is in the background and receive the notification you can save the playload to the localStorage:
$rootScope.$on('$cordovaPush:notificationReceived', function(event, notification) {
if (ionic.Platform.isAndroid() && notification.event == 'message') {
var newsid=notification.payload.newsid;
if (typeof newsid != 'undefined') {
// save the newsid to read it later in the resume event
$window.localStorage.setItem('goafterpush',newsid);
}
}
});
And then in the resume event you can read the previous saved playload and use that data to change the state of the app and redirect to another route:
$ionicPlatform.on("resume",function(event){
// read the newsid saved previously when received the notification
var goafterpush=$window.localStorage.getItem('goafterpush');
if (goafterpush) {
$window.localStorage.removeItem('goafterpush');
$state.go('app.newsdetail',{id:goafterpush});
}
});
This can simply be done if you are using phonegap-plugin-push.
Refer the blog article from here.
push.on('notification', function(data) {
if(data.additionalData.foreground){
//Do stuff you want to do if app is running on foreground while push notification received
}else{
//Do stuff you want to do if the app is running on background while the push notification received
}
});
// This is not a duplicate. It's a follow-up question on this question and others on the same topic
I'm developing a PhoneGap app using Angular and Coffeescript, and I want it to open different views when different mobile notification are clicked (GCM and APN).
I've followed this explanation, and this question. I'm sending the name of the required view inside the message, and in the notificationHandler I'm extracting the name of the view and then switch to it.
However, it seems that when the notification is clicked, first the app is loaded, and only then does the notification handler is loaded - so the app is first opened on the default view, and only then changes to the required view. How do I fix it?
gcmNotificationsHandler.coffee:
switch e.event
when "registered"
...
when "message"
console.log("DEBUG: mobile notification: Foreground? [#{e.foreground}] Coldstart? [#{e.coldstart}] Background? [#{not(e.foreground or e.coldstart)}]")
notificationAction = parseNotificationParams(e.payload)
if notificationAction.actionRequired
if e.foreground
console.log("DEBUG: Recieved message on foreground, moving to #{JSON.stringify(notificationAction)}")
$state.go notificationAction.toState, notificationAction.stateParams
else # otherwise we were launched because the user touched a notification in the notification tray.
console.log("DEBUG: Recieved message on background, saving next state as: #{JSON.stringify(notificationAction)}")
LocalStorage.setObject('STATE.new_notification', notificationAction)
if (e.foreground)
{
// ECB message event come here when your app is in foreground(not in background)
}
else
{
if (e.coldstart)
{
// ECB message event come here when you touch notification from notification tray
}
else
{
// ECB message event here when your app is in background
}
}
So use e.coldstart and also use $location angular js directive(or any other if not using angular js) to redirect to page.
Please refer following link :
https://github.com/phonegap-build/PushPlugin#plugin_api