2

What I want:-

  • To make a app which gives notification on given time by user.
  • It should also run on background.
  • I can save the time hour and minute 24hr format on SharedPreferences database so that I can overwrite the time again.
  • Else part, how to not give the notification?

But what I got

I made this, But I am facing several issues:-

  1. I am able to make notification but only when my app is on running state.
  2. Not able to store in variables Xhours and Xminute into SharedPrefrences DB.
  3. My broadcast receiver class and notification class is working fine but not able to get the notification when the app is not running. visit this :> link to see a screenshot

Please Help, Thanks.

My settings_aztro.java activity

    public void TimePickerDialog() {


        TimeTv_settings.setOnClickListener(new View.OnClickListener() {
            @SuppressWarnings("deprecation")
            @Override
            public void onClick(View v) {

                showDialog(DIALOG_ID);
            }
        });

    }

    @SuppressWarnings("deprecation")
    @Override
    protected Dialog onCreateDialog(int id) {
        if (id == DIALOG_ID) {
            return new TimePickerDialog(settings_aztro.this, mTimePickerListener, Xhour, Xminute, false);
        } else {
            Toast.makeText(this, "Unable to locate time picker", Toast.LENGTH_SHORT).show();
            return null;

        }

    }




    protected TimePickerDialog.OnTimeSetListener mTimePickerListener = new TimePickerDialog.OnTimeSetListener() {
        @Override
        public void onTimeSet(TimePicker view, int hourOfDay, int minute)
        {
            // This will give 24hr value for the notification
            Xhour = hourOfDay;
            Xminute = minute;

            // This will show the 12 hour time on the TextView
            YHour = hourOfDay;
            Yminute = minute;


            if (YHour == 0) {
                YHour += 12;
                format = "AM";
            } else if (YHour == 12) {
                format = "PM";
            } else if (YHour > 12) {
                YHour -= 12;
                format = "PM";
            } else {
                format = "AM";
            }
            SharedPreferences sharedPreferences = getSharedPreferences("timeinfo", Context.MODE_ENABLE_WRITE_AHEAD_LOGGING);
            SharedPreferences.Editor editor = sharedPreferences.edit();
            editor.putInt("keyHour",Xhour);
            editor.putInt("keyMinute",Xminute);
            editor.commit();
            TimeTv_settings.setText(YHour + ":" + Yminute + " " + format);
            if(NotifyMe==true)
            {
                SharedPreferences xsharedPreferences = getSharedPreferences("timeinfo", Context.MODE_ENABLE_WRITE_AHEAD_LOGGING);
                savedHour = xsharedPreferences.getInt("keyHour", Integer.parseInt(""));
                savedMinute = xsharedPreferences.getInt("keyMinute",Integer.parseInt(""));

                Toast.makeText(settings_aztro.this, "YHour: "+YHour+"Ymin: "+Yminute+" "+format+"\tNotify: "+NotifyMe, Toast.LENGTH_SHORT).show();
                generateCustomNotification();
            }
            else
            {
                NotifyMe = false;

            }
        }
    };

public void generateCustomNotification()
{

    CustomCalendar.set(Calendar.HOUR_OF_DAY,savedHour);
    CustomCalendar.set(Calendar.MINUTE,savedMinute);
    CustomCalendar.set(Calendar.SECOND,0);

    Intent CallReciverIntent = new Intent(settings_aztro.this,NotificationBReceiver.class);
    PendingIntent pIntent = PendingIntent.getBroadcast(settings_aztro.this,0,CallReciverIntent,PendingIntent.FLAG_UPDATE_CURRENT);

    AlarmManager alarmManager = (AlarmManager) settings_aztro.this.getSystemService(ALARM_SERVICE);
    alarmManager.set(AlarmManager.RTC_WAKEUP,CustomCalendar.getTimeInMillis(),pIntent);
    Log.d("samay","Hour-> "+Xhour+" Minute-> "+Xminute+"Time in milis -> "+CustomCalendar.getTimeInMillis());

}


public void saveNotificationTime()
{

}



    @Override
    public void onClick(View v)
    {

        switch (v.getId())
        {
            case R.id.About_settingsTv2:
                Toast.makeText(this, "C", Toast.LENGTH_SHORT).show();
                generateCustomNotification();
                Log.d("Timeqq","Hour-> "+Xhour+" Minute-> "+Xminute);
                Xhour = 0;Xminute=0;
                break;

            case R.id.FacebookSignin_settings:

                Log.d("newtime","hour= "+Xhour+" minute= "+Xminute+"Time in milis -> "+CustomCalendar.getTimeInMillis()+"Notify "+NotifyMe);
                Log.d("newtime","hour= "+savedHour+" minute= "+savedMinute+"Time in milis -> "+CustomCalendar.getTimeInMillis()+"Notify "+NotifyMe);
                break;


        }


    }

    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
    {
        if(switch_settings.isChecked())
        {
            NotifyMe = true;
            TimeRLayout.setVisibility(View.VISIBLE);
            Toast.makeText(this, "On", Toast.LENGTH_SHORT).show();
            Toast.makeText(this, "Hr -> "+Xhour+"Min -> "+Xminute, Toast.LENGTH_SHORT).show();
            Log.d("Waqt","Hour-> "+Xhour+" Minute-> "+Xminute);

        }
        else if(!switch_settings.isChecked())
        {
            NotifyMe = false;
            Toast.makeText(this, "Off", Toast.LENGTH_SHORT).show();
            if(NotifyMe==false)
            {
                TimeRLayout.setVisibility(View.GONE);

            }
            else
            {

            }

        }
    }

My Receiver class

 public class NotificationBReceiver extends BroadcastReceiver
{
    int MID = 98;
    @Override
    public void onReceive(Context context, Intent intent)
    {

long when = System.currentTimeMillis();
        NotificationManager nManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
        Intent notificationIntent = new Intent(context, list_of_signs.class);
        notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

        PendingIntent pendingIntent = PendingIntent.getActivity(context, 10,
                notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        NotificationCompat.Builder NotificationBuilder =  new NotificationCompat.Builder(context)
                .setSmallIcon(R.drawable.aztro2)
                .setSound(defaultSoundUri)
                .setContentTitle("Hello! Check Your Today's Horoscope.")
                .setAutoCancel(true)
                .setWhen(when)
                .setContentIntent(pendingIntent)
                .setVibrate(new long[]{1000, 1000, 1000, 1000, 1000});
        nManager.notify(MID, NotificationBuilder.build());
        MID++;;

Manifest file

<uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_main_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/NoActionBar">
        <activity android:name=".list_of_signs">
            <!--list_of_signs-->
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity android:name=".MainActivity"/>
        <activity android:name=".AllSignsData.class_aquarius"/>
        <activity android:name=".AllSignsData.class_aries"/>
        <activity android:name=".AllSignsData.class_cancer"/>
        <activity android:name=".AllSignsData.class_gemini"/>
        <activity android:name=".AllSignsData.class_capricorn"/>
        <activity android:name=".AllSignsData.class_leo"/>
        <activity android:name=".AllSignsData.class_libra"/>
        <activity android:name=".AllSignsData.class_pisces"/>
        <activity android:name=".AllSignsData.class_sagittarius"/>
        <activity android:name=".AllSignsData.class_scorpio"/>
        <activity android:name=".AllSignsData.class_taurus"/>
        <activity android:name=".AllSignsData.class_virgo"/>

        <activity android:name=".settings_aztro"/>



        <receiver android:name=".NotificationBReceiver"/>
    </application>

</manifest>
  • Best Practice, lookup FirebaseJobDispatcher if devices have Google Play Services, otherwise, lookup implementation of AlarmManager to schedule a RTC to start a service of your choice at a predictable interval. – Andrew Lam Oct 01 '17 at 00:47
  • I have implemented the AlarmManager class but, not able to get the notification in the background. – Harshit Sahni Oct 01 '17 at 10:52
  • That's another and but a better question, ask another question with your implementation of AlarmManager. The design pattern should be : 1) Create an intent service to post your notification, 2) Create a "operation" pending intent to launch the intent service, 3) Get an instance of an alarm manager, and then 4) use the manager to set the time and the "operation". – Andrew Lam Oct 02 '17 at 01:52
  • @AndrewLam Thank you soo much! But I have shared you the same code which I'm asking, and you have suggested, still it doesn't send notification when app is on stop state. – Harshit Sahni Oct 02 '17 at 16:45
  • AFAIK, static broadcast receivers (declared in the manifest) design pattern won't start you app in API 26+. The design pattern I suggested doesn't use broadcast receiver at all. – Andrew Lam Oct 03 '17 at 05:30

1 Answers1

1

Ok, This is the pattern for Alarm Manager. In this implementation, no statically defined Broadcast Receiver is used /nor should it be used for scheduling tasks, according to Android O best practices, in short, FORGET ABOUT BROADCAST RECEIVER if you want background notification.

1) Create an intent service (ex.ReminderAlarmService) to post your notification. This service will be started with a pending intent used by the alarmManager. onHandleIntent should create the notification and show it: ex.

ReminderAlarmService.class
 @Override
    protected void onHandleIntent(Intent intent) {

        //..... Create your notification here
        manager.notify(NOTIFICATION_ID, notification);

        //..... Persist your data here in sharedPreferences here too, with or without user action
    }

2) In the service, create an public util method to return "operation" pending intent to launch the above intent service, (also called deep-link intent) ex.

public static PendingIntent getReminderPendingIntent(Context context, Uri uri) {
    Intent action = new Intent(context, ReminderAlarmService.class);
    action.setData(uri);
    return PendingIntent.getService(context, 0, action, PendingIntent.FLAG_UPDATE_CURRENT);
}

3) Create a util class to get an instance of the alarm manager, use it to set the time and the "operation" intent created with the method above. The operation intent launches the intent service, which does the whatever you have in onHandleIntent. ex.

public class AlarmScheduler {

    public static void scheduleAlarm(Context context, long alarmTime, Uri reminderTask) {
        AlarmManager manager = AlarmManagerProvider.getAlarmManager(context);

        PendingIntent operation =
                ReminderAlarmService.getReminderPendingIntent(context, reminderTask);

        manager.setExact(AlarmManager.RTC, time, operation);
    }
}

Overall Design Flow:

  1. AlarmManager schedules the alarm with the pending intent to launch your service at the time from the time picker as parameter.
  2. When the time hits, your intent service is launched by the pending intent
  3. In the intent service, build and show your notification
  4. When your intent service completes the job, the service self-stops, clean.
Andrew Lam
  • 1,371
  • 17
  • 35