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