001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.microedition.lcdui.wma;
043:
044: import java.io.IOException;
045: import javax.microedition.io.Connector;
046: import javax.microedition.lcdui.Canvas;
047: import javax.microedition.lcdui.Command;
048: import javax.microedition.lcdui.CommandListener;
049: import javax.microedition.lcdui.Display;
050: import javax.microedition.lcdui.Displayable;
051: import javax.microedition.lcdui.Font;
052: import javax.microedition.lcdui.Graphics;
053: import javax.microedition.lcdui.TextBox;
054: import javax.microedition.lcdui.TextField;
055: import javax.wireless.messaging.MessageConnection;
056: import javax.wireless.messaging.TextMessage;
057: import org.netbeans.microedition.lcdui.laf.ColorSchema;
058: import org.netbeans.microedition.lcdui.laf.DefaultColorSchema;
059:
060: /**
061: * The <code>SMSComposer</code> provides an easy and convenient way to send SMS messages
062: * directly from JavaME application.
063: * @author
064: */
065: public class SMSComposer extends Canvas implements CommandListener {
066:
067: private static String sendCommandName = "Send";
068: /**
069: * Command fired when SMS sent.
070: */
071: public static Command SEND_COMMAND = new Command(sendCommandName,
072: Command.OK, 1);
073: private String phoneNumber = "";
074: private String message = "";
075: private Display display;
076: private int portNum = 50000;
077: private boolean useTextBoxForInput = true;
078: private boolean inputTextIsActive = false;
079: private int borderStyle;
080: private int hiBorderStyle;
081: private Font inputFont;
082: private static final int borderPadding = 2;
083: private static final int labelPadding = 2;
084: private int phoneNumberY;
085: private int messageY;
086: private int phoneNumberX;
087: private int messageX;
088: private int inputFieldsWidth;
089: private int inputFieldsHeight;
090: private int phoneNumberLabelWidth;
091: private int messageLabelWidth;
092: private static final int ACTIVE_PHONE_NUMBER = 1;
093: private static final int ACTIVE_MESSAGE = 2;
094: private int activeField = ACTIVE_PHONE_NUMBER;
095: private String phoneNumberLabel;
096: private String messageLabel;
097: private CommandListener l;
098: private ColorSchema colorSchema = new DefaultColorSchema();
099: private InputTextBox phoneBox;
100: private InputTextBox msgBox;
101: private InputTextBox current = null;
102: private boolean sendAutomatically = true;
103:
104: /**
105: * Creates a new instance of SMSComposer for given <code>Display</code> object.
106: * @param display A non-null display object.
107: */
108: public SMSComposer(Display display) {
109: this .display = display;
110: setDefaulBorderStyles();
111: setDefaultFonts();
112: addCommand(SEND_COMMAND);
113: super .setCommandListener(this );
114:
115: // default values
116: this .phoneNumberLabel = "Phone Number:";
117: this .messageLabel = "Message:";
118: phoneBox = new InputTextBox(phoneNumberLabel, 20,
119: TextField.PHONENUMBER);
120: msgBox = new InputTextBox(messageLabel, 160, TextField.ANY);
121: }
122:
123: /**
124: * Sets the phone number.
125: * @param phoneNumber phone number
126: */
127: public void setPhoneNumber(String phoneNumber) {
128: if (phoneNumber == null) {
129: phoneNumber = "";
130: }
131: this .phoneNumber = phoneNumber;
132: }
133:
134: /**
135: * Sets the phone number label.
136: * @param phoneNumberLabel phone number label
137: */
138: public void setPhoneNumberLabel(String phoneNumberLabel) {
139: if (phoneNumberLabel == null) {
140: phoneNumberLabel = "";
141: }
142: this .phoneNumberLabel = phoneNumberLabel;
143: phoneBox.setTitle(phoneNumberLabel);
144: }
145:
146: /**
147: * Sets the message text.
148: * @param message message text
149: */
150: public void setMessage(String message) {
151: if (message == null) {
152: message = "";
153: }
154: this .message = message;
155: }
156:
157: /**
158: * Sets the message text label.
159: * @param messageLabel message text label
160: */
161: public void setMessageLabel(String messageLabel) {
162: if (messageLabel == null) {
163: messageLabel = "";
164: }
165: this .messageLabel = messageLabel;
166: msgBox.setTitle(messageLabel);
167: }
168:
169: /**
170: * Sets the port number
171: * @param portNum port number
172: */
173: public void setPort(int portNum) {
174: this .portNum = portNum;
175: }
176:
177: /**
178: * Controlls execution of sendSMS method. Default value of sendAutomaticly argument is Boolean.TRUE which means that method <code>sendSMS</code> is
179: * invoked when command Send pressed. If sendAutomaticly is Boolean.FALSE then <code>sendSMS</code> method is not invoked after Send command is pressed.
180: * @param sendAutomatically Boolean.TRUE sendSMS method is executed, Boolean.FALSE sendSMS method is NOT executed
181: */
182: public void setSendAutomatically(boolean sendAutomatically) {
183: this .sendAutomatically = sendAutomatically;
184: }
185:
186: /**
187: * Sends the message based on given non null phone number and port number.
188: * @throws IOException if the message could not be sent or because
189: * of network failure or if the connection is not available
190: */
191: public void sendSMS() throws IOException {
192: if (phoneNumber == null) {
193: throw new IllegalArgumentException();
194: }
195: String address = "sms://" + phoneNumber + ":" + portNum;
196: MessageConnection smsconn = null;
197: /** Open the message connection. */
198: smsconn = (MessageConnection) Connector.open(address);
199: TextMessage txtmessage = (TextMessage) smsconn
200: .newMessage(MessageConnection.TEXT_MESSAGE);
201: txtmessage.setAddress(address);
202: txtmessage.setPayloadText(message);
203: smsconn.send(txtmessage);
204: smsconn.close();
205: }
206:
207: /**
208: * Sets Default border styles.
209: */
210: public void setDefaulBorderStyles() {
211: borderStyle = getDisplay().getBorderStyle(false);
212: hiBorderStyle = getDisplay().getBorderStyle(true);
213: }
214:
215: /**
216: * Sets Default fonts.
217: */
218: public void setDefaultFonts() {
219: inputFont = Font.getFont(Font.FONT_INPUT_TEXT);
220: }
221:
222: /**
223: * The implementation calls showNotify() immediately prior to this <code>Canvas</code>
224: * being made visible on the display. <code>Canvas</code> subclasses may override this method
225: * to perform tasks before being shown, such as setting up animations, starting
226: * timers, etc.
227: */
228: protected void showNotify() {
229: computeMetrics();
230: }
231:
232: /**
233: * Called when the drawable area of the <code>Canvas</code> has been changed. This method
234: * has augmented semantics compared to Displayable.sizeChanged.
235: * In addition to the causes listed in Displayable.sizeChanged, a size
236: * change can occur on a <code>Canvas</code> because of a change between normal and full-screen modes.
237: * If the size of a <code>Canvas</code> changes while it is actually visible on the display,
238: * it may trigger an automatic repaint request. If this occurs, the call to size Changed will
239: * occur prior to the call to paint. If the <code>Canvas</code> has become smaller, the
240: * implementation may choose not to trigger a repaint request if the remaining
241: * contents of the <code>Canvas</code> have been preserved. Similarly, if the <code>Canvas</code> has become
242: * larger, the implementation may choose to trigger a repaint only for the new region.
243: * In both cases, the preserved contents must remain stationary with respect to the origin
244: * of the <code>Canvas</code>. If the size change is significant to the contents of the <code>Canvas</code>,
245: * the application must explicitly issue a repaint request for the changed areas.
246: * Note that the application's repaint request should not cause multiple repaints,
247: * since it can be coalesced with repaint requests that are already pending.
248: * If the size of a <code>Canvas</code> changes while it is not visible, the implementation
249: * may choose to delay calls to sizeChanged until immediately prior to the call
250: * to showNotify. In that case, there will be only one call to sizeChanged,
251: * regardless of the number of size changes.
252: * An application that is sensitive to size changes can update instance
253: * variables in its implementation of sizeChanged. These updated values
254: * will be available to the code in the showNotify, hideNotify, and paint methods.
255: * @param w the new width in pixels of the drawable area of the <code>Canvas</code>
256: * @param h the new height in pixels of the drawable area of the <code>Canvas</code>
257: */
258: protected void sizeChanged(int w, int h) {
259: computeMetrics();
260: }
261:
262: private int computeYMetrics(int baseY) {
263: phoneNumberY = baseY + labelPadding;
264: messageY = phoneNumberY + inputFieldsHeight + labelPadding;
265: return messageY + inputFieldsHeight;
266: }
267:
268: private void computeMetrics() {
269:
270: final int width = getWidth();
271: final int height = getHeight();
272: final int centerY = height / 2;
273: final int centerX = width / 2;
274: int visibleInputFieldLength = 12;
275: inputFieldsWidth = inputFont.charWidth('X')
276: * visibleInputFieldLength + 2 * borderPadding;
277: if (inputFieldsWidth > width) {
278: inputFieldsWidth = width - 2;
279: }
280: inputFieldsHeight = inputFont.getHeight() + 2 * borderPadding;
281:
282: int componentsHeight = computeYMetrics(0);
283:
284: int newbaseY = (height - componentsHeight) / 4;
285: if (newbaseY > 0) {
286: computeYMetrics(newbaseY);
287: }
288:
289: phoneNumberLabelWidth = inputFont.stringWidth(phoneNumberLabel);
290: messageLabelWidth = inputFont.stringWidth(messageLabel);
291: final int labelWidth = Math.max(phoneNumberLabelWidth,
292: messageLabelWidth);
293: phoneNumberLabelWidth = labelWidth;
294: messageLabelWidth = labelWidth;
295:
296: phoneNumberX = centerX
297: - (phoneNumberLabelWidth + labelPadding + inputFieldsWidth)
298: / 2;
299: if (phoneNumberX < 0) {
300: phoneNumberX = 0;
301: inputFieldsWidth = width - labelWidth - labelPadding - 1;
302: }
303: messageX = centerX
304: - (messageLabelWidth + labelPadding + inputFieldsWidth)
305: / 2;
306: if (messageX < 0) {
307: messageY = 0;
308: inputFieldsWidth = width - labelWidth - labelPadding - 1;
309: }
310: }
311:
312: private static void setColorByState(Graphics g, int baseColor,
313: int hiColor, boolean active) {
314: if (active) {
315: g.setColor(hiColor);
316: } else {
317: g.setColor(baseColor);
318: }
319: }
320:
321: private static void setStyleByState(Graphics g, int baseStyle,
322: int hiStyle, boolean active) {
323: if (active) {
324: g.setStrokeStyle(baseStyle);
325: } else {
326: g.setStrokeStyle(hiStyle);
327: }
328: }
329:
330: /**
331: * Returnd component's color schema.
332: * @return colorSchema
333: */
334: public ColorSchema getColorSchema() {
335: return colorSchema;
336: }
337:
338: /**
339: * Paints this canvas.
340: * @param graphics the <code>Graphic</code> object to be used for rendering the <code>Canvas</code>
341: */
342: protected void paint(Graphics graphics) {
343: //System.out.println("CLIPX: "+g.getClipX()+","+g.getClipY()+","+g.getClipWidth()+","+g.getClipHeight());
344: int width = getWidth();
345: int height = getHeight();
346: //System.out.println("WIdth = "+width+", hei="+height);
347: int centerX = width / 2;
348:
349: getColorSchema().paintBackground(graphics, false);
350:
351: // draw phoneNumber
352: graphics.setFont(inputFont);
353: //g.setColor(textboxBackgroundColor);
354: int x;
355: int y;
356: int w;
357: int h;
358: //x = center - inputFieldsWidth/2;
359: x = phoneNumberX + phoneNumberLabelWidth + labelPadding;
360: y = phoneNumberY; // - inputFieldsHeight + borderPadding;
361: w = inputFieldsWidth;
362: h = inputFieldsHeight;
363: graphics.setColor(0xffffff);
364: graphics.fillRoundRect(x, y, w, h, 6, 6);
365: boolean phoneNumberActive = activeField == ACTIVE_PHONE_NUMBER;
366: setColorByState(graphics, getColorSchema().getColor(
367: Display.COLOR_BORDER), getColorSchema().getColor(
368: Display.COLOR_HIGHLIGHTED_BORDER), phoneNumberActive);
369: setStyleByState(graphics, borderStyle, hiBorderStyle,
370: phoneNumberActive);
371: graphics.drawRoundRect(x, y, w, h, 6, 6);
372: setColorByState(graphics, getColorSchema().getColor(
373: Display.COLOR_FOREGROUND), getColorSchema().getColor(
374: Display.COLOR_HIGHLIGHTED_FOREGROUND),
375: phoneNumberActive);
376: graphics.setClip(x + borderPadding, y + borderPadding, w - 2
377: * borderPadding, h - 2 * borderPadding);
378: graphics.drawString(phoneNumber, x + borderPadding,
379: phoneNumberY + borderPadding, Graphics.LEFT
380: | Graphics.TOP);
381: graphics.setClip(0, 0, width, height);
382: graphics.setColor(getColorSchema().getColor(
383: Display.COLOR_FOREGROUND));
384: graphics.drawString(phoneNumberLabel, phoneNumberX,
385: phoneNumberY + borderPadding, Graphics.LEFT
386: | Graphics.TOP);
387: // draw message
388: boolean messageActive = activeField == ACTIVE_MESSAGE;
389: setColorByState(graphics, getColorSchema().getColor(
390: Display.COLOR_BACKGROUND), getColorSchema().getColor(
391: Display.COLOR_HIGHLIGHTED_BACKGROUND), messageActive);
392: //x = center - inputFieldsWidth/2;
393: x = messageX + messageLabelWidth + labelPadding;
394: y = messageY; // - inputFieldsHeight + borderPadding;
395: w = inputFieldsWidth;
396: h = inputFieldsHeight;
397: graphics.setColor(0xffffff);
398: graphics.fillRoundRect(borderPadding, y + inputFieldsHeight
399: + labelPadding, width - 2 * borderPadding, height
400: - (y + inputFieldsHeight + labelPadding) - 2
401: * borderPadding, 6, 6);
402: setColorByState(graphics, getColorSchema().getColor(
403: Display.COLOR_BORDER), getColorSchema().getColor(
404: Display.COLOR_HIGHLIGHTED_BORDER), messageActive);
405: setStyleByState(graphics, borderStyle, hiBorderStyle,
406: messageActive);
407: graphics.drawRoundRect(borderPadding, y + inputFieldsHeight
408: + labelPadding, width - 2 * borderPadding, height
409: - (y + inputFieldsHeight + labelPadding) - 2
410: * borderPadding, 6, 6);
411: setColorByState(graphics, getColorSchema().getColor(
412: Display.COLOR_FOREGROUND), getColorSchema().getColor(
413: Display.COLOR_HIGHLIGHTED_FOREGROUND), messageActive);
414:
415: graphics.setClip(borderPadding, y + inputFieldsHeight
416: + labelPadding, width - 2 * borderPadding, height
417: - (y + inputFieldsHeight + labelPadding) - 2
418: * borderPadding);
419:
420: int messageLength = message.length();
421: int currentWidth = 0;
422: int startPoint = 0;
423: int line = 1;
424:
425: for (int i = 0; i < messageLength; i++) {
426: char c = message.charAt(i);
427: int cWidth = inputFont.stringWidth("" + c);
428: currentWidth += cWidth;
429: if (currentWidth >= width - 3 * borderPadding) {
430: //go back one char
431: i--;
432: String subMsg = message.substring(startPoint, i);
433: graphics.drawString(subMsg, 2 * borderPadding, y + line
434: * (inputFieldsHeight) + labelPadding
435: + borderPadding, Graphics.LEFT | Graphics.TOP);
436: startPoint = i;
437: currentWidth = 0;
438: line++;
439: }
440: }
441: String subMsg = message.substring(startPoint, messageLength);
442: graphics.drawString(subMsg, 2 * borderPadding, y + line
443: * (inputFieldsHeight) + labelPadding + borderPadding,
444: Graphics.LEFT | Graphics.TOP);
445:
446: graphics.setClip(0, 0, width, height);
447: graphics.setColor(getColorSchema().getColor(
448: Display.COLOR_FOREGROUND));
449: graphics.drawString(messageLabel, messageX, messageY
450: + borderPadding, Graphics.LEFT | Graphics.TOP);
451: }
452:
453: private void moveActiveField(boolean down) {
454: int add = down ? 1 : -1;
455: activeField += add;
456: if (activeField > ACTIVE_MESSAGE) {
457: activeField = ACTIVE_PHONE_NUMBER;
458: } else if (activeField < ACTIVE_PHONE_NUMBER) {
459: activeField = ACTIVE_MESSAGE;
460: }
461: repaint();
462: }
463:
464: private void fireSendEvent() {
465: CommandListener l = getCommandListener();
466: if (l != null) {
467: l.commandAction(SEND_COMMAND, this );
468: }
469: }
470:
471: /**
472: * Called when a key is released.
473: * @param keyCode the key code of the key that was released
474: */
475: protected void keyReleased(int keyCode) {
476: final int gameAction = getGameAction(keyCode);
477: if (inputTextIsActive) {
478: } else {
479: switch (gameAction) {
480: case FIRE:
481: startEditingInputText();
482: return;
483: }
484: }
485: }
486:
487: /**
488: * Called when a key is pressed.
489: * @param keyCode the key code of the key that was pressed
490: */
491: protected void keyPressed(int keyCode) {
492: final int gameAction = getGameAction(keyCode);
493: if (inputTextIsActive) {
494: } else {
495: switch (gameAction) {
496: case LEFT:
497: case DOWN:
498: moveActiveField(true);
499: return;
500: case RIGHT:
501: case UP:
502: moveActiveField(false);
503: return;
504: /*
505: case FIRE:
506: startEditingInputText();
507: return;
508: */
509: }
510: }
511: }
512:
513: private void startEditingInputText() {
514: inputTextIsActive = true;
515: if (useTextBoxForInput) {
516: if (activeField == ACTIVE_PHONE_NUMBER) {
517: phoneBox.setString(phoneNumber);
518: current = phoneBox;
519: } else if (activeField == ACTIVE_MESSAGE) {
520: msgBox.setString(message);
521: current = msgBox;
522: }
523: //System.out.println("inputtextbox: "+getInputTextBox());
524: getDisplay().callSerially(new Runnable() {
525:
526: public void run() {
527: getDisplay().setCurrent(current);
528: }
529: });
530: }
531: }
532:
533: private void stopEditingInputText(boolean confirmChanges) {
534: inputTextIsActive = false;
535: if (useTextBoxForInput) {
536: if (confirmChanges) {
537: if (activeField == ACTIVE_PHONE_NUMBER) {
538: setPhoneNumber(current.getString());
539: } else if (activeField == ACTIVE_MESSAGE) {
540: setMessage(current.getString());
541: }
542: }
543: getDisplay().setCurrent(this );
544: }
545: }
546:
547: /**
548: * Sets component's command listener.
549: * @param listener CommandListener
550: */
551: public void setCommandListener(CommandListener listener) {
552: this .l = listener;
553: }
554:
555: /**
556: * Returns component's command listener.
557: * @return CommandListener
558: */
559: public CommandListener getCommandListener() {
560: return l;
561: }
562:
563: /**
564: * Sets component's background color.
565: * @param color background color
566: */
567: public void setBGColor(int color) {
568: ((DefaultColorSchema) colorSchema).setBGColor(color);
569: }
570:
571: /**
572: * Sets component's foreground color.
573: * @param color foreground color
574: */
575: public void setFGColor(int color) {
576: ((DefaultColorSchema) colorSchema).setFGColor(color);
577: }
578:
579: /**
580: * Returns dispaly.
581: * @return Display
582: */
583: private Display getDisplay() {
584: return display;
585: }
586:
587: /**
588: * Indicates that a command event has occurred on Displayable d.
589: * @param c a Command object identifying the command. This is either
590: * one of the applications have been added to Displayable with addCommand(Command)
591: * or is the implicit SELECT_COMMAND of List.
592: * @param d the Displayable on which this event has occurred
593: */
594: public void commandAction(Command c, Displayable d) {
595: if (c.equals(SEND_COMMAND)) {
596: if (sendAutomatically) {
597: new Thread(new Runnable() {
598:
599: public void run() {
600: try {
601: sendSMS();
602: } catch (IOException ex) {
603: ex.printStackTrace();
604: }
605: }
606: }).start();
607: }
608: }
609: if (l != null) {
610: l.commandAction(c, d);
611: }
612: }
613:
614: private class InputTextBox extends TextBox implements
615: CommandListener {
616:
617: private final Command CONFIRM_COMMAND = new Command("OK",
618: Command.OK, 1);
619: private final Command CANCEL_COMMAND = new Command("Cancel",
620: Command.CANCEL, 1);
621:
622: public InputTextBox(String title, int maximumChars,
623: int constraints) {
624: super (title, null, maximumChars, constraints);
625: setCommandListener(this );
626: addCommand(CONFIRM_COMMAND);
627: addCommand(CANCEL_COMMAND);
628: }
629:
630: /**
631: * Indicates that a command event has occurred on Displayable d.
632: * @param c a <code>Command</code> object identifying the command. This is either
633: * one of the applications have been added to <code>Displayable</code> with addCommand(Command)
634: * or is the implicit <code>SELECT_COMMAND</code> of List.
635: * @param d the <code>Displayable</code> on which this event has occurred
636: */
637: public void commandAction(Command c, Displayable d) {
638: /*
639: System.out.println("Command axtion from input text box: command="+c.getLabel()+", d="+d);
640: try {
641: throw new RuntimeException("test");
642: } catch (RuntimeException e) {
643: e.printStackTrace();
644: }
645: */
646: if (d == this ) {
647: if (c == CONFIRM_COMMAND) {
648: // confirm
649: stopEditingInputText(true);
650: } else if (c == CANCEL_COMMAND) {
651: // cancel
652: stopEditingInputText(false);
653: }
654: }
655: }
656: }
657: }
|