I am building an app and ran into a problem that I couldn't find an answer to.
I have a realm database that I use to store some very simple information. Now what I want to do is every day at a set time prompt the user with a notification with a few buttons on it. And depending on what button the user clicks, I want to write a different value to the realm database. PREFERABLY without opening the app. Is this possible?
Thanks in advance!
You can do it in this way.
First, create a Notification with actions on it.
Intent intentLike = new Intent("MY_ACTION");
intentLike.putExtra("KEY","LIKE");
PendingIntent likePendingIntent = PendingIntent.getBroadcast(context,0,intentLike,PendingIntent.FLAG_UPDATE_CURRENT);
Intent intentShare = new Intent("MY_ACTION");
intentShare.putExtra("KEY","SHARE");
PendingIntent sharePendingIntent = PendingIntent.getBroadcast(context,1,intentShare,PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle("My notification")
.setContentText("Hello World!")
.addAction(R.drawable.notification_action_like, "Like", likePendingIntent)
.addAction(R.drawable.notification_action_share, "Share", sharePendingIntent);
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// mId allows you to update the notification later on.
mNotificationManager.notify(mId, mBuilder.build());
Now create a BroadcastReceiver class to receive values.
public class LikeShareReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String receivedValue = intent.getExtra("KEY");
if (receivedValue.equals("LIKE")) {
//update like in Realm database.
}else if (receivedValue.equals("SHARE")) {
//update share in Realm database.
}
}
}
Add this BroadcastReceiver in the manifest file.
<receiver android:enabled="true" android:name="LikeShareReceiver">
<intent-filter>
<action android:name="MY_ACTION" />
</intent-filter>
</receiver>
How this will work?
When a user clicks on an action button it will trigger a broadcast with an intent having values in it. BroadcastReceiver will receive this broadcast and update the database accordingly.
Note:addAction() method will only work with api level >=4.1
Related
My app runs a background service indicated by the persistent notification. Users need to use the toggle button in MainActivity to turn on/off this service and thus to remove the persistent notification.
Now I want to implement a notification action that can turn this service off & as well as the toggle within MainActivity. All in all, it should be an exit button to close my app and background service directly from the notification.
How do I achieve this?
note that I have two different java files, one for NotificationService
and the other is MainActivity. The toggle belongs to MainActivity.
edit: Is this okay to call System.exit(0) if I use pending intent with
BroadCastReceiver to exit from the app completely?
You have to use PendingIntent with BroadcastReceiver or Service to perform this. Here is an example of PendingIntent with BroadcastReciever.
Build a Notification
public static void createNotif(Context context){
Intent intentAction = new Intent(context,StopServerBroadcast.class);
//This is optional if you have more than one buttons and want to differentiate between two
intentAction.putExtra("action","actionName");
pIntent = PendingIntent.getBroadcast(context,1,intentAction,PendingIntent.FLAG_UPDATE_CURRENT);
drivingNotifBldr = (NotificationCompat.Builder) new NotificationCompat.Builder(context, CHANNEL_NAME)
.setSmallIcon(R.drawable.steeringwheel)
.setContentTitle("Stop Service")
.setContentText("Example Text")
.addAction(R.drawable.smallmanwalking, "On/off", pIntent)
.setOngoing(true);
...
}
Now the receiver which will receive this Intent
public class StopServerBroadcast extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Toast.makeText(context,"recieved",Toast.LENGTH_SHORT).show();
String action=intent.getStringExtra("action");
if(action.equals("action1")){
performAction1();
/*
Code that will stop your service
*/
}
}
}
Register Receiver in Manifest
<receiver
android:name=".StopServerBroadcast"
android:enabled="true" />
This question already has answers here:
How to show daily offline notifications in Android 10?
(3 answers)
Closed 2 years ago.
I literally read all threads about this subject but none of them worked. (None of them also has a accepted valid answer that everyone says yay! reading it)
What I am trying to do is to send notification to the user at specific time that user specifies. these are what I tried so far:
Alarm manager + Broadcast receiver: this work on the emulator, but not on my device (Xiaomi Redmi note 8). It actually works for short periods like 1 or 2 minutes, but doesnt work for longer periods like 10 or 15 minutes. there is a funny bug about alarms that I will explain later.
wrokmanager: again works on the emulator, but not on my device.
Alarm manager + Broadcast receiver + JobServiceIntent: this doesn't change anything. again it works fine on emulator as expected. But on my phone, it works for short periods but not when the alarm is set for 15 minutes later.
P.S: I also tried many variation, like different types of alarm (RTC, RTC_WAKEUP). or different methods to set it like setExact, setReapiting etc. did the same with workmanger.
Final Result(Important):
after days of research I reached to this thread and I was convinced that these are all forced by manufacturers and there is no way to solve it rather than asking the user to do some manual changes (like auto start, battery saver etc). However, I found an app that is not one of those whitelisted apps(like facebook, whatsapp etc), it's a local app and is sending notifications on time. First I though it is using FCM, then I turned off my internet connection and I still received notification exactly on time.
Now I can not really think of any other solutions, and couldn't find any on other threads that actually work. Any helps would be appreciated from the bottom of my heart.
this is my BROADCASTRECEIVER:
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("NOTIF","YESSSS, IT's WORKING");
NotifJobIntentService.enqueueWork(context,intent);
}}
this is my JobIntentSerice class:
public class NotifJobIntentService extends JobIntentService {
private static final String TAG = "NotifJobIntentService";
private Context context;
static void enqueueWork(Context context, Intent intent) {
enqueueWork(context, NotifJobIntentService.class, 123 , intent);
}
#Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
}
#Override
protected void onHandleWork(#NonNull Intent intent) {
context = getApplicationContext();
Log.d(TAG, "OnHandleWork");
String input = intent.getStringExtra("inputExtra");
intent.setFlags(intent.FLAG_INCLUDE_STOPPED_PACKAGES);
Intent intent1 = new Intent(context , MainFragment.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 0, intent, 0);
// TODO: Make this accessible to exterior projects, such as web interface.
Notification.Builder builder = new Notification.Builder(context)
.setTicker("Notification")
.setContentTitle("Important Message")
.setContentText("This is an example of a push notification using a Navigation Manager")
.setSmallIcon(R.drawable.ic_add)
.setContentIntent(pIntent);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
String channelId = "Your_channel_id";
NotificationChannel channel = new NotificationChannel(
channelId,
"Reminder to remind to review your notes",
NotificationManager.IMPORTANCE_HIGH);
channel.setDescription("Hello Dear friends"); //this is to test what this is
notificationManager.createNotificationChannel(channel);
builder.setChannelId(channelId);
}
Notification notification = builder.build();
notification.flags = Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(0, notification);
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
#Override
public boolean onStopCurrentWork() {
Log.d(TAG, "onStopCurrentWork");
return false;
}
}
this is my Main activity that sets the alarm manager:
private void setNotificationAlarm(){
//Alarm Manager
Calendar time = Calendar.getInstance();
time.set(Calendar.HOUR_OF_DAY,11);//set the alarm time
time.set(Calendar.MINUTE, 30);
time.set(Calendar.SECOND,0);
AlarmManager am =( AlarmManager)getContext().getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(getActivity(), AlarmReceiver.class);
i.setAction("android.intent.action.NOTIFY");
PendingIntent pi = PendingIntent.getBroadcast(getContext(), 0, i, PendingIntent.FLAG_ONE_SHOT);
if (Build.VERSION.SDK_INT >= 23){
am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,time.getTimeInMillis(),pi);
}
else{
am.set(AlarmManager.RTC_WAKEUP,time.getTimeInMillis(),pi);
}
}
and here is my manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ir.zima.schema">
<application
android:name=".CustomApplication"
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/Theme.AppCompat.Light.NoActionBar">
<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=".ListActivity"></activity>
<activity android:name=".MenuItemActivity"></activity>
<service
android:name=".NotifJobIntentService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="true"
android:enabled="true"/>
<receiver android:name=".AlarmReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.NOTIFY">
</action>
</intent-filter>
</receiver>
</application>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
and finally the funny bug:
There are many threads that say alarm set by alarm manager is cleared when you swipe away your app and that actually happens (i figured that with adb shell dumpsys...). However after days of research I finally found it in this thread accidently that there is something like a bug. If you run your app via android studio (clicking "run app" button) and then swipe the app away, all alarms will be cleared(adb shell dumpsys returns nothing). But if you run the same app using the launcher Icon and then swipe it away, the alarms are set and adb shell dumpsys also shows it. (which those alarms still work fine on emulator but not on my device).
Once I had made an app that gets data from server (one hour interval) then show notifications based on the data. I basically created an AlarmManager which will trigger each hour and start an IntentService which will initiate the network call and show notifications. You can see here how to start the AlarmManager. Then you can see this nice articale from Journaldev. That should work. But one thing I want to mention that, it is possible that the alarmmanger may be cleared as you mentioned. In my case I created 3 alarmanager instances, one is the main, and the other 2 was for checking that 1 was triggered or not. Worked just fine for me though it is not a good solution.
I am sharing the code. But I should tell you that it is not the most efficient and recommended way. This will only serve your needs.
For setting and canceling alarm:
private void setAlarmTask(AlarmManager alarmManager, long time, Intent intent, int request_code){
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, request_code, intent, 0);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, time, AlarmManager.INTERVAL_HOUR, pendingIntent);
}
private void cancelAlarmTask(Intent intent, int request_code){
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, request_code, intent, 0);
pendingIntent.cancel();
}
For starting scheduler:
private void setScheduler(long time_in_miliseconds){
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(time_in_miliseconds);
AlarmManager mainAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
AlarmManager failSafeAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
AlarmManager failSafeAlarmManager_2 = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent scheduleServiceExecuterIntent = new Intent(this, ScheduledServiceExecuter.class);
scheduleServiceExecuterIntent.putExtra("state", "Main");
setAlarmTask(mainAlarmManager, time_in_miliseconds, scheduleServiceExecuterIntent, 0);
calendar.add(calendar.MINUTE, 5);
scheduleServiceExecuterIntent.putExtra("state", "Fail Safe");
setAlarmTask(failSafeAlarmManager, calendar.getTimeInMillis(), scheduleServiceExecuterIntent, 1);
calendar.add(calendar.MINUTE, 5);
scheduleServiceExecuterIntent.putExtra("state", "Fail Safe 2");
setAlarmTask(failSafeAlarmManager_2, calendar.getTimeInMillis(), scheduleServiceExecuterIntent, 2);
}
For Stopping scheduler:
private void stopScheduler(){
Intent scheduleServiceExecuterIntent = new Intent(this, ScheduledServiceExecuter.class);
cancelAlarmTask(scheduleServiceExecuterIntent,0);
cancelAlarmTask(scheduleServiceExecuterIntent,1);
cancelAlarmTask(scheduleServiceExecuterIntent,2);
}
I have searched thick and thin through the Developer notes for android and am unable to find out how to perform a specific action when a device is plugged in, and perform another action when unplugged.
I have attempted to set a Broadcast Receiver like below however it will not run:
<receiver android:name=".PowerConnectionReceiver">
<intent-filter>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
</intent-filter>
</receiver>
I understand that from API 26 and up, you can no longer receive some broadcasts registered in the manifest, and must register them dynamically.
I need to have this run in the background but can't figure out how? This article updated in 2019 (after api 26) implies that I should be able to do it.
The charging status can change as easily as a device can be plugged in, so it's important to monitor the charging state for changes and alter your refresh rate accordingly.
The BatteryManager broadcasts an action whenever the device is connected or disconnected from power. It's important to receive these events even while your app isn't running particularly as these events should impact how often you start your app in order to initiate a background update so you should register a BroadcastReceiver in your manifest to listen for both events by defining the ACTION_POWER_CONNECTED and ACTION_POWER_DISCONNECTED within an intent filter.
My end goal is to call the broadcast receiver whenever device is plugged in or unplugged.
How would I go about implementing this?
The best way to accomplish this is by having a service run in the background so that you can receive the broadcast from android without the user using the app.
Start by registering a service in your manifest file.
<Service android:name=".serviceName"/>
Now create your class with the same name as registered in the manifest above, this class must extend Service.
public class BackgroundService extends Service {
private static final int NOTIF_ID = 1;
private static final String NOTIF_CHANNEL_ID = "AppNameBackgroundService";
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId){
//Add broadcast receiver for plugging in and out here
ChargeDetection chargeDetector = new ChargeDetection(); //This is the name of the class at the bottom of this code.
IntentFilter filter = new IntentFilter();
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
filter.addAction(Intent.ACTION_POWER_CONNECTED);
filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
this.registerReceiver(chargeDetector, filter);
startForeground();
return super.onStartCommand(intent, flags, startId);
}
private void startForeground() {
createNotificationChannel();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
startForeground(NOTIF_ID, new NotificationCompat.Builder(this,
NOTIF_CHANNEL_ID) // don't forget create a notification channel first
.setOngoing(true)
.setSmallIcon(R.mipmap.final_icon)
.setContentTitle(getString(R.string.app_name))
.setContentText("Background service is running")
.setContentIntent(pendingIntent)
.build());
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
NOTIF_CHANNEL_ID,
"Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
}
class ChargeDetection extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Now check if user is charging here. This will only run when device is either plugged in or unplugged.
}
}
}
You need to register this receiver from code
You could run a WorkManager to run once every 15 minutes (the minimum is once every 15 minutes)
register the receiver, check if it's charging
unregister the receiver
I am trying to get "DeleteIntent" callback from my notification.
BroadcastReceiver.onReceive not called from PendengIntent, while it works OK when broadcast is launched manually with sendBroadcast(deleteIntent).
It works well when intent target is Activity (hits Activity.onNewIntent) but it is weird to use it since activity goes foreground when message dismissed.
Please, help to get onReceive call to BroadcastReceiver.
It is visible, exported and enabled, located in the same package.
MyCancelReceiver class
public class MyCancelReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
}
}
Android manifest
<activity android:name=".MainActivity" android:launchMode="singleTask">
<MyCancelReceiver" android:enabled="true" android:exported="true">
<intent-filter>
<action android:name="notification_cancelled"/>
</intent-filter>
</receiver>
MainActivity
private MyCancelReceiver mReceiver;
#Override
public void onStart() {
super.onStart();
IntentFilter filter = new IntentFilter();
filter.addAction("notification_cancelled");
mReceiver = new MyCancelReceiver();
registerReceiver(mReceiver, filter);
}
#Override
public void onStop() {
super.onStop();
unregisterReceiver(mReceiver);
}
Notification creation:
Intent cancelIntent = new Intent(getApplicationContext(), MyCancelReceiver.class);
cancelIntent.setAction("notification_cancelled");
cancelIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent deleteIntent = PendingIntent.getActivity(getApplicationContext(), 0, cancelIntent, PendingIntent.FLAG_CANCEL_CURRENT);
Notification not = new Notification.Builder(getApplicationContext(), Util.ANDROID_CHANNEL_ID)
.setContentTitle(messageTitle)
.setContentText(messageBody)
.setSmallIcon(R.drawable.default)
.setAutoCancel(true)
.setDeleteIntent(deleteIntent)
.setContentIntent(contentIntent)
.build();
NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, not);
Thank you in advance.
It's because when you start your activity, onStart() gets called and you will get a callback in onReceive() as soon as the activity is launched. But when you click your notification onStart won't be called as the activity is already created, In this case, onStart wont be called. Try registering in onResume(), I am pretty sure it will work.
Problem solved using an answer from this thread Click on android notification icon does not trigger broadcast receiver
Explicit intent replaced with implicit one.
Intent cancelIntent = new Intent("notification_canceled");
Notification is giving old values.
I read the stackoverflow link but still not working for me:
Notification passes old Intent Extras
I have a Activity A.
When I am on the activity B and touch the Notification, the Extra parameter is given correctly and shows the Activity A with the correctc values read with getExtras(..);
Then the Activity A is still on the top - showing on the screen:
I click on the second notification with new values of putExtras(newValue) to create a new activity A but with new values.
The problem:
The intent.getExtras()` is reading old values of the first notification clicked instead new values given by the second notification.
I made a lot of combinations of Flags of Pending Intent and the combinations of the link on top, but the aplication is still taking the old values(the values of the first touched notification) for the second Notification. I tried the flag: PendingIntent.FLAG_UPDATE_CURRENT to update the values instead create a new one Activity and some others Flags.
How can I make the second notification give the correct values for the activity A when the Activity A is still shown on the screen?
The snippet of the code creating the notification.
public void notificationCreateGu(String newMessageUserUidOfSender) {
Intent it = new Intent(this,ActivityA.class);
it.putExtra(USER_UID_READER,newMessageUserUidOfSender);
StoreValuesClass.count=StoreValuesClass.count+2;
PendingIntent pi = PendingIntent.getActivity(this, StoreValuesClass.count,it, 0);
Notification notification = new NotificationCompat.Builder(this)
.setTicker(newMessageUserUidOfSender )
.setSmallIcon(android.R.mipmap.sym_def_app_icon)
.setContentTitle("Title Message ")
.setContentText(String.valueOf(newMessageUserUidOfSender))
.setContentIntent(pi)
.setAutoCancel(true)
.build();
int m;
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
m= StoreValuesClass.count=StoreValuesClass.count+2;
notificationManager.notify((m), notification);
}
//StoreValueClass.count is a static values that can be read by the activity to give an unique id for the notification.
the snippet of code reading the values.
userUidReader = getIntent().getExtras().getString(USER_UID_READER)
I tried to reload the values into onResume() but into onResume() the still taking the old values of the first time the getExtras() is read.
i understood that the operational system Android are not creating a new Activity but only giving it to the top.
using the answser of CommonsWare that helped with the override onNewIntetn and the link:
http://www.helloandroid.com/tutorials/communicating-between-running-activities
1 into xmlFile: put th android:launchMode="singleTask"
for the activity will receive the extra parameters with getExtras.
<activity android:name=".ActivityWillReceiveWithGetExtras"
android:launchMode="singleTask"
android:taskAffinity=""
android:excludeFromRecents="true">
</activity>
2.into the activity you that will receive the values with get_extras(...) override a method called onNewIntent:
2.1 Observation: put the line:
setIntent(intent);
to set the identifier of the intent.
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);//must store the new intent unless getIntent() will return the old one
getExtraParameterActual();
}
2.2 get the Extra parameters into a function that will have inside the command getExtras(...
getExtraParameterActual();
Write the function of the top getExtraParameterActual();
private void getExtraParameterActual() {
Intent intent = getIntent();//take back the value set with //setintenT of pass 2.1
user = getIntent().getExtras().getString(USER);//
}
5.
into OnCreate() call the e getExtraParameterActual();
and if necessary reload your views with a method for example reloadMyViews()
into onResume() reload your views again with the same function of the pass 5 reloadMyViews()
7 the notificatio code I used take care with the FLAGS
public void notificationCreateGu(String User) {
Log.d(TAG,nameOfTheService+"BUG createnotification for received CHAT messages useruidOfTheFriendNear="+newMessageUserUidOfSender);
Intent it = new Intent(this,ActivityWillReceiveWithGetExtras.class);
it.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
it.putExtra(USER,user);
StoreValuesClass.count=StoreValuesClass.count+2;
PendingIntent pi = PendingIntent.getActivity(this, StoreValuesClass.count,it, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new NotificationCompat.Builder(this)
.setTicker(newMessageUserUidOfSender )
.setSmallIcon(android.R.mipmap.sym_def_app_icon)
.setContentTitle("Title Message ")
.setContentText(String.valueOf(newMessageUserUidOfSender))
.setContentIntent(pi)
.setAutoCancel(true)
.build();
int m;
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
m= StoreValuesClass.count=StoreValuesClass.count+2;
notificationManager.notify((m), notification);
}
Override onNewIntent() in your activity.
getIntent() returns the Intent that was used to initially create the activity. If an existing activity instance is brought back to the foreground via a startActivity() call, onNewIntent() is called to deliver to you the Intent used for that most recent startActivity() call.