5

The clipboard code that works for API levels < 11 crashes on devices with API levels >= 11.

The clipboard code that work for API level >= 11 crashes on devices with API levels < 11.

I can not compile code for both versions because they have conflicting import requirements.

One needs: import android.text.ClipboardManager;

while the other needs: import android.content.ClipboardManager;

Surely there is a way write some code that will work on both sides of API level 11. I just can't figure it out.

***Edited (Since I can't answer my own question) *******

I found the problem. The exception message says, "Can't create handler inside a thread that has not called Looper.prepare()."

Apparently I have to jump through some more hoops since I am executing this code from an Async task.

Xarph
  • 1,429
  • 3
  • 17
  • 26
  • You should only be running this code from the main application thread, such as `onPostExecute()` of your `AsyncTask`. – CommonsWare Jan 27 '12 at 11:51

4 Answers4

22

I recently faced a similar problem. Here's how I handled it.

int currentapiVersion = android.os.Build.VERSION.SDK_INT;
if (currentapiVersion >= android.os.Build.VERSION_CODES.HONEYCOMB){
     android.content.ClipboardManager clipboard =  (android.content.ClipboardManager) getSystemService(CLIPBOARD_SERVICE); 
        ClipData clip = ClipData.newPlainText("label", "Text to Copy");
        clipboard.setPrimaryClip(clip); 
} else{
    android.text.ClipboardManager clipboard = (android.text.ClipboardManager)getSystemService(CLIPBOARD_SERVICE); 
    clipboard.setText("Text to Copy");
}
Toast.makeText(getApplicationContext(), "Text copied to clipboard", Toast.LENGTH_SHORT).show();

I'm not entirely sure if the first if block is necessary. But I'd rather not take a chance :)

Ashok Goli
  • 5,043
  • 8
  • 38
  • 68
  • What is `label` here? – Srujan Barai Nov 15 '15 at 12:34
  • 1
    @SrujanBarai - `label` is user-visible label for the clip data. This `label` field is utilized mostly by the clipboard manager apps as the title of the Clip Data that is copied. For more information refer: http://developer.android.com/reference/android/content/ClipData.html#newPlainText(java.lang.CharSequence, java.lang.CharSequence) – Ashok Goli Nov 16 '15 at 10:51
6

To avoid the exception

[FATAL EXCEPTION: GLThread 6132  java.lang.RuntimeException: 
Can't create handler inside thread that has not called Looper.prepare() ], 

-> just create the ClipboardManager once at startup, for example in your onCreate() method and use it anytime in a separate function.

tested working on 2.3.3 and 4.0.3:

import android.text.ClipboardManager;
import android.content.ClipData;
..
public class myActivity extends Activity
{
  private static ClipboardManager m_ClipboardManager;

  @Override
  protected void onCreate(..)
  {
    ...
   m_ClipboardManager = (ClipboardManager)     m_sInstance.getSystemService(Context.CLIPBOARD_SERVICE); 
  }

  public static void myCopyToClipBoard(String sTxt)
  {
    m_ClipboardManager.setText(sTxt);
  }
}
Houari
  • 5,326
  • 3
  • 31
  • 54
Christophe LB
  • 61
  • 1
  • 1
1

Write yourself your own Clipboard compat class, for example:

import android.annotation.SuppressLint;
import android.content.ClipData;
import android.content.Context;
import android.net.Uri;


public class ClipboardCompat {
    private android.content.ClipboardManager currentCM=null;
    private android.text.ClipboardManager legacyCM=null;

    public ClipboardCompat() {
        if(android.os.Build.VERSION.SDK_INT >= 11 && currentCM == null) {
            currentCM = (android.content.ClipboardManager)
                    [getsomecontext].getSystemService(Context.CLIPBOARD_SERVICE);
        }
        else if(legacyCM == null)
        {
            legacyCM = (android.content.ClipboardManager)
                    [getsomecontext].getSystemService(Context.CLIPBOARD_SERVICE);
        }
    }

    @SuppressLint("NewApi")
    public String getText() {
        if(currentCM!=null) {
            try{
                return currentCM.getPrimaryClip().getItemAt(0).getText().toString();
            } catch (NullPointerException e) {
                return null;
            }
        }
        else
        {
            try{
                return legacyCM.getText().toString();
            } catch (NullPointerException e) {
                return null;
            }
        }
    }

    @SuppressLint("NewApi")
    public Uri getUri() {
        if(currentCM!=null) {
            try{
                return currentCM.getPrimaryClip().getItemAt(0).getUri();
            } catch (NullPointerException e) {
                return null;
            }
        }
        else
        {
            return null;
        }
    }

    @SuppressLint("NewApi")
    public void set(String name, String s) {
        if(currentCM!=null) {
            ClipData clip = ClipData.newPlainText(name, s);
            currentCM.setPrimaryClip(clip);
        }
        else
        {
            legacyCM.setText(s);
        }
    }

    @SuppressLint("NewApi")
    public void set(String name, Uri u) {
        if(currentCM!=null) {
            ClipData clip = ClipData.newRawUri(name, u);
            currentCM.setPrimaryClip(clip);
        }
        else
        {
            legacyCM.setText(u.toString());
        }
    }
}
ssuukk
  • 8,200
  • 7
  • 35
  • 47
1

The clipboard code that works for API levels < 11 crashes on devices with API levels >= 11.

This sample project works quite nicely on API levels before and after 11. I just retested it on Android 2.3 (Nexus One) and Android 4.0 (Nexus S).

The clipboard code that work for API level >= 11 crashes on devices with API levels < 11.

That is not surprising. If you are referring to classes or methods that do not exist in older versions of Android, you will get a VerifyError or similar crashes.

I can not compile code for both versions because they have conflicting import requirements.

Not really.

One needs: import android.text.ClipboardManager;

That works on all API levels.

while the other needs: import android.content.ClipboardManager;

That was added to API Level 11. If your app will only run on API Level 11 or higher, use this class (method signatures are all the same IIRC).

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • I ran nearly identical code on the 4.0 Emulator. When I execute the "ClipboardManager cm=(ClipboardManager)getSystemService(CLIPBOARD_SERVICE);" I get: 01-26 16:27:10.886: W/AsyncTask(725): java.lang.InterruptedException – Xarph Jan 27 '12 at 01:38
  • Ok yah, your code is working, but still it shows deprecated method warning. Will it be a problem in future??? I mean will the method be deleted forever in future ??? – Amalan Dhananjayan Apr 08 '13 at 05:07