cse543_lab4_notes

Lab 4: Data Sharing between Apps

Outline of this lab
  • Warm-up: Share a text with another app. Receive a text from another app

  • Share any binary data (images) with another app. Receive binary data from another app.

  • An easy share button

Examples

Send Text Content
  • Generate an app. Build a new project using the wizard in Android studio, Application name is "DataShare", and company domain is "mcc". Select the Empty Activity. Use default for other settings.

  • Design a simple layout. In the res/layout/activity_main.xml, include one EditText view and one Button view. For the Button view, define the function name for onclick. The ViewGroup should be LinearLayout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="mcc.datashare.MainActivity">

    <EditText android:id="@+id/edit_message"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="message to send" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="sendData"
        android:text="send" />
</LinearLayout>
  • Important: build the intent for data sharing. In MainActivity.java, define the function public void sendData(View v){}. This functions will retrieve input text from the EditText view. An intent will be built. In the intent, the data trype will be set to be "text/plain". (Understand how an intent is built) Specifically, the sendData function is defined as follows.
    public void sendData(View v){
        EditText ed=(EditText) findViewById(R.id.edit_message);
        Intent sendIntent = new Intent();
        sendIntent.setAction(Intent.ACTION_SEND);
        sendIntent.putExtra(Intent.EXTRA_TEXT, ed.getText().toString());
        sendIntent.setType("text/plain");
        startActivity(sendIntent);
    }
Receive Text Content
  • Build a new project using the wizard in Android studio, Application name is "ReceiveData", and company domain is "mcc". Select the Empty Activity. Use default for other settings.

  • To receive an intent from another application, we need to set up the intent filter in AndroidManifest.xml. For this lab, we will only receive plain text and images. Specifically, the AndroidManifest.xml should be something like this:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="mcc.receivedata">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.SEND" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="text/plain" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.SEND_MULTIPLE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="image/*" />
            </intent-filter>
        </activity>
    </application>

</manifest>
  • Once the intent filter is set up, this app will be invoked when the corresponding intents arrive. The arriving intents can be accessed in the onCreate function of the activity. Next we will implement functions to process the intents. Use the following function to retrieve the intent.
Intent intent = getIntent();

Then we have access to all the information in this intent! We can reference documents of the Intent class for more details of what information we can get. In this lab, we need to get the intent action, intent type, and intent extras. Specifically, we will handle texts and images separately in the onCreate function, as follows:

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Intent intent = getIntent();
        String action = intent.getAction();
        String type = intent.getType();

        if (Intent.ACTION_SEND.equals(action) && type != null) {
            if ("text/plain".equals(type)) {
                handleSendText(intent); // Handle text being sent
            }
        }else if (Intent.ACTION_SEND_MULTIPLE.equals(action) && type != null) {
            if (type.startsWith("image/")) {
                handleSendMultipleImages(intent); // Handle multiple images being sent
            }
        }
    }

We will go on to implement the function handleSendText(). This function we retrieve the text received and display it on the screen. The way to do this is to use the function getStringExtra() with the key Intent.EXTRA_TEXT, as shown in the following line:

String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);

Then we will display this text on the screen using the TextView class. Specifically, the implementation of handleSendText is as follows:

void handleSendText(Intent intent) {
        String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
        if (sharedText != null) {
            TextView tv=(TextView) findViewById(R.id.txview);
            tv.setText(sharedText);
        }
    }
Send Binary Content

Next we introduce how to send a binary file to another app. As an example, we will share two images from one app to another. To do so, we need to complete the following tasks:

  • Copy the image files to external storage so that other apps can access
  • Create an Intent instance that carries the data information

  • Copy the image files to external storage so that other apps can access

In the DataShare app, edit res/layout/activity_main.xml. Include a new button to send images.

<Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="shareImages"
        android:text="Share Images" />

Download the following two images to the folder "DataShare/DataShare/app/src/main/res/drawable/".

System Architechture System Architechture

In MainActivity.java, we can easily access these two images by their IDs (R.drawable.i1). However, files in the drawable folder is private and cannot be accessed by another app. We use the MediaStore.Images.Media.insertImage() method to copy i1.jpg and i2.jpg to the media library, and reference them there.

To build and send an intent, we will set the intent action using Intent.setAction() method, Then we will put the list of image uri into the intent, and set the intent type. Finally, we broadcast the intent using the startActivity() method. Specifically, the implementation of public void shareImages(View v) is as follows:

public void shareImages(View v){
    ArrayList<Uri> imageUris = new ArrayList<Uri>();
    try {
        try {
            Uri imageUri1 = Uri.parse(MediaStore.Images.Media.insertImage(this.getContentResolver(),
                    BitmapFactory.decodeResource(getResources(), R.drawable.i1), null, null));
            imageUris.add(imageUri1);

            Uri imageUri2 = Uri.parse(MediaStore.Images.Media.insertImage(this.getContentResolver(),
                    BitmapFactory.decodeResource(getResources(), R.drawable.i2), null, null));
            imageUris.add(imageUri2);
        } catch (NullPointerException e) {
        }

    } catch (android.content.ActivityNotFoundException ex) {

    }

    Intent shareIntent = new Intent();
    shareIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
    shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, imageUris);
    shareIntent.setType("image/*");
    shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    startActivity(Intent.createChooser(shareIntent, "Share images to.."));
}

Since the MediaStore.Images.Media.insertImage() method involves writing to external storage, the permission needs to be granted. In AndroidManifest.xml, we add the following line of code.

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

For those using Android 6.0 or higher, add a permission request dialog in the onCreate() method in MainActivity.java file.

ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},0);
Receive Binary Content

To receive these images, we need to use the method intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM). This can retrieve the list of image uris. Using these uris, we can apply the InputStream class to read the image files. Finally, we a bitmap is constructed using BitmapFactory, and the image is displayed in the screen. The implementation of void handleSendMultipleImages(Intent intent) is as follows.

void handleSendMultipleImages(Intent intent) {
    ArrayList<Uri> imageUris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
    if (imageUris != null) {
        LinearLayout ll=(LinearLayout) findViewById(R.id.activity_main);
        ImageView iv;
        for(Uri uri:imageUris){
            try {
                InputStream is = getContentResolver().openInputStream(uri);
                iv=new ImageView(this);
                iv.setImageBitmap(BitmapFactory.decodeStream(is));
                ll.addView(iv);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
}

Adding an Easy Share Action

We begin by creating a menu in the app. Create the res/menu/share_menu.xml file. It should be like the following:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:support="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/menu_share"
        android:title="share"
        support:actionProviderClass="android.support.v7.widget.ShareActionProvider"
        support:showAsAction="always" />
</menu>

This will create a menu with a share button. Then we need to implement the logic for the this button. In the MainActivity.java, we need to add these functions:

private ShareActionProvider mShareActionProvider;
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.share_menu, menu);
    // Retrieve the share menu item
    MenuItem shareItem = menu.findItem(R.id.menu_share);
    // Now get the ShareActionProvider from the item
    mShareActionProvider = (android.support.v7.widget.ShareActionProvider) MenuItemCompat.getActionProvider(shareItem);
    setShareIntent(createShareIntent());
    return super.onCreateOptionsMenu(menu);
}
private Intent createShareIntent() {
    Intent shareIntent = new Intent(Intent.ACTION_SEND);
    shareIntent.setType("text/plain");
    shareIntent.putExtra(Intent.EXTRA_TEXT,
            "edit the text you want to share");
    return shareIntent;
}
private void setShareIntent(Intent shareIntent) {
    if (mShareActionProvider != null) {
        mShareActionProvider.setShareIntent(shareIntent);
    }
}

Besides, change the MainActivity class to be extending ActionBarActivity.

public class MainActivity extends ActionBarActivity

In this case, pay special attention to what packages you import. There could be a lot of errors coming from package imports.


Lab 4 Homework

Build an app that can select any image in the external storage. Share this image with another app and be able to display.

Reference: A FileChooser Dialog builder.

© Hua Huang. Built using Pelican. Theme by Giulio Fidente on github.