When I copy text to the clipboard onPrimaryClipChanged method is called twice. Any ideas why?
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
final ClipboardManager cliboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
cliboardManager
.addPrimaryClipChangedListener(new OnPrimaryClipChangedListener() {
@Override
public void onPrimaryClipChanged() {
ClipData clipData = cliboardManager.getPrimaryClip();
System.out
.println("********** clip changed, clipData: "
+ clipData.getItemAt(0));
}
});
return true;
}
Test case:
Copying the text "continue" from the BBC web site will result in the following output:
continue
continue
But if I debug the program I can see that the clipData object has value:
ClipData { text/plain {T:continue } }
the first time onPrimaryClipChanged() is called and
ClipData { text/plain "BBC - Homepage" {T:continue } }
the next time onPrimaryClipChanged() is called.
So basically the first time ClipDescription is { text/plain } and the second time is ClipDescription { text/plain "BBC - Homepage" } (i.e including the title of the web page)
I assume you didn't register multiple listeners, I can't say it is bug, you still you can workaround it. Try something like this:
String mPreviousText = "";
cliboardManager
.addPrimaryClipChangedListener(new OnPrimaryClipChangedListener() {
@Override
public void onPrimaryClipChanged() {
ClipData clipData = cliboardManager.getPrimaryClip();
System.out
.println("********** clip changed, clipData: "
+ clipData.getItemAt(0));
ClipData.Item item = clipData.getItemAt(0);
if(mPreviousText.equals(item.getText().toString())) return;
else{
/// do something
mPrevousText = item.getText().toString();
}
}
});
I meet this problem too, finally I know why it will call multi times!!!
Usually we addPrimaryClipChangedListener()
, but we don't removePrimaryClipChangedListener()
.
See:
http://developer.android.com/reference/android/content/Context.html#CLIPBOARD_SERVICE
Use with getSystemService(String) to retrieve a ClipboardManager for accessing and modifying the contents of the global clipboard.
It means we should removePrimaryClipChangedListener()
manually!
My solution code :
ClipboardManager myClipBoard ;
static boolean bHasClipChangedListener = false;
ClipboardManager.OnPrimaryClipChangedListener mPrimaryClipChangedListener = new ClipboardManager.OnPrimaryClipChangedListener() {
public void onPrimaryClipChanged() {
ClipData clipData = myClipBoard.getPrimaryClip();
Log.d("henrytest", "********** clip changed, clipData: " + clipData.getItemAt(0));
}
};
private void RegPrimaryClipChanged(){
if(!bHasClipChangedListener){
myClipBoard.addPrimaryClipChangedListener(mPrimaryClipChangedListener);
bHasClipChangedListener = true;
}
}
private void UnRegPrimaryClipChanged(){
if(bHasClipChangedListener){
myClipBoard.removePrimaryClipChangedListener(mPrimaryClipChangedListener);
bHasClipChangedListener = false;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
myClipBoard = (ClipboardManager) Clipboard.this.getSystemService(android.content.Context.CLIPBOARD_SERVICE);
}
@Override
protected void onResume() {
super.onResume();
RegPrimaryClipChanged();
}
@Override
protected void onPause() {
super.onPause();
UnRegPrimaryClipChanged();
}
@Override
protected void onStop() {
super.onStop();
//UnRegPrimaryClipChanged();
}
@Override
protected void onDestroy() {
super.onDestroy();
//UnRegPrimaryClipChanged();
}
This is my workaround to prevent ClipboardManager OnPrimaryClipChangedListener is called twice for every copy.
long lastCopiedTime = 0;
ClipboardManager.OnPrimaryClipChangedListener onPrimaryClipChangedListener = new ClipboardManager.OnPrimaryClipChangedListener() {
@Override
public void onPrimaryClipChanged() {
ClipboardManager clipBoard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
String pasteData = "";
ClipData.Item item = clipBoard.getPrimaryClip().getItemAt(0);
if (System.currentTimeMillis() - lastCopiedTime > TimeUnit.SECONDS.toMillis(1)) {
if (StringUtils.isNotBlank(item.getText())) {
String s = item.getText().toString();
if (StringUtils.isNotBlank(s)) {
pasteData = s.trim();
if (StringUtils.isNotBlank(pasteData)) {
/// do something here
}
}
}
}
lastCopiedTime = System.currentTimeMillis();
}
}
}
};
// Prevention duplicate action of OnPrimaryClipChangedListener.
private ExecutorService mThreadPool = Executors.newSingleThreadExecutor();
static boolean isExpireData = true;
class hasExpireData implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
isExpireData = true;
}
}
private ClipboardManager.OnPrimaryClipChangedListener mOnPrimaryClipChangedListener =
new ClipboardManager.OnPrimaryClipChangedListener() {
@Override
public void onPrimaryClipChanged() {
Log.d(TAG, "onPrimaryClipChanged");
ClipData clip = mClipboardManager.getPrimaryClip();
Thread expireData = new Thread(new hasExpireData());
new Handler().postDelayed(expireData, 500); // 0.5 seconds wait...
if(isExpireData) {
isExpireData = false;
mThreadPool.execute(new TextRunnable(
clip.getItemAt(0).getText()));
}
}
};
private class TextRunnable implements Runnable {
public TextRunnable(CharSequence text) {
// text store to this class
}
@Override
public void run() {
// text other process
}
}