I want to create an independent service of my main activity. This service is started at boot my phone and retrieves information via webservice every x minutes.
What are the best practices to do this?
Launch a service with BOOT_COMPLETED action?
There he other actions to launch a service without launching the main activity?
Another design or best practice?
I want a service with the same behavior as Facebook for example. This service is active all the time and displays a notification when you receive a message. If you click on the notification, it opens the Facebook application. But that kills the application, served remains active for receiving new messages.
My first test kills my service when I want to kill my main activity.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="fr.mrbmx"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="15" android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<!-- The following two permissions are not required to use
Google Maps Android API v2, but are recommended. -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="fr.mr.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name="fr.mr.service.MyReceiver"
android:enabled="true"
android:exported="false"
android:label="OnBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<service
android:name="fr.mr.service.LocalService"
android:enabled="true"
android:exported="false"
android:label="LocalService" />
<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="#string/mapKey"/>
<meta-data android:name="com.google.android.gms.version" android:value="#integer/google_play_services_version" />
</application>
public class MyReceiver extends BroadcastReceiver {
private static final String TAG = MyReceiver.class.getName();
#Override
public void onReceive( Context ctx, Intent i ) {
Log.d( TAG, "MyReceiver.onReceive : " + i.getAction() );
ctx.startService(new Intent().setComponent(new ComponentName(
ctx.getPackageName(), LocalService.class.getName())));
}
}
public class LocalService extends Service{
private static final String TAG = LocalService.class.getName();
private NotificationManager mNM;
// Unique Identification Number for the Notification.
// We use it on Notification start, and to cancel it.
private int NOTIFICATION = 1332;
private Timer timer ;
private int mId;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
Log.i(TAG, "onCreate");
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
timer = new Timer();
Notification note = new Notification( 0, null, System.currentTimeMillis() );
note.flags |= Notification.FLAG_NO_CLEAR;
startForeground( 0, note );
/*
Notification.Builder mBuilder =
new Notification.Builder(this)
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setContentTitle("test title")
.setContentText("test content")
.setAutoCancel(true)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
startForeground(1, mBuilder.getNotification());*/
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "Received start id " + startId + ": " + intent);
mId = startId;
new Thread(new Runnable() {
public void run() {
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
showNotification();
}
}, 0, 60000);
}
}).start();
return START_STICKY;
}
#Override
public void onDestroy() {
Log.i(TAG, "onDestroy");
mNM.cancel(NOTIFICATION);
}
/**
* Show a notification while this service is running.
*/
private void showNotification() {
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setContentTitle(getString(fr.mrbmx.R.string.notification_title))
.setContentText(getString(fr.mrbmx.R.string.notification_text))
.setAutoCancel(true)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
// Creates an explicit intent for an Activity in your app
Intent resultIntent = new Intent(this, MainActivity.class);
// The stack builder object will contain an artificial back stack for the
// started Activity.
// This ensures that navigating backward from the Activity leads out of
// your application to the Home screen.
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// Adds the back stack for the Intent (but not the Intent itself)
stackBuilder.addParentStack(MainActivity.class);
// Adds the Intent that starts the Activity to the top of the stack
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// mId allows you to update the notification later on.
mNotificationManager.notify(mId, mBuilder.build());
}
}
Your service does not need to be active all the time to receive messages. Facebook, along with just about every other push based system, use Google Cloud Messaging (GCM) to wake up your device and send a message to your application from the remote server.
Of course, if you only need to periodically check (rather than be pushed information in near real time), then you can schedule an alarm to start your service every X minutes or build a Sync Adapter - an Android component specifically built to periodically load data from a remote server.
Note that many applications combine the two approaches and run a sync adapter in response to a GCM push.
I want to create an independent service of my main activity. This
service is started at boot my phone and retrieves information via
webservice every x minutes.
You can certainly do this by registering a BroadcastReceiver on your AndroidManifest.xml that responds to BOOT_COMPLETED and launch the Service from it. Your app will need to be ran at least once for BOOT_COMPLETED to be delivered (before API 11 it wasn't the case). Also, consider the implications of pulling data from a server too often (i.e. battery, etc).
I want a service with the same behavior as facebook for example. This
service is active all the time and displays a notification when you
receive a message. If you click on the nitification, it opens the
facebook application. But that kills the application, served remains
active for receiving new messages.
At least for their chat application, Facebook uses MQTT, which is a M2M publish/subscribe asynchronous mechanism. They do not pull data from the server every X minutes. They just listen to incoming data when it is broadcasted. If you want to use MQTT there is an open source project called Eclipse Paho that you might be interested in.
Related
I'm building a simple reminder application that will remind you of your events.
I've just used "Alarm Manager" with "Broadcast Receiver " to trigger my service at a certain time in the future. The purpose of the service is to vibrate the device when the notification arrives. Notification is working fine but, the Service isn't working as expected. I know that I can simply put the code for vibration inside the "Broadcast Receiver" but NEED TO KNOW ABOUT THE SERVICE. Don't know what's wrong with my code help me out guys, THANKS in advance.
Broadcast Receiver as follows:
public class NotificationSetter extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("AlarmTriggered", "Alarm is triggered to start background service");
Intent serviceVibes = new Intent(context,BackgroundService.class);
context.startService(serviceVibes);
Toast.makeText(context, String.valueOf(Build.VERSION.SDK_INT), Toast.LENGTH_SHORT).show();
//creating the Notification
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "Notify");
builder.setSmallIcon(R.drawable.reminder);
builder.setContentTitle("Reminding of your event");
builder.setContentText("Time to play Cricket");
builder.setAutoCancel(true);
NotificationManagerCompat compat = NotificationManagerCompat.from(context);
compat.notify(3000, builder.build());
}
}
My service class:
public class BackgroundService extends Service {
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("ServiceStarted", "Service is started using NotificationSetter");
Vibrator vibes = (Vibrator) getSystemService(VIBRATOR_SERVICE);
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
vibes.vibrate(VibrationEffect.createOneShot(2000,200));
}else {
vibes.vibrate(2000);
}
Toast.makeText(getApplicationContext(), String.valueOf(Build.VERSION.SDK_INT), Toast.LENGTH_SHORT).show();
return START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
The Manifest file:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.remindme">
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:name="com.example.remindme.MyContext"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".StopAlarm"></activity>
<activity android:name=".GetEvent" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="com.example.remindme.BackgroundService"
android:enabled="true"
android:exported="true">
</service>
<receiver android:name=".NotificationSetter"
android:enabled="true"
android:exported="true"/>
</application>
</manifest>
According to documentation, since Android 8.0 afterwards, an app cannot start a foreground service unless in a few conditions:
https://developer.android.com/about/versions/oreo/background
Check if your service is triggered when you have your app in foreground, meaning that your app has a visible Activity to user. If it works in this condition, then it means your problem lies in background execution limits introduced in Android 8.0. So to make your service work, you can start it as a foreground service by calling this line of code:
ContextCompat.startForegroundService(context, new Intent(context, YourBackgroundService.class));
A foreground service needs a notification to be shown at status bar. You can either grab an instance of your alarm notification and use it as the foreground service notification or create a new notification.
I want to create a android app to track real time location with a android service. But when i kill the app from recent app list, Service also killed in android 6 (Huawei y3 2017). But same code works on android 8. Please help me to solve my problem. Please find below is my code.
I added START_STICKY and START_REDELIVER_INTENT flags in onStartCommand method.
I have tried to restart the service in onTaskRemoved method.
Tried to add android:process attribute to
Manifest.xml
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".Login" />
<activity android:name=".Home" />
<service
android:name=".TrackingService"
android:enabled="true"
android:exported="true" />
</application>
This is how start my service in Home activity
Intent intent = new Intent(Home.this, TrackingService.class);
startService(intent);
TrackingService.java service file
#Override
public void onCreate(){
buildNotification();
requestLocationUpdates();
Toast.makeText(getBaseContext(), "Service Started", Toast.LENGTH_SHORT).show();
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId){
return START_STICKY;
}
private void buildNotification() {
Random random = new Random();
int m = random.nextInt(9999 - 1000) + 1000;
String stop = "stop";
registerReceiver(stopReceiver, new IntentFilter(stop));
PendingIntent broadcastIntent = PendingIntent.getBroadcast(this, 0, new Intent(stop), PendingIntent.FLAG_UPDATE_CURRENT);
Notification.Builder builder = new Notification.Builder(this)
.setContentTitle(getString(R.string.app_name))
.setContentText(getString(R.string.tracking_enabled_notif))
.setOngoing(true)
.setContentIntent(broadcastIntent);
startForeground(m, builder.build());
}
protected BroadcastReceiver stopReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
unregisterReceiver(stopReceiver);
stopSelf();
}
};
private void requestLocationUpdates() {
LocationRequest request = new LocationRequest();
request.setInterval(15000);
request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
FusedLocationProviderClient client = LocationServices.getFusedLocationProviderClient(this);
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
final DatabaseReference ref = FirebaseDatabase.getInstance().getReference("connected/"+user.getUid());
int permission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION);
if (permission == PackageManager.PERMISSION_GRANTED) {
client.requestLocationUpdates(request, new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
android.location.Location location =
locationResult.getLastLocation();
String lat = Double.toString(location.getLatitude());
String lng = Double.toString(location.getLongitude());
Toast.makeText(getBaseContext(), "Lat "+lat, Toast.LENGTH_SHORT).show();
if (location != null) {
ref.setValue(locationObj);
}
enter code here}
}, null);
}
}
The fact your service isn't getting killed on Android 8 sounds like a matter of luck. Probably your Android 8 phone has enough resources and keeps the service alive, but it will be killed sooner or later.
To achieve your goal you need to run your service in foreground. Check out official documentation:
You should only use a foreground service when your app needs to perform a task that is noticeable by the user even when they're not directly interacting with the app. For this reason, foreground services must show a status bar notification with a priority of PRIORITY_LOW or higher, which helps ensure that the user is aware of what your app is doing. If the action is of low enough importance that you want to use a minimum-priority notification, you probably shouldn't be using a service; instead, consider using a scheduled job.
Every app that runs a service places an additional load on the system, consuming system resources. If an app tries to hide its services by using a low-priority notification, this can impair the performance of the app the user is actively interacting with. For this reason, if an app tries to run a service with a minimum-priority notification, the system calls out the app's behavior in the notification drawer's bottom section.
Try to register your broadcast receiver in your main activity along with the intent actions that you want your receiver to do like this
private void RegisteringBroadcastReciever()
{
broadcastReceiver = new XYZIntentReceiver();
var filter = new IntentFilter();
filter.AddAction("hearing.arc.intent.action.action1");
filter.AddAction("hearing.arc.intent.action.action2");
filter.AddAction("hearing.arc.intent.action.action3");
filter.AddAction("hearing.arc.intent.action.action4");
AppInstance.ApplicationContext.RegisterReceiver(broadcastReceiver, filter);
}
Even i had a similar kind of issue. But i had that issue on Oreo devices as well. But this fix has resolved my issue. And Android asks to unregister this receiver in onDestroy() method. But don't do that. It might kill the service again.
Hope this helps.
Post Android 7, my alarm receiver is not receiving the broadcast receiver if the app is killed by swiping it off from the recent tasks list.
Following is the piece from my AlarmReceiver, which extends BroadCastReceiver.
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d(Constants.TAG, "event received");
setContext(context);
/*
*** My logic here ***
*/
Log.d(Constants.TAG, "alarm received");
}
}
The place I am setting the alarm and registering the AlarmReceiver class for the alarm.
public class UnityNotificationManager
{
public static void SetNotification(int id, long delayMs, String title, String message, String ticker, int sound, int vibrate,
int lights, String largeIconResource, String smallIconResource, int bgColor, String bundle, String dataString)
{
Context currentActivity = UnityPlayer.currentActivity;
AlarmManager am = (AlarmManager)currentActivity.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(Constants.ALARM_RECEIVER);
intent.setClass(currentActivity, AlarmReceiver.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
intent.putExtra("ticker", ticker);
intent.putExtra("title", title);
intent.putExtra("message", message);
intent.putExtra("id", id);
intent.putExtra("color", bgColor);
intent.putExtra("sound", sound == 1);
intent.putExtra("vibrate", vibrate == 1);
intent.putExtra("lights", lights == 1);
intent.putExtra("l_icon", largeIconResource);
intent.putExtra("s_icon", smallIconResource);
intent.putExtra("bundle", bundle);
intent.putExtra("dataString", dataString);
PendingIntent pendingIntent = PendingIntent.getBroadcast(currentActivity,
id, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
am.cancel(pendingIntent);
long finalTime = System.currentTimeMillis() + delayMs;
if (Build.VERSION.SDK_INT < 23) {
am.set(AlarmManager.RTC_WAKEUP,finalTime, pendingIntent);
} else {
am.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,finalTime, pendingIntent);
}
Log.d(Constants.TAG, "event fired finalTime = "+ finalTime);
}
}
The value of the above used Constants.ALARM_RECEIVER is same as the action name defined in manifest for the broadcast AlarmReceiver will listen to.
<receiver android:name="com.example.app.AlarmReceiver">
<intent-filter>
<action android:name="com.example.app.alarm.action.trigger" />
</intent-filter>
</receiver>
The reason for using an explicit broadcast is that the android docs state that post Android Oreo, android applications won't be able to use implicit broadcasts in the background to improve performance. The link for the same: https://developer.android.com/about/versions/oreo/background.html#broadcasts
I also tried using a WakefulBroadCastReceiver to avoid optimizations made by Doze mode on android devices, but it turns out that this class has been deprecated in Android Oreo. The link for the same: https://developer.android.com/reference/android/support/v4/content/WakefulBroadcastReceiver.html
The aforementioned code works for nearly every other manufacturer except One Plus devices, which run on oxygen os. Is there any way to fix this issue as notifications for applications like WhatsApp continue to work if app is killed the same way.
Additional Information:
1. I ran the command adb shell dumpsys package <package_name> | grep stopped on One Plus 5T and it returned the following output:
User 0: ceDataInode=2039857 installed=true hidden=false suspended=false stopped=false notLaunched=false enabled=0 instant=false
which, in turn suggests that the app is not stopped on swiping the app from the recent tasks list. Also, the "Force Stop" button is enabled on the application information page, suggesting that the app hasn't been force stopped.
I was able to make the notifications work if I manually changed the Battery Optimizations setting from 'Optimized' to 'Not Optimized', but since notifications for other applications worked even when they are in the Optimized state, I don't believe this to be the solution for my problem.
Can someone please suggest any fixes or work-arounds to fix the notification issue on devices running Android 7 or above on Oxygen OS?
I have observed the same behaviour. The bug seems to be that Oxygen OS is not allowing the broadcast receiver to start from the PendingIntent. The solution was to use a Service instead on BroadcastReceiver.
Move your code from BroadcastReceiver's onReceive() to a Service's onStartCommand() method and then set this Service class in the PendingIntent.
...
intent.setClass(currentActivity, AlarmService.class);
...
PendingIntent pendingIntent = PendingIntent.getService(currentActivity, id, intent, PendingIntent.FLAG_UPDATE_CURRENT);
...
Hope this helps.
Yes, I also found it hard to get reference sample codes, as 8.0 onwards there are restrictions on always running tasks.
Do not place any intent-actions in Manifest.xml like this :
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_SMS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".Receiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<service
android:name=".CatchNumbers"
android:enabled="true"
android:exported="true" />
<service
android:name=".WatchMan"
android:enabled="true"
android:exported="true" >
</service>
<activity android:name=".developer_activity" />
<activity android:name=".WhiteListActivity" />
<activity android:name=".Contacts" />
</application>
please note
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
are not inserted by me, android studio inserted it automatically when i registered runtime receiver in service. It is necessary.
From my receiver.java on every boot_complete i registers receivers runtime in a foreground service. It is must if you want your receiver to work :
package com.example.rushi.instapromo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;
import android.widget.Toast;
public class Receiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
Log.d("BootTest : ", "\nOnBootReceiver - Received a broadcast!");
Toast.makeText(context, "OnBootReceiver Received a broadcast!!", Toast.LENGTH_LONG).show();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
context.startForegroundService(new Intent(context, WatchMan.class));
}
else
{
context.startService(new Intent(context, WatchMan.class));
}
}
}
Note that Watchman.java is foreground service which registers runtime receiver, if you do not go with this method, it will never work. I have tried a lot and invented this is the way only.
Register receivers in foreground service like
public class WatchMan extends Service
{
NotificationManager mNotifyManager;
NotificationCompat.Builder mBuilder;
NotificationChannel notificationChannel;
String NOTIFICATION_CHANNEL_ID = "17";
private BroadcastReceiver mCallBroadcastReceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
String PhoneNumber = "UNKNOWN";
Log.d("RECEIVER : ","IS UP AGAIN....");
try
{
String action = intent.getAction();
if(action.equalsIgnoreCase("android.intent.action.PHONE_STATE"))
{
if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_RINGING))
{
PhoneNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
Log.d("RECEIVER : ","Incoming number : "+PhoneNumber);
// update in database and goto catchnumber to sms
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
context.startForegroundService(new Intent(context, CatchNumbers.class));
}
else
{
context.startService(new Intent(context, CatchNumbers.class));
}
}
if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_IDLE))
{
PhoneNumber = "UNKNOWN";
}
if (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_OFFHOOK))
{
Log.d("RECEIVER : ","OUTGOING CALL RECEIVED....");
// UPDATED in database and JUST GOTO catchnumber to sms
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
context.startForegroundService(new Intent(context, CatchNumbers.class));
}
else
{
context.startService(new Intent(context, CatchNumbers.class));
}
}
}
if(action.equalsIgnoreCase("android.intent.action.NEW_OUTGOING_CALL"))
{
PhoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
Log.d("RECEIVER : ","Outgoing number : "+PhoneNumber);
// update in database and BUT DO NOT GOTO catchnumber to sms
}
}
catch (Exception e)
{
e.printStackTrace();
Log.e("RECEIVER : ", "Exception is : ", e);
}
}
};
public WatchMan() { }
#Override
public void onCreate()
{
super.onCreate();
Log.d("WatchMan : ", "\nOnCreate...");
IntentFilter CallFilter = new IntentFilter();
CallFilter.addAction("android.intent.action.NEW_OUTGOING_CALL");
CallFilter.addAction("android.intent.action.PHONE_STATE");
this.registerReceiver(mCallBroadcastReceiver, CallFilter);
Log.d("WatchMan : ", "\nmCallBroadcastReceiver Created....");
mNotifyManager = (NotificationManager) getApplicationContext().getSystemService(NOTIFICATION_SERVICE);
mBuilder = new NotificationCompat.Builder(this, null);
mBuilder.setContentTitle("Insta Promo")
.setContentText("InstaPromo Service ready")
.setTicker("InstaPromo Service ready")
.setSmallIcon(R.drawable.ic_launcher_background)
.setPriority(Notification.PRIORITY_HIGH)
.setDefaults(Notification.DEFAULT_ALL)
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setOngoing(true)
.setAutoCancel(false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "My Notifications", NotificationManager.IMPORTANCE_HIGH);
// Configure the notification channel.
notificationChannel.setDescription("Channel description");
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.RED);
notificationChannel.setVibrationPattern(new long[]{0, 1000, 500, 1000});
notificationChannel.enableVibration(true);
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
mNotifyManager.createNotificationChannel(notificationChannel);
mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
startForeground(17, mBuilder.build());
}
else
{
mBuilder.setChannelId(NOTIFICATION_CHANNEL_ID);
//startForeground(17, mBuilder.build());
mNotifyManager.notify(17, mBuilder.build());
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
Log.d("WatchMan : ", "\nmCallBroadcastReceiver Listening....");
//return super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
#Override
public void onDestroy()
{
this.unregisterReceiver(mCallBroadcastReceiver);
Log.d("WatchMan : ", "\nDestroyed....");
Log.d("WatchMan : ", "\nWill be created again....");
}
#Override
public IBinder onBind(Intent intent)
{
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
Please note how i registered and unregistered NEW_OUTGOING_CALL and PHONE_STATE intent actions. CatchNumbers is another service which i am using to do whatever i want to with incoming and outgoing numbers.
Your intent actions might be different than me.., But this is the only way if you want it to implement in 8.0 onwards. Tested and and i am working with this code. It supports 4.2 to Android P api level 29 also. On every reboot these 2 intent filters and receivers are ready fro me to use in my CatchNumbers. Hope it really helps you. or some one else.
My GCM Service is not working. I have declared a few things in my manifest file which looks something like:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="com.google.android.c2dm.permission.SEND"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<permission android:name="com.xxxx.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="com.xxxx.xxxxxx.permission.C2D_MESSAGE" />
<receiver android:name=".core.push.receiver.GCMBroadcastReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.SEND" />
<category android:name="com.xxxx.xxxxxx" />
</intent-filter>
</receiver>
<service android:name=".core.push.service.GCMIntentService" />
My Broadcast receiver code looks like:
public class GCMBroadcastReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
ComponentName messageProcessingService = new ComponentName(context.getPackageName(), GCMIntentService.class.getName());
context.startService(intent.setComponent(messageProcessingService));
setResultCode(Activity.RESULT_OK);
}
}
My Intent Service:
public class GCMIntentService extends IntentService
{
private static final int NOTIFICATION_NEW_MESSAGE = 0;
public GCMIntentService()
{
super(GCMIntentService.class.getSimpleName());
}
#Override
protected void onHandleIntent(Intent intent)
{
Bundle extras = intent.getExtras();
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
String messageType = gcm.getMessageType(intent);
if (!extras.isEmpty())
{
if (!GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType))
{
return;
}
// TODO Make more efficient. Load message stream here, not in two places later!
int newUnreadCount = MessagingController.getInstance().getUnreadCount() + 1;
MessagingController.getInstance().prepareStream();
MessagingController.getInstance().setUnreadCount(newUnreadCount);
final boolean isUserAuthenticated = !TextUtils.isEmpty(AuthenticationController.getInstance().getAuthToken());
if (isUserAuthenticated)
{
new Handler(Looper.getMainLooper()).post(new Runnable()
{
#Override
public void run()
{
App.from(GCMIntentService.this).fire(MessagingEvent.NEW_MESSAGE);
}
});
}
else
{
displayNotification(newUnreadCount + " New Message" + (newUnreadCount > 1 ? "s" : ""), newUnreadCount);
}
}
}
private void displayNotification(CharSequence message, int eventCount)
{
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
boolean shouldDisplayMessages = preferences.getBoolean("notifications_new_message", true);
if (!shouldDisplayMessages)
{
return;
}
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Intent openMessagingScreen = new Intent(this, LandingActivity.class);
openMessagingScreen.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
openMessagingScreen.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
preferences.edit().putBoolean(MessagingFragment.PREF_MESSAGE_WAITING, true).apply();
PendingIntent notificationAction = PendingIntent.getActivity(this, 0, openMessagingScreen, PendingIntent.FLAG_CANCEL_CURRENT);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(getString(R.string.app_name))
.setStyle(new NotificationCompat.BigTextStyle().bigText(message))
.setContentText(message)
.setAutoCancel(true)
.setContentIntent(notificationAction)
.setNumber(eventCount);
Notification notification = notificationBuilder.build();
notification.defaults |= Notification.DEFAULT_ALL;
try
{
notificationManager.notify(NOTIFICATION_NEW_MESSAGE, notification);
}
catch (SecurityException ex)
{
// This is required due to a bug in android
// http://stackoverflow.com/questions/13602190/java-lang-securityexception-requires-vibrate-permission-on-jelly-bean-4-2
Log.e("PPDirect", ex.getLocalizedMessage());
}
}
}
I had several questions regarding push notifications:
which call back function actually checks for arrived message and is called once push notification arrives?
Is it possible that since google have updated GCM to FCM I need to make updates to the key or migrate GCM to FCM?
Can there be some other reason?
Any help in the matter would be much appreciated.
1. which call back function actually checks for arrived message and is called once push notification arrives?
For Android, it's commonly received in onMessageReceived in the GcmListenerService. However, it may also depend whether or not the app is in background or foreground.
2. Is it possible that since google have updated GCM to FCM I need to make updates to the key or migrate GCM to FCM?
As per the note here:
Starting from Sept. 2016 new server key can only be created in the Firebase Console using the Cloud Messaging tab of the Settings panel. Existing projects that need to create a new server key can be imported in the Firebase console without affecting their existing configuration.
New users of GCM is in need of creating a Firebase projext, regardless if you are going to use FCM or not, in order to have a Server Key. For the migration steps, you can see my answer here.
3. Can there be some other reason?
This is too broad to answer. Maybe it's because of your payload structure or the implementation of your onMessageReceived(), etc.
I have implemented push notification in my android application:
In my main class:
// PUSH
Parse.initialize(this, applicationId, clientKey);
PushService.setDefaultPushCallback(this, SlidingMenuActivity.class);
ParseInstallation.getCurrentInstallation().saveInBackground();
ParseAnalytics.trackAppOpened(getIntent());
In my manifest.xml:
<!-- PUSH -->
<service android:name="com.parse.PushService" />
<receiver android:name="com.parse.ParseBroadcastReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.USER_PRESENT" />
</intent-filter>
</receiver>
When i open my application, i am receiving notification. I click on back and close the application.
I still receiving notification for approximatively 1 hours. After one hour, i send three notification and no notification appear. So i restart my app, and three notification notification appear.
I guess my broadcast receiver has been recreated. Why my android is killing my notification broadcast receiver?
How can i fix that?
try my solution it worked for me:
link your broadcast receiver to your service by adding
public void onReceive(Context context, Intent intent) {
//add the following
Intent e = new Intent(context, urservice.class);
context.startService(e);
}
then register your receiver in your service onCreate() like so
#Override
public void onCreate() {
super.onCreate();
IntentFilter filter = new IntentFilter(Intent.BOOT_COMPLETED);
filter.addAction(Intent.USER_PRESENT);
BroadcastReceiver mReceiver = new ParseBroadcastReceiver();
registerReceiver(mReceiver, filter);
}
then just delete your 'broadcastreceiver' from the manifest, lastly surly you want the service to live as long as possible,,, well then you need also 2 codes for the service in your int onStartCommand(Intent intent, int flags, int startId) make sure you put the following
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Intent bIntent = new Intent(urservice.this, urmain.class);
PendingIntent pbIntent = PendingIntent.getActivity(urservice.this, 0 , bIntent, 0);
NotificationCompat.Builder bBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("title")
.setContentText("sub title")
.setAutoCancel(true)
.setOngoing(true)
.setContentIntent(pbIntent);
barNotif = bBuilder.build();
this.startForeground(1, barNotif);
// also the following code is important
return Service.START_STICKY;
now make return sticky at the end of your onstartcommand. feel free to ask me.
hope I helped,,, good luck. :)
Is your device going to sleep? Is it getting low on memory? All of these things you have to consider. You may have to hold a wake lock or start a service that returns a value to restart sticky.