Wednesday, July 28, 2010

Google Ion - Android 2.1 - cyanogenmod

Thanks to cyanogenmod, my Google Ion is happily running 2.1 basically following these directions

Happily means it runs smoother with less wait time for many things like viewing mail, opening a new chapter in a book, menus etc.

The steps were:

1) setup fastboot on my mac

2) obtain a suitable recovery image (to flash the cynogenmod ROM) I used clockwork-recovery-image

RA-sapphire-v1.7.0 is also said to work

3) downloaded and copied the cynogenmod ROM to my sdcard (and the google apps one too) after verifying their md5sum

4) Enter fastboot mode by holding down the back key while booting. Then, from a terminal on my mac, run:

./fastboot erase cache
./fastboot erase userdata
./fastboot flash recovery recovery-clockwork-2.5.0.1-sapphire.img

5) reboot and hold down the HOME key while rebooting to enter recovery mode

6) ran a backup from the recovery options

7) choose to load a zip from the sdcard (recovery option) and select the update-cm-5.0.8-DS-signed.zip

8) rebooted into 2.1 and set up my wifi (I've no data plan at the moment)

9) rebooted into recovery mode (HOME key) again and installed the google apps by choosing-a-zip-from-the-sdcard (recovery option)

gapps-ds-ERE36B-signed.zip

10) rebooted and set up my google account

Notes:
*I don't know the effect of installing the cynogenmod ROM and google apps at the same time.
*My radio image was HTC standard from 1.6
*My menus were all Japanese but I changed that to English (somehow). I'm in Tokyo so it makes sense I guess...

Thursday, July 22, 2010

GXT MVC Code Splitting on GAE (AppEngine)

This is an example of how I used GWT code splitting with GXT and ran it on Google AppEngine.

There seems to be some misunderstanding that controllers need be registered in advance which draws in all the code and disallows code splitting. This is not the case and controllers can easily be registered when and only when requested.

Code is available via GIT and the simple demo deployed on appengine.

This is just a simple rework for the GXT mail example.

First, we need the controllers we will use later.

TaskController taskController;
ContactController contactController;

Then, we add a listener to initialize them the first time they are used.

public void onModuleLoad() {
....

final Dispatcher dispatcher = Dispatcher.get();
dispatcher.addDispatcherListener(new DispatcherListener() {

@Override
public void beforeDispatch(final MvcEvent mvce) {

switch (CONTROLLERS.getControllerByEvent(mvce.getAppEvent().getType())) {


CONTROLLERS is just an enum set to return the controller for any event. Since there may be more than one event per controller, you could conceivably end up with something like the following which would work in a switch statement to do what you want.

public enum CONTROLLERS {
APP(AppEvents.Login),
APP2(AppEvents.Error),
APP3(AppEvents.Init),
//MAIL(AppEvents.Init),
MAIL2(AppEvents.NavMail),
MAIL3(AppEvents.ViewMailItem),
MAIL4(AppEvents.ViewMailItems),
MAIL5(AppEvents.ViewMailFolders),
TASK(AppEvents.NavTasks),
CONTACT(AppEvents.NavContacts);

In real apps I use GWT's History Mechanism
and another enum that lets me look up both the AppEvents and also the appropriate controller. But that enum-fu is best left out of this simple example.
Anyway, if the controller is null when called, we use GWT.runAsync to fetch it:

switch (CONTROLLERS.getControllerByEvent(mvce.getAppEvent().getType())) {
case CONTACT:

if (contactController == null) {
mvce.setCancelled(true);

GWT.runAsync(new RunAsyncCallback() {

@Override
public void onFailure(Throwable reason) {
//log
}

@Override
public void onSuccess() {
contactController = new ContactController();
dispatcher.addController(contactController);
GWT.log("fetched and added contactController");
Dispatcher.forwardEvent(mvce.getAppEvent().getType(), mvce.getAppEvent().getData());
}

});

}
break;

... etc. ect. for the other controllers

...For AppEngine, the trick was cancelling the dispatch and then re-calling it after the code was fetched. It worked on the development server in eclipse without recalling the event, but failed silently when deployed.


if (contactController == null) {
mvce.setCancelled(true);

...
Dispatcher.forwardEvent(mvce.getAppEvent().getType(), mvce.getAppEvent().getData());


I can see the deferred code being fetched in firebug and if I run the reports on a real project when compiling GWT, can see a difference in the initial size of the download. There are portions of my app that many users will never use, so it makes sense to do it this way.

Right now for an app .6 MB, .4 is initial and .2 is deferred (and not likely to be used by the majority of users). Keep in mind gxt2.x was designed before runAsync came out and from their mailing lists seems targeted to be redone for 3.0 (well, if "nothing can be done before v3" means targeted -- I don't really know how it'll change).

Saturday, February 28, 2009

reverse GWT via DWR

This is how I've set upDWR3 to push data out to GWT 1.6. GWT1.6 is needed for the new project structure and DWR3 has json support. Both of these simplify matters greatly.

I was only able to get reverse ajax running by using techniques described in using GWT for mashups. Otherwise, GWT wasn't finding dwr even though the scripts were loaded in the main html page. This is likely due to
GWT application code being loaded in a hidden iframe.

Working from the javascript-chat example (reverse ajax) in DWR3, I modified the sample to return JSON.

public void addMessage(String text, final String callback) {


if (text != null && text.trim().length() > 0) {
messages.addFirst(new Message(text));
while (messages.size() > 10) {
messages.removeLast();
}
}

Browser.withCurrentPage(new Runnable() {
String s;
public void run() {
try {
s=JsonUtil.toJson(messages) ;
} catch (IOException e) {

e.printStackTrace();
}

ScriptSessions.addFunctionCall(callback, s);

}
});
}


Then in my onModuleLoad method for GWT, I set up DWR to load in an Iframe:

    public void onModuleLoad() {

// callbackName = reserveCallback();
callbackName = "chatGroup1";
Log.info("onModuleLoad callbackName: " + callbackName);
addDWRScripts("/reverseGWT/dwr/engine.js");
addDWRScripts("/reverseGWT/dwr/util.js");
addDWRScripts("/reverseGWT/dwr/interface/JavascriptChat.js");
setActiveReverseAjax();
setupChat(this, callbackName);
//Log.info(" setupChat: finshed");
setupChatBox(callbackName);

}



public native void addDWRScripts(String url) /*-{

var elem = document.createElement("script");
elem.setAttribute("language", "JavaScript");
elem.setAttribute("src", url);
document.getElementsByTagName("body")[0].appendChild(elem);

}-*/;


public native void setActiveReverseAjax() /*-{

try{
// a delay is needed or dwr won't have been set up properly when this is called
setTimeout("dwr.engine.setActiveReverseAjax(true)",2500);

} catch(err){
alert("setActiveReverseAjax failed :YOU WILL not be receiving pushed updates from the server though you may be able to send data and receive updates only just after data is sent. You can try increasing the delay in setActiveReverseAjax() in SampleChat.java --> Reason is: "+err );

}
}-*/;




To send a message, I hookup up a button listener to call DWR via JSNI.

Button b = new Button();

b.addListener(Events.OnClick, new Listener<BaseEvent>() {
public void handleEvent(BaseEvent be) {

addMessageScript(text.getValue(), callbackName);

}

});


    public native void addMessageScript(String text, String callbackName) /*-{

try{
//this is the call to DWR set up by addDWRScripts("/reverseGWT/dwr/interface/JavascriptChat.js");
// this results in a call to the addMessage(String text, final String callback) method in JavascriptChat.java

JavascriptChat.addMessage(text,callbackName);

}catch(err){
alert("addMessageScript err: "+err);
}
}-*/;


A JavaScript function has already been set up in the GWT onModuleLoad() method as well which will take the JSON formatted String that is returned from DWR and pass it into my GWT code.

    public native static void setupChat(SampleChat sc, String callback) /*-{

window[callback] = function(someData) {//Ljava/lang/String;
sc.@com.sample.gwt.client.SampleChat::gwtChat(Ljava/lang/String;)(someData);
// sc.@com.sample.gwt.client.SampleChat::gwtChat(Lcom/google/gwt/core/client/JavaScriptObject;)(someData);
}


}-*/;



From there, the JSON formatted String is parsed and the message retrieved:

    public void gwtChat(String jss) {
String s = jss;

JSONObject json = (JSONObject) JSONParser.parse(jss);

Label l = new Label();

try {
s = json.isObject().get("reply").isArray().get(0).isObject().get("text").isString().stringValue();

} catch (Exception e) {

e.printStackTrace();
}


Now data is being pushed from the server when other users enter a message, and you can set up your GWT client to display this pushed data how you'd like.


Notes:
Source code for this example is at reverseGWT . A deployable .war is included. To build from src, please modify build.xml to include:

<pathelement location="/your-path/gwt-user.jar" />
<pathelement location="/your-path/gwt-dev-mac.jar" />



The example is set up using GXT and gwt-log . The example really doesn't depend on using GXT in any way, and straight GWT would be fine. I just happened to be using it and so have set it up that way. Please consult the GXT, gwt-log, DWR and GWT sites for license information. This is just a proof of concept and not intended to be distributed as a usable product.

The chat example from DWR isn't necessarily thread safe as written and should probably use something from java.util.concurrent to handle the messages but this is more a post on configuration than setting up a fully functional chat system.



Followers