/* Copyright (c) 2015 Magnet Systems, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 com.magnet.wru; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.media.RingtoneManager; import android.util.Log; import com.magnet.mmx.util.Base64; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; /** * A simple implementation of an in-memory message store. */ public class MyMessageStore { private static final String TAG = MyMessageStore.class.getSimpleName(); private static final ArrayList<Message> sMessageList = new ArrayList<>(); private static final ArrayList<OnChangeListener> sListeners = new ArrayList<>(); static boolean sSuppressNotification = false; public interface OnChangeListener { void onChange(); } /** * A data object to store the fields to display */ public static class Message { private String mId; private Map<String, String> mContent; private Date mTimestamp; private boolean mIsIncoming; private Message(String id, Map<String, String> content, Date timestamp, boolean isIncoming) { mId = id; mContent = content; mTimestamp = timestamp; mIsIncoming = isIncoming; } public String getId() { return mId; } public Map<String,String> getContent() { return mContent; } public Date getTimestamp() { return mTimestamp; } public boolean isIncoming() { return mIsIncoming; } } public static List<Message> getMessageList() { return Collections.unmodifiableList(sMessageList); } public static void addMessage(String id, Map<String, String> content, Date timestamp, boolean isIncoming) { synchronized (sMessageList) { sMessageList.add(new Message(id, content, timestamp, isIncoming)); notifyListeners(); if (!sSuppressNotification) { doNotify(); } } } public static void clear() { synchronized (sMessageList) { sMessageList.clear(); notifyListeners(); } } public static void registerListener(OnChangeListener listener) { synchronized (sListeners) { boolean alreadyAdded = false; for (OnChangeListener existing : sListeners) { if (alreadyAdded = (existing == listener)) { break; } } if (!alreadyAdded) { sListeners.add(listener); } } } public static void unregisterListener(OnChangeListener listener) { synchronized (sListeners) { for (int i=sListeners.size(); --i>=0;) { if (sListeners.get(i) == listener) { sListeners.remove(i); break; } } } } private static void notifyListeners() { synchronized (sListeners) { for (OnChangeListener listener : sListeners) { try { listener.onChange(); } catch (Throwable t) { Log.e(TAG, "notifyListener(): caught exception for listener: " + listener, t); } } } } private static final HashMap<String, Bitmap> sBitmapCache = new HashMap<>(); /** * If the drawable is already cached for the specified id. This returns true * is it's ready to go (for example, if the main thread is deciding whether or not to call * getDrawable() on the main thread or on non-main threads. * @param id the id * @return true if the drawable is ready */ public static boolean isBitmapCached(String id) { synchronized (sBitmapCache) { return sBitmapCache.containsKey(id) && sBitmapCache.get(id) != null; } } /** * This should be called on a separate thread as it may block. If it is already cached, it will * return immediately. If not, it will process the drawable or wait if the id is already pending * load (this will hopefully reduce the number of image decodes). * @param id the id * @param base64EncodedImage the base64 encoded image * @return the drawable */ public static Bitmap getBitmap(String id, String base64EncodedImage) { Bitmap bitmap; synchronized (sBitmapCache) { bitmap = sBitmapCache.get(id); if (bitmap != null) { return bitmap; } else { if (!sBitmapCache.containsKey(id)) { //first time caching sBitmapCache.put(id, null); } else { //already pending, wait for the pending to finish while ((bitmap = sBitmapCache.get(id)) == null) { try { sBitmapCache.wait(); } catch (InterruptedException e) { Log.w(TAG, "getDrawable(): interrupted exception", e); } } return bitmap; } } } try { File tempAttachmentFile = File.createTempFile(id + "-attach-" + System.currentTimeMillis(), null); Base64.decodeToFile(base64EncodedImage, tempAttachmentFile.getAbsolutePath()); bitmap = BitmapFactory.decodeFile(tempAttachmentFile.getAbsolutePath()); } catch (IOException ex) { Log.e(TAG, "getDrawable(): unable to get drawable for id: " + id, ex); } synchronized (sBitmapCache) { sBitmapCache.put(id, bitmap); sBitmapCache.notify(); } return bitmap; } private static void doNotify() { Intent intent = new Intent(WRUApplication.sContext, MapActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(WRUApplication.sContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); NotificationManager noteMgr = (NotificationManager) WRUApplication.sContext.getSystemService(Context.NOTIFICATION_SERVICE); Notification note = new Notification.Builder(WRUApplication.sContext).setAutoCancel(true) .setSmallIcon(android.R.drawable.stat_notify_chat).setWhen(System.currentTimeMillis()) .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)) .setContentIntent(pendingIntent) .setContentTitle("WRU").setContentText("message received").build(); noteMgr.notify(0, note); } }