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.content.Context; 20 import android.os.Message; 21 import android.os.RemoteException; 22 import android.os.Handler; 23 import android.os.IBinder; 24 import android.os.ServiceManager; 25 import android.os.StrictMode; 26 import android.util.Log; 27 28 import java.util.ArrayList; 29 30 /** 31 * Interface to the clipboard service, for placing and retrieving text in 32 * the global clipboard. 33 * 34 * <p> 35 * You do not instantiate this class directly; instead, retrieve it through 36 * {@link android.content.Context#getSystemService}. 37 * 38 * <p> 39 * The ClipboardManager API itself is very simple: it consists of methods 40 * to atomically get and set the current primary clipboard data. That data 41 * is expressed as a {@link ClipData} object, which defines the protocol 42 * for data exchange between applications. 43 * 44 * <div class="special reference"> 45 * <h3>Developer Guides</h3> 46 * <p>For more information about using the clipboard framework, read the 47 * <a href="{@docRoot}guide/topics/clipboard/copy-paste.html">Copy and Paste</a> 48 * developer guide.</p> 49 * </div> 50 * 51 * @see android.content.Context#getSystemService 52 */ 53 public class ClipboardManager extends android.text.ClipboardManager { 54 private final static Object sStaticLock = new Object(); 55 private static IClipboard sService; 56 57 private final Context mContext; 58 59 private final ArrayList<OnPrimaryClipChangedListener> mPrimaryClipChangedListeners 60 = new ArrayList<OnPrimaryClipChangedListener>(); 61 62 private final IOnPrimaryClipChangedListener.Stub mPrimaryClipChangedServiceListener 63 = new IOnPrimaryClipChangedListener.Stub() { 64 public void dispatchPrimaryClipChanged() { 65 mHandler.sendEmptyMessage(MSG_REPORT_PRIMARY_CLIP_CHANGED); 66 } 67 }; 68 69 static final int MSG_REPORT_PRIMARY_CLIP_CHANGED = 1; 70 71 private final Handler mHandler = new Handler() { 72 @Override 73 public void handleMessage(Message msg) { 74 switch (msg.what) { 75 case MSG_REPORT_PRIMARY_CLIP_CHANGED: 76 reportPrimaryClipChanged(); 77 } 78 } 79 }; 80 81 /** 82 * Defines a listener callback that is invoked when the primary clip on the clipboard changes. 83 * Objects that want to register a listener call 84 * {@link android.content.ClipboardManager#addPrimaryClipChangedListener(OnPrimaryClipChangedListener) 85 * addPrimaryClipChangedListener()} with an 86 * object that implements OnPrimaryClipChangedListener. 87 * 88 */ 89 public interface OnPrimaryClipChangedListener { 90 91 /** 92 * Callback that is invoked by {@link android.content.ClipboardManager} when the primary 93 * clip changes. 94 */ 95 void onPrimaryClipChanged(); 96 } 97 98 static private IClipboard getService() { 99 synchronized (sStaticLock) { 100 if (sService != null) { 101 return sService; 102 } 103 IBinder b = ServiceManager.getService("clipboard"); 104 sService = IClipboard.Stub.asInterface(b); 105 return sService; 106 } 107 } 108 109 /** {@hide} */ 110 public ClipboardManager(Context context, Handler handler) { 111 mContext = context; 112 } 113 114 /** 115 * Sets the current primary clip on the clipboard. This is the clip that 116 * is involved in normal cut and paste operations. 117 * 118 * @param clip The clipped data item to set. 119 */ 120 public void setPrimaryClip(ClipData clip) { 121 try { 122 if (clip != null) { 123 clip.prepareToLeaveProcess(); 124 } 125 getService().setPrimaryClip(clip, mContext.getOpPackageName()); 126 } catch (RemoteException e) { 127 } 128 } 129 130 /** 131 * Returns the current primary clip on the clipboard. 132 */ 133 public ClipData getPrimaryClip() { 134 try { 135 return getService().getPrimaryClip(mContext.getOpPackageName()); 136 } catch (RemoteException e) { 137 return null; 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 public ClipDescription getPrimaryClipDescription() { 146 try { 147 return getService().getPrimaryClipDescription(mContext.getOpPackageName()); 148 } catch (RemoteException e) { 149 return null; 150 } 151 } 152 153 /** 154 * Returns true if there is currently a primary clip on the clipboard. 155 */ 156 public boolean hasPrimaryClip() { 157 try { 158 return getService().hasPrimaryClip(mContext.getOpPackageName()); 159 } catch (RemoteException e) { 160 return false; 161 } 162 } 163 164 public void addPrimaryClipChangedListener(OnPrimaryClipChangedListener what) { 165 synchronized (mPrimaryClipChangedListeners) { 166 if (mPrimaryClipChangedListeners.size() == 0) { 167 try { 168 getService().addPrimaryClipChangedListener( 169 mPrimaryClipChangedServiceListener, mContext.getOpPackageName()); 170 } catch (RemoteException e) { 171 } 172 } 173 mPrimaryClipChangedListeners.add(what); 174 } 175 } 176 177 public void removePrimaryClipChangedListener(OnPrimaryClipChangedListener what) { 178 synchronized (mPrimaryClipChangedListeners) { 179 mPrimaryClipChangedListeners.remove(what); 180 if (mPrimaryClipChangedListeners.size() == 0) { 181 try { 182 getService().removePrimaryClipChangedListener( 183 mPrimaryClipChangedServiceListener); 184 } catch (RemoteException e) { 185 } 186 } 187 } 188 } 189 190 /** 191 * @deprecated Use {@link #getPrimaryClip()} instead. This retrieves 192 * the primary clip and tries to coerce it to a string. 193 */ 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 public void setText(CharSequence text) { 208 setPrimaryClip(ClipData.newPlainText(null, text)); 209 } 210 211 /** 212 * @deprecated Use {@link #hasPrimaryClip()} instead. 213 */ 214 public boolean hasText() { 215 try { 216 return getService().hasClipboardText(mContext.getOpPackageName()); 217 } catch (RemoteException e) { 218 return false; 219 } 220 } 221 222 void reportPrimaryClipChanged() { 223 Object[] listeners; 224 225 synchronized (mPrimaryClipChangedListeners) { 226 final int N = mPrimaryClipChangedListeners.size(); 227 if (N <= 0) { 228 return; 229 } 230 listeners = mPrimaryClipChangedListeners.toArray(); 231 } 232 233 for (int i=0; i<listeners.length; i++) { 234 ((OnPrimaryClipChangedListener)listeners[i]).onPrimaryClipChanged(); 235 } 236 } 237 } 238