Tutorial #15 - Sharing Your Notes

Perhaps you would like to get your notes off of our book reader app and into someplace else, or perhaps you would like to share them with somebody else. Either way, we can do that using an ACTION_SEND operation, to allow the user to choose how to “send” the notes, such as sending them by email or uploading them to some third-party note service.

To make this work, we will add a ShareActionProvider to our action bar on the NoteFragment.

This is a continuation of the work we did in the previous tutorial.

You can find the results of the previous tutorial and the results of this tutorial in the book’s GitHub repository.

Step #1: Adding a ShareActionProvider

First, we need to allow the user to indicate that they want to “share” the note displayed in the current NoteFragment. By putting an action bar item on the activity where the NoteFragment is displayed, we do not need to worry about letting the user choose which note to send — we simply send whichever note they happen to be viewing or editing.

By using a ShareActionProvider, the action item will handle most of the work for allowing the user to choose where to send the note to. We only need to provide an Intent that identifies what is to be shared.

Modify res/menu/notes.xml to add in the new share toolbar button:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
  <item
    android:id="@+id/share"
    android:actionProviderClass="android.widget.ShareActionProvider"
    android:showAsAction="ifRoom"
    android:title="@string/share"/>
  <item
    android:id="@+id/delete"
    android:icon="@drawable/ic_delete_white_24dp"
    android:showAsAction="ifRoom|withText"
    android:title="@string/delete">
  </item>
</menu>
(from EmPubLite-AndroidStudio/T15-Share/EmPubLite/app/src/main/res/menu/notes.xml)

Note that this menu definition requires a new string resource, named share, with a value like Share.

Step #2: Sharing the Note

Now, we need to configure the ShareActionProvider, in particular supplying it with a continuously-updated Intent, based upon what the user has typed into the EditText.

Add a ShareActionProvider data member to NoteFragment, named share, along with an Intent data member named shareIntent configured to use ACTION_SEND of a MIME type of text/plain:

  private ShareActionProvider share=null;
  private Intent shareIntent=
    new Intent(Intent.ACTION_SEND).setType("text/plain");
(from EmPubLite-AndroidStudio/T15-Share/EmPubLite/app/src/main/java/com/commonsware/empublite/NoteFragment.java)

Then, in onCreateView(), tell the EditText to let us know when the user changes the text, via addTextChangedListener():

  @Override
  public View onCreateView(LayoutInflater inflater,
                           ViewGroup container,
                           Bundle savedInstanceState) {
    View result=inflater.inflate(R.layout.editor, container, false);

    editor=(EditText)result.findViewById(R.id.editor);
    editor.addTextChangedListener(this);

    return(result);
  }
(from EmPubLite-AndroidStudio/T15-Share/EmPubLite/app/src/main/java/com/commonsware/empublite/NoteFragment.java)

This will fail to compile, as our NoteFragment is not implementing the TextWatcher interface. So, modify the NoteFragment class declaration to include the TextWatcher interface:

public class NoteFragment extends Fragment implements TextWatcher {
(from EmPubLite-AndroidStudio/T15-Share/EmPubLite/app/src/main/java/com/commonsware/empublite/NoteFragment.java)

That, in turn, will require us to implement three methods:

  1. afterTextChanged()
  2. beforeTextChanged()
  3. onTextChanged()

In our case, we care about afterTextChanged(). So, add the following three methods to NoteFragment:

  @Override
  public void afterTextChanged(Editable s) {
    shareIntent.putExtra(Intent.EXTRA_TEXT, s.toString());
  }

  @Override
  public void beforeTextChanged(CharSequence s, int start, int count,
                                int after) {
    // ignored
  }

  @Override
  public void onTextChanged(CharSequence s, int start, int before,
                            int count) {
    // ignored
  }
(from EmPubLite-AndroidStudio/T15-Share/EmPubLite/app/src/main/java/com/commonsware/empublite/NoteFragment.java)

Here, we update the shareIntent with the latest text to be shared, storing it in EXTRA_TEXT, per the instructions in the Android developer documentation for working with ACTION_SEND.

However, we have not initialized share yet. We can do that in onCreateOptionsMenu(), adding a call to findItem() to find our R.id.share menu item, then calling getActionProvider() to get the ShareActionProvider out of the menu item:

  @Override
  public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.notes, menu);

    share=
      (ShareActionProvider)menu.findItem(R.id.share)
        .getActionProvider();
    share.setShareIntent(shareIntent);

    super.onCreateOptionsMenu(menu, inflater);
  }
(from EmPubLite-AndroidStudio/T15-Share/EmPubLite/app/src/main/java/com/commonsware/empublite/NoteFragment.java)

Here, we also attach the shareIntent to the ShareActionProvider, so when it comes time to share the text, the ShareActionProvider knows how to do that.

Step #3: Testing the Result

If you run this on a device and navigate to a filled-in note, you will see the new action bar item:

ShareActionProvider in NoteFragment
Figure 301: ShareActionProvider in NoteFragment

If you tap on it, you will get a roster of possible ways to share the text:

ShareActionProvider in NoteFragment, Expanded
Figure 302: ShareActionProvider in NoteFragment, Expanded

ShareActionProvider in NoteFragment, Fully Expanded
Figure 303: ShareActionProvider in NoteFragment, Fully Expanded

The exact options you see will vary based on your device or emulator, and what apps are installed on it that know how to share plain text. If you only have one choice (e.g., Messenger), it will appear next to the share icon, and you will only be able to tap on that one choice.

Unfortunately, your emulator may have nothing that can handle this Intent. If that is the case, you will crash with an ActivityNotFoundException. To get past this, if you enter http://goo.gl/w113e in your emulator’s browser, that should allow you to download and install a copy of the APK from the Intents/FauxSender sample project that we covered earlier in this book. When the download is complete (which should be very quick), open up the notification drawer and tap on the “download complete” notification. This should begin the installation process. Depending on your Android version, you may also need to “allow installation of non-Market apps” — after fixing this, you can use the Downloads app on the emulator to try installing the APK again. Once FauxSender is installed, it will respond to your attempts to share a note.

In Our Next Episode…

… we will allow the user to update the book’s contents over the Internet.