Native Sharing - StansAssets/com.stansassets.android-native GitHub Wiki

One of the great things about Android applications is their ability to communicate and integrate with each other. Why reinvent functionality that isn't cored to your application when it already exists in another application?

Sending simple data to other apps

When you construct an intent, you must specify the action you want the intent to "trigger." Android defines several actions, including ACTION_SEND which, as you can probably guess, indicates that the intent is sending data from one activity to another, even across process boundaries. To send data to another activity, all you need to do is specify the data and its type, the system will identify compatible receiving activities and display them to the user (if there are multiple options) or immediately start the activity (if there is only one option). Similarly, you can advertise the data types that your activities support receiving from other applications by specifying them in your manifest.

Sending and receiving data between applications with intents is most commonly used for social sharing of content. Intents allow users to share information quickly and easily, using their favorite applications.

Send text content

The most straightforward and common use of the ACTION_SEND action is sending text content from one activity to another. For example, the built-in Browser app can share the URL of the currently-displayed page as text with any application. This is useful for sharing an article or website with friends via email or social networking. Here is the code to implement this type of sharing:

using SA.Android.App;
using SA.Android.Content;
...

AN_Intent sendIntent = new AN_Intent();
sendIntent.SetAction(AN_Intent.ACTION_SEND);
sendIntent.PutExtra(AN_Intent.EXTRA_TEXT, "This is my text to send.");
sendIntent.SetType("text/plain");
AN_MainActivity.Instance.StartActivity(sendIntent);

If there's an installed application with a filter that matches ACTION_SEND and MIME type text/plain, the Android system will run it; if more than one application matches, the system displays a disambiguation dialog (a "chooser") that allows the user to choose an app.

However, if you call AN_Intent.CreateChooser(), passing it your AN_Intent object, it returns a version of your intent that will always display the chooser. This has some advantages:

  • Even if the user has previously selected a default action for this intent, the chooser will still be displayed.
  • If no applications match, Android displays a system message.
  • You can specify a title for the chooser dialog.

Here's the updated code:

using SA.Android.App;
using SA.Android.Content;
...

AN_Intent sendIntent = new AN_Intent();
sendIntent.SetAction(AN_Intent.ACTION_SEND);
sendIntent.PutExtra(AN_Intent.EXTRA_TEXT, "This is my text to send.");
sendIntent.SetType("text/plain");
AN_MainActivity.Instance.StartActivity(AN_Intent.CreateChooser(sendIntent, "Hello world!"));

Optionally, you can set some standard extras for the intent: EXTRA_EMAIL, EXTRA_CC, EXTRA_BCC, EXTRA_SUBJECT. If the receiving application is not designed to use them, it simply ignores them.

Send binary content

Binary data is shared using the ACTION_SEND action combined with setting the appropriate MIME type and placing the URI to the data in an extra named EXTRA_STREAM. This is commonly used to share an image but can be used to share any type of binary content.

For this example, we will be taking the app screen screenshots using SA_ScreenUtil.TakeScreenshot method. However, you can use any image or images you like. The only limitation, this image has to be readable.

using SA.Android.App;
using SA.Android.Content;
...

SA_ScreenUtil.TakeScreenshot((screenshot) => {
    AN_Intent sendIntent = new AN_Intent();
    sendIntent.SetAction(AN_Intent.ACTION_SEND);
    sendIntent.PutExtra(AN_Intent.EXTRA_STREAM, screenshot);
    sendIntent.SetType("image/jpeg");
    AN_MainActivity.Instance.StartActivity(AN_Intent.CreateChooser(sendIntent, "Hello world!"));
});

You can use a MIME type of "/", but this will only match activities that are able to handle generic data streams.

Send multiple pieces of content

To share multiple pieces of content, use the ACTION_SEND_MULTIPLE action together with a list of URIs pointing to the content. The MIME type varies according to the mix of content you're sharing. For example, if you share 3 JPEG images, the type is still "image/jpeg". For a mixture of image types, it should be "image/*" to match an activity that handles any type of image. You should only use "*/*" if you're sharing out a wide variety of types. As previously stated, it's up to the receiving application to parse and process your data. Here's an example:

using SA.Android.App;
using SA.Android.Content;
...

SA_ScreenUtil.TakeScreenshot((screenshot) => {
    AN_Intent sendIntent = new AN_Intent();
    sendIntent.SetType("image/jpeg");
    sendIntent.SetAction(AN_Intent.ACTION_SEND_MULTIPLE);
    var list = new List<Texture2D>() { screenshot, screenshot };
    sendIntent.PutExtra(AN_Intent.EXTRA_STREAM, list.ToArray());

    AN_MainActivity.Instance.StartActivity(AN_Intent.CreateChooser(sendIntent, "Hello world!"));
});

Activity

With Android Native, you can use AN_MainActivity and AN_ProxyActivity. The main difference is the following. AN_MainActivity represent your current app activity. And since Android Native does not override the application main activity it means that we not able to get a callback when starting and activity for the result. But the AN_ProxyActivity is an activity that Android Native uses as a proxy to display the above app main activity, and since this activity is a new crated activity we can fully control its behavior. The simplest example, is if we want to get a callback when sharing action is completed, we may use the AN_ProxyActivity instead AN_MainActivity. See the code below:

using SA.Android.App;
using SA.Android.Content;
...

AN_Intent sendIntent = new AN_Intent();
sendIntent.SetAction(AN_Intent.ACTION_SEND);
sendIntent.PutExtra(AN_Intent.EXTRA_TEXT, "This is my text to send.");
sendIntent.SetType("text/plain");

// Instaed AN_MainActivity
// AN_MainActivity.Instance.StartActivity(sendIntent);

AN_ProxyActivity proxy = new AN_ProxyActivity();
proxy.StartActivityForResult(sendIntent, (result) => {
    Debug.Log("Unity: Activity Result: " + result.ResultCode.ToString());
    proxy.Finish();
});

Simplified Sharing

Working with Activities and Intent's, track the result callback, and make sure you control the life cycle of that activities can be tricky. That's why we have created a simplified API, that allows you to perform simple sharing action in a more convenient way. For example, all the example above can be replaced with the AN_ShareComposer Again, for this example, we will be taking the app screen screenshots using SA_ScreenUtil.TakeScreenshot method. However, you can use any image or images you like. The only limitation, this image has to be readable.

using SA.Android.Social;
...

SA_ScreenUtil.TakeScreenshot((screenshot) => {
    var composer = new AN_ShareComposer();
    composer.SetTitle("Android Native Share Example");
    composer.SetText("Hello world");
    composer.AddImage(screenshot);
    composer.AddImage(screenshot);

    composer.Share(() => {
        Debug.Log("Sharing flow is finished, User has retured to the app");
    });
});

If the next articles, we will demonstrate more simplified sharing composers for different social networks. But the idea is to give you more convenient sharing API. For example if you want to send e-mail you can do this:

using SA.Android.Social;
...
SA_ScreenUtil.TakeScreenshot((screenshot) => {
    var composer = new AN_EmailComposer();

    composer.SetText("Hello world");
    composer.SetSubject("Testing the emails sharing example");
    composer.AddRecipient("[email protected]");
    composer.AddRecipient("[email protected]");
    composer.AddImage(screenshot);
    composer.AddImage(screenshot);

    composer.Share(() => {
        Debug.Log("Sharing flow is finished, User has retured to the app");
    });
});

Under the hood

Let's now see what the code snippet above does under the hood. See the snippet below. You can always use the approach as demonstrated on a snippet below, to make your own sharing list and only include applications you want

using SA.Android.App;
using SA.Android.Content;
using SA.Android.Content.Pm;
...

public static void StartFilteredChooserActivity(Texture2D screenshot) {

    AN_Intent shareIntent = new AN_Intent();
    shareIntent.SetAction(AN_Intent.ACTION_SEND_MULTIPLE);
    shareIntent.SetType(AN_MIMEDataType.Image);

    shareIntent.PutExtra(AN_Intent.EXTRA_TEXT, "This is my text to send.");

    shareIntent.PutExtra(AN_Intent.EXTRA_EMAIL, "[email protected]", "[email protected]");
    shareIntent.PutExtra(AN_Intent.EXTRA_TEXT, "EXTRA_TEXT2");
    shareIntent.PutExtra(AN_Intent.EXTRA_SUBJECT, "EXTRA_SUBJECT2s");
    var list = new List<Texture2D>() { screenshot, screenshot };
    shareIntent.PutExtra(AN_Intent.EXTRA_STREAM, list.ToArray());

    List<string> filters = new List<string>();
    filters.Add("mail");

    var pm = AN_MainActivity.Instance.GetPackageManager();

    List<AN_ResolveInfo> resolveInfoList = pm.QueryIntentActivities(shareIntent, 0);

    if (resolveInfoList.Count == 0) {
        Debug.Log("No apps found");
        return;
    }

    List<AN_Intent> intentShareList = new List<AN_Intent>();
    foreach (AN_ResolveInfo resInfo in resolveInfoList) {
        string packageName = resInfo.ActivityInfo.PackageName;
        string name = resInfo.ActivityInfo.Name;

        foreach (string filterPattern in filters) {

            AN_Logger.Log(resInfo.ActivityInfo.PackageName);

            if (resInfo.ActivityInfo.PackageName.ToLower().Contains(filterPattern) || resInfo.ActivityInfo.Name.ToLower().Contains(filterPattern) ) {
                AN_Intent intent = new AN_Intent(shareIntent);
                intent.SetPackage(packageName);
               // intent.setComponent(new ComponentName(packageName, name));
                intentShareList.Add(intent);
                AN_Logger.Log("packageName: " + packageName);

                shareIntent.SetPackage(packageName);
            }
        }
    }

    if (intentShareList.Count == 0) {
        AN_Logger.Log("CAN'T FIND PACKAGES FOR FILTER: " + filters);
    } else {
        AN_Logger.Log( "SHARE WITH FILTER: " + filters);
        AN_Intent chooserIntent = AN_Intent.CreateChooser(shareIntent, "Hello title", intentShareList.ToArray());
        AN_MainActivity.Instance.StartActivity(chooserIntent);
    }
}

But in case you would to implement more advanced sharing, you can always use all the power of Android Native classes as shown above.

So for example, if you would like to implement sharing dialog with only Facebook and Whatsapp option, you need to replace this part of code from the previous snippet to something like:

List<string> filters = new List<string>();
filters.Add("mail");

to something like:

List<string> filters = new List<string>();
filters.Add("com.whatsapp");
filters.Add("com.facebook.katana");
⚠️ **GitHub.com Fallback** ⚠️