001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.midp.appmanager;
028:
029: import javax.microedition.lcdui.*;
030:
031: import com.sun.midp.i18n.Resource;
032: import com.sun.midp.i18n.ResourceConstants;
033:
034: import com.sun.midp.installer.*;
035:
036: import com.sun.midp.midletsuite.*;
037:
038: import com.sun.midp.security.*;
039:
040: import com.sun.midp.io.j2me.push.*;
041:
042: import com.sun.midp.log.Logging;
043: import com.sun.midp.log.LogChannels;
044:
045: /**
046: * The Graphical MIDlet suite settings form.
047: */
048: public class AppSettings extends Form implements CommandListener,
049: ItemStateListener {
050:
051: /** ID for the interrupt choice. */
052: private static final int INTERRUPT_CHOICE_ID = 2000;
053:
054: /** ID for the first push option radio button. */
055: private static final int PUSH_OPTION_1_ID = 1000;
056:
057: /** Command object for "OK" command for the form. */
058: private Command saveAppSettingsCmd = new Command(Resource
059: .getString(ResourceConstants.SAVE), Command.OK, 1);
060: /** Command object for "Cancel" command for the form. */
061: private Command cancelCmd = new Command(Resource
062: .getString(ResourceConstants.CANCEL), Command.CANCEL, 1);
063:
064: /** The ID of the setting displayed in the form. */
065: private int displayedSettingID;
066: /** The ID of the popup button selected. */
067: private int lastPopupChoice;
068: /** The initial setting to display. */
069: private RadioButtonSet initialSetting;
070: /** The settings popup choice group. */
071: private RadioButtonSet settingsPopup;
072: /** The application interruption setting. */
073: private RadioButtonSet interruptChoice;
074: /** The application permission settings. */
075: private RadioButtonSet[] groupSettings;
076: /** The number of group permission settings. */
077: private int numberOfSettings;
078:
079: /** Holds the maximum levels for permissions. */
080: private byte[] maxLevels;
081: /** Holds the updated permissions. */
082: private byte[] curLevels;
083: /** Holds the updated push interrupt level. */
084: private byte pushInterruptSetting;
085: /** Holds the updated push options. */
086: private int pushOptions;
087:
088: /** Permission group information. */
089: private PermissionGroup[] groups;
090:
091: /** MIDlet Suite storage object. */
092: MIDletSuiteStorage midletSuiteStorage;
093:
094: /** UI to display error alerts. */
095: DisplayError displayError;
096:
097: /** The displayable to be displayed after dismissing AppSettings. */
098: Displayable nextScreen;
099:
100: /** Display for the Manager midlet. */
101: Display display;
102:
103: /** Display name of the suite. */
104: String suiteDisplayName;
105:
106: /** Interface to suite */
107: MIDletSuiteImpl midletSuite;
108:
109: /** Installation information of the suite. */
110: InstallInfo installInfo;
111:
112: /** Icon to display for the suite */
113: Image icon;
114:
115: /**
116: * Create and initialize a new application settings MIDlet.
117: * @param suiteId - the id of the suite for
118: * which the App settings should be displayed
119: * @param display - the display instance to be used
120: * @param displayError - the UI to be used to display error alerts.
121: * @param nextScreen - the displayable to be shown after
122: * this Form is dismissed
123: */
124: public AppSettings(int suiteId, Display display,
125: DisplayError displayError, Displayable nextScreen)
126: throws Throwable {
127: super (null);
128:
129: this .displayError = displayError;
130: midletSuiteStorage = MIDletSuiteStorage.getMIDletSuiteStorage();
131:
132: this .display = display;
133: this .nextScreen = nextScreen;
134:
135: displayApplicationSettings(suiteId);
136: }
137:
138: /**
139: * Respond to a command issued on any Screen.
140: *
141: * @param c command activated by the user
142: * @param s the Displayable the command was on.
143: */
144: public void commandAction(Command c, Displayable s) {
145: if (c == saveAppSettingsCmd) {
146: saveApplicationSettings();
147: midletSuite.close();
148: } else if (c == cancelCmd) {
149: display.setCurrent(nextScreen);
150: midletSuite.close();
151: }
152: }
153:
154: /**
155: * Called when internal state of an item in Settings form is
156: * changed by the user. This is used to dynamically display
157: * the setting the user chooses from the settings popup.
158: *
159: * @param item the item that was changed
160: */
161: public void itemStateChanged(Item item) {
162: int selected;
163:
164: if (item != settingsPopup) {
165: /* ignore the other items besides the popup */
166: return;
167: }
168:
169: selected = settingsPopup.getSelectedButton();
170: if (selected == lastPopupChoice) {
171: return;
172: }
173:
174: lastPopupChoice = selected;
175:
176: delete(displayedSettingID);
177:
178: try {
179: if (selected == INTERRUPT_CHOICE_ID) {
180: displayedSettingID = append(interruptChoice);
181: } else {
182: displayedSettingID = append(groupSettings[selected]);
183: }
184: } catch (IndexOutOfBoundsException e) {
185: // for safety/completeness.
186: displayedSettingID = 0;
187: Logging.report(Logging.ERROR, LogChannels.LC_AMS,
188: "AppSettings: selected=" + selected);
189: }
190: }
191:
192: /**
193: * Initialize the MIDlet suite info fields for a given suite.
194: *
195: * @param midletSuite the MIDletSuiteImpl object instance
196: *
197: * @exception Exception if problem occurs while getting the suite info
198: */
199: private void initMidletSuiteInfo(MIDletSuiteImpl midletSuite)
200: throws Exception {
201:
202: int numberOfMidlets = midletSuite.getNumberOfMIDlets();
203: installInfo = midletSuite.getInstallInfo();
204:
205: if (numberOfMidlets == 1) {
206: String value = midletSuite.getProperty("MIDlet-1");
207: MIDletInfo temp = new MIDletInfo(value);
208: suiteDisplayName = temp.name;
209: } else {
210: suiteDisplayName = midletSuite
211: .getProperty(MIDletSuiteImpl.SUITE_NAME_PROP);
212: }
213: }
214:
215: /**
216: * Display the MIDlet suite settings as choice groups.
217: *
218: * @param suiteId ID for suite to display
219: */
220: private void displayApplicationSettings(int suiteId)
221: throws Throwable {
222:
223: int maxLevel;
224: String[] values = new String[1];
225: int interruptSetting;
226: initialSetting = null;
227:
228: try {
229: groups = Permissions.getSettingGroups();
230:
231: midletSuite = midletSuiteStorage.getMIDletSuite(suiteId,
232: false);
233: initMidletSuiteInfo(midletSuite);
234:
235: maxLevels = (Permissions.forDomain(installInfo
236: .getSecurityDomain()))[Permissions.MAX_LEVELS];
237: curLevels = midletSuite.getPermissions();
238: pushInterruptSetting = midletSuite
239: .getPushInterruptSetting();
240: pushOptions = midletSuite.getPushOptions();
241:
242: values[0] = suiteDisplayName;
243:
244: setTitle(Resource
245: .getString(ResourceConstants.AMS_MGR_SETTINGS));
246: settingsPopup = new RadioButtonSet(Resource
247: .getString(ResourceConstants.AMS_MGR_PREFERENCES),
248: true);
249:
250: if (maxLevels[Permissions.PUSH] == Permissions.ALLOW) {
251: maxLevel = Permissions.BLANKET;
252: } else {
253: maxLevel = maxLevels[Permissions.PUSH];
254: }
255:
256: if ((pushOptions & PushRegistryInternal.PUSH_OPT_WHEN_ONLY_APP) != 0) {
257: interruptSetting = PUSH_OPTION_1_ID;
258: } else {
259: interruptSetting = pushInterruptSetting;
260: }
261:
262: interruptChoice = newSettingChoice(settingsPopup,
263: ResourceConstants.AMS_MGR_INTRUPT,
264: INTERRUPT_CHOICE_ID,
265: ResourceConstants.AMS_MGR_INTRUPT_QUE,
266: ResourceConstants.AMS_MGR_INTRUPT_QUE_DONT,
267: maxLevel, interruptSetting, suiteDisplayName,
268: ResourceConstants.AMS_MGR_SETTINGS_PUSH_OPT_ANSWER,
269: PUSH_OPTION_1_ID);
270:
271: groupSettings = new RadioButtonSet[groups.length];
272:
273: if (interruptChoice != null) {
274: numberOfSettings = 1;
275: } else {
276: numberOfSettings = 0;
277: }
278:
279: for (int i = 0; i < groups.length; i++) {
280: byte maxGroupSetting = Permissions
281: .getPermissionGroupLevel(maxLevels, groups[i]);
282: byte currentGroupSetting = Permissions
283: .getPermissionGroupLevel(curLevels, groups[i]);
284:
285: groupSettings[i] = newSettingChoice(settingsPopup,
286: groups[i].getName(), i, groups[i]
287: .getSettingsQuestion(), groups[i]
288: .getDisableSettingChoice(),
289: maxGroupSetting, currentGroupSetting,
290: suiteDisplayName, 0, 0);
291: if (groupSettings[i] != null) {
292: numberOfSettings++;
293: }
294: }
295:
296: if (numberOfSettings > 1) {
297: /*
298: * There is more then one setting to display so add the
299: * popup to the settings form
300: */
301: append(settingsPopup);
302: }
303:
304: if (initialSetting != null) {
305: displayedSettingID = append(initialSetting);
306: }
307: } catch (Throwable t) {
308: if (midletSuite != null) {
309: midletSuite.close();
310: }
311: throw t;
312: }
313:
314: addCommand(saveAppSettingsCmd);
315: addCommand(cancelCmd);
316: setCommandListener(this );
317:
318: setItemStateListener(this );
319: }
320:
321: /**
322: * Creates a new choice group in a form if it is user settable,
323: * with the 3 preset choices and a initial one set.
324: *
325: * @param popup settings popup to append to
326: * @param groupName name to add to popup
327: * i18N will be translated
328: * @param groupID button ID of group in settings popup,
329: * @param question label for the choice, i18N will be translated,
330: * if <= 0, then skip this choice
331: * @param denyAnswer answer for the deny choice of this setting,
332: * i18N will be translated
333: * @param maxLevel maximum permission level
334: * @param level current permission level
335: * @param name name of suite
336: * @param extraAnswer if > 0, add this extra answer before last
337: * answer, i18N will be translated
338: * @param extraAnswerId ID for the extra answer
339: *
340: * @return choice to put in the application settings form,
341: * or null if setting cannot be modified
342: */
343: private RadioButtonSet newSettingChoice(RadioButtonSet popup,
344: int groupName, int groupID, int question, int denyAnswer,
345: int maxLevel, int level, String name, int extraAnswer,
346: int extraAnswerId) {
347: String[] values = { name };
348: int initValue;
349: RadioButtonSet choice;
350:
351: if (question <= 0 || maxLevel == Permissions.ALLOW
352: || maxLevel == Permissions.NEVER
353: || level == Permissions.ALLOW
354: || level == Permissions.NEVER) {
355:
356: return null;
357: }
358:
359: choice = new RadioButtonSet(Resource
360: .getString(question, values), false);
361:
362: settingsPopup.append(Resource.getString(groupName), groupID);
363:
364: switch (maxLevel) {
365: case Permissions.BLANKET:
366: choice
367: .append(
368: Resource
369: .getString(ResourceConstants.AMS_MGR_SETTINGS_BLANKET_ANSWER),
370: Permissions.BLANKET_GRANTED);
371: // fall through, since this security level permits the
372: // next response.
373:
374: case Permissions.SESSION:
375: choice
376: .append(
377: Resource
378: .getString(ResourceConstants.AMS_MGR_SETTINGS_SESSION_ANSWER),
379: Permissions.SESSION);
380: // fall through, since this security level permits the
381: // next response.
382:
383: default:
384: choice
385: .append(
386: Resource
387: .getString(ResourceConstants.AMS_MGR_SETTINGS_ONE_SHOT_ANSWER),
388: Permissions.ONESHOT);
389:
390: if (extraAnswer > 0) {
391: choice.append(Resource.getString(extraAnswer),
392: extraAnswerId);
393: }
394:
395: choice.append(Resource.getString(denyAnswer),
396: Permissions.BLANKET_DENIED);
397: break;
398: }
399:
400: if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
401: Logging.report(Logging.INFORMATION, LogChannels.LC_AMS,
402: "AppSettings: " + Resource.getString(groupName)
403: + " level = " + level);
404: }
405:
406: switch (level) {
407: case Permissions.BLANKET_GRANTED:
408: case Permissions.BLANKET:
409: initValue = Permissions.BLANKET_GRANTED;
410: break;
411:
412: case Permissions.SESSION:
413: initValue = Permissions.SESSION;
414: break;
415:
416: case Permissions.ONESHOT:
417: initValue = Permissions.ONESHOT;
418: break;
419:
420: default:
421: if (level == extraAnswerId) {
422: initValue = extraAnswerId;
423: } else {
424: initValue = Permissions.BLANKET_DENIED;
425: }
426: break;
427: }
428:
429: choice.setDefaultButton(initValue);
430:
431: choice.setPreferredSize(getWidth(), -1);
432:
433: if (initialSetting == null) {
434: initialSetting = choice;
435: lastPopupChoice = groupID;
436: }
437:
438: return choice;
439: }
440:
441: /** Save the application settings the user entered. */
442: private void saveApplicationSettings() {
443: try {
444: if (interruptChoice != null) {
445: byte maxInterruptSetting;
446: int interruptSetting = interruptChoice
447: .getSelectedButton();
448:
449: if (maxLevels[Permissions.PUSH] == Permissions.ALLOW) {
450: maxInterruptSetting = Permissions.BLANKET_GRANTED;
451: } else {
452: maxInterruptSetting = maxLevels[Permissions.PUSH];
453: }
454:
455: if (interruptSetting == PUSH_OPTION_1_ID) {
456: pushOptions = PushRegistryInternal.PUSH_OPT_WHEN_ONLY_APP;
457: pushInterruptSetting = maxInterruptSetting;
458: } else {
459: pushOptions = 0;
460: Permissions.checkPushInterruptLevel(curLevels,
461: (byte) interruptSetting);
462: pushInterruptSetting = (byte) interruptSetting;
463: }
464: }
465:
466: for (int i = 0; i < groups.length; i++) {
467: if (groupSettings[i] != null) {
468: byte newSetting = (byte) groupSettings[i]
469: .getSelectedButton();
470:
471: if (newSetting != Permissions
472: .getPermissionGroupLevel(curLevels,
473: groups[i])) {
474: Permissions.setPermissionGroup(curLevels,
475: pushInterruptSetting, groups[i],
476: newSetting);
477: }
478: }
479: }
480:
481: if (numberOfSettings > 0) {
482: midletSuiteStorage.saveSuiteSettings(midletSuite
483: .getID(), pushInterruptSetting, pushOptions,
484: curLevels);
485: displaySuccessMessage(Resource
486: .getString(ResourceConstants.AMS_MGR_SAVED));
487: }
488: } catch (SecurityException ex) {
489: Alert a = new Alert(Resource
490: .getString(ResourceConstants.ERROR), ex
491: .getMessage(), null, AlertType.ERROR);
492: a.setTimeout(Alert.FOREVER);
493: display.setCurrent(a);
494: throw ex;
495: } catch (Throwable t) {
496: t.printStackTrace();
497: displayError.showErrorAlert(suiteDisplayName, t, Resource
498: .getString(ResourceConstants.EXCEPTION), null);
499: }
500: }
501:
502: /**
503: * Alert the user that an action was successful.
504: * @param successMessage message to display to user
505: */
506: private void displaySuccessMessage(String successMessage) {
507:
508: Image icon = GraphicalInstaller
509: .getImageFromInternalStorage("_dukeok8");
510:
511: Alert successAlert = new Alert(null, successMessage, icon, null);
512:
513: successAlert.setTimeout(GraphicalInstaller.ALERT_TIMEOUT);
514:
515: display.setCurrent(successAlert, nextScreen);
516: }
517: }
518:
519: /**
520: * A <code>RadioButtonSet</code> is a group radio buttons intended to be
521: * placed within a <code>Form</code>. However the radio buttons can be
522: * accessed by a assigned ID instead of by index. This lets the calling
523: * code be the same when dealing with dynamic sets.
524: */
525: class RadioButtonSet extends ChoiceGroup {
526: /** Size increment for the ID array. */
527: private static final int SIZE_INCREMENT = 5;
528:
529: /** Keeps track of the button IDs. */
530: private int[] ids;
531:
532: /**
533: * Creates a new, empty <code>RadioButtonSet</code>, specifying its
534: * title.
535: *
536: * @param label the item's label (see {@link Item Item})
537: * @param popup true if the radio buttons should be popup
538: */
539: RadioButtonSet(String label, boolean popup) {
540: super (label, popup ? Choice.POPUP : Choice.EXCLUSIVE);
541: ids = new int[SIZE_INCREMENT];
542: }
543:
544: /**
545: * Appends choice to the set.
546: *
547: * @param stringPart the string part of the element to be added
548: * @param id ID for the radio button
549: *
550: * @throws IllegalArgumentException if the image is mutable
551: * @throws NullPointerException if <code>stringPart</code> is
552: * <code>null</code>
553: * @throws IndexOutOfBoundsException this call would exceed the maximum
554: * number of buttons for this set
555: */
556: public void append(String stringPart, int id) {
557: int buttonNumber = append(stringPart, null);
558:
559: if (buttonNumber >= ids.length) {
560: expandIdArray();
561: }
562:
563: ids[buttonNumber] = id;
564: }
565:
566: /**
567: * Set the default button.
568: *
569: * @param id ID of default button
570: *
571: * @throws IndexOutOfBoundsException if <code>id</code> is invalid
572: */
573: public void setDefaultButton(int id) {
574: setSelectedIndex(indexFor(id), true);
575: }
576:
577: /**
578: * Returns the ID of the selected radio button.
579: *
580: * @return ID of selected element
581: */
582: public int getSelectedButton() {
583: return ids[getSelectedIndex()];
584: }
585:
586: /**
587: * Find the index for an ID.
588: *
589: * @param id button id
590: *
591: * @return index for a button
592: *
593: * @exception IndexOutOfBoundsException If no element exists with that ID
594: */
595: private int indexFor(int id) {
596: for (int i = 0; i < ids.length; i++) {
597: if (ids[i] == id) {
598: return i;
599: }
600: }
601:
602: throw new IndexOutOfBoundsException();
603: }
604:
605: /** Expands the ID array. */
606: private void expandIdArray() {
607: int[] prev = ids;
608:
609: ids = new int[prev.length + SIZE_INCREMENT];
610: for (int i = 0; i < prev.length; i++) {
611: ids[i] = prev[i];
612: }
613: }
614: }
|