/* This file is part of RateBeer For Android. RateBeer for Android 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. RateBeer for Android 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 RateBeer for Android. If not, see <http://www.gnu.org/licenses/>. */ package com.ratebeer.android.gui.components; import java.sql.SQLException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Locale; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import com.googlecode.androidannotations.annotations.Bean; import com.googlecode.androidannotations.annotations.EService; import com.googlecode.androidannotations.annotations.SystemService; import com.j256.ormlite.dao.Dao; import com.jakewharton.notificationcompat2.NotificationCompat2; import com.jakewharton.notificationcompat2.NotificationCompat2.Builder; import com.jakewharton.notificationcompat2.NotificationCompat2.InboxStyle; import com.ratebeer.android.R; import com.ratebeer.android.api.ApiConnection; import com.ratebeer.android.api.CommandFailureResult; import com.ratebeer.android.api.CommandResult; import com.ratebeer.android.api.CommandSuccessResult; import com.ratebeer.android.api.UserSettings; import com.ratebeer.android.api.command.GetAllBeerMailsCommand; import com.ratebeer.android.api.command.GetAllBeerMailsCommand.Mail; import com.ratebeer.android.app.ApplicationSettings; import com.ratebeer.android.app.persistance.BeerMail; import com.ratebeer.android.gui.Home_; import com.ratebeer.android.gui.components.helpers.DatabaseConsumerService; import com.ratebeer.android.gui.components.helpers.Log; @EService public class BeermailService extends DatabaseConsumerService { public static final String ACTION_VIEWBEERMAILS = "VIEW_BEERMAILS"; public static final String ACTION_VIEWBEERMAIL = "VIEW_BEERMAIL"; public static final String ACTION_REPLYBEERMAIL = "REPLY_BEERMAIL"; public static final String EXTRA_MESSENGER = "MESSENGER"; public static final String EXTRA_MAIL = "MAIL"; private static final int NOTIFY_NEWBEERMAIL = 0; public static final int RESULT_SUCCESS = 0; public static final int RESULT_FAILURE = 1; public static final int RESULT_STARTED = 2; @Bean protected Log Log; @Bean protected ApplicationSettings applicationSettings; @Bean protected ApiConnection apiConnection; @SystemService protected NotificationManager notificationManager; @SystemService protected ConnectivityManager connectivityManager; public BeermailService() { super(com.ratebeer.android.gui.components.helpers.Log.LOG_NAME + " BeermailService"); } public BeermailService(String name) { super(name); } @Override protected void onHandleIntent(Intent intent) { if (isBackgroundDataDisabled()) { Log.d(com.ratebeer.android.gui.components.helpers.Log.LOG_NAME, "Skip the update, since background data is disabled on a system-wide level"); return; } // Proper user settings? UserSettings user = applicationSettings.getUserSettings(); if (user == null) { Log.d(com.ratebeer.android.gui.components.helpers.Log.LOG_NAME, "Canceling BeerMail check intent because there are no user settings known."); return; } callbackMessenger(intent, RESULT_STARTED); // Look for (new) beermail SimpleDateFormat sentDateFormat = new SimpleDateFormat("M/d/yyyy h:m:s a", Locale.US); GetAllBeerMailsCommand allMails = new GetAllBeerMailsCommand(user); CommandResult result = allMails.execute(apiConnection); // Return error if the command was unsuccessful if (!(result instanceof CommandSuccessResult)) { String e = result instanceof CommandFailureResult ? ((CommandFailureResult) result).getException() .toString() : "Unknown error"; Log.d(com.ratebeer.android.gui.components.helpers.Log.LOG_NAME, "Error retrieving new beer mails: " + e); // If requested, call back the messenger, i.e. the calling activity callbackMessenger(intent, RESULT_FAILURE); return; } // Successfully retrieved beer mails Log.d(com.ratebeer.android.gui.components.helpers.Log.LOG_NAME, "Received " + allMails.getMails().size() + " mail headers."); final int MAX_CONTENTLENGTH = 50; int unreadMails = 0; BeerMail firstUnread = null; List<String> mailBy = new ArrayList<String>(); List<String> mailLines = new ArrayList<String>(); try { Dao<BeerMail, Integer> dao = getHelper().getBeerMailDao(); // Traverse though the mails we just received for (Mail mail : allMails.getMails()) { // Create beer mail object if we did not know about this mail yet BeerMail beerMail = dao.queryForId(mail.messageID); if (beerMail == null) { Date sent = null; try { // Parse mail date sent = sentDateFormat.parse(mail.sent); } catch (ParseException e) { // Cannot parse date; ignore and don't show instead } // Add to the database beerMail = new BeerMail(mail.messageID, mail.senderID, mail.senderName, mail.messageRead, mail.replied, sent, mail.subject, null); dao.create(beerMail); } // Count unread mails if (!beerMail.isRead()) { unreadMails++; mailBy.add(beerMail.getSenderName()); mailLines.add(beerMail.getSomeContent(MAX_CONTENTLENGTH)); if (firstUnread == null) { firstUnread = beerMail; } } } // Look for deleted or updated mails if (allMails.getMails().size() > 0) { // Consider all locally stored mails within the time period of this new update Mail oldestInUpdate = allMails.getMails().get(allMails.getMails().size() - 1); List<BeerMail> existing = dao.queryBuilder().orderBy(BeerMail.MESSAGEID_FIELD_NAME, false).where() .gt(BeerMail.MESSAGEID_FIELD_NAME, oldestInUpdate.messageID).query(); // See if these still existed in the last update for (BeerMail check : existing) { Mail present = null; for (Mail mail : allMails.getMails()) { if (mail.messageID == check.getMessageId()) { present = mail; break; } } if (present == null) { // Not available any longer: it was removed dao.delete(check); } else { // Update this entry check.setIsRead(present.messageRead); check.setIsReplied(present.replied); dao.update(check); } } } } catch (SQLException e) { Log.d(com.ratebeer.android.gui.components.helpers.Log.LOG_NAME, "Error saving beermail to database: " + e); // If requested, call back the messenger, i.e. the calling activity callbackMessenger(intent, RESULT_FAILURE); return; } // Create a notification about the new mails Log.d(com.ratebeer.android.gui.components.helpers.Log.LOG_NAME, "User has " + unreadMails + " unread mails."); if (!intent.hasExtra(EXTRA_MESSENGER) && unreadMails > 0) { // Prepare senders text String newMailsText = getString(R.string.app_newmails, unreadMails); String mailByText = ""; final int MAX_SENDERS = 3; for (int i = 0; i < mailBy.size() && i < MAX_SENDERS; i++) { mailByText += mailBy.get(i) + ", "; } mailByText = mailByText.substring(0, mailByText.length() - 2); if (mailBy.size() > MAX_SENDERS) { mailByText += getString(R.string.app_andothers); } // Set notification action target Intent contentIntent = new Intent(this, Home_.class); contentIntent.setAction(ACTION_VIEWBEERMAILS); // Retrieve user of some sender to show with the notification Bitmap avatar = null; try { avatar = BitmapFactory.decodeStream(apiConnection.getRaw("http://www.ratebeer.com/UserPics/" + firstUnread.getSenderName() + ".jpg")); } catch (Exception e) { // Could not load? Just don't show an image } // Build old style notification Builder builder = new NotificationCompat2.Builder(this).setSmallIcon(R.drawable.ic_stat_notification) .setTicker(getString(R.string.app_newmail)).setContentTitle(newMailsText) .setContentText(mailByText) .setContentIntent(PendingIntent.getActivity(this, 0, contentIntent, 0)); if (avatar != null) { builder.setLargeIcon(avatar); } // Enrich notification with Jelly Bean inbox style InboxStyle inbox = new NotificationCompat2.InboxStyle(builder).setBigContentTitle(newMailsText); for (int i = 0; i < mailBy.size() && i < MAX_SENDERS; i++) { inbox.addLine(mailBy.get(i) + ": " + mailLines.get(i)); } if (unreadMails - 3 > 0) { inbox.setSummaryText(getString(R.string.app_moremail, Integer.toString(unreadMails - 3))); } if (unreadMails == 1) { Intent replyIntent = new Intent(this, Home_.class); replyIntent.setAction(ACTION_REPLYBEERMAIL); replyIntent.putExtra(BeermailService.EXTRA_MAIL, firstUnread); builder.addAction(R.drawable.ic_stat_reply, getString(R.string.mail_reply), PendingIntent.getActivity(this, 0, replyIntent, 0)); Intent viewIntent = new Intent(this, Home_.class); viewIntent.setAction(ACTION_VIEWBEERMAIL); viewIntent.putExtra(BeermailService.EXTRA_MAIL, firstUnread); builder.addAction(R.drawable.ic_stat_viewmail, getString(R.string.mail_view), PendingIntent.getActivity(this, 0, viewIntent, 0)); } // Create notification, apply settings and release Notification notification = inbox.build(); notification.flags |= Notification.FLAG_AUTO_CANCEL; if (applicationSettings.getVibrateOnNotification()) { notification.defaults = Notification.DEFAULT_VIBRATE; } notification.ledARGB = 0xff003366; notification.ledOnMS = 300; notification.ledOffMS = 1000; notification.flags |= Notification.FLAG_SHOW_LIGHTS; notificationManager.notify(NOTIFY_NEWBEERMAIL, notification); } // If requested, call back the messenger, i.e. the calling activity callbackMessenger(intent, RESULT_SUCCESS); } @SuppressWarnings("deprecation") private boolean isBackgroundDataDisabled() { if (android.os.Build.VERSION.SDK_INT >= 14) { // Note: getBackgroundDataSetting will always return true on API level 14 and up NetworkInfo ni = connectivityManager.getActiveNetworkInfo(); return ni == null || !ni.isAvailable() || !ni.isConnected(); } else { return !connectivityManager.getBackgroundDataSetting(); } } private void callbackMessenger(Intent intent, int result) { if (intent.hasExtra(EXTRA_MESSENGER)) { // Prepare a message Messenger callback = intent.getParcelableExtra(EXTRA_MESSENGER); Message msg = Message.obtain(); msg.arg1 = result; try { // Send it back to the messenger, i.e. the activity callback.send(msg); } catch (RemoteException e) { Log.e(com.ratebeer.android.gui.components.helpers.Log.LOG_NAME, "Cannot call back to activity to deliver message '" + msg.toString() + "'"); } } } }