• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 java.util.ArrayList;
29 import java.util.Objects;
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} */
85     @UnsupportedAppUsage
ClipboardManager(Context context, Handler handler)86     public ClipboardManager(Context context, Handler handler) throws ServiceNotFoundException {
87         mContext = context;
88         mHandler = handler;
89         mService = IClipboard.Stub.asInterface(
90                 ServiceManager.getServiceOrThrow(Context.CLIPBOARD_SERVICE));
91     }
92 
93     /**
94      * Sets the current primary clip on the clipboard.  This is the clip that
95      * is involved in normal cut and paste operations.
96      *
97      * @param clip The clipped data item to set.
98      * @see #getPrimaryClip()
99      * @see #clearPrimaryClip()
100      */
setPrimaryClip(@onNull ClipData clip)101     public void setPrimaryClip(@NonNull ClipData clip) {
102         try {
103             Objects.requireNonNull(clip);
104             clip.prepareToLeaveProcess(true);
105             mService.setPrimaryClip(clip, mContext.getOpPackageName(), mContext.getUserId());
106         } catch (RemoteException e) {
107             throw e.rethrowFromSystemServer();
108         }
109     }
110 
111     /**
112      * Clears any current primary clip on the clipboard.
113      *
114      * @see #setPrimaryClip(ClipData)
115      */
clearPrimaryClip()116     public void clearPrimaryClip() {
117         try {
118             mService.clearPrimaryClip(mContext.getOpPackageName(), mContext.getUserId());
119         } catch (RemoteException e) {
120             throw e.rethrowFromSystemServer();
121         }
122     }
123 
124     /**
125      * Returns the current primary clip on the clipboard.
126      *
127      * <em>If the application is not the default IME or does not have input focus this return
128      * {@code null}.</em>
129      *
130      * @see #setPrimaryClip(ClipData)
131      */
getPrimaryClip()132     public @Nullable ClipData getPrimaryClip() {
133         try {
134             return mService.getPrimaryClip(mContext.getOpPackageName(), mContext.getUserId());
135         } catch (RemoteException e) {
136             throw e.rethrowFromSystemServer();
137         }
138     }
139 
140     /**
141      * Returns a description of the current primary clip on the clipboard
142      * but not a copy of its data.
143      *
144      * <em>If the application is not the default IME or does not have input focus this return
145      * {@code null}.</em>
146      *
147      * @see #setPrimaryClip(ClipData)
148      */
getPrimaryClipDescription()149     public @Nullable ClipDescription getPrimaryClipDescription() {
150         try {
151             return mService.getPrimaryClipDescription(mContext.getOpPackageName(),
152                     mContext.getUserId());
153         } catch (RemoteException e) {
154             throw e.rethrowFromSystemServer();
155         }
156     }
157 
158     /**
159      * Returns true if there is currently a primary clip on the clipboard.
160      *
161      * <em>If the application is not the default IME or the does not have input focus this will
162      * return {@code false}.</em>
163      */
hasPrimaryClip()164     public boolean hasPrimaryClip() {
165         try {
166             return mService.hasPrimaryClip(mContext.getOpPackageName(), mContext.getUserId());
167         } catch (RemoteException e) {
168             throw e.rethrowFromSystemServer();
169         }
170     }
171 
addPrimaryClipChangedListener(OnPrimaryClipChangedListener what)172     public void addPrimaryClipChangedListener(OnPrimaryClipChangedListener what) {
173         synchronized (mPrimaryClipChangedListeners) {
174             if (mPrimaryClipChangedListeners.isEmpty()) {
175                 try {
176                     mService.addPrimaryClipChangedListener(
177                             mPrimaryClipChangedServiceListener, mContext.getOpPackageName(),
178                             mContext.getUserId());
179                 } catch (RemoteException e) {
180                     throw e.rethrowFromSystemServer();
181                 }
182             }
183             mPrimaryClipChangedListeners.add(what);
184         }
185     }
186 
removePrimaryClipChangedListener(OnPrimaryClipChangedListener what)187     public void removePrimaryClipChangedListener(OnPrimaryClipChangedListener what) {
188         synchronized (mPrimaryClipChangedListeners) {
189             mPrimaryClipChangedListeners.remove(what);
190             if (mPrimaryClipChangedListeners.isEmpty()) {
191                 try {
192                     mService.removePrimaryClipChangedListener(
193                             mPrimaryClipChangedServiceListener, mContext.getOpPackageName(),
194                             mContext.getUserId());
195                 } catch (RemoteException e) {
196                     throw e.rethrowFromSystemServer();
197                 }
198             }
199         }
200     }
201 
202     /**
203      * @deprecated Use {@link #getPrimaryClip()} instead.  This retrieves
204      * the primary clip and tries to coerce it to a string.
205      */
206     @Deprecated
getText()207     public CharSequence getText() {
208         ClipData clip = getPrimaryClip();
209         if (clip != null && clip.getItemCount() > 0) {
210             return clip.getItemAt(0).coerceToText(mContext);
211         }
212         return null;
213     }
214 
215     /**
216      * @deprecated Use {@link #setPrimaryClip(ClipData)} instead.  This
217      * creates a ClippedItem holding the given text and sets it as the
218      * primary clip.  It has no label or icon.
219      */
220     @Deprecated
setText(CharSequence text)221     public void setText(CharSequence text) {
222         setPrimaryClip(ClipData.newPlainText(null, text));
223     }
224 
225     /**
226      * @deprecated Use {@link #hasPrimaryClip()} instead.
227      */
228     @Deprecated
hasText()229     public boolean hasText() {
230         try {
231             return mService.hasClipboardText(mContext.getOpPackageName(), mContext.getUserId());
232         } catch (RemoteException e) {
233             throw e.rethrowFromSystemServer();
234         }
235     }
236 
237     @UnsupportedAppUsage
reportPrimaryClipChanged()238     void reportPrimaryClipChanged() {
239         Object[] listeners;
240 
241         synchronized (mPrimaryClipChangedListeners) {
242             final int N = mPrimaryClipChangedListeners.size();
243             if (N <= 0) {
244                 return;
245             }
246             listeners = mPrimaryClipChangedListeners.toArray();
247         }
248 
249         for (int i=0; i<listeners.length; i++) {
250             ((OnPrimaryClipChangedListener)listeners[i]).onPrimaryClipChanged();
251         }
252     }
253 }
254