/*
*
*
* Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package javax.microedition.lcdui;
import com.sun.midp.i18n.Resource;
import com.sun.midp.i18n.ResourceConstants;
import com.sun.midp.util.ResourceHandler;
import java.util.Timer;
import java.util.TimerTask;
import com.sun.midp.security.SecurityToken;
import com.sun.midp.security.SecurityInitializer;
import com.sun.midp.security.ImplicitlyTrustedClass;
/**
* Look & Feel implementation of <code>Alert</code> based on
* platform widget.
*/
class AlertLFImpl extends DisplayableLFImpl implements AlertLF {
/**
* Creates an <code>AlertLF</code> for the passed in <code>Alert</code>
* instance.
* @param a The <code>Alert</code> associated with this look & feel
*/
AlertLFImpl(Alert a) {
super(a);
alert = a;
}
// ************************************************************
// public methods - AlertLF interface implementation
// ************************************************************
/**
* Determines if <code>Alert</code> associated with this view is modal.
*
* @return true if this <code>AlertLF</code> should be displayed as modal
*/
public boolean lIsModal() {
if (alert.numCommands > 1) {
return true;
}
if (isContentScroll < 0) {
layout();
}
return (isContentScroll == 1);
}
/**
* Gets default timeout for the <code>Alert</code> associated with
* this view.
*
* @return the default timeout
*/
public int lGetDefaultTimeout() {
return DEFAULT_TIMEOUT;
}
/**
* Return the command that should be mapped to
* <code>Alert.DISMISS_COMMAND</code>.
*
* @return command that maps to <code>Alert.DISMISS_COMMAND</code>
*/
public Command lGetDismissCommand() {
return DISMISS_COMMAND;
}
/**
* Notifies timeout change.
* Changing timeout on an already visible <code>Alert</code> will
* restart the timer, but has no effect on current layout.
*
* @param timeout the new timeout set in the corresponding
* <code>Alert</code>.
*/
public void lSetTimeout(int timeout) {
if (timerTask != null) {
try {
timerTask.cancel();
if (timeout == Alert.FOREVER) {
timerTask = null;
} else {
timerTask = new TimeoutTask();
timeoutTimer.schedule(timerTask, timeout);
}
} catch (Throwable t) { }
}
}
/**
* Notifies <code>Alert</code> type change.
* Changing type on an already visible <code>Alert</code> will only
* update the default icon. No sound will be played.
*
* @param type the new <code>AlertType</code> set in the
* corresponding <code>Alert</code>.
*/
public void lSetType(AlertType type) {
lRequestInvalidate();
}
/**
* Notifies string change.
*
* @param oldString the old string set in the corresponding
* <code>Alert</code>.
* @param newString the new string set in the corresponding
* <code>Alert</code>.
*/
public void lSetString(String oldString, String newString) {
lRequestInvalidate();
}
/**
* Notifies image change.
*
* @param oldImg the old image set in the corresponding
* <code>Alert</code>.
* @param newImg the new image set in the corresponding
* <code>Alert</code>.
*/
public void lSetImage(Image oldImg, Image newImg) {
lRequestInvalidate();
}
/**
* Notifies indicator change.
*
* @param oldIndicator the old indicator set in the corresponding
* <code>Alert</code>.
* @param newIndicator the new indicator set in the corresponding
* <code>Alert</code>.
*/
public void lSetIndicator(Gauge oldIndicator, Gauge newIndicator) {
lRequestInvalidate();
}
/**
* Notify this <code>Alert</code> that it is being displayed.
* Override the version in <code>DisplayableLFImpl</code>.
*/
void lCallShow() {
// Create native resource with title and ticker
super.lCallShow();
// Play sound
if (alert.type != null) {
currentDisplay.playAlertSound(alert.type);
}
// Setup contained items and show them
showContents();
// Show the Alert dialog window
showNativeResource0(nativeId);
// Start Java timer
// If native dialog will cause VM to freeze, this timer
// needs to be moved to native.
if (alert.time != Alert.FOREVER
&& alert.numCommands == 1
&& isContentScroll == 0) {
if (timeoutTimer == null) {
timeoutTimer = new Timer();
}
timerTask = new TimeoutTask();
timeoutTimer.schedule(timerTask, alert.time);
}
}
/**
* Notify this <code>Alert</code> that it will no longer be displayed.
* Override the version in <code>DisplayableLFImpl</code>.
*/
void lCallHide() {
// Stop the timer
if (timerTask != null) {
try {
timerTask.cancel();
timerTask = null;
} catch (Throwable t) { }
}
// Hide and delete gauge resource
if (alert.indicator != null) {
GaugeLFImpl gaugeLF = (GaugeLFImpl)alert.indicator.gaugeLF;
gaugeLF.lHideNativeResource();
gaugeLF.deleteNativeResource();
if (gaugeLF.visibleInViewport) {
gaugeLF.lCallHideNotify();
}
}
// Hide and delete alert dialog window including title and ticker
super.lCallHide();
}
/**
* Called by the event handler to perform a re-layout
* on this <code>AlertLF</code>.
*/
public void uCallInvalidate() {
synchronized (Display.LCDUILock) {
showContents();
}
}
/**
* Notify return screen about screen size change
*/
public void uCallSizeChanged(int w, int h) {
super.uCallSizeChanged(w,h);
Displayable returnScreen = alert.getReturnScreen();
if (returnScreen != null) {
(returnScreen.displayableLF).uCallSizeChanged(w,h);
}
}
// *****************************************************
// Package private methods
// *****************************************************
/**
* Called upon content change to schedule a request for relayout and
* repaint.
*/
void lRequestInvalidate() {
super.lRequestInvalidate();
isContentScroll = -1; // Unknown scrolling state
}
// *****************************************************
// Private methods
// *****************************************************
/**
* Layout the content of this <code>Alert</code>.
* Query native resource for two informations:
* - Whether the content needs scrolling, in 'isContentScroll'
* - Location of the gauge indicator
*
* SYNC NOTE: Caller of this function should hold LCDUILock around
* this call.
*/
private void layout() {
boolean wasNoNative = (nativeId == INVALID_NATIVE_ID);
// If no native resource yet, create it temporarily
if (wasNoNative) {
createNativeResource();
}
Image img = alert.image;
// If no image is specified, default icon for that type should be used
if (img == null && alert.type != null) {
img = getAlertImage(alert.type);
}
// Bounds array of gauge
// The reason gauge bounds is passed back from native is to be
// consistent with Form's Java layout code.
int[] gaugeBounds;
GaugeLFImpl gaugeLF;
if (alert.indicator == null) {
gaugeLF = null;
gaugeBounds = null;
} else {
// We temporarily use bounds array in gauge
// The real values will be set later by setSize() and setLocation()
gaugeLF = (GaugeLFImpl)alert.indicator.gaugeLF;
gaugeBounds = new int[4];
// Pass gauge's preferred size to native layout code
gaugeBounds[WIDTH] = gaugeLF.lGetPreferredWidth(-1);
gaugeBounds[HEIGHT] = gaugeLF.lGetPreferredHeight(-1);
}
ImageData imageData = null;
if (img != null) {
imageData = img.getImageData();
}
// Set content to native dialog and get layout information back
if (setNativeContents0(nativeId, imageData,
gaugeBounds, alert.text)) {
isContentScroll = 1; // scrolling needed
} else {
isContentScroll = 0; // no scrolling
}
// Set gauge location and size based on return from native layout code
if (gaugeBounds != null) {
gaugeLF.lSetSize(gaugeBounds[WIDTH], gaugeBounds[HEIGHT]);
gaugeLF.lSetLocation(gaugeBounds[X], gaugeBounds[Y]);
}
// Native resource should only be kept alive if it's visible
// Free temporarily created native resource here
if (wasNoNative) {
deleteNativeResource();
}
}
/**
* Show or update contents on a visible <code>Alert</code>.
*
* SYNC NOTE: Caller must hold LCDUILock around this call.
*/
private void showContents() {
// Make sure gauge has native resource ready
GaugeLFImpl gaugeLF = (alert.indicator == null)
? null
: (GaugeLFImpl)alert.indicator.gaugeLF;
if (gaugeLF != null && gaugeLF.nativeId == INVALID_NATIVE_ID) {
gaugeLF.createNativeResource(nativeId);
}
// Re-populate the alert with updated contents
layout();
// Make sure gauge is shown
if (gaugeLF != null) {
gaugeLF.lShowNativeResource();
// SYNC NOTE: Since Gauge show and showNotify does not involve
// application code, we can call it while holding LCDUILock
gaugeLF.lCallShowNotify();
// IMPLEMENTATION NOTE: when gauge is present in the Alert
// its visibleInViewport will always be set to true.
// If dynamic update of gauge's visibleInViewport flag is
// required in AlertLFImpl
// uViewportChanged() can be moved up from FormLFImpl to
// DisplayableLFImpl
}
}
/**
* Create native resource for this <code>Alert</code>.
* <code>Gauge</code> resource will not be created.
*/
void createNativeResource() {
nativeId = createNativeResource0(alert.title,
alert.ticker == null ? null : alert.ticker.getString(),
alert.type == null ? 0 : alert.type.getType());
}
/**
* Create native dialog with image and text widget for this
* <code>Alert</code>.
*
* @param title the title being passed to native
* @param tickerText text to be displayed on the <code>Ticker</code>
* @param type the type of <code>Alert</code>
* @return native resource id
*/
private native int createNativeResource0(String title,
String tickerText,
int type);
/**
* (Re)Show native dialog with image and text widget for this
* <code>Alert<code>.
*
* @param nativeId native resource id
*/
private native void showNativeResource0(int nativeId);
/**
* Set content to native dialog.
*
* @param nativeId IN this alert's resource id (MidpDisplayable *)
* @param imgId IN icon image native id. 0 if no image.
* @param indicatorBounds a 4 integer array for indicator gauge
* [0] : OUT x coordinate in alert dialog
* [1] : OUT y coordinate in alert dialog
* [2] : IN/OUT width of the gauge, in pixels
* [3] : IN/OUT height of the gauge, in pixels
* null if no indicator gauge present.
* @param text IN alert text string
* @return <code>true</code> if content requires scrolling
*/
private native boolean setNativeContents0(int nativeId,
ImageData imgId,
int[] indicatorBounds,
String text);
/**
* Get the corresponding image for a given alert type.
*
* @param alertType type defined in <code>AlertType</code>
* @return image object to be displayed. Null if type is invalid.
*/
private Image getAlertImage(AlertType alertType) {
if (alertType != null) {
if (alertType.equals(AlertType.INFO)) {
if (ALERT_INFO == null) {
ALERT_INFO = getSystemImage("alert.image_icon_info");
}
return ALERT_INFO;
} else if (alertType.equals(AlertType.WARNING)) {
if (ALERT_WARN == null) {
ALERT_WARN = getSystemImage("alert.image_icon_warn");
}
return ALERT_WARN;
} else if (alertType.equals(AlertType.ERROR)) {
if (ALERT_ERR == null) {
ALERT_ERR = getSystemImage("alert.image_icon_errr");
}
return ALERT_ERR;
} else if (alertType.equals(AlertType.ALARM)) {
if (ALERT_ALRM == null) {
ALERT_ALRM = getSystemImage("alert.image_icon_alrm");
}
return ALERT_ALRM;
} else if (alertType.equals(AlertType.CONFIRMATION)) {
if (ALERT_CFM == null) {
ALERT_CFM = getSystemImage("alert.image_icon_cnfm");
}
return ALERT_CFM;
}
}
return null;
}
/**
* Obtain system image resource and create Image object from it.
*
* @param imageName image name
* @return icon image
*/
private Image getSystemImage(String imageName) {
byte[] imageData = ResourceHandler.getSystemImageResource(
classSecurityToken, imageName);
if (imageData != null) {
return Image.createImage(imageData, 0, imageData.length);
} else {
// Use a empty immutable image as placeholder
return Image.createImage(Image.createImage(16, 16));
}
}
// *****************************************************
// Private members
// *****************************************************
/**
* Inner class to request security token from SecurityInitializer.
* SecurityInitializer should be able to check this inner class name.
*/
static private class SecurityTrusted
implements ImplicitlyTrustedClass {};
/** Security token to allow access to implementation APIs */
private static SecurityToken classSecurityToken =
SecurityInitializer.requestToken(new SecurityTrusted());
/**
* Internal command used to visually represent
* <code>Alert.DISMISS_COMMAND</code>.
*/
private static final Command DISMISS_COMMAND =
new Command(Resource.getString(ResourceConstants.DONE),
Command.CANCEL, 0);
/**
* The default timeout of all alerts.
*/
private static final int DEFAULT_TIMEOUT = 2000;
/**
* A <code>Timer</code> which serves all <code>Alert</code> objects
* to schedule their timeout tasks.
*/
private static Timer timeoutTimer;
/**
* <code>Alert</code> associated with this view.
*/
private Alert alert;
/**
* A <code>TimerTask</code> which will be set to expire this
* <code>Alert</code> after its timeout period has elapsed.
*/
private TimerTask timerTask;
/**
* A flag that indicates whether the content of the alert
* needs scrolling.
* Valid values are: -1: unknown, 0: no scrolling, 1: scrolling needed.
*/
private int isContentScroll = -1; // Default is unknown
/**
* An image to be drawn in <code>Alert</code> when it was
* created with AlertType ALARM.
*/
private static Image ALERT_ALRM; // = null
/**
* An image to be drawn in <code>Alert</code> when it was
* created with AlertType CONFIRMATION..
*/
private static Image ALERT_CFM; // = null
/**
* An image to be drawn in <code>Alert</code> when it was
* created with AlertType ERROR.
*/
private static Image ALERT_ERR; // = null
/**
* An image to be drawn in <code>Alert</code> when it was
* created with AlertType INFO.
*/
private static Image ALERT_INFO; // = null
/**
* An image to be drawn in <code>Alert</code> when it was
* created with AlertType WARNING.
*/
private static Image ALERT_WARN; // = null
// *****************************************************
// Inner Class for timed dismiss
// *****************************************************
/**
* A <code>TimerTask</code> subclass which will notify the
* <code>Display</code> to make the 'returnScreen' of this
* <code>Alert</code> the new current screen.
*/
private class TimeoutTask extends TimerTask {
/**
* Create a new timeout task.
* This package protected constructor is just to enable creation
* of new TimerTask instance.
*/
TimeoutTask() { }
/**
* Simply set the <code>Display</code>'s current screen to be this
* <code>Alert</code>'s return screen.
*/
public void run() {
alert.uNotifyTimeout();
}
} // TimeoutTask
}
|