Ads

Wednesday, October 9, 2013

Android Module Development - Part 2

Last week I started  a series of  articles regarding Android Module Development. Toady I am going to continue with next topic which is Using event listeners and callbacks in Module
  1. Understanding methods, properties, constants and module life cycle
  2. Using event listeners and callbacks in Module
  3. Converting native control into Titanium view
  4. Accessing module and app resources
  5. Using XML layouts in modules
Callbacks

During asynchronous operation, if we want to pass the data to javascript layer we have to use callbacks. It will call the particular method to send the data back to javascript layer. For example like onError, onSuccess. Callback will be invoked only once.

Example

Lets consider AudioRecorder module here I am using success and error callbacks, which will be called either on success or failure of audio recording process. It will be invoked only once.

In Module
// initializing callbacks
private KrollFunction successCallback = null;
private KrollFunction errorCallback = null;

// method to invoke success callback
private void sendSuccessEvent(String filepath) {
    if (successCallback != null) {
        HashMap event = new HashMap();
        event.put("filePath", filepath);
        event.put("fileName", outPutFileName);

        // Fire an event directly to the specified listener (callback)
        successCallback.call(getKrollObject(), event);
    }
}

// method to invoke error callback
private void sendErrorEvent(String message) {
    if (errorCallback != null) {
        HashMap event = new HashMap();
        event.put("message", message);

        // Fire an event directly to the specified listener (callback)
        errorCallback.call(getKrollObject(), event);
    }
}

// method to register callbacks, which all passed from javascript layer
@Kroll.method
public void registerCallbacks(HashMap args) {
    Object callback;

    // Save the callback functions, verifying that they are of the correct
    // type
    if (args.containsKey("success")) {
        callback = args.get("success");
        if (callback instanceof KrollFunction) {
            successCallback = (KrollFunction) callback;
        }
    }

    if (args.containsKey("error")) {
        callback = args.get("error");
        if (callback instanceof KrollFunction) {
            errorCallback = (KrollFunction) callback;
        }
    }
}

@Kroll.method
public void startRecording(HashMap args) {
    if (isRecording) {
        // calling sendErrorEvent to invoke error callback
        sendErrorEvent("Another audio record is inprogress");
    } else {
        recorder = null;
        // this method used to register success and error callbacks
        registerCallbacks(args);
        recorder = new MediaRecorder();

        recorder.setOutputFile(outputFileName);
        recorder.setOnErrorListener(errorListener);
        recorder.setOnInfoListener(infoListener);

        try {
            recorder.prepare();
            recorder.start();
            isRecording = true;
        } catch (IllegalStateException e) {
            System.out.println("@@## Error1 e = " + e);
            e.printStackTrace();
            isRecording = false;
            // calling sendErrorEvent to invoke error callback
            sendErrorEvent(e.toString());
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("@@## Error3 e = " + e);
            isRecording = false;
            // calling sendErrorEvent to invoke error callback
            sendErrorEvent(e.toString());
        }
    }
}
In Javascript
var audioRecorder = require("titutorial.audiorecorder");

audioRecorder.startRecording({
    outputFormat: audioRecorder.OutputFormat_THREE_GPP,
    audioEncoder: audioRecorder.AudioEncoder_AMR_NB,
    directoryName: "testdir",
    fileName: "testfile",
    maxFileSize: 7000,
    success: function (e) {
        alert("success => " + e.filePath);
    },
    error: function (d) {
        alert("error => " + e.message);
    }
});
For more detail refer AudioRecorder module source code and this blog post

Event listeners

Registered  event listeners will be called whenever the event occur.

Example


Lets consider Ratingbar module here when user change the rating, the new rating value passed  to javascript layer via event listener. It may fired so many times.

In Module
private boolean hasListenerChange = false;
//checks whether "change" event listener added to proxy or not
hasListenerChange = proxy.hasListeners("change");

ratingBar.setOnRatingBarChangeListener(new OnRatingBarChangeListener() {
 @SuppressWarnings("deprecation")
 public void onRatingChanged(RatingBar ratingBar, float rating, boolean fromUser) {
  //if listener added to proxy, it pass the data to javascript layer
  if (hasListenerChange) {
   KrollDict props = new KrollDict();
   props.put("rating", rating);
   //fires the change event
   proxy.fireEvent("change", props);
  }
 }
});
In Javascript
var ratingbarProxy = require('titutorial.ratingbar');

var ratingBar = ratingbarProxy.createRatingBar({
    top : '30dp',
    left:15,
    rating : 2,
    stars : 6,
    stepSize : 1.5,
    isIndicator : false
});
win.add(ratingBar);

ratingBar.addEventListener('change', function(e) {
    ratingValue.text = "Rating Value : "+e.rating.toString();
});

For more detailed example refer Ratingbar module source code

Continue with part 3

4 comments:

  1. Michael De GiovanniOctober 25, 2013 at 2:50 PM

    Great work with these tutorials!

    I am very new to creating android modules and am trying something out so that i can use bluetooth: My question is when you have an \Activity how do you send back the result to the application? The scenario is as follow:

    I have BluetoothModule:

    @Kroll.method
    public void startBluetooth(){
    Activity activity = TiApplication.getAppCurrentActivity();
    activity.startActivity(new Intent("com.test.bt2.BLUETOOTH"));
    }

    I have setup the TiModule.xml to communicate with the activity and in Bluetooth class which extends Activity i have an on create method which checks if bluetooth is enabled:

    if (!mBluetoothAdapter.isEnabled()) {
    Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
    }

    together with that I have the onActivityResult:

    protected void onActivityResult(int requestCode, int resultCode, Intent data){
    if (resultCode == RESULT_OK){
    Log.i(LCAT, "Connected");
    } else {
    Log.e(LCAT, "Something went wrong");
    }
    }



    The thing is i have no clue on how the result from the activityResult is pushed back to Titanium? How would you go about it? thanks

    ReplyDelete
  2. Hi Michael,

    FFor that you have to implement the TiActivityResultHandler interface and launchActivityForResult (instead of startActivityForResult ) here you can find the clear example https://bitbucket.org/prakashmcam/voice2textmodule/src/8901e4ead35504eda9ca499b4708ecc07a4dc007/src/learappcelerator/voice2text/Voice2textModule.java?at=master

    let me know if have anymore questions.

    ReplyDelete
  3. Michael De GiovanniOctober 25, 2013 at 3:27 PM

    Brilliant, thanks it worked perfectly . Keep up the good work with these tutorials as they are really helpful

    ReplyDelete
  4. You are welcome. Thanks for the appreciation.

    ReplyDelete