source: trunk/LogicMail/src/org/logicprobe/LogicMail/ui/StandardScreen.java @ 957

Revision 957, 14.7 KB checked in by octorian, 9 months ago (diff)

Attempt at fixing the blank status bar issue when closing a screen with a request in progress.

Line 
1/*-
2 * Copyright (c) 2009, Derek Konigsberg
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the project nor the names of its
15 *    contributors may be used to endorse or promote products derived
16 *    from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29 * OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31package org.logicprobe.LogicMail.ui;
32
33import org.logicprobe.LogicMail.AnalyticsDataCollector;
34import org.logicprobe.LogicMail.LogicMail;
35import org.logicprobe.LogicMail.LogicMailResource;
36import org.logicprobe.LogicMail.model.AccountNode;
37import org.logicprobe.LogicMail.model.MailManager;
38import org.logicprobe.LogicMail.model.NetworkAccountNode;
39
40import net.rim.device.api.i18n.ResourceBundle;
41import net.rim.device.api.ui.Field;
42import net.rim.device.api.ui.MenuItem;
43import net.rim.device.api.ui.Screen;
44import net.rim.device.api.ui.UiApplication;
45import net.rim.device.api.ui.component.Dialog;
46import net.rim.device.api.ui.component.Menu;
47import net.rim.device.api.ui.container.MainScreen;
48
49/**
50 * Standard UI screen implementation.
51 * This implementation is designed to separate RIM API inheritance
52 * relationships from concrete UI screens through composition.
53 * The concrete UI is implemented through a <tt>ScreenProvider</tt>
54 * implementation.
55 */
56public class StandardScreen extends MainScreen {
57    protected static ResourceBundle resources = ResourceBundle.getBundle(LogicMailResource.BUNDLE_ID, LogicMailResource.BUNDLE_NAME);
58    protected static StatusBarField statusBarField = new StatusBarField();
59    private NavigationController navigationController;
60    private Field titleField;
61    private Field originalStatusField;
62    private Field currentStatusField;
63
64    private MenuItem configItem;
65    private MenuItem aboutItem;
66    private MenuItem closeItem;
67    private MenuItem exitItem;
68
69    protected final ScreenProvider screenProvider;
70
71    /**
72     * Instantiates a new standard screen.
73     *
74     * @param navigationController the navigation controller
75     * @param screenProvider the screen provider
76     */
77    public StandardScreen(NavigationController navigationController, ScreenProvider screenProvider) {
78        super(screenProvider.getStyle());
79        if(navigationController == null || screenProvider == null) {
80            throw new IllegalArgumentException();
81        }
82
83        this.navigationController = navigationController;
84        this.screenProvider = screenProvider;
85        initialize();
86    }
87
88    NavigationController getNavigationController() {
89        return navigationController;
90    }
91   
92    /**
93     * Initialize the screen elements.
94     */
95    private void initialize() {
96        // Create screen elements
97        if(screenProvider.getTitle() != null) {
98            this.titleField = createTitleField();
99            setTitle(titleField);
100        }
101
102        initMenuItems();
103        screenProvider.setNavigationController(navigationController);
104        screenProvider.initFields(this);
105    }
106   
107    protected Field createTitleField() {
108        return new HeaderField(
109                resources.getString(LogicMailResource.APPNAME)
110                + " - "
111                + screenProvider.getTitle());
112    }
113
114    public String getScreenName() {
115        return screenProvider.getScreenName();
116    }
117   
118    public String getScreenPath() {
119        return screenProvider.getScreenPath();
120    }
121   
122    /* (non-Javadoc)
123     * @see net.rim.device.api.ui.container.MainScreen#setStatus(net.rim.device.api.ui.Field)
124     */
125    public void setStatus(Field status) {
126        originalStatusField = status;
127        superSetStatusImpl(status);
128    }
129
130    /**
131     * Wrapper for internal calls to {@link MainScreen#setStatus(Field)}
132     * that makes sure <code>IllegalStateException</code>s do not appear
133     * if the field had previously been added, and that the field does
134     * not get added if it is already the active status field.
135     *
136     * @param status the new status field
137     */
138    private void superSetStatusImpl(Field status) {
139        if(currentStatusField != status) {
140            currentStatusField = status;
141            if(status != null && status.getManager() != null) {
142                status.getManager().delete(status);
143            }
144            super.setStatus(status);
145        }
146    }
147
148    /**
149     * Update status text, showing or hiding the status bar as necessary.
150     *
151     * @param statusText the status text
152     */
153    public void updateStatus(String statusText) {
154        statusBarField.setStatusText(statusText);
155        if(statusBarField.hasStatus()) {
156            superSetStatusImpl(statusBarField);
157        }
158        else {
159            superSetStatusImpl(originalStatusField);
160        }
161    }
162
163    /* (non-Javadoc)
164     * @see net.rim.device.api.ui.Screen#onUiEngineAttached(boolean)
165     */
166    protected void onUiEngineAttached(boolean attached) {
167        if(attached) {
168            super.onUiEngineAttached(true);
169            updateStatus(navigationController.getCurrentStatus());
170            NotificationHandler.getInstance().cancelNotification();
171            screenProvider.onDisplay();
172        }
173        else {
174            screenProvider.onUndisplay();
175            superSetStatusImpl(originalStatusField);
176            super.onUiEngineAttached(false);   
177        }
178    }
179
180    /* (non-Javadoc)
181     * @see net.rim.device.api.ui.Screen#onExposed()
182     */
183    protected void onExposed() {
184        super.onExposed();
185        updateStatus(navigationController.getCurrentStatus());
186        NotificationHandler.getInstance().cancelNotification();
187    }
188
189    /* (non-Javadoc)
190     * @see net.rim.device.api.ui.Screen#onObscured()
191     */
192    protected void onObscured() {
193        super.onObscured();
194        superSetStatusImpl(originalStatusField);
195    }
196
197    /* (non-Javadoc)
198     * @see net.rim.device.api.ui.Screen#onClose()
199     */
200    public boolean onClose() {
201        boolean result = screenProvider.onClose();
202        if(result) {
203            if(this.isDisplayed()) {
204                close();
205            }
206        }
207        return result;
208    }
209
210    /* (non-Javadoc)
211     * @see net.rim.device.api.ui.Field#onVisibilityChange(boolean)
212     */
213    protected void onVisibilityChange(boolean visible) {
214        screenProvider.onVisibilityChange(visible);
215    }
216
217    private void initMenuItems() {
218        configItem = new MenuItem(resources, LogicMailResource.MENUITEM_CONFIGURATION, 800000, 9000) {
219            public void run() {
220                AnalyticsDataCollector.getInstance().onButtonClick(getScreenPath(), getScreenName(), "config");
221                showConfigScreen();
222            }
223        };
224        aboutItem = new MenuItem(resources, LogicMailResource.MENUITEM_ABOUT, 800100, 9000) {
225            public void run() {
226                AnalyticsDataCollector.getInstance().onButtonClick(getScreenPath(), getScreenName(), "about");
227                // Show the about dialog
228                AboutDialog dialog = new AboutDialog();
229                dialog.doModal();
230            }
231        };
232        closeItem = new MenuItem(resources, LogicMailResource.MENUITEM_CLOSE, 60000000, 9000) {
233            public void run() {
234                AnalyticsDataCollector.getInstance().onButtonClick(getScreenPath(), getScreenName(), "close");
235                StandardScreen.this.onClose();
236            }
237        };
238        exitItem = new MenuItem(resources, LogicMailResource.MENUITEM_EXIT, 60000100, 9000) {
239            public void run() {
240                AnalyticsDataCollector.getInstance().onButtonClick(getScreenPath(), getScreenName(), "exit");
241                tryShutdownApplication();
242            }
243        };
244    }
245
246    public void tryShutdownApplication() {
247        // Get all accounts
248        NetworkAccountNode[] accounts = MailManager.getInstance().getMailRootNode().getNetworkAccounts();
249
250        // Find out of we still have an open connection
251        boolean openConnection = false;
252        for(int i=0; i<accounts.length; i++) {
253            if(accounts[i].getStatus() == AccountNode.STATUS_ONLINE) {
254                openConnection = true;
255                break;
256            }
257        }
258
259        if(openConnection) {
260            if(Dialog.ask(Dialog.D_YES_NO, resources.getString(LogicMailResource.BASE_CLOSEANDEXIT)) == Dialog.YES) {
261                for(int i=0; i<accounts.length; i++) {
262                    if(accounts[i].getStatus() == AccountNode.STATUS_ONLINE) {
263                        accounts[i].requestDisconnect(true);
264                    }
265                }
266                doShutdownProcess();
267            }
268        }
269        else {
270            doShutdownProcess();
271        }
272    }
273
274    private void doShutdownProcess() {
275        // Iterate through the screen stack, find the LogicMail home screen,
276        // and tell it to save its state.  If other screens have persistable
277        // state, then this needs to be refactored into a common ScreenProvider
278        // method.
279        Screen screen = this;
280        while(screen != null) {
281            if(screen instanceof StandardScreen) {
282                ScreenProvider provider = ((StandardScreen)screen).screenProvider;
283                if(provider instanceof MailHomeScreen) {
284                    ((MailHomeScreen)provider).saveScreenMetadata();
285                    break;
286                }
287            }
288            screen = screen.getScreenBelow();
289        }
290       
291        cleanupTitleField(titleField);
292        LogicMail.shutdownApplication();
293    }
294
295    protected void cleanupTitleField(Field titleField) {
296        ((HeaderField)titleField).removeListeners();
297    }
298   
299    /**
300     * Shows the configuration screen.
301     * Subclasses should override this method if they need to
302     * refresh their view of the configuration after the screen
303     * is closed.
304     */
305    protected void showConfigScreen() {
306        UiApplication.getUiApplication().pushModalScreen(new ConfigScreen());
307    }
308
309    /* (non-Javadoc)
310     * @see net.rim.device.api.ui.container.MainScreen#makeMenu(net.rim.device.api.ui.component.Menu, int)
311     */
312    protected void makeMenu(Menu menu, int instance) {
313        screenProvider.makeMenu(menu, instance);
314        if(instance == Menu.INSTANCE_DEFAULT) {
315            menu.add(configItem);
316            menu.add(aboutItem);
317            menu.add(closeItem);
318            menu.add(exitItem);
319        }
320    }
321
322    /* (non-Javadoc)
323     * @see net.rim.device.api.ui.container.MainScreen#onSavePrompt()
324     */
325    protected boolean onSavePrompt() {
326        return screenProvider.onSavePrompt();
327    }
328
329    /* (non-Javadoc)
330     * @see net.rim.device.api.ui.Screen#navigationClick(int, int)
331     */
332    protected boolean navigationClick(int status, int time) {
333        return screenProvider.navigationClick(status, time);
334    }
335
336    /**
337     * Provides a way for <code>ScreenProvider</code> implementations to call
338     * {@link net.rim.device.api.ui.Screen#navigationClick(int, int)} if they
339     * do not want to override its behavior.
340     *
341     * @see net.rim.device.api.ui.Screen#navigationClick(int, int)
342     */
343    boolean navigationClickDefault(int status, int time) {
344        return super.navigationClick(status, time);
345    }
346   
347    /* (non-Javadoc)
348     * @see net.rim.device.api.ui.Screen#navigationUnclick(int, int)
349     */
350    protected boolean navigationUnclick(int status, int time) {
351        return screenProvider.navigationUnclick(status, time);
352    }
353   
354    /**
355     * Provides a way for <code>ScreenProvider</code> implementations to call
356     * {@link net.rim.device.api.ui.Screen#navigationUnclick(int, int)} if they
357     * do not want to override its behavior.
358     *
359     * @see net.rim.device.api.ui.Screen#navigationUnclick(int, int)
360     */
361    boolean navigationUnclickDefault(int status, int time) {
362        return super.navigationUnclick(status, time);
363    }
364
365    /* (non-Javadoc)
366     * @see net.rim.device.api.ui.Screen#keyChar(char, int, int)
367     */
368    protected boolean keyChar(char c, int status, int time) {
369        return screenProvider.keyChar(c, status, time);
370    }
371
372    /**
373     * Provides a way for <code>ScreenProvider</code> implementations to call
374     * {@link net.rim.device.api.ui.Screen#keyChar(char, int, int)} if they
375     * do not want to override its behavior.
376     *
377     * @see net.rim.device.api.ui.Screen#keyChar(char, int, int)
378     */
379    boolean keyCharDefault(char c, int status, int time) {
380        return super.keyChar(c, status, time);
381    }
382   
383    /**
384     * Gets the enabled state of a shortcut button.
385     * Provided for subclasses that support shortcut buttons.
386     *
387     * @param id the ID of the button
388     * @return the enabled state
389     */
390    public boolean isShortcutEnabled(int id) {
391        // Shortcuts not supported by the base screen class
392        return false;
393    }
394
395    /**
396     * Sets the enabled state of a shortcut button.
397     * Provided for subclasses that support shortcut buttons.
398     *
399     * @param id the ID of the button
400     * @param enabled the enabled state
401     */
402    public void setShortcutEnabled(int id, boolean enabled) {
403        // Shortcuts not supported by the base screen class
404    }
405   
406    /**
407     * Shows the virtual keyboard, if applicable to this device
408     */
409    public void showVirtualKeyboard() {
410        // Virtual keyboard not supported by the base screen class
411    }
412}
Note: See TracBrowser for help on using the repository browser.