I won't show a received push notification from appearing top notifications menu my notification if it has for example key update. For now if I get notification with this key, all notifications are in the notification bar. I want to not present this notifications for user.
I'm using WakefulBroadcastReceiver for handle notifications like below:
public class PusherReceiver extends WakefulBroadcastReceiver {
private boolean isAppOnForeground(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
if (appProcesses == null)
return false;
final String packageName = context.getPackageName();
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName.equals(packageName)) {
return true;
}
}
return false;
}
public void onReceive(final Context context, Intent intent) {
Log.i("SimpleWakefulReceiver", "Starting service # " + SystemClock.elapsedRealtime());
if (!isAppOnForeground((context))) {
String pushNotificationBody = intent.getStringExtra("alert");
try {
JSONObject notificationData = new JSONObject(pushNotificationBody);
// This is the Intent to deliver to our service.
Intent service = new Intent(context, BackgroundService.class);
// Put here your data from the json as extra in in the intent
service.putExtra("notification", pushNotificationBody);
Log.i("PUSH_NOTIFICATION_JSON", "RECEIVED JSON " + notificationData);
// Start the service, keeping the device awake while it is launching.
if (!notificationData.has("update")) {
startWakefulService(context, service);
} else {
// Do nothing
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
UPDATE:
I changed project a little and with Onesignal and his NotificationExtenderService, I did something like below:
public class NotificationNotDisplayingExtender extends NotificationExtenderService {
#Override
protected boolean onNotificationProcessing(OSNotificationReceivedResult receivedResult) {
String notification = receivedResult.toString();
String notificationBody = receivedResult.payload.body;
JSONObject notificationBodyJSON = null;
try {
notificationBodyJSON = new JSONObject(notificationBody);
} catch (JSONException e) {
e.printStackTrace();
}
JSONObject pushNotificationData = notificationBodyJSON;
boolean hidden = false;
if (pushNotificationData.has("update")) {
Log.i("NOTIFICATION MANAGER", "PREVENT DISPLAY NOTIFICATION");
hidden = true;
}
// Return true to stop the notification from displaying.
return hidden;
}
}
And it prevent displaying notifications with update key, but now I don't receive it in my PusherReceiver to start my service. Is there easy way to send data from my NotificationNotDisplayingExtender receivedResult to my PusherReceiver?
For now it looks like my PusherReceiver don't fire his onReceive method.
Many thanks for help in advance.
There are two types of payload.
1. Data
2. Notification
https://developers.google.com/cloud-messaging/concept-options
Use only data payload. Then you always get the call in FirebaseMessagingService onMessageRececived Method
The thing is basically we have two type of notifications.
One which can be called Notification Type, is that the push has a notification object in sent/received bundle, in which you have to handle it when your app is in foreground and the notification is received. In this case, if your app is in foreground, then you can handle it and do whatever you like which is not showing a notification. But if the app is in background, a notification will automatically create by google and it takes predefined title and message objects within the received push bundle to make the notification.
Second type which can be called Data Type, do not have any notification object in the sent/received bundle. In this scenario, your app is in foreground or background, you should handle everything. So, if you put your data in data object of your push notification message, everything will be in your hands.
So, in short, just put your data in data object of your notification and implement your desired logic.
I do not see the JSON data you are referring to. However, I suppose the update key in your JSON is containing null. In your code you are checking if the JSON data has the key update in it. This function will always return true if the key exists in the JSON body. You might have the field with null value which is indicating that you are not supposed to show the notification in the system tray.
In that case, you might consider using isNull function. It returns true if this object has no mapping for update or if it has a mapping whose value is null.
// Start the service, keeping the device awake while it is launching.
if (!notificationData.isNull("update")) {
startWakefulService(context, service);
} else {
// Do nothing
}
And yes, please use the data payload from the notification that you get.
Every time you notify the NotificationManager to show a notification, you provide an ID to be used for the notification to edit or cancel that notification later on. If you show a notification by manager.notify(notificationId, notification), you can cancel it with manager.cancel(notificationId).
If you want to remove all the notifications, you can use NotificationManager.cancelAll().
Related
I'm building a chat app that pushes notification like facebook, Instagram.
when a user sends message notification will show automatically. The problem is I don't know how to handle background service.
For example, when the same user sends the message, a notification will append the message like this:
But when FCM send notification this is what happened:
I need to be able to handle background service
Here is my code
public class FirebaseInstaceService extends FirebaseMessagingService {
FirebaseToken firebaseToken = new FirebaseToken();
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
#Override
public void onNewToken(String s) {
super.onNewToken(s);
Log.d("NewToken","New token: " +s);
if (user != null){
firebaseToken.sendToken(s, new OncompleteCallback() {
#Override
public void callback() {
Log.d("NewToken","Send complete");
}
});
}
}
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
try {
Log.d("CloudMessage",remoteMessage.getNotification().getBody());
}catch (Exception e){
Log.d("CloudMessage",e.toString());
throw e;
}
}
}
If I understand your question correctly, you need to do work on the non UI thread when a push notification is received. In that case, you're in luck - as explained here, onReceive() is called on a non-UI thread.
You just create and show Notifications as you see fit, storing the notification ID uniquely to allow user->notification mapping. That way, you can access the notification shown when the same user sends another message.
I am trying to develop a feature for an app where you have a list of "codes" that you enter and that are dialed one by one. I have looked over TelephonyManager and followed a tutorial on developing a broadcast receiver with a listener for responses but it does not always work as it should.
One idea was to store all the numbers necessary in SharedPref. If the Activity (only created for the intent towards dialer) went into onStop() (meaning that above, the dialer screen was on) and then into onResume() (call ended and activity resumed), I would remove the number last dialed from the SharedPref and then, if any remained, open the dialer again. The broadcast made sure than once the state flow of the TelephonyManager was "OFFHOOK => IDLE", it would return the user to the Activity . Short story, it did not always perform as it should have.
How should I tackle the problem ?
EDIT
My curent solution was to
Create a doPhoneCall() function that would handle the intent creation and deployment itself.
#Override
protected void doPhoneCall(){
super.onResume();
wentIntoCall = false;
/** More code here for dialing */
}
Place this function into the onResume(). Even if the onResume will be called multiple times, the wentIntoCall boolean will make sure that the function won't be called multiple times.
#Override
protected void onResume() {
super.onResume();
if(wentIntoCall)
doPhoneCall();
}
Having in mind that after a call, the phone should return to its previous state, so it would return to the Activity in which we are doing are call, we will add to the activity a CallListener, and in the case of IDLE, based on the tutorial linked above, we make the wentIntoCall be true. ( The activity will go into onResume() and, upon seeing that the boolean is true, it will initialize the next call ).
case TelephonyManager.CALL_STATE_IDLE:
Log.e(TAG, "CALL_STATE_IDLE==>"+incoming_number);
if((prev_state == TelephonyManager.CALL_STATE_OFFHOOK)){
prev_state=state;
wentIntoCall = true;
//Answered Call which is ended
}
if((prev_state == TelephonyManager.CALL_STATE_RINGING)){
prev_state=state;
wentIntoCall = true;
//Rejected or Missed call
}
My final question : is this the right way to handle this functionality, or should I try to come up with another implementation of it ?
EDIT 2
Looks like my "codes", being USSD codes, are not behaving like normal phone calls.. So for normal phone calls the code above seems to work, but for dialing codes, not that much. I have "downgraded" my solution to a simple for-loop. Seems to be working fine now.
I dont know for android O , but for android 6.0 > You cant detect answer in direct way . Call no exist number and see PhoneStateListener what will trigger in one case and track successed call also.
Make public static array , add all your numbers intro array .
I made services . Insert permissions in manifest make your own action also ( NEXT_CALL for example ) .
Than easy make intent for startServices :
SharedPreferences settings;
SharedPreferences.Editor SAVES;
Intent serviceIntent = new Intent(MainActivity.this, ServiceForCalls.class);
serviceIntent.setAction("xxx.xxx.NEXT_CALL");
startService(serviceIntent);
isCalling = true;
SAVES.putBoolean( "isCalling" , isCalling );
SAVES.commit();
SAVES.apply();
You must use timeout interval about 10 sec for next call.
Heres little help func - end call and phoneState handler :
void END_CALL () throws InvocationTargetException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException {
tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
Class c = null;
try {
c = Class.forName(tm.getClass().getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method m = null;
try {
m = c.getDeclaredMethod("getITelephony");
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
m.setAccessible(true);
Object telephonyService = m.invoke(tm); // Get the internal ITelephony object
c = Class.forName(telephonyService.getClass().getName()); // Get its class
m = c.getDeclaredMethod("endCall"); // Get the "endCall()" method
m.setAccessible(true); // Make it accessible
m.invoke(telephonyService); // invoke endCall()
if ( SIGNAL_STOP == false ) {
timerHandlerServicesStartNewNumber.postDelayed(timerRunnableServicesStartNewNumber, 1000);
}
}
private class PhoneStateChangeListener extends PhoneStateListener {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
switch(state){
case TelephonyManager.CALL_STATE_RINGING:
Log.println( Log.INFO , "RINGING" , "SERVICES%%%%%%%%%%%%%%%%RINGING%%%%%%%%%%%%%%%%%%");
wasRinging = true;
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
Log.println( Log.INFO , "OFFHOOK BROADCAST" , "SERVICES%%%%%%%%%%%%%%%%%%%%%%%%");
if (!wasRinging) {
// Start your new activity
Log.println( Log.INFO , "OFFHOOK BROADCAST" , "SERVICES%%%%%%%%%%%%%%%%%%%%%%%");
if (SIGNAL_STOP == false) {
timerHandlerServices.postDelayed(timerRunnableServices, 10000);
}
} else {
// Cancel your old activity
Log.println( Log.INFO , "OFFHOOK BROADCAST" , "SERVICES%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
}
// this should be the last piece of code before the break
wasRinging = true;
break;
case TelephonyManager.CALL_STATE_IDLE:
Log.println( Log.INFO , "IDLE BROADCAST" , "SERVICES%%%%%%%%%%%%%%%%%IDLE%%%%%%%%%%%%%%%%%%%");
// this should be the last piece of code before the break
wasRinging = false;
break;
}
}
}
Thanks for : "meaning that above, the dialer screen was on" nice catch.
As a side note, in the Android O developer preview there is a new API which allows you to send a USSD request and register a callback to receive its results. For newer versions of Android this might meet your needs better.
I'm working with notifications generated by every app (not only mine) on my Android device (android 5.1.1).
By extending NotificationListenerService I'm able to know when a push notification is posted (overriding the "onNotificationPosted" method) and when a notification is removed (overriding the "onNotificationRemoved" method).
The problem is that I would like to know how the notification was removed:
a) by clicking it (so opening the app)
or
b) by swyping it (so it is only removed)
?
Is it possible to know it?
Thank you in advance!
The best way to do it is to get the list of all running processes!
So, in the onNotificationRemoved method we can:
1. obtain the list of running processes using the Android Processes library
2. compare each process name with the packageName
3. if the comparison return a true value, we check if the process is in foreground
public void onNotificationRemoved(StatusBarNotification sbn) {
String packageName = sbn.getPackageName();
try {
List<AndroidAppProcess> processes = ProcessManager.getRunningAppProcesses();
if (processes != null) {
for (AndroidAppProcess process : processes) {
String processName = process.name;
if (processName.equals(packageName)) {
if (process.foreground ==true)
{
//user clicked on notification
}
else
{
//user swipe notification
}
}
}
}
}
catch (Exception e)
{
String error = e.toString();
}
}
When I sign in on my application and try to get the size from the datastore's table, it is empty. But after I restart the application, it works as it should and gives back the size of the datastore's table.
In my case, there are two activities. The activity, and in-turn the fragment, SettingsFragment.java has the sign-in part. The activity MainActivity.java has the part with getting the datastore's table size.
SettingsFragment.java (Sign-in).
// Dropbox Signin
mAccountManager = DbxAccountManager.getInstance(getActivity().getApplicationContext(), APP_KEY, SECRET_KEY);
mDropBoxPreference = findPreference("preference_sync_dbx");
mDropBoxPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
if (mAccountManager.hasLinkedAccount()) {
mAccountManager.unlink();
} else {
mAccountManager.startLink(getActivity(), REQUEST_LINK_TO_DBX);
}
return true;
}
});
MainActivity.java (Get datastore size).
// Dropbox Get Datastore Size
mAccountManager = DbxAccountManager.getInstance(getApplicationContext(), APP_KEY, SECRET_KEY);
if (mAccountManager.hasLinkedAccount()) {
DbxAccount mDbxAccount = mAccountManager.getLinkedAccount();
try {
mDbxStore = DbxDatastore.openDefault(mDbxAccount);
mDbxNotesTable = mDbxStore.getTable("notes");
mDbxNotesCount = mDbxNotesTable.query().count();
Log.e("mDbxNotesCount", Integer.toString(mDbxNotesCount));
} catch (DbxException e) {
e.printStackTrace();
}
}
Let's say there are 3 records in the datastore.
After I sign in, this shows up from the Logcat.
E/mDbxNotesCount﹕ 0
Once I restart it, this shows up from the Logcat.
E/mDbxNotesCount﹕ 3
What's causing it to work like this?
Edit: Added listener after using smarx's answer, and it works! Here is what I did.
mDbxStore.addSyncStatusListener(new DbxDatastore.SyncStatusListener() {
#Override
public void onDatastoreStatusChange(DbxDatastore dbxDatastore) {
if (!dbxDatastore.getSyncStatus().isDownloading) {
try {
mDbxStore.sync();
mDbxNotesCount = mDbxNotesTable.query().count();
} catch (DbxException e) {
e.printStackTrace();
}
Log.e("mDbxNotesCount", Integer.toString(mDbxNotesCount));
}
}
});
It looks like you're trying to read the contents of the datastore before it's had a chance to sync. You'll need to wait until the initial sync is completed. I think one way to do that would be to add a listener via addSyncStatusListener and wait for store.getSyncStatus().isDownloading to be false.
But typically the pattern you'll want to follow is to always show the current information... so set up a listener and update your UI any time it changes. That way whether it's the initial sync bringing in new records or updates from a different device, your app will respond appropriately when the information changes.
I've already bound an activity to my service following this tutorial.
http://developer.android.com/guide/components/bound-services.html
I'm able to call service functions, but what if I want to for example, change some of my textviews or disable some of the toggle buttons because of work done on the service (and from the service). Would there be an easy to way to do this?
You can use messages to send information between activities and services. This is an easy way to send simple data, but may not be the best option if you need to send data very frequently, or send complicated data. This is an example of some code I have in one of my apps with a service and an activity which communicate:
Code in the activity:
//this is where you set what you want to happen
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
//this switch reads the information in the message (usually just
//an integer) and will do something depending on which integer is sent
case 1: do_something();
case 2: do_something_2(); //etc.
default:
super.handleMessage(msg);
}
}
}
final Messenger myMessenger = new Messenger(new IncomingHandler());
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
myService = new Messenger(service);
myCallbackText = (TextView)findViewById(R.id.tv01); //This is a text view which will display status information as needed
myCallbackText.setText("Attached.");
try {
Message msg = Message.obtain(null,
1);
msg.replyTo = mMessenger; //here we send an instance of our messenger implementation as the replyTo address
mService.send(msg);
msg = Message.obtain(null,
3, this.hashCode(), 0);
mService.send(msg); //send a message with the value "3"
} catch (RemoteException e) {
//nothing you can do if the server isn't active
}
Toast.makeText(Service_testActivity.this, R.string.remote_service_connected,
Toast.LENGTH_SHORT).show();//confirmation that the connection happened successfully
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
mCallbackText = (TextView)findViewById(R.id.tv01);//same textview as before
mCallbackText.setText("Disconnected.");
Toast.makeText(Service_testActivity.this, R.string.remote_service_disconnected,
Toast.LENGTH_SHORT).show();
}
};
Code in the service:
In the service, you will want to have code (very similar to the code in the activity) to receive a message and save the msg.replyTo field as a Messenger object. There is an example somewhere which will have you make an object and then use an IncomingHandler like this:
ArrayList<Messenger> mClients = new ArrayList<Messenger>();
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_CLIENT:
mClients.add(msg.replyTo);
break;
case MSG_UNREGISTER_CLIENT:
mClients.remove(msg.replyTo);
break;
default:
super.handleMessage(msg);
}
}
}
This can allow your service to keep track of multiple clients at once and send messages to specified clients. To send a message simply use something like this:
mClients.get(1).send(Message.obtain(null, 3, new Random().nextInt(), 0));
//sends a message to the first client saved in the list