My app requires that a notification be sent twice a day. For testing purposes, I have shortened this time to 1 hour. This, of course, must be done in the background/when app is closed, so I have already tried AlarmManager and that did not work. I have therefore switched to WorkManager. someone suggested that I use periodicWork to accomplish my task but here is the issue:
WorkManager only executes all periodic work when the app is open
Another weird thing: If I leave the app alone for 3 hours, I will get way more than three notifications when I open the app.
I know for a fact that WorkManager is not executing because I have instantiated a Date Object whenever doWork() is called, and that timestamp from the date object is printed to the notification. This printed time will ALWAYS show as the time I have opened the app, meaning all of the queued work requests were executed at once when I opened the app.
Here is what is used to set the alarm. Note that cancelAlarm() does not cancel the alarm, but rather resets a shared preference I used for debugging
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_enter);
ToggleButton toggle = findViewById(R.id.toggleButton);
toggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked) {
setAlarm();
} else {
cancelAlarm();
}
}
});
}
private void setAlarm() {
Constraints constraints = Constraints.NONE;
PeriodicWorkRequest testRequest = new PeriodicWorkRequest.Builder(ReminderWorker.class, 1, TimeUnit.HOURS)
.setConstraints(constraints)
.build();
WorkManager.getInstance().enqueueUniquePeriodicWork("ReminderWork", ExistingPeriodicWorkPolicy.KEEP, testRequest);
}
private void cancelAlarm() {
SharedPreferences savedSharedPreferences = getApplicationContext().getSharedPreferences("USER_PREFERENCES", Context.MODE_PRIVATE);
final SharedPreferences.Editor editor = savedSharedPreferences.edit();
editor.putInt("Test", 0);
editor.commit();
}
Here is the actual ReminderWorker class, I put a SHaredPreference variable to check the amount of times the worker fired, and a Date objectto check the time fired. These are printed in the notification.
public class ReminderWorker extends Worker {
int i;
public final String CHANNEL_ID = "MainChannel";
public ReminderWorker(#NonNull Context context, #NonNull WorkerParameters params) {
super(context, params);
}
#NonNull
#Override
public Result doWork() {
Date date = new Date();
Calendar cal = Calendar.getInstance();
cal.setTime(date);
SharedPreferences savedSharedPreferences = getApplicationContext().getSharedPreferences("USER_PREFERENCES", Context.MODE_PRIVATE);
final SharedPreferences.Editor editor = savedSharedPreferences.edit();
i = savedSharedPreferences.getInt("Test", 0) + 1;
editor.putInt("Test", i);
editor.commit();
createNotificationChannel();
buildNotification(cal);
return Result.success();
}
private void createNotificationChannel() {
String name = "Birthday Notifications";
String description = "Reminds you when your friends birthday approaches";
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
channel.setDescription(description);
NotificationManager notificationManager = getApplicationContext().getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
private void buildNotification(Calendar cal) {
Context context = getApplicationContext();
Intent openTap = new Intent(context, EnterActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, openTap, 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID);
builder.setSmallIcon(R.drawable.pix_cake);
builder.setContentTitle("TestNotification");
builder.setStyle(new NotificationCompat.BigTextStyle()
.bigText("TestText" + i + " Time: " + cal.get(Calendar.HOUR) + ":" + cal.get(Calendar.MINUTE)));
builder.setPriority(NotificationCompat.PRIORITY_MAX);
builder.setContentIntent(pendingIntent);
builder.setAutoCancel(true);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
notificationManager.notify(i, builder.build());
//notificationManager.cancelAll();
}
}
In case it is needed, here is my AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myfirstapp">
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.SET_ALARM" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:allowBackup="true"
android:icon="#mipmap/main_icon"
android:label="#string/app_name"
android:roundIcon="#mipmap/main_icon_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".ListOfDaysActivity" />
<activity android:name=".MainActivity" />
<activity android:name=".EnterActivity"
android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
No errors thrown, just not the result expected. I need a notification to show up roughly every hour but that simply isn't happening.Any way around this?
Do you remove the application from task manager? Which device are you using for testing? There are some devices that force close the app and WorkManager tasks are rescheduled once you open the app again.
This answer might help you understand what is going on - https://stackoverflow.com/a/52605503/1313699
Related
I am making an application that notifies the user at certain times of the day. Like an alarm clock. The code works normally in versions prior to Android Oreo.
From what I read, Android Oreo and later versions kill actions in the background and that's why I'm having the error below.
2020-07-13 20:31:06.766 1609-1737/? W/BroadcastQueue: Background execution not allowed: receiving Intent { act=android.intent.action.PACKAGE_REPLACED dat=package:studio.com.archeagemanager flg=0x4000010 (has extras) } to air.br.com.alelo.mobile.android/co.acoustic.mobile.push.sdk.wi.AlarmReceiver
It is as if the BroadcastService is simply not triggered when it should be. But when I open the app, it starts up instantly.
AndroidManifest.xml
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="owner.custom.permission" />
<permission
android:name="owner.custom.permission"
android:protectionLevel="signatureOrSystem">
</permission>
<meta-data
android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
<application
android:allowBackup="true"
android:icon="#mipmap/app_icon_new"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".SplashScreenActivity"
android:theme="#style/AppCompat.TelaCheia">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:screenOrientation="portrait"
android:theme="#style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<receiver
android:name=".AlarmReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
TabFragEventA.java (Here I put only the function that initiates the alarm)
public void startAlarm(int eventID) {
String[] NOTIFICATION_TITLES = {"Ocleera Rift", "Ocleera Rift", "Mistmerrow Conflict", "Mistmerrow Conflict",
"Mistmerrow Conflict", "Nation Construction Quests", "Diamond Shores", "Diamond Shores", "Battle of the Golden Plains",
"Battle of the Golden Plains", "Karkasse Ridgelands", "Kraken", "The Mirage Isle Fish Fest", "Red Dragon",
"Abyssal Attack", "Lusca Awakening", "Delphinad Ghostship", "Cattler Wrangler", "Legendary Chef", "+1"};
String notificationTitle = getString(R.string.contentTitle);
String notificationText = NOTIFICATION_TITLES[eventID] + getString(R.string.contentNotificationText);
int alarmHour = localHour.get(eventID);
int alarmMinute = localMinute.get(eventID);
// Decrease 5 minutes
if(alarmMinute == 0) {
alarmHour = alarmHour - 1;
alarmMinute = 55;
} else {
alarmMinute = alarmMinute - 5;
}
// Setting the alarm moment
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, alarmHour);
calendar.set(Calendar.MINUTE, alarmMinute);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
// Analyze if the alarm moment has passed
Calendar actualTime = Calendar.getInstance();
if(actualTime.getTimeInMillis() >= calendar.getTimeInMillis()) {
calendar.add(Calendar.DAY_OF_MONTH, 1);
}
Intent intent = new Intent(getActivity().getApplicationContext(), AlarmReceiver.class);
intent.putExtra("contentTitle", notificationTitle);
intent.putExtra("contentText", notificationText);
/*if(eventID == 11 || eventID == 13) {
intent.putExtra("specificDayWeek", true);
} else {
intent.putExtra("specificDayWeek", false);
}*/
if(eventID == 12) {
intent.putExtra("castleSupply", true);
intent.putIntegerArrayListExtra("castleSupplyDayWeek", (ArrayList<Integer>) CastleSupply);
}
else if(eventID == 13) {
intent.putExtra("castleClaim", true);
intent.putIntegerArrayListExtra("castleClaimDayWeek", (ArrayList<Integer>) CastleClaim);
}
else if(eventID == 14) {
intent.putExtra("abyssalAttack", true);
intent.putIntegerArrayListExtra("abyssalDaysWeek", (ArrayList<Integer>) AbyssalDayWeek);
}
else if(eventID == 15) {
intent.putExtra("luscaAwakening", true);
intent.putIntegerArrayListExtra("luscaAwakeningDayWeek", (ArrayList<Integer>) LuscaAwakening);
} else {
intent.putExtra("abyssalAttack", false);
}
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
// PendingIntent pendingIntent = PendingIntent.getBroadcast(getActivity().getApplicationContext(), eventID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getActivity().getApplicationContext(), eventID, intent, 0);
AlarmManager alarmManager = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
}
AlarmReceiver.java
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String contentTitle = intent.getStringExtra("contentTitle");
String contentText = intent.getStringExtra("contentText");
String CHANNEL_ID = "my_channel_01";
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = "Channel Name";
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name, importance);
notificationManager.createNotificationChannel(mChannel);
}
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.icon_notification)
.setContentTitle(contentTitle)
.setContentText(contentText)
.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.icon_notification))
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
.setVibrate(new long[]{800, 500, 600, 300});
notificationManager.notify(0, notificationBuilder.build());
Log.d("ALARMRECEIVER", "INSIDE");
}
}
I would like to know what to do to make the Broadcast Receiver work in the background. Or an alternative to set this alarm clock on Android Oreo + so that it notifies the user even with the application closed or in the background.
Android has a doze mode in which it goes to sleep after some inactivity, Even if you manage to do this on some phones chinese ROMs will trouble you for sure ( in which removing from recent apps works as force stopping application)
For your problem there are solutions Like Work manager , Foregroundservices ,jobscheduler it should work but again can't say for all the ROMs. I think right now there isn't a proper solution to this background processing.
But One thing you can do is sending a FCM notification from server with high priority.
You can see that Facebook and whatsapp can work in background because they are whitelisted by the companies. You can whitelist your application by enabling auto start from settings.But you need to do it manually which isnt a case when we talk about fb and whatsapp
Check this website for more details : https://dontkillmyapp.com/
With this issue Most affected are alarm clocks, health trackers, automation apps, calendars or simply anything which needs to do some job for you at a particular moment when you don’t use your phone.
With Android 6 (Marshmallow), Google has introduced Doze mode to the base Android, in an attempt to unify battery saving across the various Android phones.
Unfortunately, some manufacturers (e.g. Xiaomi, Huawei, OnePlus or even Samsung..) did not seem to catch that ball and they all have their own battery savers, usually very poorly written, saving battery only superficially with side effects.
Starting from Android oreo and up no more background services are supported. the suggestion from the developer documentation is to use a foreground service. To keep the foreground service running you need to hook it up with a notification. you can configure the notification to hide or not visible in the UI at all lately.
check this solution
#RequiresApi(api = Build.VERSION_CODES.O)
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
context = this;
String input = intent.getStringExtra("inputExtra");
createNotificationChannel();
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("GPS Service")
.setContentText("GPS Service")
// .setLargeIcon(emailObject.getSenderAvatar())
.setStyle(new NotificationCompat.BigTextStyle()
.bigText("Ready\nAndroi " + android.os.Build.VERSION.SDK_INT))
.setPriority(Notification.PRIORITY_HIGH)
.setOngoing(true)
.build();
startForeground(1, notification);
getLocation();
//do heavy work on a background thread
locationModel = new LocationModel("", "", "", "", "");
LongOperation longOperation = new LongOperation();
longOperation.execute();
//stopSelf();
return START_STICKY;
}
and in the main activity onCreate
if (!isMyServiceRunning(ForegroundService.class)) {
BroadcastReceiver br = new GPSBroadcastReceiver();
IntentFilter filter = new IntentFilter(CONNECTIVITY_ACTION);
filter.addAction(Intent.ACTION_BOOT_COMPLETED);
getApplicationContext().registerReceiver(br, filter);
}
checking whether the service is running
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
here is an example from one of my projects. Foreground service is the way to go!
some helpful sources.
foreground service example 1
foreground service example 2
repeating alarm manager example
If you are writing an Clock apps, use AlarmManager.setAlarmClock. It be allowed to trigger even if the system is in a low-power idle (a.k.a. doze) mode. When you set an AlarmClock, it's visible to the user. And SystemUI may show different icons or alarms.
If you just want to be triggered at certain time to do some work, there is no good way to escape the background limit.
My app work when my android is not restart but when I turn off my Android the app not working despite I add BOOT_COMPLETED.
I have looked for similar questions but all of them work just as I do, I do not know what is wrong
Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.proyect.d.alarm">
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.SET_ALARM" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<service android:name=".BootService" />
<receiver
android:name=".RestartAlarmsReceiver"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver
android:name=".MyAlarmReceiver"
android:process=":remote" />
</application>
RestartAlarmsReciver
public class RestartAlarmsReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {
Intent i = new Intent(context, BootService.class);
ComponentName service = context.startService(i);
}
}
}
BootService: it's equals that my main AlarmService
public class BootService extends IntentService {
public BootService(String name) {
super(name);
}
private NotificationManager notificationManager;
private final int NOTIFICATION_ID = 1010;
private AdminSQLiteOpenHelper admin;
private Cursor fila;
private SQLiteDatabase bd;
private String alarm, descrip, title;
#Override
protected void onHandleIntent(#Nullable Intent intent) {
Calendar calenda = Calendar.getInstance();
int hour, min, day, m, year;
String cadenaF, cadenaH, date_system, time_system;
day = calenda.get(Calendar.DAY_OF_MONTH);
m = calenda.get(Calendar.MONTH) + 1;
year = calenda.get(Calendar.YEAR);
hour = calenda.get(Calendar.HOUR_OF_DAY);
min = calenda.get(Calendar.MINUTE);
date_system = m + "-" + day + "-" + year + " ";
time_system = hour + ":" + min;
admin = new AdminSQLiteOpenHelper(getApplicationContext(), vars.bd, null, vars.version);
bd = admin.getWritableDatabase();
if (bd != null) {
fila = bd.rawQuery("SELECT * FROM alarma WHERE datea='" + date_system + "' AND timea= '" + time_system + "'", null);
if (fila.moveToFirst()) {
alarm = fila.getString(0);
title = fila.getString(1);
descrip = fila.getString(2);
triggerNotification(getApplicationContext(), title + "\n" + descrip);
}
}
bd.close();
}
private void triggerNotification(Context contexto, String t) {
Intent notificationIntent = new Intent(contexto, MainActivity.class);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent contentIntent = PendingIntent.getActivity(contexto, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Uri defaultSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
long[] pattern = new long[]{2000, 1000, 2000};
NotificationCompat.Builder builder = new NotificationCompat.Builder(contexto);
builder.setContentIntent(contentIntent)
.setTicker("")
.setContentTitle("alarm ")
.setContentTitle("")
.setContentText(t)
.setContentInfo("Info")
.setLargeIcon(BitmapFactory.decodeResource(contexto.getResources(), R.drawable.ic_launcher_background))
.setSmallIcon(R.drawable.ic_launcher_background)
.setAutoCancel(true)
.setSound(defaultSound)
.setVibrate(pattern);
Notification notificacion = new NotificationCompat.BigTextStyle(builder)
.bigText(t)
.setBigContentTitle("example")
.setSummaryText("more example")
.build();
notificationManager = (NotificationManager) contexto.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID, notificacion);
}
}
Thanks
Can you change your receiver to add export and category?
<receiver android:name=".RestartAlarmsReceiver" android:enabled="true" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
As per official documentation
android:exported
Whether or not the broadcast receiver can receive messages from sources outside its application — "true" if it can, and "false" if not. If "false", the only messages the broadcast receiver can receive are those sent by components of the same application or applications with the same user ID.
The default value depends on whether the broadcast receiver contains intent filters. The absence of any filters means that it can be invoked only by Intent objects that specify its exact class name. This implies that the receiver is intended only for application-internal use (since others would not normally know the class name). So in this case, the default value is "false". On the other hand, the presence of at least one filter implies that the broadcast receiver is intended to receive intents broadcast by the system or other applications, so the default value is "true".
This attribute is not the only way to limit a broadcast receiver's external exposure. You can also use a permission to limit the external entities that can send it messages (see the permission attribute).
Hope this will help
Im trying to create a notification from an edittext and broadcast receiver. In my first Activity the user should input a message and push the broadcast button. I want to take that string and create a notification from it and open a new activity that displays the message. I am doing all the notification work in my broadcast receiver class.
I have looked around onlne at examples and other peoples code but im not sure what im not getting right. The application loads up just fine and the broadcast button sends the broadcast to the receiver and Logs the string but the notification is never created.
Thanks for any help.
Broadcast class that sends broadcast message:
public class BroadcastReceiverActivity extends Activity
{
EditText et;
Button btn1;
public static String BString = "HappyHemingway";
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_broadcast_receiver);
et = (EditText)findViewById(R.id.et1);
btn1 = (Button)findViewById(R.id.btn1);
btn1.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
String message = et.getText().toString();
send(message);
}
});
}
/*
* This function creates an intent and
* sends a broadcast from the message
* parameter passed in.
*/
protected void send(String msg)
{
Log.i("msg", msg);
Intent i = new Intent();
i.putExtra("message",msg);
i.setAction(BString);
sendBroadcast(i);
}
}
Receiver class that creates notification:
public class Receiver extends BroadcastReceiver
{
// #SuppressLint("NewApi")
#Override
public void onReceive(Context context, Intent intent)
{
String action = intent.getAction();
if(action!=null&&action.equals("HappyHemingway"))
{
String msg = intent.getStringExtra("message");
Log.i("Received",msg);
Intent i = new Intent(context,ViewNotification.class);
i.putExtra("message",msg);
PendingIntent pi = PendingIntent.getActivity(context, 0, i,
PendingIntent.FLAG_UPDATE_CURRENT);
Notification.Builder builder = new Notification.Builder(context).
setSmallIcon(0).setAutoCancel(true).setTicker(msg).
setWhen(System.currentTimeMillis()).setContentTitle("New Notification!").
setContentText(msg).setContentIntent(pi);
NotificationManager mgr = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
Notification n = builder.build();
mgr.notify(0, n);
Log.i("Received again",msg);
}
}
}
notification viewer class that is never launched
public class ViewNotification extends Activity
{
String text;
TextView txttext;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.viewnotification);
NotificationManager notificationmanager;
notificationmanager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
notificationmanager.cancel(0);
Intent i = getIntent();
text = i.getStringExtra("message");
txttext = (TextView) findViewById(R.id.text);
txttext.setText(text);
Log.i("made it", "made it made it made it");
}
}
manifest
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".BroadcastReceiverActivity"
android:label="#string/app_name" >
<action android:name="android.intent" />
<category android:name="android.intent.category.LAUNCHER" />
</activity>
<activity android:name=".ViewNotification"></activity>
<receiver android:name="Receiver">
<intent-filter>
<action android:name="HappyHemingway">
</action>
</intent-filter>
</receiver>
</application>
</manifest>
Hopefully its just a simple error I'm overlooking.This is my first time using Android Studio instead of Eclipse but I dont see how that could make any difference under than my unfamiliarity with the IDE.
Anything helps
thanks.
I'm not sure why I had setSmallIcon(0.)
When I changed it to setSmallIcon(R.drawable.ic_launcher) everything worked fine.
I have certain dates which once attained lose their relevance and new dates for these fields in the DB should be calculated, I know I can leverage the AlarmManager class for this, however I have a few concerns regarding this:
1) Note: Beginning with API 19 (KITKAT) alarm delivery is inexact: the OS will shift alarms in order to minimize wakeups and battery use. There are new APIs to support applications which need strict delivery guarantees; see setWindow(int, long, long, PendingIntent) and setExact(int, long, PendingIntent). Applications whose targetSdkVersion is earlier than API 19 will continue to see the previous behavior in which all alarms are delivered exactly when requested.
So do I need to code for both cases separately or if I target kitkat, will that work for older versions too? Also as my code execution is time critical, say after 12AM in the midnight of some date my Data loses relevance, how to overcome shifting of alarms.
2)Registered alarms are retained while the device is asleep (and can optionally wake the device up if they go off during that time), but will be cleared if it is turned off and rebooted.
2.1) Set the RECEIVE_BOOT_COMPLETED permission in your application's manifest. This allows your app to receive the ACTION_BOOT_COMPLETED that is broadcast after the system finishes booting (this only works if the app has already been launched by the user at least once)
2.1.1) If I have set an alarm at 12, the service related to this alarm fires at 12, Now when I reboot the device, the time "at 12" has already passed, the alarm will be fired again immediately and the service will be called again?
At reboot what mechanism do I need to implement in-order to stick to my code execution policy at certain time? How do I set the alarm if the user does not launch my app?
The third thing is that if my app is uninstalled I want to clear all alarms set by my code, how do I listen to when the app is uninstalled?
Also I want to know, my app is very time critical, the values in my DB get obsolete by 12 am each night, while I am updating the app, what would be the result if a user chooses to use my app at 12 while I use a service to update it and its running in the background?
EDIT: What I have tried so far:
I have a Database in which records get stale past midnight, say sharp at 12:00. I invoked an Alarm Manager(In a test project as I like to isolate the problem code) to fire a service. I also acquire a PARTIAL_WAKE_LOCK on the device so that my huge database manipulation is done properly. I have also implemented a thread to do my time consuming task. Following is my MainActivity Class which I invoke at 12 to initiate the alarm(Random time for test purpose):
public class MainActivity extends Activity {
private AlarmManager alarmMgr;
private PendingIntent alarmIntent;
BroadcastReceiver br;
TextView t;
int sum;
public void setSum(int s){
sum = s;
// t = (TextView)findViewById(R.id.textView1);
// t.setText(sum);
System.out.println("In Set Sum"+s);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setup();
t = (TextView)findViewById(R.id.textView1);
ComponentName receiver = new ComponentName(getApplicationContext(), SampleBootReceiver.class);
PackageManager pm = getApplicationContext().getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 17);
calendar.set(Calendar.MINUTE, 05); // Particular minute
calendar.set(Calendar.SECOND, 0);
alarmMgr = (AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
1000*60*60*24, alarmIntent);
}
public void setup() {
br = new BroadcastReceiver() {
#Override
public void onReceive(Context c, Intent i) {
Toast.makeText(c, "Rise and Shine!", Toast.LENGTH_LONG).show();
//Invoke the service here Put the wake lock and initiate bind service
t.setText("Hello Alarm set");
startService(new Intent(MainActivity.this, MyService.class));
stopService(new Intent(MainActivity.this, MyService.class));
}
};
registerReceiver(br, new IntentFilter("com.testrtc") );
alarmIntent = PendingIntent.getBroadcast( this, 0, new Intent("com.testrtc"),0 );
alarmMgr = (AlarmManager)(this.getSystemService( Context.ALARM_SERVICE ));
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
This is my SampleBootReceiver Class inorder to check for reboots and set Alarms again after reboot, I am not sure if it works as intended. I could find no means to test if this is working properly but I do receive the Toast message about completion of boot.
public class SampleBootReceiver extends BroadcastReceiver {
private AlarmManager alarmMgr;
private PendingIntent alarmIntent;
BroadcastReceiver br;
TextView t;
MainActivity main;
#Override
public void onReceive(Context context, Intent intent) {
main= new MainActivity();
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
Toast.makeText(context, "Hello from Bootloader", 10000).show();
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 15);
calendar.set(Calendar.MINUTE, 50); // Particular minute
calendar.set(Calendar.SECOND, 0);
alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
1000*60*60*24, alarmIntent);
context.getApplicationContext().registerReceiver(br, new IntentFilter("com.testrtc") );
alarmIntent = PendingIntent.getBroadcast( context.getApplicationContext(), 0, new Intent("com.testrtc"),
0 );
alarmMgr = (AlarmManager)(context.getApplicationContext().getSystemService( Context.ALARM_SERVICE ));
}
}
}
The following is my service Class, unsure about the return I am doing here in the onStartCommand method:
public class MyService extends Service {
int a = 2;
int b = 2;
int c = a+b;
public MainActivity main = new MainActivity();
public MyService() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onCreate() {
Toast.makeText(this, "The new Service was Created", Toast.LENGTH_LONG).show();
}
#Override
public int onStartCommand(Intent i, int flags , int startId){
WakeLock wakeLock = null;
try{
PowerManager mgr = (PowerManager)getApplicationContext().getSystemService(Context.POWER_SERVICE);
wakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
wakeLock.acquire();
Toast.makeText(this, " Service Started", Toast.LENGTH_LONG).show();
new Thread(new Runnable() {
public void run(){
//Will be substituted with a time consuming long task.
main.setSum(c);
}
}).start();
}catch(Exception e){
System.out.println(e);
}finally{
wakeLock.release();
}
return 1;
}
#Override
public void onDestroy() {
Toast.makeText(this, "Service Destroyed", Toast.LENGTH_LONG).show();
}
}
Also in the above, I want to know if the thread I am starting will interfare with how I am acquiring the wake lock. Also if I can use an async task and release the wakelock in onPostExecute?
Finally here is my Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.testrtc"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.testrtc.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=".SampleBootReceiver"
android:enabled="false" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" >
</action>
</intent-filter>
</receiver>
<service
android:name="com.testrtc.MyService"
android:enabled="true"
android:exported="true" >
</service>
</application>
</manifest>
THis is my log Cat after reboot, there are many log messages however I find these related to the app, they contain th epackage name:
01-22 15:18:35.652: V/ActivityManager(419): getTasks: max=1, flags=0, receiver=null
01-22 15:18:35.652: V/ActivityManager(419): com.xxx.xxx/.MainActivity: task=TaskRecord{425c58f0 #5 A com.xxx.xxx U 0}
01-22 15:18:35.653: V/ActivityManager(419): We have pending thumbnails: null
More Questions: Where should I set up the Alarm, if I do it in the onCreate of my Splash screen it will be called each time the app starts, maybe overwriting the older values.
Second I want to acquire a lock on the DB when my service is running, if in this time the user tries to open my app what do I do? (As data is getting updated I dont have anything to show).
Third in the above code I am still finding problems to register an alarm after reboot.
1) To handle alarm at exact times you have 2 options:
a) Set minimum SDK level at 18 and not 19. This will make your app work on kitkat at the exact time
b) Use the setExact() method to tell Kitkat to keep your alarm at the exact time specified
Source: http://developer.android.com/about/versions/android-4.4.html
2) The boot completed notification is the best way to go. You can set up a Broadcast receiver to get the notification on a reboot. You can store alarms in a Database and retrieve on reboot.
2.1) It is horrible programming practice to do something the user did not want you to do, i.e. set an alarm even though user has not opened the app.
3) As long as you don't create files on the SD card, all application data is removed upon uninstall of the app
4) You should lock/unlock data when writing or reading from it to solve your 12AM problem. If the data is locked, it would not get affected until the user has committed the transaction. If you are using SQLite you can get more information on this from: http://www.sqlite.org/lockingv3.html
I think, you can write a Service something similar to the below one (This will do your task for every 5 minutes) and start the Service from the BroadcastReceiver which listens to BOOT_COMPLETED state.
public class YourService extends IntentService {
public YourService() {
super("YourService");
}
public YourService(String name) {
super(name);
}
private static Timer timer = new Timer();
private class mainTask extends TimerTask {
public void run() {
// TASK WHATEVER YOU WANT TO DO
}
}
public void onDestroy() {
super.onDestroy();
}
#Override
protected void onHandleIntent(Intent intent) {
timer.scheduleAtFixedRate(new mainTask(), 0, 300000); // 5 minutes
}
}
I have a reminder app I'm developing and everything works like its supposed to except the time the user is notified. I use a Time Picker to choose the time, but do not know how to add the user-set time to the pending intent. I have researched the android docs as well as other sites with similar searches, but to no avail. Any ideas?
MainActivity.java
public class MainActivity extends Activity{
int mHour;
int mMinute;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public void setAlarm(View view) {
TimePicker picker = (TimePicker)findViewById(R.id.timePicker1);
EditText text = (EditText)findViewById(R.id.editText1);
Toast toast = Toast (text, picker);
toast.show();
Intent intent = new Intent(this, AlarmBoadcastReceiver.class);
PendingIntent pendingintent = PendingIntent.getBroadcast(this.getApplicationContext(), 1234, intent, 0);
Calendar AlarmCal = Calendar.getInstance();
AlarmCal.setTimeInMillis(System.currentTimeMillis());
AlarmCal.set(Calendar.HOUR_OF_DAY, mHour);
AlarmCal.set(Calendar.MINUTE, mMinute);
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, /*No Idea what to put here*/, pendingintent);
OnTimeSetListener mTimeSetListener = new OnTimeSetListener() {
#Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
// TODO Auto-generated method stub
mHour = hourOfDay;
mMinute = minute;
}
};
}
private Toast Toast(EditText text, TimePicker picker) {
return Toast.makeText(this, "Reminder set for " + picker.getCurrentHour().toString() + ":"
+ picker.getCurrentMinute() + " with message " + "'" + text.getText().toString() + "'",
Toast.LENGTH_LONG);
}
public void exitApp(View view) {
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
AlarmBoadcastReceiver.java
public class AlarmBoadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(2000);
}
}
Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.reminder"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.VIBRATE" >
</uses-permission>
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.reminder.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="AlarmBoadcastReceiver" >
</receiver>
</application>
</manifest>
With what is posted above the phone vibrates every time the Set Time button is pressed, not when the Time Picker time is set for. Thanks for any help!
The first thing you should note, is that the AlarmManager has two modes for firing off an Alarm at a set time.
The set() will fire off the Alarm at the given time. You will note as of API 19, Alarms defined using the set() method may be batched and therefore may not be fired at the exact time:
Note: Beginning in API 19, the trigger time passed to this method is
treated as inexact: the alarm will not be delivered before this time,
but may be deferred and delivered some time later.
http://developer.android.com/reference/android/app/AlarmManager.html#set(int, long, android.app.PendingIntent)
The setExact() method was added as of API 19 and guarantees that the Alarm will be fired off at the exact given time.
The times passed in are defined as per the first parameter of the set() or setExact() methods. You have chosen RTC_WAKEUP, you need to define the wakeup time in milliseconds UTC time (as given by the System.currentTimeMillis() method).
See: http://developer.android.com/reference/android/app/AlarmManager.html#RTC_WAKEUP
The easiest way of getting this time is by calling:
AlarmCal.getTimeInMillis()
Just a small note, if the given time is in the past, then the Alarm will instantly fire. This is why your Alarm was being triggered the moment you set it.
Additional note from the documentation:
Registered alarms are retained while the device is asleep (and can
optionally wake the device up if they go off during that time), but
will be cleared if it is turned off and rebooted.