Android Programming

Android AlarmManager - Repeating/Recurring Alarms

How to make a repeating alarm with cancellation. The alarm can ring even if the activity is not started.

Gradle Scripts.
These are the gradle scripts that we modify

Build.Gradle(Module:App)

  • Our app level build.gradle file.
  • We specify compilesdk,minimum sdk,target sdk and dependencies.
  • Note that the minimum sdk for this project isn't that strict,it is much lower than that specified below.
  • We also add dependencies using 'compile' statement.
  • Our activity shall derive from the appCompatActivity to make it target earlier android versions.
    apply plugin: 'com.android.application'

    android {
        compileSdkVersion 26
        buildToolsVersion "26.0.0"
        defaultConfig {
            applicationId "com.tutorials.hp.repeatingalarm"
            minSdkVersion 15
            targetSdkVersion 26
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    }

    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
            exclude group: 'com.android.support', module: 'support-annotations'
        })
        compile 'com.android.support:appcompat-v7:26.+'
        compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha7'
        compile 'com.android.support:design:26.+'
        testCompile 'junit:junit:4.12'
    }

AndroidManifest.xml

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.tutorials.hp.repeatingalarm">

        <application
            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/AppTheme">
            <activity
                android:name=".MainActivity"
                android:label="@string/app_name"
                android:theme="@style/AppTheme.NoActionBar">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />

                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
            <receiver android:name="MyReceiver" >
            </receiver>
        </application>

    </manifest>

LAYOUTS

activity_main.xml

  • ActivityMain.xml.
  • This is a template layout for our MainActivity.
  • Root layout tag is CoordinatorLayout from design support library.
  • CordinatorLayout is viewgroup that is a superpowered on framelayout.
  • CoordinatorLayout is intended for two primary use cases:
    As a top-level application decor or chrome layout
    As a container for a specific interaction with one or more child views
  • Inside our CordinatorLayout we add : AppBarLayout,FloatingActionButton and include content_main.xml.
  • AppBarLayout is vertical LinearLayout that implements scrolling features of material design concept.
  • It should be a direct child of CordinatorLayout, otherwise alot of features won't work.
  • Inside the AppBarLayout we add our toolbar,which we give a blue color.
  • We will add our widgets in our content_main.xml, not here as this is a template layout.
  • Finally we have a FloatingActionButton, a class that derives from android.support.design.widget.VisibilityAwareImageButton.
    Its the round button you see in our user interface.
    <?xml version="1.0" encoding="utf-8"?>

    <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.tutorials.hp.repeatingalarm.MainActivity">

        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/AppTheme.AppBarOverlay">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:popupTheme="@style/AppTheme.PopupOverlay" />

        </android.support.design.widget.AppBarLayout>

        <include layout="@layout/content_main" />

        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_margin="@dimen/fab_margin"
            app:srcCompat="@android:drawable/ic_dialog_email" />

    </android.support.design.widget.CoordinatorLayout>

content_main.xml

  • Our ContentMain.xml file.
  • Shall get inflated to MainActivity.
  • Root tag is ConstraintLayout.
  • Contains EditTexts and two buttons.
  • User will enter the number of seconds after which alarm rings in edittext.
  • Then click start button to start alarm.
  • And Cancel button to cancel alarm.
    <?xml version="1.0" encoding="utf-8"?>

    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:context="com.tutorials.hp.repeatingalarm.MainActivity"
        tools:showIn="@layout/activity_main">

        <LinearLayout
            android:layout_width="368dp"
            android:layout_height="327dp"
            android:orientation="vertical"
            app:layout_constraintTop_toTopOf="parent"
            android:layout_marginTop="8dp">
            <EditText
                android:id="@+id/timeTxt"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_alignParentTop="true"
                android:ems="10"
                android:hint="Number of seconds"
                android:inputType="numberDecimal"
                />

            <Button
                android:id="@+id/startBtn"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignRight="@+id/timeTxt"
                android:layout_below="@+id/timeTxt"
                android:text="Start"
                />
            <Button
                android:id="@+id/cancelBtn"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_alignRight="@+id/timeTxt"
                android:layout_below="@+id/timeTxt"
                android:text="Cancel"
                />

        </LinearLayout>

    </android.support.constraint.ConstraintLayout>

MyReceiver.java

  • Our MyReceiver class.
  • Derives from android.content.BroadcastReceiver.
  • We override onReceive() method and perform task to be done when alarm rings here.
    package com.tutorials.hp.repeatingalarm;

    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.widget.Toast;

    public class MyReceiver extends BroadcastReceiver {
        /*
       RING ALARM WHEN IN WHEN WE RECEIVE OUR BROADCAST
        */
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "Alarm Ringing...", Toast.LENGTH_SHORT).show();
        }
    }

MainActivity.java

  • Our MainActivity class.
  • Derives from AppCompatActivity which is a Base class for activities that use the support library action bar features.
  • Methods: onCreate(),initializeViews(),initializeAlarmManager(),go().
  • Inflated From activity_main.xml using the setContentView() method.
  • The views we use are EditTexts and buttons.
  • Reference them from our layout specification using findViewById().
  • Initialize alarm manager.
  • Start alarm using the setInExactRepeating().
    package com.tutorials.hp.repeatingalarm;

    import android.app.AlarmManager;
    import android.app.PendingIntent;
    import android.content.Intent;
    import android.os.Build;
    import android.os.Bundle;
    import android.support.design.widget.FloatingActionButton;
    import android.support.v7.app.AppCompatActivity;
    import android.support.v7.widget.Toolbar;
    import android.view.View;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.Toast;

    public class MainActivity extends AppCompatActivity {

        Button startBtn,cancelBtn;
        EditText timeTxt;
        AlarmManager alarmManager;
        PendingIntent pi;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
            setSupportActionBar(toolbar);

             initializeViews();
             initializeAlarmManager();

            FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
            fab.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    go();
                }
            });

        }

        /*
        INITIALIZE VIEWS
         */
        private void initializeViews()
        {
            timeTxt= (EditText) findViewById(R.id.timeTxt);
            startBtn= (Button) findViewById(R.id.startBtn);
            cancelBtn= (Button) findViewById(R.id.cancelBtn);

            startBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    go();
                }
            });
            cancelBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    if(alarmManager != null)
                    {
                        alarmManager.cancel(pi);
                    }
                }
            });
        }
        /*
        INITIALIZE AND START OUR ALARM
        */
        private void initializeAlarmManager()
        {
            // INITIALIZE INTENT
            Intent intent=new Intent(this,MyReceiver.class);

            //PASS CONTEXT,YOUR PRIVATE REQUEST CODE,INTENT OBJECT AND FLAG
            pi= PendingIntent.getBroadcast(this,0,intent,0);

            //INITIALIZE ALARM MANAGER
            alarmManager= (AlarmManager) this.getSystemService(ALARM_SERVICE);
        }

        /*
        START OUR ALARM
         */
        private void go()
        {
            //GET TIME IN SECONDS
            int time=Integer.parseInt(timeTxt.getText().toString());

            //SET THE ALARM
           if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CUPCAKE) {
                alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP,System.currentTimeMillis()+(time*1000),time*1000,pi);
                Toast.makeText(MainActivity.this, "Alarm set in "+time+" seconds", Toast.LENGTH_SHORT).show();

            }else
            {
                alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME,System.currentTimeMillis()+(time*1000),time*1000,pi);
                Toast.makeText(MainActivity.this, "Yes Alarm set in "+time+" seconds", Toast.LENGTH_SHORT).show();

            }
        }

    }