Home | History | Annotate | Download | only in content
      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.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          */
     82         void onPrimaryClipChanged();
     83     }
     84 
     85     /** {@hide} */
     86     @UnsupportedAppUsage
     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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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 
    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
    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
    222     public void setText(CharSequence text) {
    223         setPrimaryClip(ClipData.newPlainText(null, text));
    224     }
    225 
    226     /**
    227      * @deprecated Use {@link #hasPrimaryClip()} instead.
    228      */
    229     @Deprecated
    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
    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