/** * MessageListDemoDaemon.java * * Copyright � 1998-2011 Research In Motion Limited * * 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. * * Note: For the sake of simplicity, this sample application may not leverage * resource bundles and resource strings. However, it is STRONGLY recommended * that application developers make use of the localization features available * within the BlackBerry development platform to ensure a seamless application * experience across a variety of languages and geographies. For more information * on localizing your application, please refer to the BlackBerry Java Development * Environment Development Guide associated with this release. */ package com.rim.samples.device.messagelistdemo; import java.util.Date; import net.rim.blackberry.api.menuitem.ApplicationMenuItem; import net.rim.blackberry.api.messagelist.ApplicationFolderIntegrationConfig; import net.rim.blackberry.api.messagelist.ApplicationIcon; import net.rim.blackberry.api.messagelist.ApplicationIndicator; import net.rim.blackberry.api.messagelist.ApplicationIndicatorRegistry; import net.rim.blackberry.api.messagelist.ApplicationMessage; import net.rim.blackberry.api.messagelist.ApplicationMessageFolder; import net.rim.blackberry.api.messagelist.ApplicationMessageFolderListener; import net.rim.blackberry.api.messagelist.ApplicationMessageFolderRegistry; import net.rim.device.api.system.Application; import net.rim.device.api.system.ApplicationDescriptor; import net.rim.device.api.system.EncodedImage; import net.rim.device.api.ui.UiApplication; import net.rim.device.api.ui.image.Image; import net.rim.device.api.ui.image.ImageFactory; /** * Daemon process that runs in the background. It's tasks include non-gui * message operations such as message deletion, marking messages as read/unread * and (in a typical real world scenario) synchronization with a mail server. */ public final class MessageListDemoDaemon extends Application implements ApplicationMessageFolderListener { private static final String APPLICATION_NAME = "Message List Demo"; /** * Called during device startup. Registers application descriptors, message * folder listeners, message icons and menu items. */ void init() { // 1. Register folders and application descriptors // ---------------------- final ApplicationMessageFolderRegistry reg = ApplicationMessageFolderRegistry.getInstance(); // Some context menu items don't need a GUI (e.g. an item for deleting a // message) and will be run in the current daemon application. final ApplicationDescriptor daemonDescr = ApplicationDescriptor.currentApplicationDescriptor(); // Main application descriptor - causes application to be launched with // default welcome screen if a user clicks on the "Message List Demo" // header in the home screen notifications view. final ApplicationDescriptor mainDescr = new ApplicationDescriptor(daemonDescr, APPLICATION_NAME, new String[] {}); // This application descriptor launches this application with a GUI to // execute listener callbacks, e.g. to display a message. final ApplicationDescriptor uiCallbackDescr = new ApplicationDescriptor(daemonDescr, APPLICATION_NAME, new String[] { "gui" }); // Get existing messages from storage and register them in folders final ApplicationFolderIntegrationConfig inboxIntegration = new ApplicationFolderIntegrationConfig(true, true, mainDescr); final ApplicationFolderIntegrationConfig deletedIntegration = new ApplicationFolderIntegrationConfig(false); final MessageListDemoStore messages = MessageListDemoStore.getInstance(); final ApplicationMessageFolder inbox = reg.registerFolder(MessageListDemo.INBOX_FOLDER_ID, "Inbox", messages.getInboxMessages(), inboxIntegration); final ApplicationMessageFolder deleted = reg.registerFolder(MessageListDemo.DELETED_FOLDER_ID, "Deleted Messages", messages.getDeletedMessages(), deletedIntegration); // Register as a listener for callback notifications inbox.addListener( this, ApplicationMessageFolderListener.MESSAGE_DELETED | ApplicationMessageFolderListener.MESSAGE_MARKED_OPENED | ApplicationMessageFolderListener.MESSAGE_MARKED_UNOPENED | ApplicationMessageFolderListener.MESSAGES_MARKED_OLD, daemonDescr); deleted.addListener(this, ApplicationMessageFolderListener.MESSAGE_DELETED, daemonDescr); messages.setFolders(inbox, deleted); // We've registered two folders, specify root folder name for the // [View Folder] screen. reg.setRootFolderName(APPLICATION_NAME); // 2. Register message icons ------------------------------------------- final ApplicationIcon newIcon = new ApplicationIcon(EncodedImage .getEncodedImageResource("img/new.png")); final ApplicationIcon readIcon = new ApplicationIcon(EncodedImage .getEncodedImageResource("img/read.png")); final ApplicationIcon repliedIcon = new ApplicationIcon(EncodedImage .getEncodedImageResource("img/replied.png")); final ApplicationIcon deletedIcon = new ApplicationIcon(EncodedImage .getEncodedImageResource("img/deleted.png")); reg.registerMessageIcon(DemoMessage.DEMO_MESSAGE_TYPE, MessageListDemo.STATUS_NEW, newIcon); reg.registerMessageIcon(DemoMessage.DEMO_MESSAGE_TYPE, MessageListDemo.STATUS_OPENED, readIcon); reg.registerMessageIcon(DemoMessage.DEMO_MESSAGE_TYPE, MessageListDemo.STATUS_REPLIED, repliedIcon); reg.registerMessageIcon(DemoMessage.DEMO_MESSAGE_TYPE, MessageListDemo.STATUS_DELETED, deletedIcon); // 3. Register message menu items -------------------------------------- final ApplicationMenuItem openMenuItem = new OpenContextMenu(0x230010); final ApplicationMenuItem replyMenuItem = new ReplyContextMenu(0x230020); final ApplicationMenuItem markOpenedMenuItem = new MarkOpenedContextMenu(0x230030); final ApplicationMenuItem markUnopenedMenuItem = new MarkUnreadContextMenu(0x230040); final ApplicationMenuItem[] newGuiMenuItems = new ApplicationMenuItem[] { openMenuItem }; final ApplicationMenuItem[] newDaemonMenuItems = new ApplicationMenuItem[] { markOpenedMenuItem, replyMenuItem }; final ApplicationMenuItem[] openedGuiMenuItems = new ApplicationMenuItem[] { openMenuItem }; final ApplicationMenuItem[] openedDaemonMenuItems = new ApplicationMenuItem[] { markUnopenedMenuItem, replyMenuItem }; final ApplicationMenuItem[] repliedGuiMenuItems = new ApplicationMenuItem[] { openMenuItem }; final ApplicationMenuItem[] repliedDaemonMenuItems = new ApplicationMenuItem[] { markUnopenedMenuItem }; final ApplicationMenuItem[] deletedGuiMenuItems = new ApplicationMenuItem[] { openMenuItem, }; reg.registerMessageMenuItems(DemoMessage.DEMO_MESSAGE_TYPE, MessageListDemo.STATUS_NEW, newGuiMenuItems, uiCallbackDescr); reg.registerMessageMenuItems(DemoMessage.DEMO_MESSAGE_TYPE, MessageListDemo.STATUS_NEW, newDaemonMenuItems, daemonDescr); reg.registerMessageMenuItems(DemoMessage.DEMO_MESSAGE_TYPE, MessageListDemo.STATUS_OPENED, openedGuiMenuItems, uiCallbackDescr); reg.registerMessageMenuItems(DemoMessage.DEMO_MESSAGE_TYPE, MessageListDemo.STATUS_OPENED, openedDaemonMenuItems, daemonDescr); reg.registerMessageMenuItems(DemoMessage.DEMO_MESSAGE_TYPE, MessageListDemo.STATUS_REPLIED, repliedGuiMenuItems, uiCallbackDescr); reg.registerMessageMenuItems(DemoMessage.DEMO_MESSAGE_TYPE, MessageListDemo.STATUS_REPLIED, repliedDaemonMenuItems, daemonDescr); reg.registerMessageMenuItems(DemoMessage.DEMO_MESSAGE_TYPE, MessageListDemo.STATUS_DELETED, deletedGuiMenuItems, uiCallbackDescr); reg.setBulkMarkOperationsSupport(DemoMessage.DEMO_MESSAGE_TYPE, MessageListDemo.STATUS_NEW, true, false); reg.setBulkMarkOperationsSupport(DemoMessage.DEMO_MESSAGE_TYPE, MessageListDemo.STATUS_OPENED, false, true); reg.setBulkMarkOperationsSupport(DemoMessage.DEMO_MESSAGE_TYPE, MessageListDemo.STATUS_REPLIED, false, true); } /** * Changes the indicator value by the specified amount * * @param value * The amount by which the indicator must change */ public static void changeIndicator(final int value) { // Update indicator final ApplicationIndicator indicator = ApplicationIndicatorRegistry.getInstance() .getApplicationIndicator(); if (indicator == null) { return; } indicator.setValue(indicator.getValue() + value); // Check if there are any new messages and show indicator accordingly if (indicator.getValue() <= 0) { indicator.setVisible(false); } else { indicator.setVisible(true); } } /** * Normally the reply command would have GUI interaction, but in this sample * application we merely simulate the reply action. */ private static class ReplyContextMenu extends ApplicationMenuItem { /** * Creates a new ApplicationMenuItem instance with provided menu * position * * @param order * Display order of this item, lower numbers correspond to * higher placement in the menu */ public ReplyContextMenu(final int order) { super(order); // Set icon for GCM menu final EncodedImage eiReply = EncodedImage.getEncodedImageResource("img/sm_reply.png"); if (eiReply != null) { final Image image = ImageFactory.createImage(eiReply); this.setIcon(image); } } /** * @see ApplicationMenuItem#run(Object) */ public Object run(final Object context) { if (context instanceof DemoMessage) { final DemoMessage message = (DemoMessage) context; if (message.isNew()) { // Mark the message viewed changeIndicator(-1); } message.reply("You replied on " + new Date()); final ApplicationMessageFolderRegistry reg = ApplicationMessageFolderRegistry.getInstance(); final ApplicationMessageFolder folder = reg.getApplicationFolder(MessageListDemo.INBOX_FOLDER_ID); folder.fireElementUpdated(message, message); } return context; } /** * @see java.lang.Object#toString() */ public String toString() { return "Reply to Demo Message"; } } /** * Mark Opened menu item. After the method marks the message read, it fires * an update event. */ private static class MarkOpenedContextMenu extends ApplicationMenuItem { /** * Creates a new ApplicationMenuItem instance with provided menu * position * * @param order * Display order of this item, lower numbers correspond to * higher placement in the menu */ MarkOpenedContextMenu(final int order) { super(order); // Set icon for GCM menu final EncodedImage eiMarkOpened = EncodedImage .getEncodedImageResource("img/sm_mark_opened.png"); if (eiMarkOpened != null) { final Image image = ImageFactory.createImage(eiMarkOpened); this.setIcon(image); } } /** * Marks the context message opened * * @see ApplicationMenuItem#run(Object) */ public Object run(final Object context) { if (context instanceof DemoMessage) { final DemoMessage message = (DemoMessage) context; message.markRead(); final ApplicationMessageFolderRegistry reg = ApplicationMessageFolderRegistry.getInstance(); final ApplicationMessageFolder folder = reg.getApplicationFolder(MessageListDemo.INBOX_FOLDER_ID); folder.fireElementUpdated(message, message); changeIndicator(-1); } return context; } /** * @see java.lang.Object#toString() */ public String toString() { return "Mark Demo Message Read"; } } /** * Mark Unread menu item. After the method marks the message unread, it * fires an update event. */ private static class MarkUnreadContextMenu extends ApplicationMenuItem { /** * Creates a new ApplicationMenuItem instance with provided menu * position * * @param order * Display order of this item, lower numbers correspond to * higher placement in the menu */ MarkUnreadContextMenu(final int order) { super(order); // Set icon for GCM menu final EncodedImage eiMarkUnOpened = EncodedImage .getEncodedImageResource("img/sm_mark_unopened.png"); if (eiMarkUnOpened != null) { final Image image = ImageFactory.createImage(eiMarkUnOpened); this.setIcon(image); } } /** * @see ApplicationMenuItem#run(Object) */ public Object run(final Object context) { if (context instanceof DemoMessage) { // Mark the context message unread final DemoMessage message = (DemoMessage) context; message.markAsNew(); final ApplicationMessageFolderRegistry reg = ApplicationMessageFolderRegistry.getInstance(); final ApplicationMessageFolder folder = reg.getApplicationFolder(MessageListDemo.INBOX_FOLDER_ID); folder.fireElementUpdated(message, message); changeIndicator(1); } return context; } /** * @see java.lang.Object#toString() */ public String toString() { return "Mark Demo Message Unread"; } } /** * Open Context menu item. Marks read and opens the selected message for * viewing. */ static class OpenContextMenu extends ApplicationMenuItem { /** * Creates a new ApplicationMenuItem instance with provided menu * position * * @param order * Display order of this item, lower numbers correspond to * higher placement in the menu */ public OpenContextMenu(final int order) { super(order); // Set icon for GCM menu final EncodedImage eiOpen = EncodedImage.getEncodedImageResource("img/sm_open.png"); if (eiOpen != null) { final Image image = ImageFactory.createImage(eiOpen); this.setIcon(image); } } /** * @see ApplicationMenuItem#run(Object) */ public Object run(final Object context) { if (context instanceof DemoMessage) { final DemoMessage message = (DemoMessage) context; // Update status if message is new if (message.isNew()) { message.markRead(); final ApplicationMessageFolderRegistry reg = ApplicationMessageFolderRegistry.getInstance(); final ApplicationMessageFolder folder = reg.getApplicationFolder(MessageListDemo.INBOX_FOLDER_ID); folder.fireElementUpdated(message, message); changeIndicator(-1); } // Show message final DemoMessageScreen previewScreen = new DemoMessageScreen(message); final UiApplication uiApplication = UiApplication.getUiApplication(); uiApplication.pushScreen(previewScreen); uiApplication.requestForeground(); } return context; } /** * @see java.lang.Object#toString() */ public String toString() { return "View Demo Message"; } } /** * @see ApplicationMessageFolderListener#actionPerformed(int, * ApplicationMessage[], ApplicationMessageFolder) */ public void actionPerformed(final int action, final ApplicationMessage[] messages, final ApplicationMessageFolder folder) { final MessageListDemoStore messageStore = MessageListDemoStore.getInstance(); // Check if user opened the Message list and marked messages as not // 'new' if (action == ApplicationMessageFolderListener.MESSAGES_MARKED_OLD) { // User opened the message list and viewed messages, remove the // 'notification' state from the indicator. final ApplicationIndicator indicator = ApplicationIndicatorRegistry.getInstance() .getApplicationIndicator(); if (indicator != null) { indicator.setNotificationState(false); } // No further processing return; } synchronized (messageStore) { // Check if action was performed on multiple messages if (messages.length == 1) { final DemoMessage message = (DemoMessage) messages[0]; switch (action) { case ApplicationMessageFolderListener.MESSAGE_DELETED: if (folder.getId() == MessageListDemo.INBOX_FOLDER_ID) { // Message from Inbox was deleted, update storage, // the message will go into the Deleted folder messageStore.deleteInboxMessage(message); // Notify GUI that message has moved to // another folder. messageStore.getDeletedFolder().fireElementAdded( message); // Note: There is no need to fireElementRemoved(), // message was already deleted. } else { // Message was deleted completely from the Deleted // folder, update storage folder. messageStore.deleteMessageCompletely(message); // Note: There is no need to fireElementRemoved(), // message was already deleted. } break; case ApplicationMessageFolderListener.MESSAGE_MARKED_OPENED: // Update the indicator if (message.isNew()) { changeIndicator(-1); } // Update message message.markRead(); // Update storage messageStore.commitMessage(message); // Notify GUI that message has changed folder.fireElementUpdated(message, message); break; case ApplicationMessageFolderListener.MESSAGE_MARKED_UNOPENED: // Update the indicator if (!message.isNew()) { changeIndicator(1); } // Update message message.markAsNew(); // Update storage messageStore.commitMessage(message); // Notify GUI that message has changed folder.fireElementUpdated(message, message); break; } } else { // Multiple messages were affected, optimize notifications ApplicationMessageFolder resetFolder = folder; for (int i = 0; i < messages.length; i++) { final DemoMessage message = (DemoMessage) messages[i]; switch (action) { case ApplicationMessageFolderListener.MESSAGE_DELETED: if (folder.getId() == MessageListDemo.INBOX_FOLDER_ID) { // Update storage, the message will go // into the Deleted folder. messageStore.deleteInboxMessage(message); } else { // Message was deleted completely from the // Deleted folder, update storage. messageStore.deleteMessageCompletely(message); } break; case ApplicationMessageFolderListener.MESSAGE_MARKED_OPENED: if (message.isNew()) { changeIndicator(-1); } // Update message message.markRead(); // Update storage messageStore.commitMessage(message); // Notify GUI that message has changed folder.fireElementUpdated(message, message); break; case ApplicationMessageFolderListener.MESSAGE_MARKED_UNOPENED: if (!message.isNew()) { changeIndicator(1); } // Update message message.markAsNew(); // Update storage messageStore.commitMessage(message); // Notify GUI that message has changed folder.fireElementUpdated(message, message); break; } } if (action == ApplicationMessageFolderListener.MESSAGE_DELETED && folder.getId() == MessageListDemo.INBOX_FOLDER_ID) { // There is no need to reset the Inbox folder, all the // messages have already been deleted from it. We need to // reset Deleted folder because messages were appended to // it. resetFolder = messageStore.getDeletedFolder(); } if (resetFolder != null) { resetFolder.fireReset(); } } } } }