com.iskrembilen.quasseldroid.util.QuasseldroidNotificationManager.java Source code

Java tutorial

Introduction

Here is the source code for com.iskrembilen.quasseldroid.util.QuasseldroidNotificationManager.java

Source

/*
QuasselDroid - Quassel client for Android
Copyright (C) 2015 Ken Brge Viktil
Copyright (C) 2015 Magnus Fjell
Copyright (C) 2015 Martin Sandsmark <martin.sandsmark@kde.org>
    
This program 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, or under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.
    
This program 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 and the
GNU Lesser General Public License along with this program.  If not, see
<http://www.gnu.org/licenses/>.
 */

package com.iskrembilen.quasseldroid.util;

import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Typeface;
import android.net.Uri;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.style.StyleSpan;
import android.util.Log;
import android.util.SparseArray;

import com.google.common.base.Optional;
import com.iskrembilen.quasseldroid.protocol.state.Buffer;
import com.iskrembilen.quasseldroid.protocol.state.BufferInfo;
import com.iskrembilen.quasseldroid.protocol.state.Client;
import com.iskrembilen.quasseldroid.protocol.state.IrcMessage;
import com.iskrembilen.quasseldroid.protocol.state.NetworkCollection;
import com.iskrembilen.quasseldroid.Quasseldroid;
import com.iskrembilen.quasseldroid.R;
import com.iskrembilen.quasseldroid.events.InitProgressEvent;
import com.iskrembilen.quasseldroid.gui.LoginActivity;
import com.iskrembilen.quasseldroid.gui.MainActivity;
import com.iskrembilen.quasseldroid.service.CoreConnService;
import com.squareup.otto.Subscribe;

import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;

public class QuasseldroidNotificationManager {

    //TODO: lots of duplicate code in this class, clean up

    private Context context;
    private SharedPreferences preferences;
    private final List<Integer> highlightedBuffers = new ArrayList<>();
    private final SparseArray<List<IrcMessage>> highlightedMessages = new SparseArray<>();
    private List<Integer> buffers = new ArrayList<>();

    android.app.NotificationManager notifyManager;
    private boolean connected = false;
    private boolean initDone = false;
    private boolean pendingHighlightNotification;
    private PendingIntent contentIntent;

    public QuasseldroidNotificationManager(Context context) {
        this.context = context;
        preferences = PreferenceManager.getDefaultSharedPreferences(context);
        notifyManager = (android.app.NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        //Remove any disconnect notification since we are connecting again
        notifyManager.cancel(R.id.NOTIFICATION_DISCONNECTED);
        BusProvider.getInstance().register(this);
        SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
            @Override
            public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
                if (key.equals(QuasseldroidNotificationManager.this.context.getResources()
                        .getString(R.string.preference_notify_hide_persistence)) && connected
                        && highlightedMessages.size() == 0) {
                    notifyConnected(false);
                }
            }
        };
        PreferenceManager.getDefaultSharedPreferences(context)
                .registerOnSharedPreferenceChangeListener(sharedPreferenceListener);
    }

    public void notifyHighlightsRead(Integer bufferId) {
        Log.d(getClass().getSimpleName(), String.format("Intent. Setting highlights read for buffer %d", bufferId));
        synchronized (highlightedBuffers) {
            if (highlightedBuffers.contains(bufferId)) {
                highlightedMessages.remove(bufferId);
                highlightedBuffers.remove(bufferId);
                buffers.remove((Integer) bufferId);
                if (highlightedBuffers.size() == 0) {
                    notifyConnected(false);
                } else if (!connected) {
                    notifyConnected(false);
                    pendingHighlightNotification = true;
                } else {
                    notifyHighlights();
                }
            }
        }
    }

    private void notifyConnected(boolean withPhysicalNotifications) {
        int defaults = 0;
        connected = true;
        if (checkPending()) {
            notifyHighlights();
        } else {
            NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
                    .setSmallIcon(R.drawable.stat_normal).setContentTitle(context.getText(R.string.app_name))
                    .setContentText(context.getText(R.string.notification_connected)).setAutoCancel(true)
                    .setPriority(
                            preferences.getBoolean("notifypersistence", false) ? NotificationCompat.PRIORITY_MIN
                                    : NotificationCompat.PRIORITY_LOW)
                    .setCategory(NotificationCompat.CATEGORY_SERVICE).setWhen(System.currentTimeMillis());

            if (withPhysicalNotifications
                    && preferences.getBoolean(context.getString(R.string.preference_notify_connect), false)) {
                if (preferences.getBoolean(context.getString(R.string.preference_notification_vibrate), true))
                    defaults |= Notification.DEFAULT_VIBRATE;

                if (preferences.getBoolean(context.getString(R.string.preference_notification_sound_active), false)
                        && !preferences.getBoolean(context.getString(R.string.has_focus), true) && preferences
                                .getBoolean(context.getString(R.string.preference_notification_sound), false)) {
                    Uri ringtone = Uri.parse(preferences
                            .getString(context.getString(R.string.preference_notification_connect_sound_file), ""));
                    if (ringtone.equals(Uri.EMPTY))
                        defaults |= Notification.DEFAULT_SOUND;
                    else
                        builder.setSound(ringtone);
                } else if (!preferences.getBoolean(context.getString(R.string.preference_notification_sound_active),
                        true)
                        && preferences.getBoolean(context.getString(R.string.preference_notification_sound),
                                false)) {
                    Uri ringtone = Uri.parse(preferences
                            .getString(context.getString(R.string.preference_notification_connect_sound_file), ""));
                    if (ringtone.equals(Uri.EMPTY))
                        defaults |= Notification.DEFAULT_SOUND;
                    else
                        builder.setSound(ringtone);
                }
            }
            if (defaults != 0)
                builder.setDefaults(defaults);

            Intent launch = new Intent(context, MainActivity.class);
            launch.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            if (contentIntent != null)
                contentIntent.cancel();
            contentIntent = PendingIntent.getActivity(context, 0, launch, 0);
            builder.setContentIntent(contentIntent);

            builder.setColor(context.getResources().getColor(R.color.primary));

            Intent disconnect = new Intent(context, CoreConnService.class);
            disconnect.putExtra("disconnect", true);

            PendingIntent actionIntent = PendingIntent.getService(context, 0, disconnect,
                    PendingIntent.FLAG_UPDATE_CURRENT);

            builder.addAction(R.drawable.ic_disconnect, context.getString(R.string.action_disconnect),
                    actionIntent);

            // Send the notification.
            notifyManager.notify(R.id.NOTIFICATION, builder.build());
        }
    }

    private boolean checkPending() {
        if (pendingHighlightNotification && getHighlightedMessageCount() == 0)
            pendingHighlightNotification = false;

        return pendingHighlightNotification;
    }

    public void notifyConnected() {
        notifyConnected(true);
    }

    public Notification getConnectingNotification() {
        return getConnectingNotification(Optional.<String>absent());
    }

    public Notification getConnectingNotification(Optional<String> status) {
        NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
                .setSmallIcon(R.drawable.stat_connecting).setContentTitle(context.getText(R.string.app_name))
                .setContentText(status.or(context.getText(R.string.notification_connecting).toString()))
                .setPriority(NotificationCompat.PRIORITY_LOW).setWhen(System.currentTimeMillis());

        Intent launch = new Intent(context, MainActivity.class);
        launch.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        if (contentIntent != null)
            contentIntent.cancel();
        contentIntent = PendingIntent.getActivity(context, 0, launch, 0);
        builder.setContentIntent(contentIntent);
        builder.setColor(context.getResources().getColor(R.color.primary));
        return builder.build();

    }

    public void notifyConnecting() {
        notifyConnecting(Optional.<String>absent());
    }

    public void notifyConnecting(Optional<String> status) {
        // Send the notification.
        notifyManager.notify(R.id.NOTIFICATION, getConnectingNotification(status));
    }

    public void addMessage(IrcMessage message) {
        synchronized (highlightedBuffers) {
            // If the buffer in question isnt in the list of highlighted buffers, add it
            if (!highlightedBuffers.contains(message.bufferInfo.id)) {
                highlightedBuffers.add(message.bufferInfo.id);
                // If the buffer has had no highlights yet, add a new list of highlights
                if (highlightedMessages.get(message.bufferInfo.id) == null)
                    highlightedMessages.put(message.bufferInfo.id, new ArrayList<IrcMessage>());
            }
            if (!highlightedMessages.get(message.bufferInfo.id).contains(message))
                highlightedMessages.get(message.bufferInfo.id).add(message);
        }

        if (buffers.contains(message.bufferInfo.id)) {
            buffers.remove((Integer) message.bufferInfo.id);
        }
        buffers.add(message.bufferInfo.id);

        pendingHighlightNotification = true;

        if (connected)
            notifyHighlights();
    }

    int getHighlightedMessageCount() {
        synchronized (highlightedBuffers) {
            int res = 0;
            for (Integer bufferId : highlightedBuffers) {
                res += highlightedMessages.get(bufferId).size();
            }
            return res;
        }
    }

    public void notifyHighlights() {
        if (!connected || !initDone)
            return;

        synchronized (highlightedBuffers) {
            boolean displayColors = PreferenceManager.getDefaultSharedPreferences(context)
                    .getBoolean(context.getString(R.string.preference_colored_text), true);

            int defaults = 0;

            Resources res = context.getResources();

            int highlightedMessageCount = getHighlightedMessageCount();

            // Building the base notification
            NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
            builder.setSmallIcon(R.drawable.stat_highlight).setPriority(NotificationCompat.PRIORITY_DEFAULT)
                    .setWhen(System.currentTimeMillis()).setNumber(highlightedMessageCount);

            if (highlightedBuffers.size() == 1 && highlightedMessages.get(highlightedBuffers.get(0)).size() == 1) {
                IrcMessage message = highlightedMessages.get(highlightedBuffers.get(0)).get(0);

                builder.setContentTitle(message.bufferInfo.name).setContentText(MessageUtil.parseStyleCodes(context,
                        String.format("%s: %s", message.getNick(), message.content), displayColors));
            } else if (highlightedBuffers.size() == 1) {
                NetworkCollection networks = Client.getInstance().getNetworks();
                Buffer buffer = networks.getBufferById(highlightedBuffers.get(0));
                List<IrcMessage> messages = highlightedMessages.get(highlightedBuffers.get(0));

                builder.setContentTitle(buffer.getInfo().name)
                        .setContentText(
                                String.format(res.getString(R.string.notification_hightlights_on_buffers),
                                        res.getQuantityString(R.plurals.notification_x_highlights,
                                                highlightedMessageCount, highlightedMessageCount),
                                        buffer.getInfo().name));

                NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();

                // Sets a title for the Inbox in expanded layout
                inboxStyle
                        .setBigContentTitle(
                                String.format(res.getString(R.string.notification_hightlights_on_buffers),
                                        res.getQuantityString(R.plurals.notification_x_highlights,
                                                highlightedMessageCount, highlightedMessageCount),
                                        buffer.getInfo().name));

                // Moves events into the expanded layout

                if (buffer.getInfo().type == BufferInfo.Type.QueryBuffer) {
                    SpannableString s;

                    for (IrcMessage m : messages) {
                        s = MessageUtil.parseStyleCodes(context, String.format("%s: %s", m.getNick(), m.content),
                                displayColors);
                        s.setSpan(new StyleSpan(Typeface.BOLD), 0, m.getNick().length(),
                                Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
                        inboxStyle.addLine(s);
                    }
                } else {
                    SpannableStringBuilder s = new SpannableStringBuilder(buffer.getInfo().name);
                    s.append(":");
                    s.setSpan(new StyleSpan(Typeface.BOLD), 0, buffer.getInfo().name.length(),
                            Spannable.SPAN_INCLUSIVE_EXCLUSIVE);

                    inboxStyle.addLine(s);
                    for (IrcMessage m : messages) {
                        inboxStyle.addLine(String.format("  %s: %s", m.getNick(), m.content));
                    }
                }

                // Moves the expanded layout object into the notification object.
                builder.setStyle(inboxStyle);
            } else {
                builder.setContentTitle(context.getText(R.string.app_name))
                        .setContentText(String.format(res.getString(R.string.notification_hightlights_on_buffers),
                                res.getQuantityString(R.plurals.notification_x_highlights, highlightedMessageCount,
                                        highlightedMessageCount),
                                res.getQuantityString(R.plurals.notification_on_x_buffers,
                                        highlightedBuffers.size(), highlightedBuffers.size())));

                NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();

                // Sets a title for the Inbox in expanded layout
                inboxStyle.setBigContentTitle(
                        String.format(res.getString(R.string.notification_hightlights_on_buffers),
                                res.getQuantityString(R.plurals.notification_x_highlights, highlightedMessageCount,
                                        highlightedMessageCount),
                                res.getQuantityString(R.plurals.notification_on_x_buffers,
                                        highlightedBuffers.size(), highlightedBuffers.size())));

                synchronized (highlightedBuffers) {
                    // Moves events into the expanded layout
                    for (Integer bufferId : highlightedBuffers) {
                        Buffer buffer = Client.getInstance().getNetworks().getBufferById(bufferId);
                        List<IrcMessage> messages = highlightedMessages.get(bufferId);

                        if (messages.size() == 1) {
                            IrcMessage m = messages.get(0);
                            SpannableString s;
                            if (m.bufferInfo.type == BufferInfo.Type.QueryBuffer) {
                                s = MessageUtil.parseStyleCodes(context,
                                        String.format("%s: %s", m.getNick(), m.content), displayColors);
                                s.setSpan(new StyleSpan(Typeface.BOLD), 0, m.getNick().length(),
                                        Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
                            } else {
                                s = MessageUtil.parseStyleCodes(context,
                                        String.format("%s %s: %s", m.bufferInfo.name, m.getNick(), m.content),
                                        displayColors);
                                s.setSpan(new StyleSpan(Typeface.BOLD), 0, m.bufferInfo.name.length(),
                                        Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
                            }
                            inboxStyle.addLine(s);
                        } else if (buffer.getInfo().type == BufferInfo.Type.QueryBuffer) {
                            SpannableString s;

                            for (IrcMessage m : messages) {
                                s = MessageUtil.parseStyleCodes(context,
                                        String.format("%s: %s", m.getNick(), m.content), displayColors);
                                s.setSpan(new StyleSpan(Typeface.BOLD), 0, m.getNick().length(),
                                        Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
                                inboxStyle.addLine(s);
                            }
                        } else {
                            SpannableStringBuilder s = new SpannableStringBuilder(buffer.getInfo().name);
                            s.append(":");
                            s.setSpan(new StyleSpan(Typeface.BOLD), 0, buffer.getInfo().name.length(),
                                    Spannable.SPAN_INCLUSIVE_EXCLUSIVE);

                            inboxStyle.addLine(s);
                            for (IrcMessage m : messages) {
                                inboxStyle.addLine(String.format("  %s: %s", m.getNick(), m.content));
                            }
                        }
                    }
                }
                // Moves the expanded layout object into the notification object.
                builder.setStyle(inboxStyle);
            }

            builder.setColor(ThemeUtil.Color.chatHighlight);
            if (hasDirectMessage()) {
                builder.setCategory(NotificationCompat.CATEGORY_MESSAGE);
            } else {
                builder.setCategory(NotificationCompat.CATEGORY_SOCIAL);
            }

            Intent launch = new Intent(context, MainActivity.class);
            if (!buffers.isEmpty())
                launch.putExtra("extraBufferId", buffers.get(0));
            launch.putExtra("extraDrawer", false);

            Uri.Builder uriBuilder = new Uri.Builder();
            uriBuilder.scheme("content");
            uriBuilder.path(Quasseldroid.class.getCanonicalName());
            uriBuilder.appendPath("open-buffer");
            launch.setData(uriBuilder.build());

            launch.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            if (contentIntent != null)
                contentIntent.cancel();
            contentIntent = PendingIntent.getActivity(context, highlightedMessages.hashCode(), launch, 0);
            builder.setContentIntent(contentIntent);

            if (pendingHighlightNotification) {
                if (preferences.getBoolean(context.getString(R.string.preference_notification_sound_active), false)
                        && !preferences.getBoolean(context.getString(R.string.has_focus), true) && preferences
                                .getBoolean(context.getString(R.string.preference_notification_sound), false)) {

                    Uri ringtone = Uri.parse(preferences
                            .getString(context.getString(R.string.preference_notification_sound_file), ""));
                    if (ringtone.equals(Uri.EMPTY))
                        defaults |= Notification.DEFAULT_SOUND;
                    else
                        builder.setSound(ringtone);
                } else if (!preferences.getBoolean(context.getString(R.string.preference_notification_sound_active),
                        true)
                        && preferences.getBoolean(context.getString(R.string.preference_notification_sound),
                                false)) {

                    Uri ringtone = Uri.parse(preferences
                            .getString(context.getString(R.string.preference_notification_sound_file), ""));
                    if (ringtone.equals(Uri.EMPTY))
                        defaults |= Notification.DEFAULT_SOUND;
                    else
                        builder.setSound(ringtone);
                }
                if (preferences.getBoolean(context.getString(R.string.preference_notification_light), false))
                    defaults |= Notification.DEFAULT_LIGHTS;
                if (preferences.getBoolean(context.getString(R.string.preference_notification_vibrate), false))
                    defaults |= Notification.DEFAULT_VIBRATE;

                if (defaults != 0)
                    builder.setDefaults(defaults);
            }

            //builder.setColor(Color.parseColor(preferences.getString(context.getString(R.string.preference_notification_light_color), context.getString(R.string.notification_light_color_default))));

            // Send the notification.
            notifyManager.notify(R.id.NOTIFICATION, builder.build());

            pendingHighlightNotification = false;
        }
    }

    private boolean hasDirectMessage() {
        NetworkCollection networks = Client.getInstance().getNetworks();
        synchronized (highlightedBuffers) {
            for (Integer bufferId : highlightedBuffers) {
                Buffer buffer = networks.getBufferById(bufferId);

                // TODO: Maybe add Groupbuffer here as well?
                if (buffer != null && buffer.getInfo() != null
                        && buffer.getInfo().type == BufferInfo.Type.QueryBuffer)
                    return true;
            }
        }
        return false;
    }

    @Subscribe
    public void onInitProgressed(InitProgressEvent event) {
        if (event.done) {
            initDone = true;
            if (getHighlightedMessageCount() > 0)
                notifyHighlights();
        } else {
            notifyConnecting(Optional.of(event.progress));
        }
    }

    public void notifyDisconnected() {
        connected = false;
        NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
                .setSmallIcon(R.drawable.stat_disconnected).setContentTitle(context.getText(R.string.app_name))
                .setContentText(context.getText(R.string.notification_disconnected)).setAutoCancel(true)
                .setPriority(NotificationCompat.PRIORITY_LOW).setWhen(System.currentTimeMillis());

        // Clean up existing messages

        // The PendingIntent to launch our activity if the user selects this notification
        Intent launch = new Intent(context, LoginActivity.class);
        launch.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        if (contentIntent != null)
            contentIntent.cancel();
        contentIntent = PendingIntent.getActivity(context, 0, launch, 0);

        // Set the info for the views that show in the notification panel.
        builder.setContentIntent(contentIntent);

        builder.setColor(context.getResources().getColor(R.color.chat_line_error_dark));
        //Send the notification.
        notifyManager.notify(R.id.NOTIFICATION_DISCONNECTED, builder.build());
    }

    public void clear() {
        buffers.clear();
        highlightedBuffers.clear();
        highlightedMessages.clear();
    }
}