mobile/android/base/NotificationClient.java

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/mobile/android/base/NotificationClient.java	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,209 @@
     1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
     1.5 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +package org.mozilla.gecko;
    1.10 +
    1.11 +import android.app.Notification;
    1.12 +import android.app.PendingIntent;
    1.13 +import android.text.TextUtils;
    1.14 +import android.util.Log;
    1.15 +
    1.16 +import java.util.LinkedList;
    1.17 +import java.util.concurrent.ConcurrentHashMap;
    1.18 +
    1.19 +/**
    1.20 + * Client for posting notifications through a NotificationHandler.
    1.21 + */
    1.22 +public abstract class NotificationClient {
    1.23 +    private static final String LOGTAG = "GeckoNotificationClient";
    1.24 +
    1.25 +    private volatile NotificationHandler mHandler;
    1.26 +    private boolean mReady;
    1.27 +    private final LinkedList<Runnable> mTaskQueue = new LinkedList<Runnable>();
    1.28 +    private final ConcurrentHashMap<Integer, UpdateRunnable> mUpdatesMap =
    1.29 +            new ConcurrentHashMap<Integer, UpdateRunnable>();
    1.30 +
    1.31 +    /**
    1.32 +     * Runnable that is reused between update notifications.
    1.33 +     *
    1.34 +     * Updates happen frequently, so reusing Runnables prevents frequent dynamic allocation.
    1.35 +     */
    1.36 +    private class UpdateRunnable implements Runnable {
    1.37 +        private long mProgress;
    1.38 +        private long mProgressMax;
    1.39 +        private String mAlertText;
    1.40 +        final private int mNotificationID;
    1.41 +
    1.42 +        public UpdateRunnable(int notificationID) {
    1.43 +            mNotificationID = notificationID;
    1.44 +        }
    1.45 +
    1.46 +        public synchronized boolean updateProgress(long progress, long progressMax, String alertText) {
    1.47 +            if (progress == mProgress
    1.48 +                    && mProgressMax == progressMax
    1.49 +                    && TextUtils.equals(mAlertText, alertText)) {
    1.50 +                return false;
    1.51 +            }
    1.52 +
    1.53 +            mProgress = progress;
    1.54 +            mProgressMax = progressMax;
    1.55 +            mAlertText = alertText;
    1.56 +            return true;
    1.57 +        }
    1.58 +
    1.59 +        @Override
    1.60 +        public void run() {
    1.61 +            long progress;
    1.62 +            long progressMax;
    1.63 +            String alertText;
    1.64 +
    1.65 +            synchronized (this) {
    1.66 +                progress = mProgress;
    1.67 +                progressMax = mProgressMax;
    1.68 +                alertText = mAlertText;
    1.69 +            }
    1.70 +
    1.71 +            mHandler.update(mNotificationID, progress, progressMax, alertText);
    1.72 +        }
    1.73 +    };
    1.74 +
    1.75 +    /**
    1.76 +     * Adds a notification.
    1.77 +     *
    1.78 +     * @see NotificationHandler#add(int, String, String, String, PendingIntent, PendingIntent)
    1.79 +     */
    1.80 +    public synchronized void add(final int notificationID, final String aImageUrl,
    1.81 +            final String aAlertTitle, final String aAlertText, final PendingIntent contentIntent) {
    1.82 +        mTaskQueue.add(new Runnable() {
    1.83 +            @Override
    1.84 +            public void run() {
    1.85 +                mHandler.add(notificationID, aImageUrl, aAlertTitle, aAlertText, contentIntent);
    1.86 +            }
    1.87 +        });
    1.88 +        notify();
    1.89 +
    1.90 +        if (!mReady) {
    1.91 +            bind();
    1.92 +        }
    1.93 +    }
    1.94 +
    1.95 +    /**
    1.96 +     * Adds a notification.
    1.97 +     *
    1.98 +     * @see NotificationHandler#add(int, Notification)
    1.99 +     */
   1.100 +    public synchronized void add(final int notificationID, final Notification notification) {
   1.101 +        mTaskQueue.add(new Runnable() {
   1.102 +            @Override
   1.103 +            public void run() {
   1.104 +                mHandler.add(notificationID, notification);
   1.105 +            }
   1.106 +        });
   1.107 +        notify();
   1.108 +
   1.109 +        if (!mReady) {
   1.110 +            bind();
   1.111 +        }
   1.112 +    }
   1.113 +
   1.114 +    /**
   1.115 +     * Updates a notification.
   1.116 +     *
   1.117 +     * @see NotificationHandler#update(int, long, long, String)
   1.118 +     */
   1.119 +    public void update(final int notificationID, final long aProgress, final long aProgressMax,
   1.120 +            final String aAlertText) {
   1.121 +        UpdateRunnable runnable = mUpdatesMap.get(notificationID);
   1.122 +
   1.123 +        if (runnable == null) {
   1.124 +            runnable = new UpdateRunnable(notificationID);
   1.125 +            mUpdatesMap.put(notificationID, runnable);
   1.126 +        }
   1.127 +
   1.128 +        // If we've already posted an update with these values, there's no
   1.129 +        // need to do it again.
   1.130 +        if (!runnable.updateProgress(aProgress, aProgressMax, aAlertText)) {
   1.131 +            return;
   1.132 +        }
   1.133 +
   1.134 +        synchronized (this) {
   1.135 +            if (mReady) {
   1.136 +                mTaskQueue.add(runnable);
   1.137 +                notify();
   1.138 +            }
   1.139 +        }
   1.140 +    }
   1.141 +
   1.142 +    /**
   1.143 +     * Removes a notification.
   1.144 +     *
   1.145 +     * @see NotificationHandler#remove(int)
   1.146 +     */
   1.147 +    public synchronized void remove(final int notificationID) {
   1.148 +        if (!mReady) {
   1.149 +            return;
   1.150 +        }
   1.151 +
   1.152 +        mTaskQueue.add(new Runnable() {
   1.153 +            @Override
   1.154 +            public void run() {
   1.155 +                mHandler.remove(notificationID);
   1.156 +                mUpdatesMap.remove(notificationID);
   1.157 +            }
   1.158 +        });
   1.159 +        notify();
   1.160 +    }
   1.161 +
   1.162 +    /**
   1.163 +     * Determines whether a notification is showing progress.
   1.164 +     *
   1.165 +     * @see NotificationHandler#isProgressStyle(int)
   1.166 +     */
   1.167 +    public boolean isOngoing(int notificationID) {
   1.168 +        final NotificationHandler handler = mHandler;
   1.169 +        return handler != null && handler.isOngoing(notificationID);
   1.170 +    }
   1.171 +
   1.172 +    protected void bind() {
   1.173 +        mReady = true;
   1.174 +    }
   1.175 +
   1.176 +    protected void unbind() {
   1.177 +        mReady = false;
   1.178 +        mUpdatesMap.clear();
   1.179 +    }
   1.180 +
   1.181 +    protected void connectHandler(NotificationHandler handler) {
   1.182 +        mHandler = handler;
   1.183 +        new Thread(new NotificationRunnable()).start();
   1.184 +    }
   1.185 +
   1.186 +    private class NotificationRunnable implements Runnable {
   1.187 +        @Override
   1.188 +        public void run() {
   1.189 +            Runnable r;
   1.190 +            try {
   1.191 +                while (true) {
   1.192 +                    // Synchronize polls to prevent tasks from being added to the queue
   1.193 +                    // during the isDone check.
   1.194 +                    synchronized (NotificationClient.this) {
   1.195 +                        r = mTaskQueue.poll();
   1.196 +                        while (r == null) {
   1.197 +                            if (mHandler.isDone()) {
   1.198 +                                unbind();
   1.199 +                                return;
   1.200 +                            }
   1.201 +                            NotificationClient.this.wait();
   1.202 +                            r = mTaskQueue.poll();
   1.203 +                        }
   1.204 +                    }
   1.205 +                    r.run();
   1.206 +                }
   1.207 +            } catch (InterruptedException e) {
   1.208 +                Log.e(LOGTAG, "Notification task queue processing interrupted", e);
   1.209 +            }
   1.210 +        }
   1.211 +    }
   1.212 +}

mercurial