views:

1085

answers:

1

Does the native Blackberry API allow for the modification of any messaging interface?

For example, I'd like to be able to build an app that added a button at the bottom of a received message which says 'translate this'. I've heard that this kind of thing is possible using J2ME plus the native BlackBerry API. Can this be done for all kinds of messages, eg SMS, email and BB messaging?

The translation aspect is not relevant, it just provides the context for the kind of feature I'm after.

A: 

Try Google Translation API, should be no problem to manage correct translation over http requests:

For Flash developers, and those developers that have a need to access the AJAX Language API from other Non-Javascript environments, the API exposes a simple RESTful interface. In all cases, the method supported is GET, and the response format is a JSON encoded result with embedded status codes. For google.language.translate, the POST method is available. Applications that use this interface must abide by all existing terms of use. An area to pay special attention to relates to correctly identifying yourself in your requests. Applications MUST always include a valid and accurate http referer header in their requests. In addition, we ask, but do not require, that each request contains a valid API Key. By providing a key, your application provides us with a secondary identification mechanism that is useful should we need to contact you in order to correct any problems.

And talking about integration of such service, I see two alternatives:

  • you can handle receive message event and use global dialog to choose translation settings, then again show result in global dialog
  • you can integrate menu item into native message applications to translate selected item, and then use global dialog to show translation results

UPDATE I have finally managed to put my hands on this nice idea :)
Here's the deal: there will be two applications: main and service. Main app can be launched standalone and is used for translation. Service will be responsible for handling messages and triggering main application for translation.

Main Application code:

public class TranslatorApp extends UiApplication {
    public TranslatorApp(String text) {
        pushScreen(new TranslationScreen(text));
    }

    public TranslatorApp() {
        pushScreen(new TranslationScreen());
    }

    public static void main(String[] args) {
        TranslatorApp app = null;
        if (null == args || args.length == 0) {
            app = new TranslatorApp();
        } else {
            app = new TranslatorApp(args[0]);
        }
        app.enterEventDispatcher();
    }
}

class TranslationScreen extends MainScreen implements TranslateCallback {
    final static int TMODE_MANUAL = 0;
    final static int TMODE_INTEGRATED = 1;
    Translator mTranslator = null;
    BasicEditField mOriginalTextEdit = new BasicEditField("", "");
    LabelField mOriginalTextLabel = new LabelField("");
    LabelField mTranslatedTextLabel = new LabelField("");
    String mOriginalText = "";
    int mTranslationMode = TMODE_MANUAL;

    public TranslationScreen() {
        mTranslationMode = TMODE_MANUAL;
        add(mOriginalTextEdit);
        add(mTranslatedTextLabel);
        mTranslator = new Translator(this);
    }

    protected void makeMenu(Menu menu, int instance) {
        super.makeMenu(menu, instance);
        if (mTranslationMode == TMODE_MANUAL) {
            menu.add(new MenuItem("translate", 0, 0) {
                public void run() {
                    mOriginalText = mOriginalTextEdit.getText();
                    translate();
                }
            });
        }
    }

    public TranslationScreen(String text) {
        mTranslationMode = TMODE_INTEGRATED;
        mOriginalText = text;
        mOriginalTextLabel.setText(mOriginalText);
        add(mOriginalTextLabel);
        add(mTranslatedTextLabel);
        mTranslator = new Translator(this);
        translate();
    }

    public void translationComplete(final String translatedText) {
        UiApplication.getUiApplication().invokeLater(
            new Runnable(){
                public void run() {
                mTranslatedTextLabel.setText(translatedText);   
        }});
    }

    public void exception(Exception ex) {
        Dialog.alert(ex.getMessage());
    }

    private void translate() {
        if (null != mOriginalText
                && 0 != mOriginalText.trim().length()) {
            mTranslator.translate(mOriginalText);
        } else {
            Dialog.alert("text is empty");
        }
    }
}

interface ExceptionHandler {
    public void exception(Exception ex);
}

interface TranslateCallback extends ExceptionHandler {
    public void translationComplete(String translatedText);
}

class Translator {
    TranslateCallback mCallback;
    static final String url = "http://ajax.googleapis.com/ajax/"+
            "services/language/translate?";

    //remember to change referrer :)
    static final String referrerUrl = "http://stackoverflow.com";

    public Translator(TranslateCallback callback) {
        mCallback = callback;
    }

    public void translate(final String text) {
        Runnable translation = new Runnable() {
            public void run() {
                String result = "";
                StringBuffer sb = new StringBuffer();
                sb.append(url);
                sb.append("v=1.0&");
                sb.append("q=");
                sb.append(URLencode(text));
                sb.append("&");
                sb.append("langpair=en|uk");
                String requestUrl = sb.toString();
                try {
                    HttpConnection connection = (HttpConnection) Connector
                            .open(requestUrl);
                    connection.setRequestMethod("POST");
                    connection.setRequestProperty("Referer", referrerUrl);
                    result = new String(IOUtilities.streamToBytes(connection
                            .openInputStream()), "UTF-8");
                } catch (IOException e) {
                    mCallback.exception(e);
                }
                translationEnd(result);
            }
        };

        Thread t = new Thread(translation);
        t.start();
    }

    public static String URLencode(String s)
    {
        if (s!=null) {
            StringBuffer tmp = new StringBuffer();
            int i=0;
            try {
                while (true) {
                    int b = (int)s.charAt(i++);
                    if ((b>=0x30 && b<=0x39) || (b>=0x41 && b<=0x5A) 
                        || (b>=0x61 && b<=0x7A)) {
                        tmp.append((char)b);
                    }
                    else {
                        tmp.append("%");
                        if (b <= 0xf) tmp.append("0");
                        tmp.append(Integer.toHexString(b));
                    }
                }
            }
            catch (Exception e) {}
            return tmp.toString();
        }
        return null;
    }

    private void translationEnd(String result) {
        String key = "translatedText";
        int index = result.indexOf(key)+key.length()+"\"".length();     
        int beginIndex = result.indexOf("\"", index + 1)+1;
        int endIndex = result.indexOf("\"", beginIndex);
        String traslatedText = result.substring(beginIndex, endIndex);      
        mCallback.translationComplete(traslatedText);
    }
}

And here goes Service code:

public class TranslatorSrvc extends Application implements MessageProcessor {
    SMSListener mSMSListener = null;
    EmailListener mEmailListener = null;
    PIMListener mPIMListener = null;
    public TranslatorSrvc() {
        // SMS
        mSMSListener = new SMSListener(this);

        // Email
        mEmailListener = new EmailListener(this);

        //PIM
        mPIMListener = new PIMListener(this);
    }

    public static void main(String[] args) {
        TranslatorSrvc app = new TranslatorSrvc();
        app.enterEventDispatcher();
    }

    private void showMessage(String message) {
        synchronized (Application.getEventLock()) {
            Dialog dlg = new Dialog(Dialog.D_OK, message, Dialog.OK, null,
                    Manager.FIELD_HCENTER);
            Ui.getUiEngine().pushGlobalScreen(dlg, 1, UiEngine.GLOBAL_QUEUE);
        }
    }

    public void processMessage(String text) {
        try {
            ApplicationManager.getApplicationManager().launch(
                    "SOTranslatorApp?" + text);
        } catch (ApplicationManagerException e) {
            exception(e);
        }
    }

    public void exception(Exception ex) {
        showMessage(ex.getMessage());
    }
}

interface ExceptionHandler {
    public void exception(Exception ex);
}

interface MessageProcessor extends ExceptionHandler {
    public void processMessage(String text);
}

abstract class AMessageListener {
    MessageProcessor mProcessor;

    public AMessageListener(MessageProcessor processor) {
        mProcessor = processor;
    }
}

SMS Listener code:

import java.io.IOException;

import javax.microedition.io.Connector;
import javax.wireless.messaging.Message;
import javax.wireless.messaging.MessageConnection;
import javax.wireless.messaging.MessageListener;
import javax.wireless.messaging.TextMessage;

public class SMSListener extends AMessageListener implements MessageListener {
    MessageConnection mSMSConnection = null;
    boolean mSMSReceiveDone = false;
    Reader mSMSReader = null;

    public SMSListener(MessageProcessor processor) {
        super(processor);
        try {
            mSMSConnection = (MessageConnection) Connector.open("sms://:0");
            mSMSConnection.setMessageListener(this);
            mSMSReader = new Reader();
            new Thread(mSMSReader).start();
        } catch (IOException e) {
            mProcessor.exception(e);
        }
    }

    public void notifyIncomingMessage(MessageConnection conn) {
        if (mSMSConnection == conn) {
            mSMSReader.handleMessage();
        }
    }

    // Isolate blocking I/O on a separate thread, so callback
    // can return immediately.
    class Reader implements Runnable {
        private int pendingMessages = 0;

        // The run method performs the actual message reading.
        public void run() {
            while (!mSMSReceiveDone) {
                synchronized (this) {
                    if (pendingMessages == 0) {
                        try {
                            wait();
                        } catch (Exception e) {
                            mProcessor.exception(e);
                        }
                    }
                    pendingMessages--;
                }
                try {
                    Message mess = mSMSConnection.receive();
                    processSMSMessage(mess);
                } catch (IOException ioe) {
                    mProcessor.exception(ioe);
                }
            }
        }

        public synchronized void handleMessage() {
            pendingMessages++;
            notify();
        }
    }

    public void processSMSMessage(Message mess) {
        if (mess instanceof TextMessage) {
            TextMessage txtMessage = (TextMessage) mess;
            String messageText = txtMessage.getPayloadText();
            mProcessor.processMessage(messageText);
        }
    }
}

Email Listener code:

import net.rim.blackberry.api.mail.Session;
import net.rim.blackberry.api.mail.event.FolderEvent;
import net.rim.blackberry.api.mail.event.FolderListener;

public class EmailListener extends AMessageListener implements FolderListener {
    public EmailListener(MessageProcessor processor) {
        super(processor);
        Session.getDefaultInstance().getStore().addFolderListener(this);
    }

    public void messagesAdded(FolderEvent e) {
        String text = e.getMessage().getBodyText();
        mProcessor.processMessage(text);
    }

    public void messagesRemoved(FolderEvent e) {
    }
}

PIM Listener code (unfortunately not tested, would be grateful for feedbacks!):

import java.io.UnsupportedEncodingException;

import net.rim.blackberry.api.blackberrymessenger.BlackBerryMessenger;
import net.rim.blackberry.api.blackberrymessenger.Message;
import net.rim.blackberry.api.blackberrymessenger.Session;
import net.rim.blackberry.api.blackberrymessenger.SessionListener;
import net.rim.blackberry.api.blackberrymessenger.SessionRequestListener;
import net.rim.device.api.system.ApplicationDescriptor;

public class PIMListener extends AMessageListener implements
        SessionRequestListener, SessionListener {
    ApplicationDescriptor mAppDescr = ApplicationDescriptor
            .currentApplicationDescriptor();

    public PIMListener(MessageProcessor processor) {
        super(processor);
        BlackBerryMessenger msgr = BlackBerryMessenger.getInstance();
        msgr.addSessionRequestListener(this, mAppDescr);
    }

    public void sessionRequestAccepted(Session session) {
        session.addListener(this, mAppDescr);
    }

    public void messageReceived(Session session, Message message) {
        try {
            mProcessor.processMessage(new String(message.getData(), "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            mProcessor.exception(e);
        }
    }

    public void messageSent(Session session, Message message) {
    }

    public void sessionClosed(Session session) {
    }

    public void messageDelivered(Session session, Message message) {
    }

    public void messageQueuedForSend(Session session, Message message) {
    }
}
Max Gontar
Sorry, maybe my question wasn't clear enough. The translation side of it is not particularly relevant. I'd like to know whether the various messaging features of the BlackBerry OS can be modified via an app.
humble coffee
sure, see update
Max Gontar
Ok cool, so it is possible. Do you know if that can be done with all the types of messages that I mention in the question?
humble coffee
To handle receive event use http://www.blackberry.com/developers/docs/4.6.0api/javax/wireless/messaging/MessageListener.html (there is sms sample, can't tell about other types)
Max Gontar
and to handle new mail message you can use http://www.blackberry.com/developers/docs/4.5.0api/net/rim/blackberry/api/mail/event/FolderListener.html
Max Gontar
and finally you can use http://www.blackberry.com/developers/docs/4.5.0api/net/rim/blackberry/api/blackberrymessenger/BlackBerryMessenger.html to add app as a service and register SessionListener (to receive income message event)
Max Gontar
Wow, that's quite a comprehensive answer now. I was just logging onto accept your answer, and that was before you had posted the code. Thanks heaps.
humble coffee
You're welcome!
Max Gontar