Encapsulating alarm creation code in a different class than the Activity - java

I'm creating my first Android Notifications app, so I'm very much a beginner. I have a class, Notification.java, that asks the user for the time and date. Using these data, it creates an alarm that is triggered at the specified date and time.
Here is my code for Notification.java
public class Notification extends Activity {
private PendingIntent pendingIntent;
private SetAlarm alarm;
private Date date;
private Time time;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_alarm);
findViewById(R.id.setTime).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
setAlarmTime();
}
});
findViewById(R.id.setDate).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
setAlarmDate();
}
});
findViewById(R.id.checkBox).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
createAlarm();
}
});
}
private void setAlarmTime() {
}
private void setAlarmDate() {
}
private void createAlarm() {
alarm = new SetAlarm();
}
}
The createAlarm() method is supposed to actually create the alarm using the information that the user has provided (i.e. time and date). However, I understand that I need the following code block to create the alarm?
private void setTheAlarm() {
Intent alarmIntent = new Intent(SetAlarm.this, AlarmReceiver.class);
pendingIntent = PendingIntent.getBroadcast(SetAlarm.this, 0, alarmIntent, 0);
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
int interval;
/* Set the alarm to the date specified by user */
/* Repeating on every x minutes interval */
}
However, the Notification.java is where I am extending 'Activity'. It is also where I have the 'pendingIntent; code.
So essentially, how can I move the alarm creation code into a separate class when the code dealing with the Activity is in an entirely different class?
Thanks for the help. I hope my question is clear enough.

Not exactly clear if that is what you want, but if I understand you correct, you need the alarmManager inside an extra class to reach it from everywhere? You could make a static one like this:
public class MyAlarmManager{
private static AlarmManager mAlarmManager;
private static PendingIntent mPendingIntent;
//start alarm
public static void setAlarm(Context context, int alarmId, long alarmTime) {
if (mAlarmManager== null) {
mAlarmManager= (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
}
Intent startAlarmIntent = new Intent(context, YouReceiver.class);
if(mPendingIntent==null){
mPendingIntent = PendingIntent.getBroadcast(context, alarmId,
startAlarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
}
//check the version because of doze mode since MM
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,
alarmTime, mPendingIntent);
} else {
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTime, mPendingIntent);
}
}
//stop alarm
public static void stopAlarm(Context context, int id) {
if (mAlarmManager == null) {
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
}
Intent stopAlarmIntent = new Intent(context, YourReceiver.class);
if(mPendingIntent==null){
mPendingIntent= PendingIntent.getBroadcast(context, id, stopAlarmIntent, 0);
}
mAlarmManager.cancel(mPendingIntent);
mPendingIntent.cancel();
}
}
Then you can call it like:
MyAlarmManager.setAlarm(this, id, interval);
and stop it:
MyAlarmManager.stopAlarm(this, id);
You can do this from every class by passing the context and the identical id . The alarm id must be the same as you passed by starting the alarm, otherwise it will not work. Notice that above MarshMallow, there are some changes for AlarmManager and it´s possible that it does not work in every case. If your app get´s killed or goes into idle mode, the alarm won´t be triggered in every circumstance. To handle doze mode, see this:https://developer.android.com/training/monitoring-device-state/doze-standby.html
And be aware of any third party app and battery managers, that could kill your app. Also, Huawei devices have their own battery management besides the doze mode.
If this is not what you wanted, come back. Can´t guarantee that there is no error because I have overseen something, it´s from scratch.

Related

Can't cancel a notification after updating it

I'm developing a countdown app, and currently trying to show a notification when you exit the app while the countdown is running. Correspondingly, I want the notification to disappear when the user returns to the app.
So far I've managed to make it work for a simple notification with static text, do the following: in MainActivity.java, in onStop(), I create an intent and initiate the service with startService(intent). Symmetrically, in onStart() I run stopService(intent) so that when you return to the app the service gets canceled. This works like a charm, the notification appears and disappears when it must.
The next step has been trying to make the notification show a text that varies (it will say "X minutes remaining"). According to the info out there, to update an existing notification you have to create a new one, give it the same ID as the existing one, and call .notify of a NotificationManager. When I do this the notification indeed gets updated correctly (the text changes as expected), BUT: now, returning to the main activity does not cancel the notification. The icon stays up there and doesn't get interrupted.
I've been trying to solve this for hours and hours. I've also tried hacks like sending signals via shared preferences to tell the service to stop, but for some reason, it seems to completely ignore the command stopself() too.
Does anybody have a suggestion of what could be the cause? Any help would be greatly appreciated. Here is the relevant code:
MainActivity.java:
#Override
protected void onStart() {
super.onStart();
Intent serviceIntent = new Intent(getBaseContext(), CounterService.class);
stopService(serviceIntent);
}
protected void onStop() {
super.onStop();
Intent serviceIntent = new Intent(getBaseContext(), CounterService.class);
startService(serviceIntent);
}
CounterService.java:
public class CounterService extends Service {
Notification notification;
NotificationManager notificator;
Intent intentNoti;
CountDownTimer counter;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
intentNoti = new Intent(this, MainActivity.class);
final PendingIntent pending = PendingIntent.getActivity(this, 0, intentNoti, 0);
final Bitmap icon = BitmapFactory.decodeResource(getResources(),
R.drawable.common_full_open_on_phone);
//Countdown
counter = new CountDownTimer (30000, 1000) {
public void onTick(long millisUntilFinished) {
String time = String.valueOf(System.currentTimeMillis());
notification = new Notification.Builder(CounterService.this)
.setContentTitle("Name")
.setContentText(time)
.setSmallIcon(R.drawable.icon_start)
.setLargeIcon(Bitmap.createScaledBitmap(icon, 128, 128, false))
.setContentIntent(pending)
.setOngoing(true).build();
notificator = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificator.notify(1001, notification);
}
public void onFinish() {
}
}.start();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
counter.cancel();
}
}
First create a Timer like this
private Timer timer;
private TimerTask timerTask;
public void startTimer() {
timer = new Timer();
timerTask = new TimerTask() {
public void run() {
// Add your code
}
};
timer.schedule(timerTask, 1000, 1000); //
}
Also you need to stop your timer.
So
public void stoptimertask() {
if (timer != null) {
timer.cancel();
timer = null;
}
}
Call StartTimer and StopTimer in OnStartCommand() and onDestroy() respectively. Add these lines in onDestroy()
Intent broadcastIntent = new Intent();
broadcastIntent.setAction("restartservice");
broadcastIntent.setClass(this, Restarter.class);
this.sendBroadcast(broadcastIntent);
it can be handled in multiple ways, you have not stopped your timer
Note:- posting code in Kotlin
1)
override fun onDestroy() {
counter.cancel()
}
in your activity
override fun onResume() {
super.onResume()
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.cancelAll()
}

Android - Call a function from outside an activity (through AlarmManager and Notification)

Ok, so I have a main activity called 'Main.java'. This main activity starts an AlarmManager which fires an intent leading to 'AlarmReceiver.java'.
This 'AlarmReceiver.java' then creates a notification which has two buttons on it. One of the buttons is a deletion button, and so when the user clicks on that button, another intent is fired, leading it to 'DelPair.java'.
In DelPair.java, I modify a table in a Database, but then I need the UI of Main.java to reflect this change. I have created two functions in Main.java called updateArrayFromDB() and updateUIFromArray() to do this for me:
updateArrayFromDB() will sync an ArrayList created in Main.java to a
certain table in the DB.
updateUIFromArray() will change the UI of
Main.java to represent the ArrayList that has just been changed.
The problem is that I cannot call these two functions from DelPair.java (they don't exist in that space). I have come across Serializables in trying to find an answer but I don't know enough to know if they apply here or exactly how to implement them across the AlarmManager and the NotificationManager.
How can I access these methods from DelPair.java?
In Main.java:
public void updateArrayFromDB(){
//... The code for this is long and irrelevant
}
public void updateUIFromArray(){
//... The code for this is long and irrelevant
}
private void SendNotification() {
Intent intent = new Intent(this, AlarmReceiver.class);
//...
PendingIntent sender = PendingIntent.getBroadcast(this, 2 , intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.setRepeating(AlarmManager.RTC_WAKEUP, 5000, notif_freq, sender);
}
In AlarmReceiver.java:
Intent delPairI = new Intent(context, DelPair.class);
PendingIntent delPairPI = PendingIntent.getService(context, 0, delPairI, PendingIntent.FLAG_UPDATE_CURRENT);
Notification noti;
noti = new Notification.Builder(context)
//...
.addAction(R.drawable.ic_delete_icon, "Delete the thing", delPairPI)
.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, noti);
and then in DelPair.java:
public class DelPair extends IntentService {
//...
#Override
protected void onHandleIntent(final Intent intent) {
//...
Intent it = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
getApplicationContext().sendBroadcast(it);
handler.post(new Runnable() {
#Override
public void run() {
//... here is where I update the database, which works perfectly
//now need to update the UI and array in Main.java
updateArrayFromDB(); //these lines
updateUIFromArray(); //obviously don't work
}
});
}
}
Why not use broadcasts ? in onHandleIntent just send a broadcast
Intent i = new Intent();
i.setAction(CUSTOM_INTENT);
//put relevant data in intent
getApplicationContext().sendBroadcast(i);
The broadcast receiver:
public class IncomingReceiver extends BroadcastReceiver {
private MainActivity act;
public IncomingReceiver(MainActivity main){
this.act = act;
}
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(CUSTOM_INTENT)) {
System.out.println("GOT THE INTENT");
// call the method on act
}
}
}
In your activity onResume - register new IncomingReceiver, onPause unregister
private IncomingReceiver receiver;
public void onCreate(Bundle bOs){
//other codes
receiver = new IncomingReceiver(this);
}
#Override
protected void onResume() {
IntentFilter filter = new IntentFilter();
filter.addAction(CUSTOM_INTENT);
registerReceiver(receiver, filter);
super.onResume();
}
#Override
protected void onPause() {
unregisterReceiver(receiver);
super.onPause();
}
Since you need to have an updated UI based on database changes, you can call updateArrayFromDB() and updateUIFromArray() in the onResume() method of your activity so the UI gets updated each time the user enters the activity.

reminder as part of app in android

I am working on a project on android studio, it about medication reminder and I have some difficulties that how can I import a reminder in it.
for example I want the user to set the time as when he want to take his pill and then alert him at that time.
So, I search a lot but I did not find a suitable thing.
the reminder is part of my app so I want it as simple as possible.
I hope you understand me.
I tried this code but it didn't work !
final Calendar calender = Calendar.getInstance();
final int hours = calender.get(Calendar.HOUR_OF_DAY);
final int minutes = calender.get(Calendar.MINUTE);
alarm.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
TimePickerDialog pickTime = new TimePickerDialog(AddMedication.this, new TimePickerDialog.OnTimeSetListener() {
#Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
alarm.setText(hourOfDay + ":" + minute);
}
}, hours, minutes, true);
}
});
the method you show in your post is suitable when the app is running in the foreground all the time which is usually not the case.
The solution you need is using AlarmManager and BroadcastReceiver to accomplish this:
AlarmManager alarmMgr;
PendingIntent alarmIntent;
alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReceiver.class);
alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() +
60 * 1000, alarmIntent); //this alarm for example is set for the next minute
in your AlarmReceiver class you will have onReceive method:
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Either start an activity or send a notification here
}
}

Creating a background service that launches an app at a specific time.

I just started on android development and I'm trying to write and a service that runs in the background and will launch an app at a specific time.
The program I wrote is based on a tutorial I came across, basically the app has 2 buttons, a start and stop. Once the user presses the start button, it'll start the background service and will check the time and if the time is right, it'll launch the app.
However I noticed that it doesn't always checks the time, it only does it when the user presses the button. How do I make it so that, it'll keep checking the time, once the user presses the button?.
Here is my code.
MyService.java
public class MyService extends Service{
private static final String TAG = "MyService";
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
//Toast.makeText(this, "Congrats! MyService Created", Toast.LENGTH_LONG).show();
Log.d(TAG, "onCreate");
}
#Override
public void onStart(Intent intent, int startId) {
//Toast.makeText(this, "My Service Started", Toast.LENGTH_LONG).show();
startApp("com.example.myApp");
Log.d(TAG, "onStart");
}
#Override
public void onDestroy() {
Toast.makeText(this, "MyService Stopped", Toast.LENGTH_LONG).show();
Log.d(TAG, "onDestroy");
}
public void startApp(String packageName){
Calendar c = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
String strDate = sdf.format(c.getTime());
if(strDate == "09:00" || strDate == "15:00" || strDate == "21:00"){
Toast.makeText(this,strDate,Toast.LENGTH_LONG).show();
Toast.makeText(this,"Starting the App",Toast.LENGTH_LONG).show();
Intent intent = getPackageManager().getLaunchIntentForPackage(packageName);
if(intent != null){
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
}
}
MainActivity.Java
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//start the service
public void onClickStartServie(View V)
{
//start the service from here
startService(new Intent(this, MyService.class));
}
//Stop the started service
public void onClickStopService(View V)
{
//Stop the running service from here
//Service will only stop if it is already running.
stopService(new Intent(this, MyService.class));
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
I think using alarm manager will be a better option. Set an alarm for that time and receive broadcast in your receiver at that time like below code:
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, AppConstants.ALARM_ID_TESTING, new Intent(
AppConstants.FILTER_TESTING), PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + millisAfterCurrent, pendingIntent);
Add receiver in manifest:
<receiver
android:name=“TestReceiver"
<intent-filter>
<action android:name="filter.test.time” />
</intent-filter>
And receiver class as:
class TestReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
if (intent.getAction().equals(AppConstants.FILTER_TESTING))
{
// code here
}
}
}
Why dont you try Time Changed Receiver instead of service, Since using a background service is bad programming approah.A service keeps on running in background and takes a lot of system memory and a burden battery. Moreover system can stop any of extra services running at anytime for adjustment of memory issues.
A time change receiver is the best approach and in that you can compare time and open any app accordingly.
i suggest you this two ways:
Use the "new" ScheduledThreadPoolExecutor as replacement for Timer
Example:
int threadCount = 1;
ScheduledThreadPoolExecutor service = new ScheduledThreadPoolExecutor(threadCount);
long initialDelay = 5;
long period = 10;
ScheduledFuture<?> task = service.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
// DO IT EVERY 10 SECONDS
}
}, initialDelay, period, TimeUnit.SECONDS);
[...]
//if you want you can cancel the task later
task.cancel(true); // mayInterruptIfRunning = true
Since Activities, Threads and Processes can be terminated anytime by Android OS it is not guaranteed that the job is always executed.
Therefore, it is better to use AlarmManager
Official howto: Scheduling Repeating Alarms
Another nice example: Repeat Alarm Example In Android Using AlarmManager

Static Variable in Broadcast Receiver

Please review following code, i am not understanding where i am WRONG.
I need to know in Broadcastreciever whether APPLICATION IS AT FOREGROUND or in BACKGROUND. But in Recevier it always Returning FALSE. Why the value of static variable in BASEAPPLICATION lost?? Why its Showing FALSE always
public class Main extends Activity {
#Override
protected void onResume() {
super.onResume();
BaseApplication.activityResumed();
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, Recevier.class);
PendingIntent pi = PendingIntent.getBroadcast(context, intentCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
am.set(AlarmManager.RTC_WAKEUP, System.currentMilliSeconds() + 2000, pi);
}
protected void onStop() {
super.onStop();
BaseApplication.activityPaused();
}
BROADCAST RECIEVER
#Override
public void onReceive(Context context, Intent intent) {
if(BaseApplication.isActivityVisible()){
// Application is Running
}
else{
// Applicaiton is not Running
//**ALWAYS GIVING FALSE**
}
}
*BASE APPLICATION *
public class BaseApplication {
public static boolean isActivityVisible() {
return activityVisible;
}
public static void activityResumed() {
activityVisible = true;
}
public static void activityPaused() {
activityVisible = false;
}
private static boolean activityVisible;
}
You had better to check the top activity of activity stack.
public boolean isActivityVisible(Context context) {
ActivityManager am = (ActivityManager) context.
getSystemService(Activity.ACTIVITY_SERVICE);
String className = am.getRunningTasks(1).get(0).topActivity.getClassName();
return Main.class.getName().equals(className);
}
The problem is the application is not aware of your BaseApplication class.
You should:
Your base application class should extend Application object
Your manifest should point out in the application tag the attribute name and point to you application object
P.S. the BaseApplication.activityPaused(); should go into onPause and not onStop
If you want to keep the state of "activityVisible" you need save it in SharedPreferences.

Categories