0001: /*
0002: *
0003: *
0004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
0005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
0006: *
0007: * This program is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU General Public License version
0009: * 2 only, as published by the Free Software Foundation.
0010: *
0011: * This program is distributed in the hope that it will be useful, but
0012: * WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * General Public License version 2 for more details (a copy is
0015: * included at /legal/license.txt).
0016: *
0017: * You should have received a copy of the GNU General Public License
0018: * version 2 along with this work; if not, write to the Free Software
0019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0020: * 02110-1301 USA
0021: *
0022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0023: * Clara, CA 95054 or visit www.sun.com if you need additional
0024: * information or have any questions.
0025: */
0026:
0027: package com.sun.midp.appmanager;
0028:
0029: import javax.microedition.lcdui.*;
0030:
0031: import com.sun.midp.configurator.Constants;
0032:
0033: import com.sun.midp.installer.*;
0034: import com.sun.midp.main.*;
0035: import com.sun.midp.midletsuite.*;
0036: import com.sun.midp.midlet.MIDletSuite;
0037: import com.sun.midp.io.j2me.push.PushRegistryInternal;
0038:
0039: import com.sun.midp.i18n.Resource;
0040: import com.sun.midp.i18n.ResourceConstants;
0041:
0042: import com.sun.midp.log.Logging;
0043: import com.sun.midp.log.LogChannels;
0044:
0045: import com.sun.midp.payment.PAPICleanUp;
0046:
0047: import java.io.*;
0048: import javax.microedition.rms.*;
0049: import java.util.*;
0050:
0051: /**
0052: * The Graphical MIDlet selector Screen.
0053: * <p>
0054: * It displays a list (or grid to be exact) of currently installed
0055: * MIDlets/MIDlet suites (including the Installer MIDlet). Each MIDlet or
0056: * MIDlet suite is represented by an icon with a name under it.
0057: * An icon from a jad file for the MIDlet/MIDlet suite representation
0058: * is used if possible, otherwise a default icon is used.
0059: *
0060: * There is a a set of commands per MIDlet/MIDlet suite. Note that
0061: * the set of commands can change depending on the corresponding MIDlet state.
0062: * For MIDlets/MIDlet suites that are not running the following commands are
0063: * available:
0064: * <ul>
0065: * <li><b>Launch</b>: Launch the MIDlet or the MIDlet Selector
0066: * if it is a suite.
0067: * <li><b>Remove</b>: Remove the MIDlet/MIDlet suite teh user selected
0068: * (with confirmation). </li>
0069: * <li><b>Update</b>: Update the MIDlet/MIDlet suite the user selected.</li>
0070: * <li><b>Info</b>: Show the user general information
0071: * of the selected MIDlet/MIdlet suite. </li>
0072: * <li><b>Settings</b>: Let the user change the manager's settings.
0073: * </ul>
0074: *
0075: * For MIDlets/MIDlet suites that are running the following commands are
0076: * available:
0077: * <ul>
0078: * <li><b>Bring to foreground</b>: Bring the running MIDlet to foreground
0079: * <li><b>End</b>: Terminate the running MIDlet
0080: * <li><b>Remove</b>: Remove the MIDlet/MIDlet suite teh user selected
0081: * (with confirmation). </li>
0082: * <li><b>Update</b>: Update the MIDlet/MIDlet suite the user selected.</li>
0083: * <li><b>Info</b>: Show the user general information
0084: * of the selected MIDlet/MIdlet suite. </li>
0085: * <li><b>Settings</b>: Let the user change the manager's settings.
0086: * </ul>
0087: *
0088: * Exactly one MIDlet from a MIDlet suite could be run at the same time.
0089: * Each MIDlet/MIDlet suite representation corresponds to an instance of
0090: * MidletCustomItem which in turn maintains a reference to a MIDletSuiteInfo
0091: * object (that contains info about this MIDlet/MIDlet suite).
0092: * When a MIDlet is launched or a MIDlet form a MIDlet suite is launched
0093: * the proxy instance in the corresponding MidletCustomItem is set to
0094: * a running MIDletProxy value. It is set back to null when MIDlet exits.
0095: *
0096: * Running midlets can be distinguished from non-running MIdlets/MIDlet suites
0097: * by the color of their name.
0098: */
0099: class AppManagerUI extends Form implements ItemCommandListener,
0100: CommandListener {
0101:
0102: /** Constant for the discovery application class name. */
0103: private static final String DISCOVERY_APP = "com.sun.midp.installer.DiscoveryApp";
0104:
0105: /** Constant for the certificate manager class name */
0106: private static final String CA_MANAGER = "com.sun.midp.appmanager.CaManager";
0107:
0108: /** Constant for the graphical installer class name. */
0109: private static final String INSTALLER = "com.sun.midp.installer.GraphicalInstaller";
0110:
0111: /** Constant for the graphical installer class name. */
0112: private static final String SUITE_SELECTOR = "com.sun.midp.midletsuite.Selector";
0113:
0114: /**
0115: * The font used to paint midlet names in the AppSelector.
0116: * Inner class cannot have static variables thus it has to be here.
0117: */
0118: private static final Font ICON_FONT = Font.getFont(
0119: Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_SMALL);
0120:
0121: /**
0122: * The image used to draw background for the midlet representation.
0123: * IMPL NOTE: it is assumed that background image is larger or equal
0124: * than all other images that are painted over it
0125: */
0126: private static final Image ICON_BG = GraphicalInstaller
0127: .getImageFromInternalStorage("_ch_hilight_bg");
0128:
0129: /**
0130: * Cashed background image width.
0131: */
0132: private static final int bgIconW = ICON_BG.getWidth();
0133:
0134: /**
0135: * Cashed background image height.
0136: */
0137: private static final int bgIconH = ICON_BG.getHeight();
0138:
0139: /**
0140: * The icon used to display that user attention is requested
0141: * and that midlet needs to brought into foreground.
0142: */
0143: private static final Image FG_REQUESTED = GraphicalInstaller
0144: .getImageFromInternalStorage("_ch_fg_requested");
0145:
0146: /**
0147: * The image used to draw disable midlet representation.
0148: */
0149: private static final Image DISABLED_IMAGE = GraphicalInstaller
0150: .getImageFromInternalStorage("_ch_disabled");
0151:
0152: /**
0153: * The color used to draw midlet name
0154: * for the hilighted non-running running midlet representation.
0155: */
0156: private static final int ICON_HL_TEXT = 0x000B2876;
0157:
0158: /**
0159: * The color used to draw the shadow of the midlet name
0160: * for the non hilighted non-running midlet representation.
0161: */
0162: private static final int ICON_TEXT = 0x003177E2;
0163:
0164: /**
0165: * The color used to draw the midlet name
0166: * for the non hilighted running midlet representation.
0167: */
0168: private static final int ICON_RUNNING_TEXT = 0xbb0000;
0169:
0170: /**
0171: * The color used to draw the midlet name
0172: * for the hilighted running midlet representation.
0173: */
0174: private static final int ICON_RUNNING_HL_TEXT = 0xff0000;
0175:
0176: /**
0177: * Tha pad between custom item's icon and text
0178: */
0179: private static final int ITEM_PAD = 2;
0180:
0181: /**
0182: * Cashed truncation mark
0183: */
0184: private static final char truncationMark = Resource.getString(
0185: ResourceConstants.TRUNCATION_MARK).charAt(0);
0186:
0187: /** Command object for "Exit" command for splash screen. */
0188: private Command exitCmd = new Command(Resource
0189: .getString(ResourceConstants.EXIT), Command.BACK, 1);
0190:
0191: /** Command object for "Launch" install app. */
0192: private Command launchInstallCmd = new Command(Resource
0193: .getString(ResourceConstants.LAUNCH), Command.ITEM, 1);
0194:
0195: /** Command object for "Launch" CA manager app. */
0196: private Command launchCaManagerCmd = new Command(Resource
0197: .getString(ResourceConstants.LAUNCH), Command.ITEM, 1);
0198:
0199: /** Command object for "Launch". */
0200: private Command launchCmd = new Command(Resource
0201: .getString(ResourceConstants.LAUNCH), Command.ITEM, 1);
0202: /** Command object for "Info". */
0203: private Command infoCmd = new Command(Resource
0204: .getString(ResourceConstants.INFO), Command.ITEM, 2);
0205: /** Command object for "Remove". */
0206: private Command removeCmd = new Command(Resource
0207: .getString(ResourceConstants.REMOVE), Command.ITEM, 3);
0208: /** Command object for "Update". */
0209: private Command updateCmd = new Command(Resource
0210: .getString(ResourceConstants.UPDATE), Command.ITEM, 4);
0211: /** Command object for "Application settings". */
0212: private Command appSettingsCmd = new Command(Resource
0213: .getString(ResourceConstants.APPLICATION_SETTINGS),
0214: Command.ITEM, 5);
0215:
0216: /** Command object for "Cancel" command for the remove form. */
0217: private Command cancelCmd = new Command(Resource
0218: .getString(ResourceConstants.CANCEL), Command.CANCEL, 1);
0219: /** Command object for "Remove" command for the remove form. */
0220: private Command removeOkCmd = new Command(Resource
0221: .getString(ResourceConstants.REMOVE), Command.SCREEN, 1);
0222:
0223: /** Command object for "Back" command for back to the AppSelector. */
0224: Command backCmd = new Command(Resource
0225: .getString(ResourceConstants.BACK), Command.BACK, 1);
0226:
0227: /** Command object for "Bring to foreground". */
0228: private Command fgCmd = new Command(Resource
0229: .getString(ResourceConstants.FOREGROUND), Command.ITEM, 1);
0230:
0231: /** Command object for "End" midlet. */
0232: private Command endCmd = new Command(Resource
0233: .getString(ResourceConstants.END), Command.ITEM, 1);
0234:
0235: /** Command object for "Yes" command. */
0236: private Command runYesCmd = new Command(Resource
0237: .getString(ResourceConstants.YES), Command.OK, 1);
0238:
0239: /** Command object for "No" command. */
0240: private Command runNoCmd = new Command(Resource
0241: .getString(ResourceConstants.NO), Command.BACK, 1);
0242:
0243: /** Display for the Manager MIDlet. */
0244: ApplicationManager manager;
0245:
0246: /** MIDlet Suite storage object. */
0247: private MIDletSuiteStorage midletSuiteStorage;
0248:
0249: /** Display for the Manager MIDlet. */
0250: Display display; // = null
0251:
0252: /** Keeps track of when the display last changed, in milliseconds. */
0253: private long lastDisplayChange;
0254:
0255: /** MIDlet to be removed after confirmation screen was accepted */
0256: private RunningMIDletSuiteInfo removeMsi;
0257:
0258: /** last Item that was selected */
0259: private RunningMIDletSuiteInfo lastSelectedMsi;
0260:
0261: /**
0262: * There are several Application Manager
0263: * midlets from the same "internal" midlet suite
0264: * that should not be running in the background.
0265: * appManagerMidlet helps to destroy them
0266: * (see MidletCustomItem.showNotify).
0267: */
0268: private MIDletProxy appManagerMidlet;
0269:
0270: /** UI used to display error messages. */
0271: private DisplayError displayError;
0272:
0273: /** True, if the CA manager is included. */
0274: private boolean caManagerIncluded;
0275:
0276: private MIDletSwitcher midletSwitcher;
0277:
0278: /**
0279: * Creates and populates the Application Selector Screen.
0280: * @param manager - The application manager that invoked it
0281: * @param displayError - The UI used to display error messages
0282: * @param display - The display instance associated with the manager
0283: * @param first - true if this is the first time AppSelector is being
0284: * shown
0285: * @param ms - MidletSuiteInfo that should be selected. For the internal
0286: * suites midletToRun should be set, for the other suites
0287: * suiteId is enough to find the corresponding item.
0288: */
0289: AppManagerUI(ApplicationManager manager, Display display,
0290: DisplayError displayError, boolean first, MIDletSuiteInfo ms) {
0291: super (null);
0292:
0293: try {
0294: caManagerIncluded = Class.forName(CA_MANAGER) != null;
0295: } catch (ClassNotFoundException e) {
0296: // keep caManagerIncluded false
0297: }
0298:
0299: this .manager = manager;
0300: this .display = display;
0301: this .displayError = displayError;
0302:
0303: midletSwitcher = new MIDletSwitcher(this , manager, display);
0304:
0305: midletSuiteStorage = MIDletSuiteStorage.getMIDletSuiteStorage();
0306:
0307: setTitle(Resource.getString(ResourceConstants.AMS_MGR_TITLE));
0308: updateContent();
0309:
0310: addCommand(exitCmd);
0311: setCommandListener(this );
0312:
0313: if (first) {
0314: display.setCurrent(new SplashScreen(display, this ));
0315: } else {
0316: // if a MIDlet was just installed
0317: // getLastInstalledMidletItem() will return MidletCustomItem
0318: // corresponding to this suite, then we have to prompt
0319: // the user if he want to launch a midlet from the suite.
0320: MidletCustomItem mci = getLastInstalledMidletItem();
0321: if (mci != null) {
0322: askUserIfLaunchMidlet();
0323: } else {
0324: display.setCurrent(this );
0325: if (ms != null) {
0326: // Find item to select
0327: if (ms.suiteId == MIDletSuite.INTERNAL_SUITE_ID) {
0328: for (int i = 0; i < size(); i++) {
0329: MidletCustomItem mi = (MidletCustomItem) get(i);
0330: if ((mi.msi.suiteId == MIDletSuite.INTERNAL_SUITE_ID)
0331: && (mi.msi.midletToRun
0332: .equals(ms.midletToRun))) {
0333: display.setCurrentItem(mi);
0334: break;
0335: }
0336: }
0337: } else {
0338: for (int i = 0; i < size(); i++) {
0339: MidletCustomItem mi = (MidletCustomItem) get(i);
0340: if (mi.msi.suiteId == ms.suiteId) {
0341: display.setCurrentItem(mi);
0342: break;
0343: }
0344: }
0345: }
0346: } // ms != null
0347: }
0348: }
0349: }
0350:
0351: /**
0352: * Called when midlet selector needed.
0353: *
0354: * @param onlyFromLaunchedList true if midlet should
0355: * be selected from the list of already launched midlets,
0356: * if false then possibility to launch midlet is needed.
0357: */
0358: public void showMidletSwitcher(boolean onlyFromLaunchedList) {
0359: if (onlyFromLaunchedList && midletSwitcher.hasItems()) {
0360: display.setCurrent(midletSwitcher);
0361: } else {
0362: display.setCurrent(this );
0363: }
0364: }
0365:
0366: /**
0367: * Called to determine MidletSuiteInfo of the last selected Item.
0368: *
0369: * @return last selected MidletSuiteInfo
0370: */
0371: public RunningMIDletSuiteInfo getSelectedMIDletSuiteInfo() {
0372: return lastSelectedMsi;
0373: }
0374:
0375: /**
0376: * Respond to a command issued on any Screen.
0377: *
0378: * @param c command activated by the user
0379: * @param s the Displayable the command was on.
0380: */
0381: public void commandAction(Command c, Displayable s) {
0382:
0383: if (c == exitCmd) {
0384: if (s == this ) {
0385: manager.shutDown();
0386: }
0387: return;
0388: }
0389:
0390: // for the rest of the commands
0391: // we will have to request AppSelector to be displayed
0392: if (c == removeOkCmd) {
0393:
0394: // suite to remove was set in confirmRemove()
0395: try {
0396: remove(removeMsi);
0397: } catch (Throwable t) {
0398: if (Logging.REPORT_LEVEL <= Logging.WARNING) {
0399: Logging.report(Logging.WARNING, LogChannels.LC_AMS,
0400: "Throwable in removeSuitee");
0401: }
0402: }
0403: return;
0404:
0405: } else if (c == cancelCmd) {
0406:
0407: // null out removeMsi in remove confirmation screen
0408: removeMsi = null;
0409:
0410: } else if (c == runYesCmd) {
0411:
0412: // user decided run the midlet suite after installation
0413: MidletCustomItem mciToRun = getLastInstalledMidletItem();
0414: if (mciToRun != null) {
0415: display.setCurrentItem(mciToRun);
0416: launchMidlet(mciToRun.msi);
0417: return;
0418: }
0419:
0420: } else if (c == runNoCmd) {
0421:
0422: /*
0423: * user decided not to run the newly installed midlet suite
0424: *
0425: * if a MIDlet was just installed
0426: * displayLastInstalledMidlet() will return true and
0427: * make "this" visible with
0428: * the right MIDlet icon hilighted.
0429: */
0430: if (displayLastInstalledMidlet()) {
0431: // Last installed midlet was set as the current item
0432: return;
0433: }
0434:
0435: } else if (c != backCmd) {
0436: return;
0437: }
0438:
0439: // for back we just need to display AppSelector
0440: display.setCurrent(this );
0441: }
0442:
0443: /**
0444: * Respond to a command issued on an Item in AppSelector
0445: *
0446: * @param c command activated by the user
0447: * @param item the Item the command was on.
0448: */
0449: public void commandAction(Command c, Item item) {
0450: RunningMIDletSuiteInfo msi = ((MidletCustomItem) item).msi;
0451: if (msi == null) {
0452: return;
0453: }
0454:
0455: if (c == launchInstallCmd) {
0456:
0457: manager.installSuite();
0458:
0459: } else if (c == launchCaManagerCmd) {
0460:
0461: manager.launchCaManager();
0462:
0463: } else if (c == launchCmd) {
0464:
0465: launchMidlet(msi);
0466:
0467: } else if (c == infoCmd) {
0468:
0469: try {
0470: AppInfo appInfo = new AppInfo(msi.suiteId);
0471: appInfo.addCommand(backCmd);
0472: appInfo.setCommandListener(this );
0473: display.setCurrent(appInfo);
0474: } catch (Throwable t) {
0475: displayError.showErrorAlert(msi.displayName, t, null,
0476: null);
0477: }
0478:
0479: } else if (c == removeCmd) {
0480:
0481: confirmRemove(msi);
0482:
0483: } else if (c == updateCmd) {
0484:
0485: manager.updateSuite(msi);
0486: display.setCurrent(this );
0487:
0488: } else if (c == appSettingsCmd) {
0489:
0490: try {
0491: AppSettings appSettings = new AppSettings(msi.suiteId,
0492: display, displayError, this );
0493: display.setCurrent(appSettings);
0494:
0495: } catch (Throwable t) {
0496: displayError.showErrorAlert(msi.displayName, t, null,
0497: null);
0498: }
0499:
0500: } else if (c == fgCmd) {
0501:
0502: manager.moveToForeground(msi);
0503: display.setCurrent(this );
0504:
0505: } else if (c == endCmd) {
0506: manager.exitMidlet(msi);
0507: display.setCurrent(this );
0508:
0509: }
0510: }
0511:
0512: /**
0513: * Called when a new midlet was launched.
0514: *
0515: * @param midlet proxy of a newly added MIDlet
0516: */
0517: void notifyMidletStarted(MIDletProxy midlet) {
0518: String midletClassName = midlet.getClassName();
0519:
0520: if (midletClassName.equals(manager.getClass().getName())) {
0521: return;
0522: }
0523:
0524: if (midlet.getSuiteId() == MIDletSuite.INTERNAL_SUITE_ID
0525: && !midletClassName.equals(DISCOVERY_APP)
0526: && !midletClassName.equals(INSTALLER)
0527: && !midletClassName.equals(CA_MANAGER)) {
0528: appManagerMidlet = midlet;
0529: } else {
0530: MidletCustomItem ci;
0531: for (int i = 0; i < size(); i++) {
0532: ci = (MidletCustomItem) get(i);
0533:
0534: if (ci.msi.equals(midlet)) {
0535: ci.removeCommand(launchCmd);
0536: ci.removeCommand(launchInstallCmd);
0537:
0538: if (caManagerIncluded) {
0539: ci.removeCommand(launchCaManagerCmd);
0540: }
0541:
0542: ci.setDefaultCommand(fgCmd);
0543: ci.addCommand(endCmd);
0544: if (ci.msi.proxy == null) {
0545: // add item to midlet switcher
0546: midletSwitcher.append(ci.msi);
0547: }
0548: ci.msi.proxy = midlet;
0549: return;
0550: }
0551: }
0552: }
0553: }
0554:
0555: /**
0556: * Called when state of a running midlet was changed.
0557: *
0558: * @param midlet proxy of a newly added MIDlet
0559: */
0560: void notifyMidletStateChanged(MIDletProxy midlet) {
0561: MidletCustomItem mci = null;
0562:
0563: for (int i = 0; i < size(); i++) {
0564: mci = (MidletCustomItem) get(i);
0565: if (mci.msi.proxy == midlet) {
0566: mci.update();
0567: }
0568: }
0569: }
0570:
0571: /**
0572: * Called when a running midlet exited.
0573: *
0574: * @param midlet proxy of a newly added MIDlet
0575: */
0576: void notifyMidletExited(MIDletProxy midlet) {
0577: String midletClassName = midlet.getClassName();
0578:
0579: if (midlet.getSuiteId() == MIDletSuite.INTERNAL_SUITE_ID
0580: && !midletClassName.equals(DISCOVERY_APP)
0581: && !midletClassName.equals(INSTALLER)
0582: && !midletClassName.equals(CA_MANAGER)) {
0583: appManagerMidlet = null;
0584: } else {
0585: MidletCustomItem ci;
0586:
0587: for (int i = 0; i < size(); i++) {
0588: ci = (MidletCustomItem) get(i);
0589:
0590: if (ci.msi.equals(midlet)) {
0591: ci.removeCommand(fgCmd);
0592: ci.removeCommand(endCmd);
0593:
0594: if (ci.msi.midletToRun != null
0595: && ci.msi.midletToRun.equals(DISCOVERY_APP)) {
0596: ci.setDefaultCommand(launchInstallCmd);
0597: } else if (caManagerIncluded
0598: && ci.msi.midletToRun != null
0599: && ci.msi.midletToRun.equals(CA_MANAGER)) {
0600: ci.setDefaultCommand(launchCaManagerCmd);
0601: } else {
0602: if (ci.msi.enabled) {
0603: ci.setDefaultCommand(launchCmd);
0604: }
0605: }
0606:
0607: midletSwitcher.remove(ci.msi);
0608: ci.msi.proxy = null;
0609:
0610: if (removeMsi != null && removeMsi.equals(midlet)) {
0611: remove(removeMsi);
0612: }
0613:
0614: /*
0615: * When the Installer midlet quites
0616: * (it is removed from the running apps list)
0617: * this is a good time to see if any new MIDlet suites
0618: * where added
0619: * Also the CA manager could have disabled a MIDlet.
0620: */
0621: if (INSTALLER.equals(midletClassName)) {
0622: updateContent();
0623: /*
0624: * After a MIDlet suite is successfully installed on the
0625: * device, ask the user whether or not to launch
0626: * a MIDlet from the suite.
0627: */
0628: MidletCustomItem mci = getLastInstalledMidletItem();
0629: if (mci != null) {
0630: askUserIfLaunchMidlet();
0631: return;
0632: }
0633: } else {
0634: if (CA_MANAGER.equals(midletClassName)) {
0635: updateContent();
0636: }
0637: ci.update();
0638: }
0639:
0640: return;
0641: }
0642: }
0643: }
0644:
0645: // Midlet quited; display the application Selector
0646: display.setCurrent(this );
0647: }
0648:
0649: /**
0650: * Called when a midlet could not be launched.
0651: *
0652: * @param suiteId suite ID of the MIDlet
0653: * @param className class name of the MIDlet
0654: * @param errorCode error code
0655: * @param errorDetails error code details
0656: */
0657: void notifyMidletStartError(int suiteId, String className,
0658: int errorCode, String errorDetails) {
0659: Alert a;
0660: String errorMsg;
0661:
0662: switch (errorCode) {
0663: case Constants.MIDLET_SUITE_NOT_FOUND:
0664: errorMsg = Resource
0665: .getString(ResourceConstants.AMS_MIDLETSUITELDR_MIDLETSUITE_NOTFOUND);
0666: break;
0667:
0668: case Constants.MIDLET_CLASS_NOT_FOUND:
0669: errorMsg = Resource
0670: .getString(ResourceConstants.AMS_MIDLETSUITELDR_CANT_LAUNCH_MISSING_CLASS);
0671: break;
0672:
0673: case Constants.MIDLET_INSTANTIATION_EXCEPTION:
0674: errorMsg = Resource
0675: .getString(ResourceConstants.AMS_MIDLETSUITELDR_CANT_LAUNCH_ILL_OPERATION);
0676: break;
0677:
0678: case Constants.MIDLET_ILLEGAL_ACCESS_EXCEPTION:
0679: errorMsg = Resource
0680: .getString(ResourceConstants.AMS_MIDLETSUITELDR_CANT_LAUNCH_ILL_OPERATION);
0681: break;
0682:
0683: case Constants.MIDLET_OUT_OF_MEM_ERROR:
0684: errorMsg = Resource
0685: .getString(ResourceConstants.AMS_MIDLETSUITELDR_QUIT_OUT_OF_MEMORY);
0686: break;
0687:
0688: case Constants.MIDLET_RESOURCE_LIMIT:
0689: case Constants.MIDLET_ISOLATE_RESOURCE_LIMIT:
0690: errorMsg = Resource
0691: .getString(ResourceConstants.AMS_MIDLETSUITELDR_RESOURCE_LIMIT_ERROR);
0692: break;
0693:
0694: case Constants.MIDLET_ISOLATE_CONSTRUCTOR_FAILED:
0695: errorMsg = Resource
0696: .getString(ResourceConstants.AMS_MIDLETSUITELDR_CANT_EXE_NEXT_MIDLET);
0697: break;
0698:
0699: case Constants.MIDLET_SUITE_DISABLED:
0700: errorMsg = Resource
0701: .getString(ResourceConstants.AMS_MIDLETSUITELDR_MIDLETSUITE_DISABLED);
0702: break;
0703:
0704: case Constants.MIDLET_INSTALLER_RUNNING:
0705: String[] values = new String[1];
0706: values[0] = className;
0707: errorMsg = Resource
0708: .getString(
0709: ResourceConstants.AMS_MGR_UPDATE_IS_RUNNING,
0710: values);
0711: break;
0712:
0713: default:
0714: errorMsg = Resource
0715: .getString(ResourceConstants.AMS_MIDLETSUITELDR_UNEXPECTEDLY_QUIT);
0716: }
0717:
0718: if (errorDetails != null) {
0719: errorMsg += "\n\n" + errorDetails;
0720: }
0721:
0722: displayError.showErrorAlert(null, null, Resource
0723: .getString(ResourceConstants.EXCEPTION), errorMsg);
0724: }
0725:
0726: // ------------------------------------------------------------------
0727:
0728: /**
0729: * Read in and create a MIDletInfo for newly added MIDlet suite and
0730: * check enabled state of currently added MIDlet suites.
0731: */
0732: private void updateContent() {
0733: int[] suiteIds;
0734: RunningMIDletSuiteInfo msi = null;
0735: boolean newlyAdded;
0736:
0737: suiteIds = midletSuiteStorage.getListOfSuites();
0738:
0739: // Add the Installer as the first installed midlet
0740: if (size() > 0) {
0741: msi = ((MidletCustomItem) get(0)).msi;
0742: }
0743:
0744: if (msi == null || msi.midletToRun == null
0745: || !msi.midletToRun.equals(DISCOVERY_APP)) {
0746:
0747: msi = new RunningMIDletSuiteInfo(
0748: MIDletSuite.INTERNAL_SUITE_ID,
0749: DISCOVERY_APP,
0750: Resource
0751: .getString(ResourceConstants.INSTALL_APPLICATION),
0752: true) {
0753: public boolean equals(MIDletProxy midlet) {
0754: if (super .equals(midlet)) {
0755: return true;
0756: }
0757:
0758: // there is one exception when 2 midlets belong to the
0759: // same icon: Discovery app & Graphical installer.
0760: // Graphical Installer can be launched by Discover app
0761: // or when MIdlet update is needed.
0762: // In such cases we simply need to set the proxy on
0763: // corresponding icon (MidletCustomItem).
0764: // Note that when Discovery app exits and
0765: // Installer is launched
0766: // notifyMidletExited() will not find corresponding
0767: // icon in the list of MidletCustomItems.
0768: // (that midlet exit will be ignored).
0769: return (INSTALLER.equals(midlet.getClassName()));
0770: }
0771: };
0772:
0773: append(msi);
0774: }
0775:
0776: if (caManagerIncluded) {
0777: // Add the CA manager as the second installed midlet
0778: if (size() > 1) {
0779: msi = ((MidletCustomItem) get(1)).msi;
0780: }
0781:
0782: if (msi == null || msi.midletToRun == null
0783: || !msi.midletToRun.equals(CA_MANAGER)) {
0784: msi = new RunningMIDletSuiteInfo(
0785: MIDletSuite.INTERNAL_SUITE_ID,
0786: CA_MANAGER,
0787: Resource
0788: .getString(ResourceConstants.CA_MANAGER_APP),
0789: true);
0790: append(msi);
0791: }
0792: }
0793:
0794: // Add the rest of the installed midlets
0795: for (int lowest, i = 0; i < suiteIds.length; i++) {
0796:
0797: lowest = i;
0798:
0799: for (int k = i + 1; k < suiteIds.length; k++) {
0800: if (suiteIds[k] < suiteIds[lowest]) {
0801: lowest = k;
0802: }
0803: }
0804:
0805: try {
0806: MIDletSuiteInfo temp = midletSuiteStorage
0807: .getMIDletSuiteInfo(suiteIds[lowest]);
0808:
0809: RunningMIDletSuiteInfo suiteInfo = new RunningMIDletSuiteInfo(
0810: temp, midletSuiteStorage);
0811:
0812: newlyAdded = true;
0813: for (int k = 0; k < size(); k++) {
0814: MidletCustomItem mci = (MidletCustomItem) get(k);
0815:
0816: if (suiteIds[lowest] == mci.msi.suiteId) {
0817: newlyAdded = false;
0818: boolean isEnabled = suiteInfo.enabled;
0819:
0820: if (mci.msi.enabled != isEnabled) {
0821: mci.msi.enabled = isEnabled;
0822:
0823: // MIDlet suite being enabled
0824: if (isEnabled) {
0825: mci.setDefaultCommand(launchCmd);
0826: } else { // MIDlet suite is being disabled
0827:
0828: if (mci.msi.proxy == null) { // Not running
0829: mci.removeCommand(launchCmd);
0830:
0831: }
0832:
0833: // running MIDlets will continue to run
0834: // even when disabled
0835: }
0836: }
0837:
0838: // Update all information about the suite;
0839: // if the suite's icon was changed, reload it.
0840: String oldIconName = mci.msi.iconName;
0841: int oldNumberOfMidlets = mci.msi.numberOfMidlets;
0842: MIDletProxy oldProxy = mci.msi.proxy;
0843:
0844: mci.msi = suiteInfo;
0845: mci.msi.proxy = oldProxy;
0846:
0847: if ((suiteInfo.iconName != null && !suiteInfo.iconName
0848: .equals(oldIconName))
0849: || (suiteInfo.iconName == null && suiteInfo.numberOfMidlets != oldNumberOfMidlets)) {
0850: mci.msi.icon = null;
0851: mci.msi.loadIcon(midletSuiteStorage);
0852: mci.icon = mci.msi.icon;
0853: }
0854:
0855: break;
0856: }
0857: }
0858:
0859: if (newlyAdded) {
0860: append(suiteInfo);
0861: }
0862:
0863: } catch (Exception e) {
0864: // move on to the next suite
0865: }
0866:
0867: suiteIds[lowest] = suiteIds[i];
0868: }
0869: }
0870:
0871: /**
0872: * Appends a MidletCustomItem to the App Selector Screen
0873: *
0874: * @param suiteInfo the midlet suite info
0875: * of the recently started midlet
0876: */
0877: private void append(RunningMIDletSuiteInfo suiteInfo) {
0878:
0879: MidletCustomItem ci = new MidletCustomItem(suiteInfo);
0880:
0881: if (suiteInfo.midletToRun != null
0882: && suiteInfo.midletToRun.equals(DISCOVERY_APP)) {
0883: // setDefaultCommand will add default command first
0884: ci.setDefaultCommand(launchInstallCmd);
0885: } else if (caManagerIncluded && suiteInfo.midletToRun != null
0886: && suiteInfo.midletToRun.equals(CA_MANAGER)) {
0887: // setDefaultCommand will add default command first
0888: ci.setDefaultCommand(launchCaManagerCmd);
0889: } else {
0890: ci.addCommand(infoCmd);
0891: ci.addCommand(removeCmd);
0892: ci.addCommand(updateCmd);
0893: ci.addCommand(appSettingsCmd);
0894:
0895: if (suiteInfo.enabled) {
0896: // setDefaultCommand will add default command first
0897: ci.setDefaultCommand(launchCmd);
0898: }
0899: }
0900:
0901: ci.setItemCommandListener(this );
0902: append(ci);
0903: ci.setOwner(this );
0904: }
0905:
0906: /**
0907: * Removes a midlet from the App Selector Screen
0908: *
0909: * @param suiteInfo the midlet suite info of a recently removed MIDlet
0910: */
0911: private void remove(RunningMIDletSuiteInfo suiteInfo) {
0912: RunningMIDletSuiteInfo msi;
0913:
0914: if (suiteInfo == null) {
0915: // Invalid parameter, should not happen.
0916: return;
0917: }
0918:
0919: // the last item in AppSelector is time
0920: for (int i = 0; i < size(); i++) {
0921: msi = (RunningMIDletSuiteInfo) ((MidletCustomItem) get(i)).msi;
0922: if (msi == suiteInfo) {
0923: PAPICleanUp.removeMissedTransaction(suiteInfo.suiteId);
0924:
0925: if (msi.proxy != null) {
0926: msi.proxy.destroyMidlet();
0927: }
0928:
0929: try {
0930: midletSuiteStorage.remove(suiteInfo.suiteId);
0931: } catch (Throwable t) {
0932: if (t instanceof MIDletSuiteLockedException) {
0933: String[] val = new String[1];
0934: val[0] = suiteInfo.displayName;
0935: displayError
0936: .showErrorAlert(
0937: suiteInfo.displayName,
0938: null,
0939: Resource
0940: .getString(ResourceConstants.ERROR),
0941: Resource
0942: .getString(
0943: ResourceConstants.AMS_MGR_REMOVE_LOCKED_SUITE,
0944: val), this );
0945: } else {
0946: displayError
0947: .showErrorAlert(
0948: suiteInfo.displayName,
0949: t,
0950: Resource
0951: .getString(ResourceConstants.ERROR),
0952: null, this );
0953: }
0954:
0955: return;
0956: }
0957:
0958: try {
0959: PushRegistryInternal
0960: .unregisterConnections(suiteInfo.suiteId);
0961: } catch (Throwable t) {
0962: // Intentionally ignored: suite has been removed already,
0963: // we can't do anything meaningful at this point.
0964: }
0965:
0966: delete(i);
0967: removeMsi = null;
0968: break;
0969: }
0970: }
0971:
0972: display.setCurrent(this );
0973: }
0974:
0975: /**
0976: * Alert the user that an action was successful.
0977: * @param successMessage message to display to user
0978: */
0979: private void displaySuccessMessage(String successMessage) {
0980: Image icon;
0981: Alert successAlert;
0982:
0983: icon = GraphicalInstaller
0984: .getImageFromInternalStorage("_dukeok8");
0985:
0986: successAlert = new Alert(null, successMessage, icon, null);
0987:
0988: successAlert.setTimeout(GraphicalInstaller.ALERT_TIMEOUT);
0989:
0990: // We need to prevent "flashing" on fast development platforms.
0991: while (System.currentTimeMillis() - lastDisplayChange < GraphicalInstaller.ALERT_TIMEOUT)
0992: ;
0993:
0994: display.setCurrent(successAlert, this );
0995: lastDisplayChange = System.currentTimeMillis();
0996: }
0997:
0998: /**
0999: * Confirm the removal of a suite.
1000: *
1001: * @param suiteInfo information for suite to remove
1002: */
1003: private void confirmRemove(RunningMIDletSuiteInfo suiteInfo) {
1004: Form confirmForm;
1005: StringBuffer temp = new StringBuffer(40);
1006: Item item;
1007: String extraConfirmMsg;
1008: String[] values = new String[1];
1009: MIDletSuiteImpl midletSuite = null;
1010:
1011: try {
1012: midletSuite = midletSuiteStorage.getMIDletSuite(
1013: suiteInfo.suiteId, false);
1014: confirmForm = new Form(null);
1015:
1016: confirmForm.setTitle(Resource
1017: .getString(ResourceConstants.AMS_CONFIRMATION));
1018:
1019: if (suiteInfo.hasSingleMidlet()) {
1020: values[0] = suiteInfo.displayName;
1021: } else {
1022: values[0] = midletSuite
1023: .getProperty(MIDletSuiteImpl.SUITE_NAME_PROP);
1024: }
1025:
1026: item = new StringItem(null, Resource.getString(
1027: ResourceConstants.AMS_MGR_REMOVE_QUE, values));
1028: item.setLayout(Item.LAYOUT_NEWLINE_AFTER | Item.LAYOUT_2);
1029: confirmForm.append(item);
1030:
1031: extraConfirmMsg = PAPICleanUp
1032: .checkMissedTransactions(midletSuite.getID());
1033: if (extraConfirmMsg != null) {
1034: temp.setLength(0);
1035: temp.append(" \n");
1036: temp.append(extraConfirmMsg);
1037: item = new StringItem(null, temp.toString());
1038: item.setLayout(Item.LAYOUT_NEWLINE_AFTER
1039: | Item.LAYOUT_2);
1040: confirmForm.append(item);
1041: }
1042:
1043: extraConfirmMsg = midletSuite
1044: .getProperty("MIDlet-Delete-Confirm");
1045: if (extraConfirmMsg != null) {
1046: temp.setLength(0);
1047: temp.append(" \n");
1048: temp.append(extraConfirmMsg);
1049: item = new StringItem(null, temp.toString());
1050: item.setLayout(Item.LAYOUT_NEWLINE_AFTER
1051: | Item.LAYOUT_2);
1052: confirmForm.append(item);
1053: }
1054:
1055: if (!suiteInfo.hasSingleMidlet()) {
1056: temp.setLength(0);
1057: temp
1058: .append(Resource
1059: .getString(ResourceConstants.AMS_MGR_SUITE_CONTAINS));
1060: temp.append(": ");
1061: item = new StringItem(temp.toString(), "");
1062: item.setLayout(Item.LAYOUT_NEWLINE_AFTER
1063: | Item.LAYOUT_2);
1064: confirmForm.append(item);
1065: appendMIDletsToForm(midletSuite, confirmForm);
1066: }
1067:
1068: String[] recordStores = midletSuiteStorage
1069: .listRecordStores(suiteInfo.suiteId);
1070: if (recordStores != null) {
1071: temp.setLength(0);
1072: temp
1073: .append(Resource
1074: .getString(ResourceConstants.AMS_MGR_SUITE_RECORD_STORES));
1075: temp.append(": ");
1076: item = new StringItem(temp.toString(), "");
1077: item.setLayout(Item.LAYOUT_NEWLINE_AFTER
1078: | Item.LAYOUT_2);
1079: confirmForm.append(item);
1080: appendRecordStoresToForm(recordStores, confirmForm);
1081: }
1082:
1083: temp.setLength(0);
1084: temp.append(" \n");
1085: temp.append(Resource.getString(
1086: ResourceConstants.AMS_MGR_REM_REINSTALL, values));
1087: item = new StringItem("", temp.toString());
1088: confirmForm.append(item);
1089: } catch (Throwable t) {
1090: displayError
1091: .showErrorAlert(
1092: suiteInfo.displayName,
1093: t,
1094: Resource
1095: .getString(ResourceConstants.AMS_CANT_ACCESS),
1096: null);
1097: return;
1098: } finally {
1099: if (midletSuite != null) {
1100: midletSuite.close();
1101: }
1102: }
1103:
1104: confirmForm.addCommand(cancelCmd);
1105: confirmForm.addCommand(removeOkCmd);
1106: confirmForm.setCommandListener(this );
1107: removeMsi = suiteInfo;
1108: display.setCurrent(confirmForm);
1109: }
1110:
1111: /**
1112: * Appends a names of all the MIDlets in a suite to a Form, one per line.
1113: *
1114: * @param midletSuite information of a suite of MIDlets
1115: * @param form form to append to
1116: */
1117: private void appendMIDletsToForm(MIDletSuiteImpl midletSuite,
1118: Form form) {
1119: int numberOfMidlets;
1120: MIDletInfo midletInfo;
1121: StringItem item;
1122:
1123: numberOfMidlets = midletSuite.getNumberOfMIDlets();
1124: for (int i = 1; i <= numberOfMidlets; i++) {
1125: midletInfo = new MIDletInfo(midletSuite
1126: .getProperty("MIDlet-" + i));
1127:
1128: item = new StringItem(null, midletInfo.name);
1129: item.setLayout(Item.LAYOUT_NEWLINE_AFTER | Item.LAYOUT_2);
1130: form.append(item);
1131: }
1132: }
1133:
1134: /**
1135: * Appends names of the record stores owned by the midlet suite
1136: * to a Form, one per line.
1137: *
1138: * @param recordStores list of the record store names
1139: * @param form form to append to
1140: */
1141: private void appendRecordStoresToForm(String[] recordStores,
1142: Form form) {
1143: StringItem item;
1144:
1145: for (int i = 0; i < recordStores.length; i++) {
1146: item = new StringItem(null, recordStores[i]);
1147: item.setLayout(Item.LAYOUT_NEWLINE_AFTER | Item.LAYOUT_2);
1148: form.append(item);
1149: }
1150: }
1151:
1152: /**
1153: * Open the settings database and retreive an id of the midlet suite
1154: * that was installed last.
1155: *
1156: * @return ID of the midlet suite that was installed last or
1157: * MIDletSuite.UNUSED_SUITE_ID.
1158: */
1159: private int getLastInstalledMIDlet() {
1160: ByteArrayInputStream bas;
1161: DataInputStream dis;
1162: byte[] data;
1163: RecordStore settings = null;
1164: int ret = MIDletSuite.UNUSED_SUITE_ID;
1165:
1166: try {
1167: settings = RecordStore.openRecordStore(
1168: GraphicalInstaller.SETTINGS_STORE, false);
1169:
1170: /** we should be guaranteed that this is always the case! */
1171: if (settings.getNumRecords() > 0) {
1172:
1173: data = settings
1174: .getRecord(GraphicalInstaller.SELECTED_MIDLET_RECORD_ID);
1175:
1176: if (data != null) {
1177: bas = new ByteArrayInputStream(data);
1178: dis = new DataInputStream(bas);
1179: ret = dis.readInt();
1180: }
1181: }
1182:
1183: } catch (RecordStoreException e) {
1184: // ignore
1185: } catch (IOException e) {
1186: // ignore
1187: } finally {
1188: if (settings != null) {
1189: try {
1190: settings.closeRecordStore();
1191: } catch (RecordStoreException e) {
1192: // ignore
1193: }
1194: }
1195: }
1196:
1197: return ret;
1198: }
1199:
1200: /**
1201: * Finds a MidletCustomItem corresponding to the last installed
1202: * midlet suite.
1203: * @return the midlet custom item if it was found, null otherwise
1204: */
1205: private MidletCustomItem getLastInstalledMidletItem() {
1206: int installedMidlet = getLastInstalledMIDlet();
1207:
1208: if (installedMidlet != MIDletSuite.UNUSED_SUITE_ID
1209: && installedMidlet != MIDletSuite.INTERNAL_SUITE_ID) {
1210: for (int i = 0; i < size(); i++) {
1211: MidletCustomItem ci = (MidletCustomItem) get(i);
1212: if (ci.msi.suiteId == installedMidlet) {
1213: return ci;
1214: }
1215: }
1216: }
1217:
1218: return null;
1219: }
1220:
1221: /**
1222: * Displayas AppManagerUI with a recently installed midlet hilighted.
1223: * @return true if display.setCurrentItem() was called,
1224: * false - otherwise
1225: */
1226: private boolean displayLastInstalledMidlet() {
1227: MidletCustomItem ci = getLastInstalledMidletItem();
1228:
1229: if (ci != null) {
1230: display.setCurrentItem(ci);
1231: return true;
1232: }
1233:
1234: return false;
1235: }
1236:
1237: /**
1238: * Launches the midlet suite described by the given MIDletSuiteInfo.
1239: * @param msi a structure with information about the midlet suite
1240: * that must be launched
1241: */
1242: private void launchMidlet(RunningMIDletSuiteInfo msi) {
1243: if (msi.hasSingleMidlet()) {
1244: manager.launchSuite(msi, msi.midletToRun);
1245: display.setCurrent(this );
1246: } else {
1247: try {
1248: new MIDletSelector(msi, display, this , manager);
1249: } catch (Throwable t) {
1250: displayError.showErrorAlert(msi.displayName, t, null,
1251: null);
1252: }
1253: }
1254: }
1255:
1256: /**
1257: * Prompts the user to specify whether to launch a midlet from
1258: * the midlet suite that was just installed.
1259: */
1260: private void askUserIfLaunchMidlet() {
1261: // Ask the user if he wants to run a midlet from
1262: // the newly installed midlet suite
1263: String title = Resource
1264: .getString(
1265: ResourceConstants.AMS_MGR_RUN_THE_NEW_SUITE_TITLE,
1266: null);
1267: String msg = Resource.getString(
1268: ResourceConstants.AMS_MGR_RUN_THE_NEW_SUITE, null);
1269:
1270: Alert alert = new Alert(title, msg, null,
1271: AlertType.CONFIRMATION);
1272: alert.addCommand(runNoCmd);
1273: alert.addCommand(runYesCmd);
1274: alert.setCommandListener(this );
1275: alert.setTimeout(Alert.FOREVER);
1276:
1277: display.setCurrent(alert);
1278: }
1279:
1280: /** A Timer which will handle firing repaints of the ScrollPainter */
1281: protected static Timer textScrollTimer;
1282:
1283: /** Text auto-scrolling parameters */
1284: private static int SCROLL_RATE = 250;
1285:
1286: private static int SCROLL_DELAY = 500;
1287:
1288: private static int SCROLL_SPEED = 10;
1289:
1290: /**
1291: * Inner class used to display a running midlet in the AppSelector.
1292: * MidletCustomItem consists of an icon and name associated with the
1293: * corresponding midlet. In addition if a midlet requests to be
1294: * put into foreground (requires user attention) an additional
1295: * system provided icon will be displayed.
1296: */
1297: class MidletCustomItem extends CustomItem {
1298:
1299: /**
1300: * Constructs a midlet representation for the App Selector Screen.
1301: * @param msi The MIDletSuiteInfo for which representation has
1302: * to be created
1303: */
1304: MidletCustomItem(RunningMIDletSuiteInfo msi) {
1305: super (null);
1306: this .msi = msi;
1307: icon = msi.icon;
1308: text = msi.displayName.toCharArray();
1309: textLen = msi.displayName.length();
1310: truncWidth = ICON_FONT.charWidth(truncationMark);
1311: truncated = false;
1312: if (textScrollTimer == null) {
1313: textScrollTimer = new Timer();
1314: }
1315: xScrollOffset = 0;
1316:
1317: }
1318:
1319: /**
1320: * Gets the minimum width of a midlet representation in
1321: * the App Selector Screen.
1322: * @return the minimum width of a midlet representation
1323: * in the App Selector Screen.
1324: */
1325: protected int getMinContentWidth() {
1326: return AppManagerUI.this .getWidth();
1327: }
1328:
1329: /**
1330: * Gets the minimum height of a midlet representation in
1331: * the App Selector Screen.
1332: * @return the minimum height of a midlet representation
1333: * in the App Selector Screen.
1334: */
1335: protected int getMinContentHeight() {
1336: return ICON_BG.getHeight() > ICON_FONT.getHeight() ? ICON_BG
1337: .getHeight()
1338: : ICON_FONT.getHeight();
1339: }
1340:
1341: /**
1342: * Gets the preferred width of a midlet representation in
1343: * the App Selector Screen based on the passed in height.
1344: * @param height the amount of height available for this Item
1345: * @return the minimum width of a midlet representation
1346: * in the App Selector Screen.
1347: */
1348: protected int getPrefContentWidth(int height) {
1349: return AppManagerUI.this .getWidth();
1350: }
1351:
1352: /**
1353: * Gets the preferred height of a midlet representation in
1354: * the App Selector Screen based on the passed in width.
1355: * @param width the amount of width available for this Item
1356: * @return the minimum height of a midlet representation
1357: * in the App Selector Screen.
1358: */
1359: protected int getPrefContentHeight(int width) {
1360: return ICON_BG.getHeight() > ICON_FONT.getHeight() ? ICON_BG
1361: .getHeight()
1362: : ICON_FONT.getHeight();
1363: }
1364:
1365: /**
1366: * On size change event we define the item's text
1367: * according to item's new width
1368: * @param w The current width of this Item
1369: * @param h The current height of this Item
1370: */
1371: protected void sizeChanged(int w, int h) {
1372: width = w;
1373: height = h;
1374: int widthForText = w - ITEM_PAD - ICON_BG.getWidth();
1375: int msiNameWidth = ICON_FONT.charsWidth(text, 0, textLen);
1376: scrollWidth = msiNameWidth - widthForText + w / 5;
1377: truncated = msiNameWidth > widthForText;
1378: }
1379:
1380: /**
1381: * Paints the content of a midlet representation in
1382: * the App Selector Screen.
1383: * Note that icon representing that foreground was requested
1384: * is painted on to of the existing ickon.
1385: * @param g The graphics context where painting should be done
1386: * @param w The width available to this Item
1387: * @param h The height available to this Item
1388: */
1389: protected void paint(Graphics g, int w, int h) {
1390: int cX = g.getClipX();
1391: int cY = g.getClipY();
1392: int cW = g.getClipWidth();
1393: int cH = g.getClipHeight();
1394:
1395: if ((cW + cX) > bgIconW) {
1396: if (text != null && h > ICON_FONT.getHeight()) {
1397:
1398: int color;
1399: if (msi.proxy == null) {
1400: color = hasFocus ? ICON_HL_TEXT : ICON_TEXT;
1401: } else {
1402: color = hasFocus ? ICON_RUNNING_HL_TEXT
1403: : ICON_RUNNING_TEXT;
1404: }
1405:
1406: g.setColor(color);
1407: g.setFont(ICON_FONT);
1408:
1409: boolean truncate = (xScrollOffset == 0)
1410: && truncated;
1411:
1412: g.clipRect(bgIconW + ITEM_PAD, 0, truncate ? w
1413: - truncWidth - bgIconW - 2 * ITEM_PAD : w
1414: - bgIconW - 2 * ITEM_PAD, h);
1415: g.drawChars(text, 0, textLen, bgIconW + ITEM_PAD
1416: + xScrollOffset,
1417: (h - ICON_FONT.getHeight()) / 2,
1418: Graphics.LEFT | Graphics.TOP);
1419: g.setClip(cX, cY, cW, cH);
1420:
1421: if (truncate) {
1422: g.drawChar(truncationMark, w - truncWidth,
1423: (h - ICON_FONT.getHeight()) / 2,
1424: Graphics.LEFT | Graphics.TOP);
1425: }
1426:
1427: }
1428: }
1429:
1430: if (cX < bgIconW) {
1431: if (hasFocus) {
1432: g.drawImage(ICON_BG, 0, (h - bgIconH) / 2,
1433: Graphics.TOP | Graphics.LEFT);
1434: }
1435:
1436: if (icon != null) {
1437: g.drawImage(icon, (bgIconW - icon.getWidth()) / 2,
1438: (bgIconH - icon.getHeight()) / 2,
1439: Graphics.TOP | Graphics.LEFT);
1440: }
1441:
1442: // Draw special icon if user attention is requested and
1443: // that midlet needs to be brought into foreground by the user
1444: if (msi.proxy != null && msi.proxy.isAlertWaiting()) {
1445: g.drawImage(FG_REQUESTED, bgIconW
1446: - FG_REQUESTED.getWidth(), 0, Graphics.TOP
1447: | Graphics.LEFT);
1448: }
1449:
1450: if (!msi.enabled) {
1451: // indicate that this suite is disabled
1452: g.drawImage(DISABLED_IMAGE,
1453: (bgIconW - DISABLED_IMAGE.getWidth()) / 2,
1454: (bgIconH - DISABLED_IMAGE.getHeight()) / 2,
1455: Graphics.TOP | Graphics.LEFT);
1456: }
1457: }
1458:
1459: }
1460:
1461: /**
1462: * Start the scrolling of the text
1463: */
1464: protected void startScroll() {
1465: if (!hasFocus || !truncated) {
1466: return;
1467: }
1468: stopScroll();
1469: textScrollPainter = new TextScrollPainter();
1470: textScrollTimer.schedule(textScrollPainter, SCROLL_DELAY,
1471: SCROLL_RATE);
1472: }
1473:
1474: /**
1475: * Stop the scrolling of the text
1476: */
1477: protected void stopScroll() {
1478: if (textScrollPainter == null) {
1479: return;
1480: }
1481: xScrollOffset = 0;
1482: textScrollPainter.cancel();
1483: textScrollPainter = null;
1484: repaint(bgIconW, 0, width, height);
1485: }
1486:
1487: /**
1488: * Called repeatedly to animate a side-scroll effect for text
1489: */
1490: protected void repaintScrollText() {
1491: if (-xScrollOffset < scrollWidth) {
1492: xScrollOffset -= SCROLL_SPEED;
1493: repaint(bgIconW, 0, width, height);
1494: } else {
1495: // already scrolled to the end of text
1496: stopScroll();
1497: }
1498: }
1499:
1500: /**
1501: * Handles traversal.
1502: * @param dir The direction of traversal (Canvas.UP, Canvas.DOWN,
1503: * Canvas.LEFT, Canvas.RIGHT)
1504: * @param viewportWidth The width of the viewport in the AppSelector
1505: * @param viewportHeight The height of the viewport in the AppSelector
1506: * @param visRect_inout The return array that tells AppSelector
1507: * which portion of the MidletCustomItem has to be made visible
1508: * @return true if traversal was handled in this method
1509: * (this MidletCustomItem just got focus or there was an
1510: * internal traversal), otherwise false - to transfer focus
1511: * to the next item
1512: */
1513: protected boolean traverse(int dir, int viewportWidth,
1514: int viewportHeight, int visRect_inout[]) {
1515: // entirely visible and hasFocus
1516: if (!hasFocus) {
1517: hasFocus = true;
1518: lastSelectedMsi = this .msi;
1519: }
1520:
1521: visRect_inout[0] = 0;
1522: visRect_inout[1] = 0;
1523: visRect_inout[2] = width;
1524: visRect_inout[3] = height;
1525:
1526: startScroll();
1527:
1528: return false;
1529: }
1530:
1531: /**
1532: * Handles traversal out. This method is called when this
1533: * MidletCustomItem looses focus.
1534: */
1535: protected void traverseOut() {
1536: hasFocus = false;
1537: stopScroll();
1538: }
1539:
1540: /**
1541: * Repaints MidletCustomItem. Called when internal state changes.
1542: */
1543: public void update() {
1544: repaint();
1545: }
1546:
1547: /**
1548: * Sets the owner (AppManagerUI) of this MidletCustomItem
1549: * @param hs The AppSelector in which this MidletCustomItem is shown
1550: */
1551: void setOwner(AppManagerUI hs) {
1552: owner = hs;
1553: }
1554:
1555: /**
1556: * Sets default <code>Command</code> for this <code>Item</code>.
1557: *
1558: * @param c the command to be used as this <code>Item's</code> default
1559: * <code>Command</code>, or <code>null</code> if there is to
1560: * be no default command
1561: */
1562: public void setDefaultCommand(Command c) {
1563: default_command = c;
1564: super .setDefaultCommand(c);
1565: }
1566:
1567: /**
1568: * Called when MidletCustomItem is shown.
1569: */
1570: public void showNotify() {
1571:
1572: // Unfortunately there is no Form.showNotify method where
1573: // this could have been done.
1574:
1575: // When icon for the Installer
1576: // is shown we want to make sure
1577: // that there are no running midlets from the "internal" suite.
1578: // The only 2 midlets that can run in bg from
1579: // "internal" suite are the DiscoveryApp and the Installer.
1580: // Icon for the Installer will be shown each time
1581: // the AppSelector is made current since it is the top
1582: // most icon and we reset the traversal to start from the top
1583: if (msi.suiteId == MIDletSuite.INTERNAL_SUITE_ID
1584: && appManagerMidlet != null) {
1585: appManagerMidlet.destroyMidlet();
1586: }
1587: }
1588:
1589: /** A TimerTask which will repaint scrolling text on a repeated basis */
1590: protected TextScrollPainter textScrollPainter;
1591:
1592: /**
1593: * Width of the scroll area for text
1594: */
1595: protected int scrollWidth;
1596:
1597: /**
1598: * If text is truncated
1599: */
1600: boolean truncated;
1601:
1602: /**
1603: * pixel offset to the start of the text field (for example, if
1604: * xScrollOffset is -60 it means means that the text in this
1605: * text field is scrolled 60 pixels left of the left edge of the
1606: * text field)
1607: */
1608: protected int xScrollOffset;
1609:
1610: /**
1611: * Helper class used to repaint scrolling text
1612: * if needed.
1613: */
1614: private class TextScrollPainter extends TimerTask {
1615: /**
1616: * Repaint the item text
1617: */
1618: public final void run() {
1619: repaintScrollText();
1620: }
1621: }
1622:
1623: /** True if this MidletCustomItem has focus, and false - otherwise */
1624: boolean hasFocus; // = false;
1625:
1626: /** The owner of this MidletCustomItem */
1627: AppManagerUI owner; // = false
1628:
1629: /** The MIDletSuiteInfo associated with this MidletCustomItem */
1630: RunningMIDletSuiteInfo msi; // = null
1631:
1632: /** The width of this MidletCustomItem */
1633: int width; // = 0
1634: /** The height of this MIDletSuiteInfo */
1635: int height; // = 0
1636:
1637: /** Cashed width of the truncation mark */
1638: int truncWidth;
1639:
1640: /** The text of this MidletCustomItem */
1641: char[] text;
1642:
1643: /** Length of the text */
1644: int textLen;
1645:
1646: /**
1647: * The icon to be used to draw this midlet representation.
1648: */
1649: Image icon; // = null
1650: /** current default command */
1651: Command default_command; // = null
1652: }
1653: }
|