1 /** 2 * Copyright (c) 2010, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.content; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.SystemService; 22 import android.os.Handler; 23 import android.os.RemoteException; 24 import android.os.ServiceManager; 25 import android.os.ServiceManager.ServiceNotFoundException; 26 27 import com.android.internal.util.Preconditions; 28 29 import java.util.ArrayList; 30 31 /** 32 * Interface to the clipboard service, for placing and retrieving text in 33 * the global clipboard. 34 * 35 * <p> 36 * The ClipboardManager API itself is very simple: it consists of methods 37 * to atomically get and set the current primary clipboard data. That data 38 * is expressed as a {@link ClipData} object, which defines the protocol 39 * for data exchange between applications. 40 * 41 * <div class="special reference"> 42 * <h3>Developer Guides</h3> 43 * <p>For more information about using the clipboard framework, read the 44 * <a href="{@docRoot}guide/topics/clipboard/copy-paste.html">Copy and Paste</a> 45 * developer guide.</p> 46 * </div> 47 */ 48 @SystemService(Context.CLIPBOARD_SERVICE) 49 public class ClipboardManager extends android.text.ClipboardManager { 50 private final Context mContext; 51 private final Handler mHandler; 52 private final IClipboard mService; 53 54 private final ArrayList<OnPrimaryClipChangedListener> mPrimaryClipChangedListeners 55 = new ArrayList<OnPrimaryClipChangedListener>(); 56 57 private final IOnPrimaryClipChangedListener.Stub mPrimaryClipChangedServiceListener 58 = new IOnPrimaryClipChangedListener.Stub() { 59 @Override 60 public void dispatchPrimaryClipChanged() { 61 mHandler.post(() -> { 62 reportPrimaryClipChanged(); 63 }); 64 } 65 }; 66 67 /** 68 * Defines a listener callback that is invoked when the primary clip on the clipboard changes. 69 * Objects that want to register a listener call 70 * {@link android.content.ClipboardManager#addPrimaryClipChangedListener(OnPrimaryClipChangedListener) 71 * addPrimaryClipChangedListener()} with an 72 * object that implements OnPrimaryClipChangedListener. 73 * 74 */ 75 public interface OnPrimaryClipChangedListener { 76 77 /** 78 * Callback that is invoked by {@link android.content.ClipboardManager} when the primary 79 * clip changes. 80 */ onPrimaryClipChanged()81 void onPrimaryClipChanged(); 82 } 83 84 /** {@hide} */ ClipboardManager(Context context, Handler handler)85 public ClipboardManager(Context context, Handler handler) throws ServiceNotFoundException { 86 mContext = context; 87 mHandler = handler; 88 mService = IClipboard.Stub.asInterface( 89 ServiceManager.getServiceOrThrow(Context.CLIPBOARD_SERVICE)); 90 } 91 92 /** 93 * Sets the current primary clip on the clipboard. This is the clip that 94 * is involved in normal cut and paste operations. 95 * 96 * @param clip The clipped data item to set. 97 * @see #getPrimaryClip() 98 * @see #clearPrimaryClip() 99 */ setPrimaryClip(@onNull ClipData clip)100 public void setPrimaryClip(@NonNull ClipData clip) { 101 try { 102 Preconditions.checkNotNull(clip); 103 clip.prepareToLeaveProcess(true); 104 mService.setPrimaryClip(clip, mContext.getOpPackageName()); 105 } catch (RemoteException e) { 106 throw e.rethrowFromSystemServer(); 107 } 108 } 109 110 /** 111 * Clears any current primary clip on the clipboard. 112 * 113 * @see #setPrimaryClip(ClipData) 114 */ clearPrimaryClip()115 public void clearPrimaryClip() { 116 try { 117 mService.clearPrimaryClip(mContext.getOpPackageName()); 118 } catch (RemoteException e) { 119 throw e.rethrowFromSystemServer(); 120 } 121 } 122 123 /** 124 * Returns the current primary clip on the clipboard. 125 * 126 * @see #setPrimaryClip(ClipData) 127 */ getPrimaryClip()128 public @Nullable ClipData getPrimaryClip() { 129 try { 130 return mService.getPrimaryClip(mContext.getOpPackageName()); 131 } catch (RemoteException e) { 132 throw e.rethrowFromSystemServer(); 133 } 134 } 135 136 /** 137 * Returns a description of the current primary clip on the clipboard 138 * but not a copy of its data. 139 * 140 * @see #setPrimaryClip(ClipData) 141 */ getPrimaryClipDescription()142 public @Nullable ClipDescription getPrimaryClipDescription() { 143 try { 144 return mService.getPrimaryClipDescription(mContext.getOpPackageName()); 145 } catch (RemoteException e) { 146 throw e.rethrowFromSystemServer(); 147 } 148 } 149 150 /** 151 * Returns true if there is currently a primary clip on the clipboard. 152 */ hasPrimaryClip()153 public boolean hasPrimaryClip() { 154 try { 155 return mService.hasPrimaryClip(mContext.getOpPackageName()); 156 } catch (RemoteException e) { 157 throw e.rethrowFromSystemServer(); 158 } 159 } 160 addPrimaryClipChangedListener(OnPrimaryClipChangedListener what)161 public void addPrimaryClipChangedListener(OnPrimaryClipChangedListener what) { 162 synchronized (mPrimaryClipChangedListeners) { 163 if (mPrimaryClipChangedListeners.isEmpty()) { 164 try { 165 mService.addPrimaryClipChangedListener( 166 mPrimaryClipChangedServiceListener, mContext.getOpPackageName()); 167 } catch (RemoteException e) { 168 throw e.rethrowFromSystemServer(); 169 } 170 } 171 mPrimaryClipChangedListeners.add(what); 172 } 173 } 174 removePrimaryClipChangedListener(OnPrimaryClipChangedListener what)175 public void removePrimaryClipChangedListener(OnPrimaryClipChangedListener what) { 176 synchronized (mPrimaryClipChangedListeners) { 177 mPrimaryClipChangedListeners.remove(what); 178 if (mPrimaryClipChangedListeners.isEmpty()) { 179 try { 180 mService.removePrimaryClipChangedListener( 181 mPrimaryClipChangedServiceListener); 182 } catch (RemoteException e) { 183 throw e.rethrowFromSystemServer(); 184 } 185 } 186 } 187 } 188 189 /** 190 * @deprecated Use {@link #getPrimaryClip()} instead. This retrieves 191 * the primary clip and tries to coerce it to a string. 192 */ 193 @Deprecated getText()194 public CharSequence getText() { 195 ClipData clip = getPrimaryClip(); 196 if (clip != null && clip.getItemCount() > 0) { 197 return clip.getItemAt(0).coerceToText(mContext); 198 } 199 return null; 200 } 201 202 /** 203 * @deprecated Use {@link #setPrimaryClip(ClipData)} instead. This 204 * creates a ClippedItem holding the given text and sets it as the 205 * primary clip. It has no label or icon. 206 */ 207 @Deprecated setText(CharSequence text)208 public void setText(CharSequence text) { 209 setPrimaryClip(ClipData.newPlainText(null, text)); 210 } 211 212 /** 213 * @deprecated Use {@link #hasPrimaryClip()} instead. 214 */ 215 @Deprecated hasText()216 public boolean hasText() { 217 try { 218 return mService.hasClipboardText(mContext.getOpPackageName()); 219 } catch (RemoteException e) { 220 throw e.rethrowFromSystemServer(); 221 } 222 } 223 reportPrimaryClipChanged()224 void reportPrimaryClipChanged() { 225 Object[] listeners; 226 227 synchronized (mPrimaryClipChangedListeners) { 228 final int N = mPrimaryClipChangedListeners.size(); 229 if (N <= 0) { 230 return; 231 } 232 listeners = mPrimaryClipChangedListeners.toArray(); 233 } 234 235 for (int i=0; i<listeners.length; i++) { 236 ((OnPrimaryClipChangedListener)listeners[i]).onPrimaryClipChanged(); 237 } 238 } 239 } 240