Android: Copy-paste with Intent and support for HTML

Android provides support for copy and paste feature using ClipBoardManager. The developer.android.com provides a brief description along with an image which helps to understand the Copy-paste framework.

“To copy data, an application puts a ClipData object on the ClipboardManager global clipboard. The ClipData contains one or more ClipData.Item objects and one ClipDescription object. To paste data, an application gets the ClipData, gets its MIME type from the ClipDescription, and gets the data either from the ClipData.Item or from the content provider referred to by ClipData.Item.”

This tutorial covers the latest feature introduced in JellyBean, supporting the styled text. We will cover the methods listed below.

HTML supported methods

  • ClipData.newHtmlText() with clipboard
  • Intent.putExtra() with Intent.EXTRA_HTML_TEXT
  • Intent.setClipData() with newHtmlText() method

Project Information : Meta-data about the project.
Platform Version : Android API Level 16.
IDE : Eclipse Helios Service Release 2
Emulator : Android 4.1(API 16)
Prerequisite : Preliminary knowledge of Android application framework and Intent.

Example source code

To start with, create project by Eclipse > File> New Project>Android Application Project. The following dialog box will appear. Fill the required field, i.e Application Name, Project Name and Package. Now press the next button.

Once the dialog box appears, select the BlankActivity and click the next button.

Fill the Activity Name and Layout file name in the dialog box shown below and hit the finish button.

This process setups the basic project files. Now we are going to add view components in the layout activity_jbclipboard.xml file. You can modify the layout file using either Graphical Layout editor or xml editor. In this layout, we will add four buttons, namely copy, paste, send html Intent, and send clipdata intent and attach onClick methods with CopyHtml, pasteHtml, sendHtmlIntent and sendClipdataIntent buttons, respectively. These methods will be defined in JBClipboard class. We will also include two radio buttons i.e Paste HTML and Paste Text. These radio buttons help to select the required type of text to be extracted from the clipboard. We also have three EditViews to show the text. The first Editview contains the Styled text, the second one shows either HTML or text string. The third one shows the Coerce HTML string. The layout file is shown below.

<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'>

    <EditText
        android:id='@+id/etCopy'
        android:layout_width='fill_parent'
        android:layout_height='wrap_content'
        android:padding='@dimen/padding_medium'
        android:gravity='top'   
        android:scrollHorizontally='false'
        android:inputType='textMultiLine'
         />
    <RadioGroup 
       android:id='@+id/rbgTextHTML'
       android:orientation='horizontal'
       android:layout_width='fill_parent'
       android:layout_height='wrap_content'
       >
         <RadioButton 
           android:id='@+id/rbHtml'
           android:layout_weight='.5'
           android:layout_width='0dp' 
           android:layout_height='wrap_content'
           android:checked='true'
           android:text='@string/rbHtml'/>
       <RadioButton
           android:id='@+id/rbText'
           android:layout_weight='.5'
           android:layout_width='0dp' 
           android:layout_height='wrap_content'
           android:text='@string/rbText'/>
     
   </RadioGroup>
    <LinearLayout 
        android:layout_width='match_parent'
        android:layout_height='wrap_content'
        android:orientation='horizontal'>
   <Button 
       android:layout_width='0dp'
       android:layout_height='wrap_content'
       android:layout_weight='.5'
       android:onClick='copyHtml'
       android:text='@string/btCopy'/>
   <Button 
       android:layout_width='0dp'
       android:layout_height='wrap_content'
       android:layout_weight='.5'
       android:onClick='pasteHtml'
       android:text='@string/btPaste'/>
   </LinearLayout>
   <LinearLayout android:layout_width='fill_parent'
       android:layout_height='wrap_content'
       android:orientation='horizontal'>
       <Button
           android:layout_width='0dp'
           android:layout_weight='.5'
           android:layout_height='wrap_content'
           android:onClick='sendHtmlIntent'
           android:text='@string/btSendHtmlIntent'/>
       <Button 
           android:layout_width='0dp'
           android:layout_weight='.5'
           android:layout_height='wrap_content'
           android:onClick='sendClipdataIntent'
           android:text='@string/btSendClipdataIntent'/>
   </LinearLayout>
   <TextView
       android:layout_width='fill_parent'
       android:layout_height='wrap_content'
       android:text='@string/tvCopiedText'/>
   <EditText
       android:id='@+id/etPaste' 
       android:layout_width='fill_parent'
       android:layout_height='wrap_content'
       android:inputType='textMultiLine'
        android:gravity='top'   
        android:scrollHorizontally='false'
       />
   <TextView
       android:layout_width='fill_parent'
       android:layout_height='wrap_content'
       android:text='@string/tvcoerceText'/>
   <EditText
       android:id='@+id/etPasteCoerceText' 
       android:layout_width='fill_parent'
       android:layout_height='wrap_content'
       android:gravity='top'   
       android:scrollHorizontally='false'
       android:inputType='textMultiLine'/>
</LinearLayout>

Now we need to add two more layout files for two different activities. Let’s define the first Layout file by Eclipse > File > Android XML file. Eventually we get the following dialog box. Make sure that the Layout resource type is selected. Name the layout file as activity_htmlintent and then click finish.

Add two TextViews for showing text tags and two EditTexts view for showing the HTML and text strings. This layout file will be attached with HTMLIntentActivity.java which gets invoked once “text/html” type intent is broadcasted. The content of the activity_htmlintent.xml is given below:

<LinearLayout xmlns:android='http://schemas.android.com/apk/res/android'
    android:layout_width='match_parent'
    android:layout_height='match_parent'
    android:orientation='vertical' >
    <TextView
        android:layout_width='match_parent'
        android:layout_height='wrap_content'
        android:text='@string/tvIntentHtml' />
    <EditText
        android:id='@+id/etHtml'
        android:layout_width='match_parent'
        android:layout_height='wrap_content'
        android:inputType='textMultiLine'
        android:padding='@dimen/padding_medium'
        android:scrollHorizontally='false' />
    <TextView
        android:layout_width='match_parent'
        android:layout_height='wrap_content'
        android:text='@string/tvIntentText' />
    <EditText
        android:id='@+id/etText'
        android:layout_width='match_parent'
        android:layout_height='wrap_content'
        android:inputType='textMultiLine'
        android:padding='@dimen/padding_medium'
        android:scrollHorizontally='false' />
</LinearLayout>

Now we will define another layout file called activity_clipdataintent.xml. Follow the similar steps given above to generate the layout xml file. This file is similar to the above file and contains two EditView and two TextViewThe. The content of the file is given below. This layout file will be attached to the ClipdataIntentActivity class which gets invoked when an intent with Clipdata object is passed.

<LinearLayout xmlns:android='http://schemas.android.com/apk/res/android'
    android:layout_width='match_parent'
    android:layout_height='match_parent'
    android:orientation='vertical' >

    <TextView
        android:layout_width='match_parent'
        android:layout_height='wrap_content'
        android:text='@string/tvIntentClipdataHtml' />

    <EditText
        android:id='@+id/etClipBoardHtml'
        android:layout_width='match_parent'
        android:layout_height='wrap_content'
        android:inputType='textMultiLine'
        android:padding='@dimen/padding_medium'
        android:scrollHorizontally='false' />

    <TextView
        android:layout_width='match_parent'
        android:layout_height='wrap_content'
        android:text='@string/tvIntentClipdataText' />

    <EditText
        android:id='@+id/etClipBoardText'
        android:layout_width='match_parent'
        android:layout_height='wrap_content'
        android:inputType='textMultiLine'
        android:padding='@dimen/padding_medium'
        android:scrollHorizontally='false' />
</LinearLayout>

You may have notice there are string resources have been used in the layout file. Define this string constants in string.xml as shown below.

<resources>

    <string name='app_name'>JellyBeanClipboard</string>
    <string name='menu_settings'>Settings</string>
    <string name='title_activity_jbclipboard'>JBClipboard</string>
    <string name='title_activity_htmlintent'>Html Intent Activity</string>
    <string name='title_activity_clipdataintent'>ClipData Intent Activity</string> 
    <!-- CDATA tag is required otherwise you can't have the html text
       properly parsed in Textview  -->
   <string name='tvHtml'><![CDATA[<b>Link:</b> <a href='http://www.code4reference.com'>Code4Reference</a>]]></string>
   
   <!-- Text string for button -->
   <string name='btCopy'>Copy</string>
   <string name='btPaste'>Paste</string>
   <string name='btSendHtmlIntent'>send HTML Intent</string>
   <string name='btSendClipdataIntent'>send ClipData Intent</string>
   
   <!-- Text string for RadioButton -->
   <string name='rbHtml'>Paste Html</string>
   <string name='rbText'>Paste Text</string>
   
   <!-- Text string for Text View -->
   <string name='tvCopiedText'><b><i>Copied text</i></b></string>
   <string name='tvcoerceText'><b><i>Copied coerce Text</i></b></string>
    <string name='tvIntentText'><b><i>Intent Text</i></b></string>
   <string name='tvIntentHtml'><b><i>Intent Html</i></b></string>
   <string name='tvIntentClipdataText'><b><i>Intent Clipdata Text</i></b></string>
   <string name='tvIntentClipdataHtml'><b><i>Intent Clipdata Html</i></b></string>
</resources>

Once we are done with the layout files, it’s time to define the activity classes. Let’s define the main activity called JBClipboard. This activity has various methods which use the HTML supported APIs. The embedded comments will help to understand the code.

package com.code4reference.rakesh.jellybeanclipboard;

import android.app.Activity;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.text.Html;
import android.text.Spannable;
import android.view.View;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.Toast;

import com.example.jellybeanclipboard.R;

public class JBClipboard extends Activity {

 EditText etCopy;
 EditText etPaste;
 EditText etPasteCoerceText;
 RadioButton rbText;
 RadioButton rbHtml;
 ClipboardManager mClipboard;

 ClipboardManager.OnPrimaryClipChangedListener mPrimaryChangeListener = new ClipboardManager.OnPrimaryClipChangedListener() {
  /**
   * This method is a callback. It get called when the primary clip 
   * on the clipboard changes. 
   */
  public void onPrimaryClipChanged() {
   //Toast message will appear whenever the clipboad 
   //primary data changes.
   Utility.showToastMessage(getApplicationContext(),
     'Primary clipdata changed', Toast.LENGTH_SHORT);
  }
 };

 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_jbclipboard);
  etCopy = (EditText) findViewById(R.id.etCopy);
  etPaste = (EditText) findViewById(R.id.etPaste);
  etPasteCoerceText = (EditText) findViewById(R.id.etPasteCoerceText);
  etCopy.setText(Html.fromHtml(getString(R.string.tvHtml)));
  rbText = (RadioButton) findViewById(R.id.rbText);
  rbHtml = (RadioButton) findViewById(R.id.rbHtml);
  mClipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
  mClipboard.addPrimaryClipChangedListener(mPrimaryChangeListener);
 }

 /**
  * This method gets called when 'Copy' button get pressed.
  * @param view
  */
 public void copyHtml(View view) {
  String htmlText = getHtmltxt(etCopy);
  String plainText = getOnlyText(etCopy);
  mClipboard.setPrimaryClip(ClipData.newHtmlText('HTML Text', plainText,
    htmlText));

 }
 /**
  * This method gets called when 'Paste' button get pressed.
  * @param view
  */
 public void pasteHtml(View view) {
  // Check if there is primary clip exsiting.
  // If it does then echeck the mime type to make sure
  // it has HTML content.
  if (mClipboard.hasPrimaryClip()
    && mClipboard.getPrimaryClipDescription().hasMimeType(
      ClipDescription.MIMETYPE_TEXT_HTML)) {
   // Get the very first item from the clip.
   ClipData.Item item = mClipboard.getPrimaryClip().getItemAt(0);

   // If 'Paste HTML' radio button is selected then paste
   // HTML in the Textview.
   if (rbHtml.isChecked()) {
    etPaste.setText(item.getHtmlText());
   } else {
    // Paste the only text version.
    etPaste.setText(item.getText());
   }
   // Paste the CoerceText .
   etPasteCoerceText.setText(item.coerceToText(this));
  }
 }
 /**
  * This method gets called when 'send Html Intent' button get pressed.
  * @param view
  */
 public void sendHtmlIntent(View view) {
  // This kind of intent can be handle by this application
  // Or other application which handle text/html type Intent
  Intent intent = new Intent(Intent.ACTION_SEND);

  String htmlText = getHtmltxt(etCopy);
  String text = getOnlyText(etCopy);
  intent.putExtra(Intent.EXTRA_HTML_TEXT, htmlText);
  intent.putExtra(Intent.EXTRA_TEXT, text);
  intent.setType('text/html');
  startActivity(Intent.createChooser(intent, null));
 }

 /**
  * This method gets called when 'send Clipdata Intent' button get pressed.
  * 
  * @param view
  */
 public void sendClipdataIntent(View view) {
  String htmlText = getHtmltxt(etCopy);
  String plainText = getOnlyText(etCopy);
  Intent intent = new Intent(this, ClipdataIntentActivity.class);
                //create a clipdata object with HTML text.
                //and associate with the intent.
  intent.setClipData(ClipData.newHtmlText(
    'HTML text in Intent's clipdata', plainText, htmlText));
                //Start the activity which can handle clipData object.
  startActivity(intent);
 }
 @Override
    protected void onDestroy() {
        super.onDestroy();
        //Remove the ClipChanged Listener to save the resources.
        mClipboard.removePrimaryClipChangedListener(mPrimaryChangeListener);
    }
 /**
  * This method gets the EditText object and returns the HTML text. It
  * can be called only with EditTexts having spannable object with
  * the HTML text.
  * 
  * @param editText
  * @return
  */
 private String getHtmltxt(EditText editText) {
                //get the spannable object from EditText
  Spannable spannable = (Spannable) editText.getText();
                //return the HTML text from spannable object.
  return Html.toHtml(spannable);
 }

 /**
  * This method takes the EditText object which has spannable object with HTML
  * text and returns the simple text without HTML tags.
  * 
  * @param editText
  * @return
  */
 private String getOnlyText(EditText editText) {
  return editText.getText().toString();
 }
}

Now we are going to define HTMLIntentActivitywhich handles intent with HTML text. The intended intent will be triggered by the main activity (JBClibboard)

package com.code4reference.rakesh.jellybeanclipboard;

import com.example.jellybeanclipboard.R;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.EditText;

public class HtmlIntentActivity extends Activity {

 private EditText etHtml;
 private EditText etText;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_htmlintent);
  etHtml = (EditText) findViewById(R.id.etHtml);
  etText = (EditText) findViewById(R.id.etText);

  //Get the intent that started this activity
  Intent intent = getIntent();
                //Make sure intent and its type is not null.
  if (intent != null && intent.getType() != null
    && intent.getType().equals('text/html')) {
   //This contition will full-fill when this application receive the 
   //intent who's type is 'test/html'. In this application sendHtmlIntent
   //method sends this type of Intent. 
   Bundle bundle = intent.getExtras();
   if(bundle != null){
    etHtml.setText(bundle.getCharSequence(Intent.EXTRA_HTML_TEXT));
    etText.setText(bundle.getCharSequence(Intent.EXTRA_TEXT));
   }
  }
 }
}

Now we will define another activity called ClipdataIntentActivity which handles the Intent having clilpdata object. The content of the ClipdataIntentActivity class is given below.

package com.code4reference.rakesh.jellybeanclipboard;

import android.app.Activity;
import android.content.ClipboardManager;
import android.content.Intent;
import android.os.Bundle;
import android.widget.EditText;
import android.widget.Toast;
import android.content.ClipData;
import android.content.ClipDescription;

import com.example.jellybeanclipboard.R;

public class ClipdataIntentActivity extends Activity {
 private EditText etHtml;
 private EditText etText;
 ClipboardManager mClipboard;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_clipdataintent);
  etHtml = (EditText) findViewById(R.id.etClipBoardHtml);
  etText = (EditText) findViewById(R.id.etClipBoardText);

  //Get the intent that started this activity
  Intent intent = getIntent();
  if (intent != null) {
   ClipData clipdata = intent.getClipData();
                        //Make sure clipdata object is not null and it has HTML MIME type.
   if (clipdata != null
     && clipdata.getDescription().hasMimeType(
       ClipDescription.MIMETYPE_TEXT_HTML)) {

    ClipData.Item item = clipdata.getItemAt(0);
    etHtml.setText(item.getHtmlText());
    etText.setText(item.getText());
   } else {
    Utility.showToastMessage(this,
      'Intent clipdata doesn't have HTML', Toast.LENGTH_SHORT);
   }

  }
 }

}

Create another class called Utility. It contains method to display the toast message. As you may have noticed that this function is static, so there is no need to create an object to invoke this method.
It’s always a good idea to put the utility methods in a separate file and access this file in different places. In this way your code will be organized.

package com.code4reference.rakesh.jellybeanclipboard;

import android.content.Context;
import android.widget.Toast;

public class Utility {

 public static void showToastMessage(Context context, String message, int duration){
  Toast.makeText(context, message, duration).show();
 }
}

And finally define the Anroid Manifest file which basically provides Application information to the Android system. Here, you should notice that the HtmlIntentActivity Activity has an intent filter and specifies the Intent type as “text/html”. It basically means that the activity can handle intent whose type is text/html. Remaining part of the file is simple and easy to understand.

<manifest xmlns:android='http://schemas.android.com/apk/res/android'
    package='com.example.jellybeanclipboard'
    android:versionCode='1'
    android:versionName='1.0' >

    <uses-sdk
        android:minSdkVersion='16'
        android:targetSdkVersion='16' />

    <application
        android:icon='@drawable/ic_launcher'
        android:label='@string/app_name'
        android:theme='@style/AppTheme' >
        <activity
            android:name='com.code4reference.rakesh.jellybeanclipboard.JBClipboard'
            android:label='@string/title_activity_jbclipboard' >
            <intent-filter>
                <action android:name='android.intent.action.MAIN' />
                <category android:name='android.intent.category.LAUNCHER' />
            </intent-filter>
        </activity>
        <activity android:name='com.code4reference.rakesh.jellybeanclipboard.ClipdataIntentActivity'
            android:label='@string/title_activity_clipdataintent' >
        </activity>
        <activity android:name='com.code4reference.rakesh.jellybeanclipboard.HtmlIntentActivity'
            android:label='@string/title_activity_htmlintent' >
            <intent-filter>
                <action android:name='android.intent.action.SEND' />
                <category android:name='android.intent.category.DEFAULT' />
                <!-- This activity will get launched when proper Intent type will match.
                In this case Intent type is 'text/html' -->
                <data android:mimeType='text/html' />
            </intent-filter>
        </activity>
    </application>

</manifest>

Once you are done with the coding, just execute it. You can launch this application on your android device or emulator. Make sure that the Android version is 16(Jelly Bean) or higher, only then this application will work. Screen-shots of the applications are shown below.

You can get the source code here at github.

Happy coding and don’t forget to share!

Reference: Copy-paste with Intent and support for HTML on Android. from our JCG partner Rakesh Cusat at the Code4Reference blog.

Related Whitepaper:

Rapid Android Development: Build Rich, Sensor-Based Applications with Processing

Create mobile apps for Android phones and tablets faster and more easily than you ever imagined

Use 'Processing', the free, award-winning, graphics-savvy language and development environment, to work with the touchscreens, hardware sensors, cameras, network transceivers, and other devices and software in the latest Android phones and tablets.

Get it Now!  

Leave a Reply


nine + = 16



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books