Guide Area
Guidearea banner

Alarm Application in Android (Tutorial using AlarmManager)

This feature is included in my Oversleep application and there is some of the things that are happening in the background and need to be explained. Most of the programmers/users go for alarms which are dismiss-able either by pressing a button or by solving a riddle. What we will be doing is exactly the opposite – we will not give the user a few choices to cancel the alarm, we will try to prohibit all of them.

There is one important thing to mention here – there are still two ways to cancel Oversleeper:
– Force-stop the application from Settings
– Turn off the phone

Although the first way could easily be solved, let’s mention that the whole purpose of Oversleeper is to help users wake up without sneezing all the alarms and oversleeping anyway. If they decide to turn off their phone or force-stop the application, they are causing harm only and only to themselves. That is why I did not put any importance to solving these two ‘factors’.

LET’S START

1.) We want to create an alarm that gets fired off some time in the future. It is unnecessary to create custom service that will run in background and check if the alarm time and current time match. That is why we will use Android’s AlarmManager.

AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
WakefulBroadcasterReceiver wbr = new WakefulBroadcastReceiver();
registerReceiver(wbr, new IntentFilter("Receiver"));
Intent myIntent = new Intent("Receiver");
myIntent.putExtra("uri",audioFileUri.toString()); // Here we pass the URI of audio file
PendingIntent pendingIntent = PendingIntent.getBroadcast(MainActivity.this, 1234, myIntent, 0);
alarmManager.setExact(AlarmManager.RTC_WAKEUP, alarmtime, pendingIntent);

This code should be easy to understand. We get the system alarm manager that we will use to add an alarm. Then we create a new WakefulBroadcasterReceiver (code below) which extends WakefulBroadcastReceiver. This receiver gets fired off when our alarm fires off too. We will use this class to start the alarm and show a custom activity. Later on, we create Intent with the same name as the IntentFilter above. This will bind the Receiver and Intent together, so when the alarm goes off and starts the PendingIntent, Receiver will be started as well. AlarmManager offers more triggers (I think it’s 4). We use setExact() because this trigger sets the alarm to the exact time and disables the operating system to manipulate with this time in case of need, which will cause the alarm to go off exactly on specified time.

2.) This Receiver, as soon as it’s called, will create a new Intent that will display on screen. The idea is that we will display a message to user together with some animation, so he knows what’s happening when the sound starts to play. We also pass the sound URI further, so we can then play it later on.

public class WakefulBroadcasterReceiver extends WakefulBroadcastReceiver {
    @Override
    public void onReceive(final Context context, Intent intent) {
        Intent n = new Intent(context,AlarmReceiverActivity.class);
        n.putExtra("uri",intent.getExtras().getString("uri"));
        context.startActivity(n);
    }
}

3.) Now we create the Activity itself. Before we do, I should probably explain some consequences to you. There are a few of situations that you have to predict:

  • User will try to cancel the Alarm Activity by pressing the back button
  • User will try to cancel the alarm by removing it from the recent tasks window
  • User will try to lower the sound in order to avoid the music

So what we do is that we simply exclude all of our activities from the recent tasks (they will run, but won’t be visible), override the volume buttons later on in the BackgroundService, and finally override back button and recent tasks button on the navbar of users phone. The recent tasks button needs to be overriden too, because the application will disappear from recent tasks only after you minimize it. If you press the recent tasks button while Activity is in front, Oversleeper will still appear there. So what we will do is that we will bring the Activity back to front of the screen immediately after user clicks on recent tasks button. There might be a small visual glitch between these screen changes, but it is the most efficient solution that I have found.

Just add this line to your Manifest activities:

android:excludeFromRecents="true"

We will also register a brand new Receiver. This one will make sure that after the alarm song has finished, our Alarm activity will be finished.

private final BroadcastReceiver abcd = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            finish();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_alarm_receiver);

        registerReceiver(abcd, new IntentFilter("finishAlarmReceived"));

        Intent n = new Intent(AlarmReceiverActivity.this,BackgroundService.class);
        n.putExtra("uri",getIntent().getExtras().getString("uri"));
        startService(n);

    }

    @Override
    protected void onPause() {
        super.onPause();

        ActivityManager activityManager = (ActivityManager) getApplicationContext()
                .getSystemService(Context.ACTIVITY_SERVICE);
        activityManager.moveTaskToFront(getTaskId(), 0);
    }

    @Override
    public void onBackPressed(){

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(abcd);
    }

Right now we have all we need. We have an activity that loads when the time is out, we also successfully restricted user from shutting down our alarm. The last step is to create a background service that will play the sound.

4.) Let’s talk a bit about the service proposition. I imagine that the first thing that came up in your mind after I mention services at the beginning of my article was: ‘Make it STICKY’. You could create a STICKY background service, but if the user force-stopped Oversleeper together with the existing service and the new service would get started automatically, then the song would stop at some point and then started again with the new service. That isn’t what we want and although there is a workaround, I already described why we will not implement it at the beginning of this article.

So we will instead create a NOT_STICKY service. This service will take care of playing the song, but it will also make sure that while the song is playing, it’s not possible to lower the volume level. First, we get the URI of the song that user selected at the very beginning of the Oversleeper life-cycle. Then we play this song using MediaPlayer and we will also create a new Timer instance. This Timer will set the volume level to maximum every two seconds, in order to prevent the last consequence from point 3. of this article. The last thing we do is that we override the onDestroy() method, in which we send a broadcast that we already initialized in point 3. and named it “finishAlarmReceiver”.

public class BackgroundService extends Service implements MediaPlayer.OnPreparedListener {
    private PowerManager.WakeLock wl;
    private Ringtone ringtone;
    private MediaPlayer player;

    public BackgroundService() {
    }

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // do your jobs here
        Uri uri = Uri.parse(intent.getExtras().getString("uri"));

        player = new MediaPlayer();
        try {
            player.setDataSource(getApplicationContext(),uri);
            player.setVolume(100,100);
            player.setLooping(false);
            player.setOnPreparedListener(this);
            player.prepareAsync();
        } catch (IOException e) {
            e.printStackTrace();
        }

        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                AudioManager am =
                        (AudioManager) getSystemService(Context.AUDIO_SERVICE);
                am.setStreamVolume(
                        AudioManager.STREAM_MUSIC,
                        am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
                        0);
            }
        },0,2000);

        return START_NOT_STICKY;
    }

    @Override
    public void onDestroy(){
        super.onDestroy();
        player.stop();
        player.release();
        sendBroadcast(new Intent("finishAlarmReceived"));
    }

    public void onPrepared(final MediaPlayer player) {
        player.start();
        System.out.println("Player song started!");
        Handler handler = new Handler();
        handler.postDelayed(new Runnable()
        {
            @Override
            public void run()
            {
                stopSelf();
            }
        }, player.getDuration());
    }
}

And that is about it! I did not include the layouts or any other class/file contents, it is completely up to you how you implement your application. This article is just an explanation of a workflow used in my application Oversleeper. Feel free to try it out by clicking on the button below:

Oversleeper – Apps on Google Play

How often does it happen to you that you wake up, dismiss your alarm while thinking ‘Just 3 more minutes’ but those 3 minutes stretch to 60? Your morning is ruined and your boss or teacher is angry. Would something change if the snooze button did not exist?

Vladimir Marton

DevOps Engineer focused on cloud infrastructure, automation, CI/CD and programming in Javascript, Python, PHP and SQL. Guidearea is my oldest project where I write articles about programming, marketing, SEO and others.

1 comment