Thursday, July 18, 2013

Using Intents in Xamarin to catch "Send to" and "Open with" file prompts

In Xamarin for Android you don't directly edit the AndroidManifest.xml file which means you need to create code that will generate the necessary portions of the manifest which direct your application to make itself available to handle incoming file hand-offs.  This is done using an IntentFilter which you place at the top of the class for the activity which you want to handle the incoming data.  In my case, I chose a neutral splashscreen as the loading activity so that the user will see my app's loading screen while I determine and load the true activity that will be handling the file.

namespace MyApp

{

 [Activity (Label="MyAppName", MainLauncher = true, Icon="@drawable/icon", Theme = "@style/ThemeBase.Splash", NoHistory = true)]

 [IntentFilter (new[]{Intent.ActionView},

 Categories=new[]{Intent.ActionDefault, Intent.CategoryBrowsable, Intent.ActionSend, Intent.ActionSendMultiple},

 DataScheme="mimetype",

 DataPathPattern="*/*",

 DataHost="*.*")]

 public class SplashActivity : Activity

 {

  protected override void OnCreate (Bundle bundle)

  {

   base.OnCreate (bundle);



   Intent intent = Intent;

   String action = intent.Action;

   String type = intent.Type;

   if (Intent.ActionSend.Equals(action) && type != null) {

    if (type.StartsWith("image/")) {

     handleSendImage(intent); // Handle single image being sent

    }

   } else if (Intent.ActionSendMultiple.Equals(action) && type != null) {

    if (type.StartsWith("image/")) {

     handleSendMultipleImages(intent); // Handle multiple images being sent

    }

   } 

   else

   {
    // Start our real activity if no acceptable file received

    StartActivity (typeof (MainActivity));

   }

  }

    }

}

The above code allows me to handle any filetype at all (DataHost="*.*") and will cause my application to appear as an option in the "Send" link of any other app.  If you only need to handle a specific filetype you would change the second * in DataHost to the filetype you need, such as (DataHost="*.png") would only handle PNG (Portable Network Graphics) files.  

In my case, since I am accepting all filetypes, I need to determine what type of file I was given and handle it appropriately.  You can see this occurring in the operations with the data derived from intent (intent, action, type), which is the incoming file details and requested action by the other app.  The action type gives a hint on what needs to be done with the file being sent (Send, Open, View, etc.) and the type tells you the MIME type information for the file, whether it's a PDF, image file, APK, MP3, webpage, etc.  If you aren't sure what file type you need to receive, set your DataHost to *.* then set a breakpoint, debug your app and inspect the intent.type after opening the desired file.