Android clipboard code that works on all API levels

13,951

Solution 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).

Solution 2

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 :)

Solution 3

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);
  }
}

Solution 4

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());
        }
    }
}
Share:
13,951
Xarph
Author by

Xarph

Updated on June 08, 2022

Comments

  • Xarph
    Xarph about 2 years

    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.