com.philliphsu.clock2.chronometer.ChronometerNotificationThread.java Source code

Java tutorial

Introduction

Here is the source code for com.philliphsu.clock2.chronometer.ChronometerNotificationThread.java

Source

/*
 * Copyright 2017 Phillip Hsu
 *
 * This file is part of ClockPlus.
 *
 * ClockPlus is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ClockPlus is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ClockPlus.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.philliphsu.clock2.chronometer;

import android.annotation.SuppressLint;
import android.app.NotificationManager;
import android.content.res.Resources;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;

/**
 * Created by Phillip Hsu on 9/10/2016.
 *
 * A thread that updates a chronometer-based notification. While notifications
 * have built-in support for using a chronometer, it lacks pause/resume functionality
 * and the ability to choose between count up or count down.
 */
public class ChronometerNotificationThread extends HandlerThread {
    private static final String TAG = "ChronomNotifThread";

    private static final int MSG_WHAT = 2;

    private final ChronometerDelegate mDelegate;
    private final NotificationManager mNotificationManager;
    private final NotificationCompat.Builder mNoteBuilder;
    private final Resources mResources;
    private final String mNoteTag;
    private final int mNoteId;

    private Handler mHandler;

    /**
     * @param delegate Configured by the client service, including whether to be counting down or not.
     * @param builder A preconfigured Builder from the client service whose content
     *                text will be updated and eventually built from.
     * @param resources Required only if the ChronometerDelegate is configured to count down.
    *                  Used to retrieve a String resource if/when the countdown reaches negative.
    *                  TODO: Will the notification be cancelled fast enough before the countdown
     * @param noteTag An optional tag for posting notifications.
     */
    public ChronometerNotificationThread(@NonNull ChronometerDelegate delegate,
            @NonNull NotificationManager manager, @NonNull NotificationCompat.Builder builder,
            @Nullable Resources resources, @Nullable String noteTag, int noteId) {
        super(TAG);
        mDelegate = delegate;
        mNotificationManager = manager;
        mNoteBuilder = builder;
        mResources = resources;
        mNoteTag = noteTag;
        mNoteId = noteId;
    }

    // There won't be a memory leak since our handler is using a looper that is not
    // associated with the main thread. The full Lint warning confirmed this.
    @SuppressLint("HandlerLeak")
    @Override
    protected void onLooperPrepared() {
        // This is called after the looper has completed initializing, but before
        // it starts looping through its message queue. Right now, there is no
        // message queue, so this is the place to create it.
        // By default, the constructor associates this handler with this thread's looper.
        mHandler = new Handler() {
            @Override
            public void handleMessage(Message m) {
                updateNotification(true);
                sendMessageDelayed(Message.obtain(this, MSG_WHAT), 1000);
            }
        };
        // Once the handler is initialized, we may immediately begin our work.
        mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_WHAT), 1000);
    }

    /**
     * @param updateText whether the new notification should update its chronometer.
     *                   Use {@code false} if you are updating everything else about the notification,
     *                   e.g. you just want to refresh the actions due to a start/pause state change.
     */
    public void updateNotification(boolean updateText) {
        if (updateText) {
            CharSequence text = mDelegate.formatElapsedTime(SystemClock.elapsedRealtime(), mResources);
            mNoteBuilder.setContentText(text);
        }
        mNotificationManager.notify(mNoteTag, mNoteId, mNoteBuilder.build());
    }

    @Override
    public boolean quit() {
        // TODO: I think we can call removeCallbacksAndMessages(null)
        // to remove ALL messages.
        mHandler.removeMessages(MSG_WHAT);
        return super.quit();
    }
}