1 package org.robolectric.shadows; 2 3 import static android.os.Build.VERSION_CODES.N; 4 import static android.os.Build.VERSION_CODES.O; 5 import static android.os.Build.VERSION_CODES.P; 6 import static org.robolectric.RuntimeEnvironment.getApiLevel; 7 import static org.robolectric.util.reflector.Reflector.reflector; 8 9 import android.content.ClipData; 10 import android.content.ClipDescription; 11 import android.content.ClipboardManager; 12 import android.content.ClipboardManager.OnPrimaryClipChangedListener; 13 import android.os.SystemClock; 14 import java.util.Collection; 15 import java.util.concurrent.CopyOnWriteArrayList; 16 import org.robolectric.annotation.Implementation; 17 import org.robolectric.annotation.Implements; 18 import org.robolectric.annotation.RealObject; 19 import org.robolectric.util.ReflectionHelpers; 20 import org.robolectric.util.ReflectionHelpers.ClassParameter; 21 import org.robolectric.util.reflector.ForType; 22 23 @SuppressWarnings("UnusedDeclaration") 24 @Implements(ClipboardManager.class) 25 public class ShadowClipboardManager { 26 @RealObject private ClipboardManager realClipboardManager; 27 private final Collection<OnPrimaryClipChangedListener> listeners = 28 new CopyOnWriteArrayList<OnPrimaryClipChangedListener>(); 29 private ClipData clip; 30 31 @Implementation setPrimaryClip(ClipData clip)32 protected void setPrimaryClip(ClipData clip) { 33 if (getApiLevel() >= O) { 34 if (clip != null) { 35 final ClipDescription description = clip.getDescription(); 36 if (description != null) { 37 final long currentTimeMillis = SystemClock.uptimeMillis(); 38 ReflectionHelpers.callInstanceMethod( 39 ClipDescription.class, 40 description, 41 "setTimestamp", 42 ClassParameter.from(long.class, currentTimeMillis)); 43 } 44 } 45 } 46 if (getApiLevel() >= N) { 47 if (clip != null) { 48 clip.prepareToLeaveProcess(true); 49 } 50 } else { 51 if (clip != null) { 52 ReflectionHelpers.callInstanceMethod(ClipData.class, clip, "prepareToLeaveProcess"); 53 } 54 } 55 56 this.clip = clip; 57 58 for (OnPrimaryClipChangedListener listener : listeners) { 59 listener.onPrimaryClipChanged(); 60 } 61 } 62 63 @Implementation(minSdk = P) clearPrimaryClip()64 protected void clearPrimaryClip() { 65 setPrimaryClip(null); 66 } 67 68 @Implementation getPrimaryClip()69 protected ClipData getPrimaryClip() { 70 return clip; 71 } 72 73 @Implementation getPrimaryClipDescription()74 protected ClipDescription getPrimaryClipDescription() { 75 return clip == null ? null : clip.getDescription(); 76 } 77 78 @Implementation hasPrimaryClip()79 protected boolean hasPrimaryClip() { 80 return clip != null; 81 } 82 83 @Implementation addPrimaryClipChangedListener(OnPrimaryClipChangedListener listener)84 protected void addPrimaryClipChangedListener(OnPrimaryClipChangedListener listener) { 85 listeners.add(listener); 86 } 87 88 @Implementation removePrimaryClipChangedListener(OnPrimaryClipChangedListener listener)89 protected void removePrimaryClipChangedListener(OnPrimaryClipChangedListener listener) { 90 listeners.remove(listener); 91 } 92 93 @Implementation setText(CharSequence text)94 protected void setText(CharSequence text) { 95 setPrimaryClip(ClipData.newPlainText(null, text)); 96 } 97 98 @Implementation hasText()99 protected boolean hasText() { 100 CharSequence text = reflector(ClipboardManagerReflector.class, realClipboardManager).getText(); 101 return text != null && text.length() > 0; 102 } 103 104 @ForType(ClipboardManager.class) 105 interface ClipboardManagerReflector { getText()106 CharSequence getText(); 107 } 108 } 109