0

I have a custom notification that has four RemoteView TextViews. When onPause() gets called I want to post the notification and then update it every second while the screen is on. When onResume() gets called I want to cancel the notification. So far I have it set up so that I have a volatile boolean that gets set to true when onPause() gets called and set to false when onResume() gets called. I have a method that starts a HandlerThread and then checks the volatile boolean. If the boolean is true it is supposed to update the notification and if it is false it is supposed to cancel the notification and shutdown the HandlerThread.

Everything seems to be working fine as far as starting the notification and updating it every second. However, when onResume() is called it is not stopping the notification.

I do not have the screen on stuff coded in yet because I was trying to figure out how to shutdown the notification and and HandlerThread before I moved on to that.

Here is my code so far:

void setUpTimerNotif() {

    handlerThread = new HandlerThread("TimerNotificationHandler", Process.THREAD_PRIORITY_BACKGROUND);

    handlerThread.start();

    timerNotificationHandler = new Handler(handlerThread.getLooper());

    timerNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

    Intent timerNotifIntent = new Intent(MainActivity.this, MainActivity.class);
    timerNotifIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);

    final PendingIntent pendingTimerNotifIntent = PendingIntent.getActivity(MainActivity.this,
            1234, timerNotifIntent, PendingIntent.FLAG_UPDATE_CURRENT);

    final RemoteViews remoteTimerViews = new RemoteViews(getPackageName(), R.layout.timer_notification);

    final NotificationCompat.Builder timerNotificationBuilder = new NotificationCompat.Builder(MainActivity.this);

    if (timerNotificationRunning) {
        timerNotificationHandler.postDelayed(
                new Runnable() {
                    @Override
                    public void run() {

                        long timerNotifTimeOne = wakeUpTime - System.currentTimeMillis();
                        long timerNotifTimeTwo = wakeUpTimeTwo - System.currentTimeMillis();
                        long timerNotifTimeThree = wakeUpTimeThree - System.currentTimeMillis();
                        long timerNotifTimeFour = wakeUpTimeFour - System.currentTimeMillis();

                        String timeOne = timerFormat(timerNotifTimeOne);
                        String timeTwo = timerFormat(timerNotifTimeTwo);
                        String timeThree = timerFormat(timerNotifTimeThree);
                        String timeFour = timerFormat(timerNotifTimeFour);

                        String hmsOne = ("Timer 1: " + timeOne);
                        String hmsTwo = ("Timer 2: " + timeTwo);
                        String hmsThree = ("Timer 3: " + timeThree);
                        String hmsfour = ("Timer 4: " + timeFour);

                        remoteTimerViews.setTextViewText(R.id.timer_one_notification_view, hmsOne);

                        remoteTimerViews.setTextViewText(R.id.timer_two_notification_view, hmsTwo);

                        remoteTimerViews.setTextViewText(R.id.timer_three_notification_view, hmsThree);

                        remoteTimerViews.setTextViewText(R.id.timer_four_notification_view, hmsfour);

                        timerNotificationBuilder.setPriority(Notification.PRIORITY_HIGH)
                                .setAutoCancel(false)
                                .setOngoing(true)
                                .setOnlyAlertOnce(true)
                                .setSmallIcon(R.mipmap.ic_launcher)
                                .setContentIntent(pendingTimerNotifIntent)
                                .setCustomBigContentView(remoteTimerViews)
                                .setVisibility(NotificationCompat.VISIBILITY_PUBLIC);

                        timerNotificationManager.notify(1234, timerNotificationBuilder.build());

                        timerNotificationHandler.post(this);

                    }
                }, 1000
        );
    } else {
        timerNotificationManager.cancelAll();

        if (timerNotificationHandler != null) {
            timerNotificationHandler.removeCallbacksAndMessages(null);
        }

        timerNotificationHandler.getLooper().quit();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            handlerThread.quitSafely();
        } else {
            handlerThread.quit();

            /*try {
                handlerThread.join();
                handlerThread = null;
                timerNotificationHandler = null;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }*/
        }

        Log.v(TAG, "Timer Notification Cancelled");
    }

}
IINEWMANII
  • 53
  • 8

1 Answers1

1

The most "android" way to do this would be to use a background Service.

https://developer.android.com/guide/components/services.html

When your app moves into the background (onPause) you either start the service or post a message to the saying stating this, and the opposite with onResume.

(the problem with your current solution is your Activity could be killed at anytime onStop/onDestroy but you would still want the on going notification updates)


Notice you create your HandlerThread using the MainThreads Looper:

 timerNotificationHandler = new Handler(handlerThread.getLooper());

but then in your else statement you tell this looper to quit:

 timerNotificationHandler.getLooper().quit();

I'm not quite sure what behaviour this gives! But i have a feeling it is wrong - you don't want to stop the mainthreads looper from looping, it means your activity won't work as expected (and might be your issue).

https://developer.android.com/reference/android/os/Looper.html#quit()


In your else statement you might also want to cancel your notification:

https://developer.android.com/reference/android/app/NotificationManager.html#cancel(int)

timerNotificationManager.cancel(1234);
Blundell
  • 75,855
  • 30
  • 208
  • 233
  • Thank you very much for your response. I have not tried using a service to do this yet. I have only been programming for about two months and this is my first real project. In short i'm a noob. If I remove the Looper.quit() and change to NotificationManager.cancel(1234) and uncomment the try catch block it will cancel the notification but then it comes back one second later. Any idea what might cause that? Also, just to be sure.....I should be using handlerthread for this, right? That is what seemed like the best solution. Thank you again for your response. – IINEWMANII Dec 26 '16 at 23:19
  • Thats probably because you call `removeCallbacksAndMessages` but `postDelayed` doesn't post for 1 second, so it is posts the message afterwards. You could fix it multiple ways: create another runnable that cancels all after 1.1 seconds, or add a flag to the runnable to not post itself again once cancelled – Blundell Dec 27 '16 at 08:35
  • I finally got it working without causing the phone to freeze up lol. Now I am moving on to making it so the notification will only update if the screen is on. I am trying to target api as low as 17 so it seems like it might difficult to figure out since isScreenOn() is deprecated. – IINEWMANII Jan 03 '17 at 19:15