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