Lessons learned : Appcelerator

A couple of points learnt while writing an Android application using the excellent titanium appcelerator.

  • Determining intent type:

var intentType=Ti.Android.currentActivity.getIntent().getType();

intentType will then hold strings such as:

‘android.intent.action.MAIN’: an application has been opened directly, not via a share or view intent

‘android.intent.action.SEND’: also known as the share intent, since this is the action that gets broadcast when a user would like to “share” a file

‘android.intent.action.VIEW’: used when trying to view a file via for example, browsers or email clients

  • Show your application as a choice when a user wants to preview a document via email:

Add the following to your AndroidManifest.xml file under the ../platform/android folder:

<intent-filter>
          <data android:scheme=”content” />
          <action android:name=”android.intent.action.VIEW” />
          <category android:name=”android.intent.category.DEFAULT” />
          <category android:name=”android.intent.category.BROWSABLE” />
          <!– open any mime type file –>
          <data android:mimeType=”application/*” />
          <data android:mimeType=”audio/*” />
          <data android:mimeType=”image/*” />
          <data android:mimeType=”message/*” />
          <data android:mimeType=”multipart/*” />
          <data android:mimeType=”text/*” />
          <data android:mimeType=”video/*” />
</intent-filter>

The important note here is the intent filter containing the VIEW action and the content scheme. In appcelerator, you can then access the file passed via the intent by using:

var currentActivity = Ti.Android.currentActivity.getIntent();

var passedData = currentActivity.getData();

if(passedData !== null){
          var tmpFile = Ti.Filesystem.getFile(String(passedData));
          var fileData = Ti.Filesystem.getFile(Ti.Filesystem.getTempDirectory() + tmpFile.name);
          tmpFile.copy(fileData.nativePath); //the copy can be handled like any other normal file

}

Note how it’s necessary to copy the file to a temporary object, you are not allowed to manipulate the file returned by the intent directly.

  • Show your application as a choice when a user wants to “Share” a document:

Add the following to your AndroidManifest.xml file under the ../platform/android folder:

<intent-filter>
        <action android:name=”android.intent.action.SEND” />
          <category android:name=”android.intent.category.DEFAULT”/>
          <data android:mimeType=”application/*” />
          <data android:mimeType=”audio/*” />
          <data android:mimeType=”image/*” />
          <data android:mimeType=”message/*” />
          <data android:mimeType=”multipart/*” />
          <data android:mimeType=”text/*” />
          <data android:mimeType=”video/*” />
</intent-filter>

Note that the action is now SEND and there is no content scheme. If you try to include the content scheme as we did in the previous example, your application will not show up as a valid choice in the share list. However, as a consequence of this omission, we now cannot use the getData() method as we did in the previous example. Instead, this necessitates using the EXTRA_STREAM android variable, like so:

var iname = Ti.Android.EXTRA_STREAM;
var currentActivity = Ti.Android.currentActivity.getIntent();

if (currentActivity && currentActivity.hasExtra(iname)) {
          var fileData = currentActivity.getBlobExtra(iname);

          //you can then treat “fileData” as usual…

}

  • Retrieving the file name of via an “ACTION_SEND” intent:

Extending the above point, since we cannot use getData(), we also need an alternative to get the filename of a file that has been passed via a “SEND” intent. This can be done like so:

var currentActivity = Ti.Android.currentActivity.getIntent();

var fileURI = currentActivity.getStringExtra(iname);

if (fileURI !== null){
          var absFileName=fileURI.split(‘/’);

          //lets get the relative file name for kicks…
          fileName = fileURI.split(‘/’)[absFileName.length – 1];
          alert(fileName);
}

  • Dont forget that when using intents, android passes files via content URIs not absolute file names

As shown in various other places on the web, you need to first change the content URI into an actual filesystem object (the absolute file). Here’s my own example:

var iname = Ti.Android.EXTRA_STREAM;

var fileURI = currentActivity.getStringExtra(iname);

var tmpFile = Ti.Filesystem.getFile(fileURI);
var fileData = Ti.Filesystem.getFile(Ti.Filesystem.getTempDirectory() + tmpFile.name);
tmpFile.copy(fileData.nativePath); //the copy can be handled like any other normal file

var xhr = Titanium.Network.createHTTPClient({
username:user,
password:pass
});

xhr.onload = function(e) {
Ti.UI.createAlertDialog({
title:’Success’,
message:’File has been successfully uploaded to the Mobile Uploads folder’
}).show();
};

var url = ‘https://’+host+’/~’+ user +’/Mobile Uploads/’ + fileName;
xhr.open(‘PUT’,url);

xhr.send(fileData);

  • Note on uncommon HTTP verbs (DELETE / PUT)

The titanium createHTTPclient allows you to use HTTP verbs apart from GET / POST… such as the webdav verbs delete and put, pretty much as you normally would. However, note that when using the PUT verb in the above example, I passed the file object (fileData) directly into xhr.send, without specifying any parameter names or curly brackets as is needed for POST requests.

  • Note on table views and scrolling views:

You may notice some extremely poor performance when trying out table views. For example, the screen will scroll very slowly / lagging, or events will not fire (you try to swipe, but the screen will not scroll). Make sure that your table view is not within a scrolling view. Table views are inherently scroll-able, so putting them inside a scrolling view is a bad idea, since both the table view and scrolling view will fire, leading to disasters…. Add the table to a normal view.

References:

http://developer.appcelerator.com/question/122035/current-activitys-intent—getdata-returns-null

http://stackoverflow.com/questions/17413525/android-how-to-open-email-attachment-with-titanium-2-1-3

https://wiki.appcelerator.org/display/guides/Android+Intent+Cookbook