Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-2587] Android: Support setting true URIs for EXTRA_STREAM

GitHub Issuen/a
TypeNew Feature
Resolution Date2011-04-17T01:59:32.000+0000
Affected Version/sn/a
Fix Version/sRelease 1.6.0 M05
Labelsandroid, feature, intent, release-1.6.0
ReporterBill Dawson
AssigneeBill Dawson


Our IntentProxy.getData is implemented by returning Intent.getDataString ("The same as getData(), but returns the URI as an encoded String" [android docs]). This means we can't then take that information and pass it back into a new intent's EXTRA_STREAM field. (EXTRA_STREAM: "A content: URI holding a stream of data associated with the Intent, used with ACTION_SEND to supply the data being sent.").

This, in turn, means we can't use our new intents and such to do something like the following:

  • Start the sound recorder activity and get the Uri to the recorded sound file back.
  • Pass that uri to a picker for ACTION_SEND (so as to send the audio file to somebody.)

For example, if you try the attached win.js and select the Gmail app as the sending mechanism, Gmail crashes with

W/Bundle  (30615): Key android.intent.extra.STREAM expected Parcelable but value was a java.lang.String.  The default value <null> was returned.
W/Bundle  (30615): Attempt to cast generated internal exception:
W/Bundle  (30615): java.lang.ClassCastException: java.lang.String




  1. Bill Dawson 2011-04-15

    @Marshall, in IntentProxy.putExtra we could check if key == Intent.EXTRA_STREAM && (value instanceof String) and then set it to a URI. But maybe we need a less-hacky, more general solution? (I haven't looked at all EXTRAs to see if others of them expect "non-primitive" data types.)

    You can assign back to me after answer.

  2. Marshall Culpepper 2011-04-15

    The formatting of my commit got messed up, here's the commit that fixed this:
    https://github.com/appcelerator/titanium_mobile/commit/f4d42cf4d2e1ca7bbf3a7ca2e987e27c778191e7"> https://github.com/appcelerator/titanium_mobile/commit/f4d42cf4d2e1...

  3. Marshall Culpepper 2011-04-15

    test used to verify this:

       var win = Ti.UI.createWindow({
           backgroundColor: 'white'
       // const value grabbed from
       // http://developer.android.com/reference/android/provider/MediaStore.Audio.Media.html#RECORD_SOUND_ACTION
       var RECORD_SOUND_ACTION = "android.provider.MediaStore.RECORD_SOUND";
       var soundUri = null; // Will be set as a result of recording action.
       var recordButton = Titanium.UI.createButton({
           top: 10, left: 10, right: 10, height: 35, title: "Record Audio"
       var labelResultCaption = Titanium.UI.createLabel({
           top: 50, left: 10, right: 10, height: 35, visible: false, color: 'yellow'
       var labelResult = Titanium.UI.createLabel({
           top: 90, left: 10, right: 10, height: 35, visible: false,
           backgroundColor: 'white', color: 'black',
           verticalAlign: 'top'
       var sendButton = Titanium.UI.createButton({
           top: 130, left: 10, right: 10, height: 35, 
           title: "Share Recorded Audio", visible: false
       sendButton.addEventListener('click', function(){
           var intent = Titanium.Android.createIntent({
               action: Titanium.Android.ACTION_SEND,
               type: 'application/octet-stream'
           intent.putExtraUri(Titanium.Android.EXTRA_STREAM, soundUri);
           var chooserIntent = Titanium.Android.createIntentChooser(intent, 'Send Sound via');
       recordButton.addEventListener('click', function() {
           var intent = Titanium.Android.createIntent({ action: RECORD_SOUND_ACTION });
           Titanium.Android.currentActivity.startActivityForResult(intent, function(e) {
               if (e.error) {
                   labelResultCaption.text = 'Error: ' + e.error;
                   labelResultCaption.visible = true;
               } else {
                   if (e.resultCode === Titanium.Android.RESULT_OK) {
                       labelResultCaption.text = 'Audio Captured.  Path to audio:';
                       soundUri = e.intent.data;
                       labelResult.text = soundUri;
                       labelResultCaption.visible = true;
                       labelResult.visible = true;
                       sendButton.visible = true;
                   } else {
                       labelResultCaption.text = 'Canceled/Error? Result code: ' + e.resultCode;
                       labelResultCaption.visible = true;
  4. Opie Cyrus 2011-04-15

    verified on droid2 2.2

  5. Andre Carregal 2011-04-15

    Could you extend the test example in order to create a new file with the recorded audio? How would soundUri be used in this case?

  6. Dawson Toth 2011-04-15

    Andre Carregal, check out this gist for an example of how to save the response to a file. It works on videos, but the exact same thing can be applied to sound recording as well. What you want is towards the end of the gist:


JSON Source