-5

I have a very subtle question that might look grotesque, to more expert eyes than mine.

I'm implementing 2 distinct alarms, that survive reboot/shutdown and show a notification when fired.
One is a repeating daily reminder, and the other one is a monthly reminder which is reset by the app each time the user completes a given task.
Just to remind him/her to do it again when a month is passed.

Now, everything is working just fine.
So what's the problem?

Nothing, but I have the BootReceiver, AlarmReceiver and AlarmService doubled
Only the Notification builder is in common.

My question is then: Can I unify those elements and not have them splitted for any alarm?
Because if not, if in the future I'll need to schedule a weekly alarm, I'd have to make another boot receiver, alarm receiver and alarm service.

Which doesn't seem too smart, to me (say I add a weekly and a yearly tasks: I'd have to add 2 more of all receivers and services!! Which seems crazy, to me).
But maybe I'm wrong and things are to be like this?

In a previous app I wrote (before recognizing that it didn't pass reboots), it worked with the alarms sharing all the classes.

Thank you, guys, for your time.

If you need to see my code, just ask for it. But it's a bit long...

This is my Manifest file, just to show what my doubt is about:

<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android.scheduler2"
    android:versionCode="1"
    android:versionName="1.14.02.11 b"
    >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="18"
    />

    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

    <application
        android:label="@string/app_name"
        android:icon="@drawable/ic_launcher"
        android:theme="@style/Theme.Sample"
        >

        <!-- The Main Activity -->
        <activity
            android:name="com.example.android.scheduler2.ACT_Base"
            android:label="@string/app_name"
            >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- The Preference Activity -->
        <activity android:name="com.example.android.scheduler2.ACT_Prefs" />

        <!-- 2 Alarms = 2 Alarm Boot receivers -->
        <!-- The One Shot Alarm Boot Receiver -->
        <receiver
            android:name="com.example.android.scheduler2.RCV_Boot_One"
            android:enabled="false"
            >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
        <!-- The Repeating Alarm Boot Receiver -->
        <receiver
            android:name="com.example.android.scheduler2.RCV_Boot_Rep"
            android:enabled="false"
            >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

        <!-- 2 Alarms = 2 Alarm receivers -->
        <!-- The One Shot Alarm Receiver -->
        <receiver android:name="com.example.android.scheduler2.RCV_Alarm_One" />
        <!-- The Repeating Alarm Receiver -->
        <receiver android:name="com.example.android.scheduler2.RCV_Alarm_Rep" />

        <!-- 2 Alarms = 2 Alarm services -->
        <!-- The One Shot Alarm Service -->
        <service android:name="com.example.android.scheduler2.SVC_Alarm_One" />
        <!-- The Repeating Alarm Service -->
        <service android:name="com.example.android.scheduler2.SVC_Alarm_Rep" />
    </application>
</manifest>
Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
  • You should change the title of your question as it's a little bit misleading. The question is if one can avoid to "repeat" code for every kind of alarm, or not? – Endzeit Feb 18 '14 at 12:55
  • @Endzeit (nice name for an alarm clock!): Thank you, I'll change it soon. – Phantômaxx Feb 18 '14 at 12:56
  • What exactly do you write in your noticiations after "catching" the alarms? Do you simply want to have several time intervalls and show the same message or does the message depent on the time intervall? – Endzeit Feb 18 '14 at 12:57
  • @EndZeit: I'm going to show an icon and a text (and led, vibration and sound: the FULL PACK). It will be different for the daily (repeating) and the monthly (1 shot, reset by the app when the user does something). The pending intents have different IDs, so to activate and cancel the proper alarm without interfering with the other one. – Phantômaxx Feb 18 '14 at 13:01
  • Why don't you use one BroadcastReceiver which then will start a new Service. The Intent to start the Service includes the id and in the Service itself you decide depending on the ID which notification you want to show . I cannot give you a code snippet as I am on my way home but I think you understand my basic approach dont you? – Endzeit Feb 18 '14 at 14:34
  • Yes. I already did something like that (used "one for all" of boot receiver, alarm receiver and alarm service, but it didn't work - the alarms were overlapping). But maybe I failed something. I think I have to reroute my steps to that path, do you think it could work? – Phantômaxx Feb 18 '14 at 14:36
  • I posted an answer. If you only see one of both notifications you should have a look at [the documentation.](http://developer.android.com/reference/android/app/NotificationManager.html). You need to set unique id's to show more than one notification at once. – Endzeit Feb 18 '14 at 15:22
  • 1
    Nice Klaus! Hearing you. – statosdotcom May 23 '18 at 16:35

2 Answers2

4

As I already suggested in the comments section something like the following may be a solution.

This approach requieres four classes:

  1. A BootBroadcastReceiver.class which extends BroadcastReceiver to listen to the onBootCompleted broadcast. As soon as the boot is completed this class will start the AlarmStarterService.class.
  2. The AlarmStarterService.class which extends Service will start both alarms then.
  3. The AlarmBroadcastReceiver.class which also extends BroadcastReceiver will receive the broadcasts fired by the alarms and then start the ShowNotificationService.class and passing the ID to it through the Intent.
  4. And finally the ShowNotificationService.class which also extends Service and will show a notification based on the ID passed trough the Intent.

The BootBroadcastReceiver may look like this.

public class BootBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        context.startService(new Intent(context, AlarmStarterService.class));
    }
}

public class AlarmStarterService extends Service {

    public static final int REQUEST_CODE_DAILY = 10000;
    public static final int REQUEST_CODE_MONTHLY = 10001;

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    @Override
    public void onCreate() {
        createAlarm();

        stopSelf();
    }  

    private void createAlarm() {
        PendingIntent pIntentDaily = getPendingIntent(REQUEST_CODE_DAILY);
        PendingIntent pIntentMonthly = getPendingIntent(REQUEST_CODE_MONTHLY);

        AlarmManager am = (AlarmManager) this
                .getSystemService(Context.ALARM_SERVICE);

        am.setRepeating(AlarmManager.RTC, TIME_DESIRED, AlarmManager.INTERVAL_DAY, pIntentDaily);
        am.setRepeating(AlarmManager.RTC, TIME_DESIRED, AlarmManager.INTERVAL_DAY * 30, pIntentMonthly );
    }

    private PendingIntent getPendingIntent(int requestCode) {
        Intent i= new Intent(this, AlarmBroadcastReceiver.class);
        i.putExtra("_id", requestCode);
        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);                     

        return PendingIntent.getBroadcast(this, requestCode, i, PendingIntent.FLAG_UPDATE_CURRENT);
    }
}


public class AlarmBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        context.startService(new Intent(context, ShowNotificationService.class)
                .putExtra("_id", intent.getIntExtra("_id", -1)));
    }
}


public class ShowNotificationService extends Service {

    int id;

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        id = intent.getIntExtra("_id", -1);
        return START_STICKY;
    }

    @Override
    public void onCreate() {
        switch(id) {
            case AlarmStarterService.REQUEST_CODE_DAILY:
                showDailyNotification();
                break;
            case larmStarterService.REQUEST_CODE_MONTHLY:
                showMonthlyNotification();
                break;
        }

        stopSelf();
    }  
}
Endzeit
  • 4,810
  • 5
  • 29
  • 52
  • There may be some spelling mistakes as I wrote it down without any ADT but the general approach as such should work. Note that you have to set two different IDs on the NotificationManagers notify() method if you want to show both notifications at the same time. (You can use the both "request code"s for that matter.) – Endzeit Feb 18 '14 at 15:26
1

I think what you are trying to do can be done using only one for both of them, and handle the different cases (even if it exceeds two) through setting application preferences.

Like you can save the last monthly alarm time and the last weekly and set the alarm to fire with the earliest time.

Hakem Zaied
  • 14,061
  • 1
  • 23
  • 25
  • But I want the alarms to be fired outside the app. One of the alarms reminds the user to use the app. The other reminds him/her of a recurring event. Both should fire without reading a Preference, but be application independent – Phantômaxx Feb 18 '14 at 13:06
  • but making two receivers is not application independent, right ?? – Hakem Zaied Feb 18 '14 at 13:10
  • Yes it is. It works just fine, as you can read in my question. For "application independent" I mean that they work even if the app is closed and terminated - Now they are managed by the system. – Phantômaxx Feb 18 '14 at 13:11
  • Yes, and even if you make it only one receiver it can be manged even if the app is closed (not force closed), it should work just fine, if you need more help in the handling let me know, but it should work just fine with the preferences :) – Hakem Zaied Feb 18 '14 at 14:57
  • Now I'm more confused... why do you talk about preferences? Will it survive reboots / shutdowns as well? – Phantômaxx Feb 18 '14 at 15:10
  • Yes, it will survive any changes apart from an uninstall or clear data which is the same case as yours, or if you want it to be more precis you can save the state to a file. – Hakem Zaied Feb 18 '14 at 15:12
  • I'm getting more and more confused... ;) – Phantômaxx Feb 18 '14 at 15:15
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/47773/discussion-between-hakem-zaied-and-artoo-detoo) – Hakem Zaied Feb 18 '14 at 15:19
  • Basically what I am saying is that you need to save your state of the nearest alarm and this is the one you will fire, and when it is fired you check which alarm is the nearest and fire it .... and so on. – Hakem Zaied Feb 18 '14 at 15:26
  • It seems an overkill! – Phantômaxx Feb 18 '14 at 15:32
  • This will help you if you have a new alarm that needs to be added you won't need to add it as a receiver. – Hakem Zaied Feb 18 '14 at 15:38