In this tutorial, you’ll start with WorkManager, we’ll create a simple quotes app that sends a notification every day

what is WorkManager ?

The WorkManager API makes it easy to schedule deferrable, asynchronous tasks that are expected to run even if the app exits or device restarts.

Benefits of using WorkManager

  • Backwards compatible up to api 14 which ensures you don’t have to work out the application functionality and choose a compatible api. Just hand the work over to the workmanager. It will select the best alternative for the project to be conducted.
  • Add work constraints like network availability or charging status workmanager will be therefore take care of all the restrictions to insure that the job is carried out even when when the system is rebooted when the app is exited without completing the project and that our task is completed.
  • Ensures task execution, even if the app or device restarts.
  • Chain tasks it ensures that you can use the job manager to build a copy of your work and ask one after the other.

To configure your app to use WorkManager , add the WorkManager components in build.gradle

apply plugin: 'com.android.application'
android {
    compileSdkVersion 29
    buildToolsVersion "29.0.2"
    defaultConfig {
        applicationId "com.materialuiux.androidworkmanagerexample"
        minSdkVersion 15
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'

    // WorkManager  components
    implementation "androidx.work:work-runtime:2.2.0"

    // Recycler View components
    implementation 'androidx.recyclerview:recyclerview:1.0.0'

    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

}

we are going to read file from assets folder that has list of quotes so let’s create DataReader class that from it we will receive the data from it

DataReader.java

import android.content.Context;
import com.materialuiux.androidworkmanagerexample.model.Quotes;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Random;
import java.util.StringTokenizer;

public class DataReader {

    /**
     * here we will get all the quotes and display it in the main activity.
     */
    public static ArrayList<Quotes> getAllQuotes(Context mContext) {
        ArrayList<Quotes> quotesArrayList = new ArrayList<>();
        InputStream fIn = null;
        InputStreamReader isr = null;
        BufferedReader input = null;
        try {
            fIn = mContext.getResources().getAssets().open("quotes.txt", Context.MODE_WORLD_READABLE);
            isr = new InputStreamReader(fIn);
            input = new BufferedReader(isr);
            while (input.readLine() != null) {
                StringTokenizer st = new StringTokenizer(input.readLine(), ";");
                String quotes = st.nextToken();
                String publisher = st.nextToken();
                quotesArrayList.add(new Quotes(quotes, publisher));
            }
        } catch (Exception e) {
            e.getMessage();
        } finally {
            try {
                if (isr != null)
                    isr.close();
                if (fIn != null)
                    fIn.close();
                if (input != null)
                    input.close();
            } catch (Exception e2) {
                e2.getMessage();
            }
        }
        return quotesArrayList;
    }

    /**
     * here we are going to get one random quotes for the notification.
     */
    public static Quotes getRandomQuotes(Context mContext) {
        ArrayList<Quotes> quotesArrayList = new ArrayList<>();
        InputStream fIn = null;
        InputStreamReader isr = null;
        BufferedReader input = null;
        try {
            fIn = mContext.getResources().getAssets().open("quotes.txt", Context.MODE_WORLD_READABLE);
            isr = new InputStreamReader(fIn);
            input = new BufferedReader(isr);
            while (input.readLine() != null) {
                StringTokenizer st = new StringTokenizer(input.readLine(), ";");
                String quotes = st.nextToken();
                String publisher = st.nextToken();
                quotesArrayList.add(new Quotes(quotes, publisher));
            }
        } catch (Exception e) {
            e.getMessage();
        } finally {
            try {
                if (isr != null)
                    isr.close();
                if (fIn != null)
                    fIn.close();
                if (input != null)
                    input.close();
            } catch (Exception e2) {
                e2.getMessage();
            }
        }
        Random rn = new Random();
        int range = quotesArrayList.size() - 1;
        int randomNum = rn.nextInt(range) + 1;
        return quotesArrayList.get(randomNum);
    }
}

Quotes.java

public class Quotes {

    private String quotes;
    private String publisher;


    public Quotes(String quotes, String publisher) {
        this.quotes = quotes;
        this.publisher = publisher;
    }


    public String getQuotes() {
        return quotes;
    }

    public String getPublisher() {
        return publisher;
    }
}

Okay now lets create alarmreceiver that handles the broadcast message and generates notification

AlarmReceiver.java

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import androidx.core.app.NotificationCompat;
import com.materialuiux.androidworkmanagerexample.Constants;
import com.materialuiux.androidworkmanagerexample.DataReader;
import com.materialuiux.androidworkmanagerexample.MainActivity;
import com.materialuiux.androidworkmanagerexample.model.Quotes;
import java.util.Date;

/**
 * AlarmReceiver handles the broadcast message and generates Notification
 */
public class AlarmReceiver extends BroadcastReceiver {

    private NotificationManager notifManager;
    Context mContext;

    @Override
    public void onReceive(Context context, Intent intent) {
        String type = intent.getStringExtra(Constants.EXTRA_TYPE);
        if (type != null && type.equalsIgnoreCase(Constants.TYPE_RELEASE)) {
            mContext = context;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Quotes quotes = DataReader.getRandomQuotes(mContext);
                    createNotification(quotes.getPublisher(), quotes.getQuotes(), mContext);

                }
            }).start();
        }
    }


    public void createNotification(String publisher, String quotes, Context context) {
        int NOTIFY_ID = (int) ((new Date().getTime() / 1000L) % Integer.MAX_VALUE);
        String id = "fcm_default_channel"; // default_channel_id
        String title = "mrning notrification"; // Default Channel
        Intent intent;
        PendingIntent pendingIntent;
        NotificationCompat.Builder builder;
        if (notifManager == null) {
            notifManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            int importance = NotificationManager.IMPORTANCE_HIGH;
            NotificationChannel mChannel = notifManager.getNotificationChannel(id);
            if (mChannel == null) {
                mChannel = new NotificationChannel(id, title, importance);
                mChannel.enableVibration(true);
                mChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
                notifManager.createNotificationChannel(mChannel);
            }
            builder = new NotificationCompat.Builder(context, id);
            intent = new Intent(context, MainActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
            pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
            builder.setContentTitle(publisher)                            // required
                    .setSmallIcon(android.R.drawable.ic_popup_reminder)   // required
                    .setDefaults(Notification.DEFAULT_ALL)
                    .setAutoCancel(true)
                    .setStyle(new NotificationCompat.BigTextStyle().bigText(quotes))
                    .setContentIntent(pendingIntent)
                    .setVibrate(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
        } else {
            builder = new NotificationCompat.Builder(context, id);
            intent = new Intent(context, MainActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
            pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
            builder.setContentTitle(publisher)
                    .setSmallIcon(android.R.drawable.ic_popup_reminder)   // required
                    .setDefaults(Notification.DEFAULT_ALL)
                    .setAutoCancel(true)
                    .setStyle(new NotificationCompat.BigTextStyle().bigText(quotes))
                    .setContentIntent(pendingIntent)
                    .setVibrate(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400})
                    .setPriority(Notification.PRIORITY_HIGH);
        }
        Notification notification = builder.build();
        notifManager.notify(NOTIFY_ID, notification);
    }
}

and now we will craete Notification Helper class that from it we will schedule Repeating Notification

NotificationHelper.java

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import com.materialuiux.androidworkmanagerexample.Constants;
import java.util.Calendar;
import static android.content.Context.ALARM_SERVICE;


public class NotificationHelper {


    private static AlarmManager alarmManagerRTC;
    private static PendingIntent alarmIntentRTC;

    /**
     * here we will schedule Repeating Notification
     *
     * @param context
     * @param hour    hour Notification that set by user
     * @param min     min  Notification that set by user
     */
    public static void scheduleRepeatingNotification(Context context, String hour, String min) {
        //get calendar instance to be able to select what time notification should be scheduled
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(System.currentTimeMillis());
        //Setting time of the day (8am here) when notification will be sent every day (default)
        calendar.set(Calendar.HOUR_OF_DAY, Integer.getInteger(hour, 8));
        calendar.set(Calendar.MINUTE, Integer.getInteger(min, 0));
        calendar.set(Calendar.SECOND, 0);

        //Setting intent to class where Alarm broadcast message will be handled
        Intent intent = new Intent(context, AlarmReceiver.class);
        intent.putExtra(Constants.EXTRA_TYPE, Constants.TYPE_RELEASE);

        //Setting alarm pending intent
        alarmIntentRTC = PendingIntent.getBroadcast(context, Constants.ALARM_TYPE_RTC, intent, PendingIntent.FLAG_UPDATE_CURRENT);

        //getting instance of AlarmManager service
        alarmManagerRTC = (AlarmManager) context.getSystemService(ALARM_SERVICE);

        //Setting alarm to wake up device every day for clock time.
        //AlarmManager.RTC_WAKEUP is responsible to wake up device for sure, which may not be good practice all the time.
        if (alarmManagerRTC != null) {
            alarmManagerRTC.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, alarmIntentRTC);
        }
    }

    /**
     * here we will cancel alarms that been set in past
     */
    public static void cancelAlarm() {
        if (alarmManagerRTC != null) {
            alarmManagerRTC.cancel(alarmIntentRTC);
        }
    }
}

and now let’s create recyclerview to show the all Quotes and also Dialog for the User so he can set the time for receiving a notification in the MainActivity

MainActivity.java

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.app.Dialog;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ToggleButton;
import com.materialuiux.androidworkmanagerexample.Adapter.Ad_Quotes;
import com.materialuiux.androidworkmanagerexample.model.Quotes;
import com.materialuiux.androidworkmanagerexample.notification.NotificationHelper;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private Context mContext;
    private EditText hours;
    private EditText minutes;
    RecyclerView recyclerView;

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

        mContext = getApplicationContext();
        recyclerView = findViewById(R.id.recyclerView);
        setData();
    }

    // set data into recyclerView
    private void setData() {
        new AsyncTask<Void, Void, ArrayList<Quotes>>() {
            @Override
            protected ArrayList<Quotes> doInBackground(Void... voids) {
                return DataReader.getAllQuotes(mContext);
            }

            @Override
            protected void onPostExecute(ArrayList<Quotes> quotesArrayList) {
                Log.d("HERE", quotesArrayList.toString());
                Ad_Quotes ad_quotes = new Ad_Quotes(mContext, quotesArrayList);
                recyclerView.setLayoutManager(new LinearLayoutManager(mContext));
                recyclerView.setAdapter(ad_quotes);
            }
        }.execute();
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_notifications) {
            showDialog();
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    /*
     *
     * Dialog for setting the time for the notification
     *
     */
    public void showDialog() {
        final Dialog dialog = new Dialog(this);
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        dialog.setCancelable(true);
        dialog.setContentView(R.layout.dialog);
        dialog.getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT);
        hours = dialog.findViewById(R.id.editTextHH);
        minutes = dialog.findViewById(R.id.editTextMM);
        ToggleButton setNotification = dialog.findViewById(R.id.toggleButton);
        setNotification.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                clickToggleButton(view);
            }
        });
        Button cancelNotification = dialog.findViewById(R.id.cancel_action);
        cancelNotification.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                cancelAlarms();
            }
        });
        dialog.show();

    }

    public void clickToggleButton(View view) {
        boolean isEnabled = view.isEnabled();
        if (isEnabled) {
            NotificationHelper.scheduleRepeatingNotification(mContext, hours.getText().toString(), minutes.getText().toString());
        } else {
            NotificationHelper.cancelAlarm();
        }
    }

    public void cancelAlarms() {
        NotificationHelper.cancelAlarm();
    }

}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

dialog.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:layout_gravity="center"
    android:orientation="vertical"
    android:padding="10dp">
    
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:text="Receive quotes for every day at :"
        android:textSize="16sp"
        android:textStyle="bold" />

    <LinearLayout
        android:id="@+id/layout_rtc"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="6dp"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/editTextHH"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight=".25"
            android:ems="10"
            android:hint="HH"
            android:inputType="numberSigned" />

        <EditText
            android:id="@+id/editTextMM"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight=".25"
            android:ems="10"
            android:hint="MM"
            android:inputType="numberSigned" />

        <ToggleButton
            android:id="@+id/toggleButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textOff="Turn ON Notifications"
            android:textOn="Turn Off Notifications" />

    </LinearLayout>
    
    <Button
        android:id="@+id/cancel_action"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_marginTop="6dp"
        android:text="Cancel Alarms" />

</LinearLayout>

Ad_Quotes.java

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.materialuiux.androidworkmanagerexample.R;
import com.materialuiux.androidworkmanagerexample.model.Quotes;
import java.util.ArrayList;

public class Ad_Quotes extends RecyclerView.Adapter<Ad_Quotes.ViewHolder> {

    Context mContext;
    ArrayList<Quotes> dataList;

    public Ad_Quotes(@NonNull Context context, ArrayList<Quotes> quotesArrayList) {
        mContext = context;
        dataList = quotesArrayList;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_quites, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        Quotes quotes = dataList.get(position);
        holder.tx_publisher.setText(quotes.getPublisher());
        holder.tx_quotes.setText(quotes.getQuotes());
    }

    @Override
    public int getItemCount() {
        return dataList.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder {

        TextView tx_publisher, tx_quotes;

        ViewHolder(@NonNull View itemView) {
            super(itemView);
            tx_publisher = itemView.findViewById(R.id.publisher);
            tx_quotes = itemView.findViewById(R.id.quotes);
        }
    }
}

and thats it get the full example on Github

Leave a Reply

×