Making Android Intents Useful

You are a titanium developer, which means you are a cross-platform native developer. It is important that you understand
the native capabilities of each platform so that you can leverage them where it makes sense. You will have better performing apps
and deliver a experience that your users expect. (I won’t get started on my opinion of mocking Action Bars with views…)

One powerful capability that Android offers are Intents.
It facilitates inter-application messaging between the application requiring an Intent to be fulfilled and
those applications that are listening for them. The question of whether to use an Intent to enhance your application
or build that feature yourself is a good one and will depend on the experience you are looking to offer.

While Titanium makes creating Intents simple, there are some gaps. We will cover it in this article.

Sound File Example

The following intent can be used to retrieve an existing audio file.

var intent = Ti.Android.createIntent({
  action: Ti.Android.ACTION_GET_CONTENT,
  type: "audio/*"
});

In order to start the activity and retrieve the result you need to do the following:

var activity = win.getActivity();
activity.startActivityForResult(intent, function(e) {
  // RESULT_OK confirms the done was selected and not cancelled.
  if (e.resultCode == Ti.Android.RESULT_OK) {
    var result = e.intent.data;
  }
});

Nice, But…

So far so good and you could have got all that from the Titanium and Android docs.
The issue is that the e.intent.data returns a url. While some applications like Dropbox
return a file:// url, others return a content:// url. Content urls are not supported throughout the
Titanium SDK. Ti.Media.Sound will happily work, but Ti.Filesystem.File and images in general do not.
So if want to, e.g. upload the result to a web service, at this point you are stuck.

To get out of this problem, there is a native module TiContentResolver
that resolves the content URLS to their native path.

So the above code would then become something like this:

var activity = win.getActivity();
activity.startActivityForResult(intent, function(e) {
  if (e.resultCode == Ti.Android.RESULT_OK) {
    var Content = require("yy.ticontent");
    var nativePath = e.intent.data;
    if (nativePath.indexOf("content://") === 0) {
      nativePath = "file://" + Content.resolveAudioPath(e.intent.data);
    }
  }
});

The final gotcha I found was that when an applications returns a file:// url it is exactly that, a url – so the file name and path will be URL encoded.
Ti.Filesystem.getFile(), e.g. does not expect an URL encoded url even if prefixed with file://.

So these are the final changes to resolve that:

var activity = win.getActivity();
activity.startActivityForResult(intent, function(e) {
  if (e.resultCode == Ti.Android.RESULT_OK) {
    var Content = require("yy.ticontent");
    var nativePath = e.intent.data;
    if (nativePath.indexOf("content://") === 0) {
      nativePath = "file://" + Content.resolveAudioPath(e.intent.data);
    } else {
      nativePath = decodeURIComponent(nativePath);
    }
  }
});

David Bankier leads YY Digital, a company that builds integrated mobile, tablet and server side applications for enterprise. He has used Titanium for mobile dev since 2010 and uses a mix of Node and Scala on the backend. TCE, Titan, OSS dev and he likes to CodeFast


Comments

  • Vinicius Oliveira

    I have a problem with Intents.
    With the app closed the intent works, but if the app is already open don’t works.

    win.addEventListener(‘open’, function(e) {
    var intent = Ti.Android.currentActivity.getIntent();

    var iname = Ti.Android.EXTRA_STREAM;
    if (intent && intent.hasExtra(iname)) {
    var blob = intent.getBlobExtra(iname);
    } else {
    alert(‘No extra named “‘ + iname + ‘” found in Intent’);
    }
    });