ch.carteggio.provider.sync.NotificationService.java Source code

Java tutorial

Introduction

Here is the source code for ch.carteggio.provider.sync.NotificationService.java

Source

/*******************************************************************************
 * Copyright (c) 2014, Lorenzo Keller
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
package ch.carteggio.provider.sync;

import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
import android.media.AudioManager;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import ch.carteggio.R;
import ch.carteggio.provider.CarteggioContract;
import ch.carteggio.provider.CarteggioProviderHelper;
import ch.carteggio.ui.MainActivity;
import ch.carteggio.ui.NetworkStatusActivity;

/**
 * 
 * This service is responsible to show notifications.
 * 
 * Design considerations: the service is very similar to an
 * IntentService. It however is implemented as an extension
 * of Service directly because, differently from IntentServices,
 * we want this service to stay alive to make sure it will be
 * notified of changes in the list of messages.
 * 
 */

public class NotificationService extends Service {

    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;

    private static final int ERROR_NOTIFICATION_ID = 1;
    private static final int INCOMING_NOTIFICATION_ID = 2;

    private static final String UPDATE_SENDING_STATE_ACTION = "ch.carteggio.provider.sync.NotificationService.UPDATE_SENDING_STATE_ACTION";
    private static final String UPDATE_RECEIVING_STATE_ACTION = "ch.carteggio.provider.sync.NotificationService.UPDATE_RECEIVING_STATE_ACTION";
    private static final String UPDATE_UNREAD_STATE_ACTION = "ch.carteggio.provider.sync.NotificationService.UPDATE_UNREAD_STATE_ACTION";

    private static final String FAILURE_MESSAGE_EXTRA = "ch.carteggio.provider.sync.NotificationService.FAILURE_MESSAGE_EXTRA";
    private static final String FAILURE_EXTRA = "ch.carteggio.provider.sync.NotificationService.SUCCESS_EXTRA";
    private static final String NEW_MESSAGE_EXTRA = "ch.carteggio.provider.sync.NotificationService.NEW_MESSAGE_EXTRA";

    private static final long DISCONNECTION_TIME_THRESHOLD = 30 * 1000;

    /**
     * 
     * An intent with this action is broadcasted to inform the UI that the network state has changed. This allows us
     * to update the screen as soon as it changes.
     * 
     */
    public static final String NETWORK_STATE_CHANGED_ACTION = "ch.carteggio.provider.sync.NotificationService.NETWORK_STATE_CHANGED_ACTION";

    private Observer mObserver;

    private boolean mSendFailure;
    private boolean mReceiveFailure;

    private long mLastSendSuccessTime;
    private long mLastReceiveSuccessTime;

    private String mSendMessage;
    private String mReceiveMessage;

    @Override
    public IBinder onBind(Intent intent) {
        return new Binder();
    }

    @Override
    public void onCreate() {
        super.onCreate();

        HandlerThread thread = new HandlerThread("NotificationService");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);

        mObserver = new Observer();

        getContentResolver().registerContentObserver(CarteggioContract.Messages.CONTENT_URI, true, mObserver);

    }

    @Override
    public void onDestroy() {

        getContentResolver().unregisterContentObserver(mObserver);

        mServiceLooper.quit();

        super.onDestroy();
    }

    protected void onHandleIntent(Intent intent) {

        if (intent != null) {

            if (UPDATE_RECEIVING_STATE_ACTION.equals(intent.getAction())) {

                mReceiveFailure = intent.getBooleanExtra(FAILURE_EXTRA, true);

                mReceiveMessage = intent.getStringExtra(FAILURE_MESSAGE_EXTRA);

                if (!mReceiveFailure) {
                    mLastReceiveSuccessTime = SystemClock.elapsedRealtime();
                }

            } else if (UPDATE_SENDING_STATE_ACTION.equals(intent.getAction())) {

                mSendFailure = intent.getBooleanExtra(FAILURE_EXTRA, true);

                mSendMessage = intent.getStringExtra(FAILURE_MESSAGE_EXTRA);

                if (!mSendFailure) {
                    mLastSendSuccessTime = SystemClock.elapsedRealtime();
                }

            } else if (UPDATE_UNREAD_STATE_ACTION.equals(intent.getAction())) {

                boolean newMessage = intent.getBooleanExtra(NEW_MESSAGE_EXTRA, false);

                updateUnreadNotification(newMessage);

            }

        }

        // update the notifications
        if ((mSendFailure && SystemClock.elapsedRealtime() - mLastSendSuccessTime > DISCONNECTION_TIME_THRESHOLD)
                || (mReceiveFailure && SystemClock.elapsedRealtime()
                        - mLastReceiveSuccessTime > DISCONNECTION_TIME_THRESHOLD)) {
            showFailureNotification();
        } else {
            hideFailureNotification();
        }

        // inform UI of the changes
        broadcastNetworkStateChange();

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);

        Message msg = mServiceHandler.obtainMessage();
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);

        // we want the service to be sticky to make sure we remember the state
        return START_STICKY;
    }

    private void updateUnreadNotification(boolean newMessage) {

        NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

        CarteggioProviderHelper helper = new CarteggioProviderHelper(this);

        int unreadCount = helper.getUnreadCount();

        if (unreadCount == 0) {

            mNotificationManager.cancel(INCOMING_NOTIFICATION_ID);

        } else {

            String quantityString = getResources().getQuantityString(R.plurals.notification_new_incoming_messages,
                    unreadCount);

            NotificationCompat.Builder notifyBuilder = new NotificationCompat.Builder(this)
                    .setContentTitle(String.format(quantityString, unreadCount))
                    .setSmallIcon(android.R.drawable.stat_notify_chat)
                    .setContentText(getString(R.string.notification_text_new_messages));

            Intent resultIntent = new Intent(this, MainActivity.class);

            TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
            stackBuilder.addParentStack(MainActivity.class);
            stackBuilder.addNextIntent(resultIntent);

            PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

            notifyBuilder.setContentIntent(resultPendingIntent);

            if (newMessage) {

                Uri uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);

                AudioManager manager = (AudioManager) getSystemService(AUDIO_SERVICE);

                long pattern[] = { 1000, 500, 2000 };

                if (manager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) {
                    notifyBuilder.setSound(uri);
                    notifyBuilder.setVibrate(pattern);
                } else if (manager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
                    notifyBuilder.setVibrate(pattern);
                }
            }

            mNotificationManager.notify(INCOMING_NOTIFICATION_ID, notifyBuilder.build());

        }

    }

    private void hideFailureNotification() {

        NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

        mNotificationManager.cancel(ERROR_NOTIFICATION_ID);
    }

    private void showFailureNotification() {

        NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

        NotificationCompat.Builder notifyBuilder = new NotificationCompat.Builder(this)
                .setContentTitle("Carteggio network error").setSmallIcon(android.R.drawable.stat_notify_error);

        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
        stackBuilder.addParentStack(NetworkStatusActivity.class);
        stackBuilder.addNextIntent(new Intent(this, NetworkStatusActivity.class));

        notifyBuilder.setContentIntent(stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT));

        if (mSendFailure && mReceiveFailure) {
            notifyBuilder.setContentText("There was a problem while delivering and receiving messages");
        } else if (mSendFailure) {
            notifyBuilder.setContentText("There was a problem while delivering messages");
        } else if (mReceiveFailure) {
            notifyBuilder.setContentText("There was a problem while receiving messages");
        }

        mNotificationManager.notify(ERROR_NOTIFICATION_ID, notifyBuilder.build());

    }

    private void broadcastNetworkStateChange() {

        Intent intent = new Intent(NETWORK_STATE_CHANGED_ACTION);

        sendBroadcast(intent);

    }

    public static void setSendingError(Context c, boolean error, String message) {

        Intent service = new Intent(c, NotificationService.class);
        service.setAction(UPDATE_SENDING_STATE_ACTION);
        service.putExtra(FAILURE_EXTRA, error);

        if (error) {
            service.putExtra(FAILURE_MESSAGE_EXTRA, message);
        }

        c.startService(service);

    }

    public static void setReceivingError(Context c, boolean error, String message) {

        Intent service = new Intent(c, NotificationService.class);
        service.setAction(UPDATE_RECEIVING_STATE_ACTION);
        service.putExtra(FAILURE_EXTRA, error);

        if (error) {
            service.putExtra(FAILURE_MESSAGE_EXTRA, message);
        }

        c.startService(service);

    }

    public static void notifyNewIncomingMessages(Context c) {

        Intent service = new Intent(c, NotificationService.class);
        service.setAction(UPDATE_UNREAD_STATE_ACTION);
        service.putExtra(NEW_MESSAGE_EXTRA, true);
        c.startService(service);

    }

    public static void updateUnreadNotification(Context c) {

        Intent service = new Intent(c, NotificationService.class);
        service.setAction(UPDATE_UNREAD_STATE_ACTION);

        c.startService(service);

    }

    private final class ServiceHandler extends Handler {

        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent) msg.obj);
        }
    }

    private class Observer extends ContentObserver {

        public Observer() {
            super(new Handler());
        }

        @Override
        public void onChange(boolean selfChange) {

            // we send an intent here because we don't want to do database operations in the main thread
            updateUnreadNotification(NotificationService.this);
        }

    }

    public class Binder extends android.os.Binder {

        public boolean isOutgoingMessagesFailure() {
            return mSendFailure;
        }

        public boolean isIncomingMessagesFailure() {
            return mReceiveFailure;
        }

        public String getOutgoingMessageError() {
            return mSendMessage;
        }

        public String getIncomingMessageError() {
            return mReceiveMessage;
        }

    }

}