1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.ui.base;
6
7import android.content.ClipData;
8import android.content.ClipboardManager;
9import android.content.Context;
10import android.widget.Toast;
11
12import org.chromium.base.ApiCompatibilityUtils;
13import org.chromium.base.CalledByNative;
14import org.chromium.base.JNINamespace;
15import org.chromium.ui.R;
16
17/**
18 * Simple proxy that provides C++ code with an access pathway to the Android
19 * clipboard.
20 */
21@JNINamespace("ui")
22public class Clipboard {
23    // Necessary for coercing clipboard contents to text if they require
24    // access to network resources, etceteras (e.g., URI in clipboard)
25    private final Context mContext;
26
27    private final ClipboardManager mClipboardManager;
28
29    /**
30     * Use the factory constructor instead.
31     *
32     * @param context for accessing the clipboard
33     */
34    public Clipboard(final Context context) {
35        mContext = context;
36        mClipboardManager = (ClipboardManager)
37                context.getSystemService(Context.CLIPBOARD_SERVICE);
38    }
39
40    /**
41     * Returns a new Clipboard object bound to the specified context.
42     *
43     * @param context for accessing the clipboard
44     * @return the new object
45     */
46    @CalledByNative
47    private static Clipboard create(final Context context) {
48        return new Clipboard(context);
49    }
50
51    /**
52     * Emulates the behavior of the now-deprecated
53     * {@link android.text.ClipboardManager#getText()} by invoking
54     * {@link android.content.ClipData.Item#coerceToText(Context)} on the first
55     * item in the clipboard (if any) and returning the result as a string.
56     * <p>
57     * This is quite different than simply calling {@link Object#toString()} on
58     * the clip; consumers of this API should familiarize themselves with the
59     * process described in
60     * {@link android.content.ClipData.Item#coerceToText(Context)} before using
61     * this method.
62     *
63     * @return a string representation of the first item on the clipboard, if
64     *         the clipboard currently has an item and coercion of the item into
65     *         a string is possible; otherwise, <code>null</code>
66     */
67    @SuppressWarnings("javadoc")
68    @CalledByNative
69    private String getCoercedText() {
70        final ClipData clip = mClipboardManager.getPrimaryClip();
71        if (clip != null && clip.getItemCount() > 0) {
72            final CharSequence sequence = clip.getItemAt(0).coerceToText(mContext);
73            if (sequence != null) {
74                return sequence.toString();
75            }
76        }
77        return null;
78    }
79
80    /**
81     * Gets the HTML text of top item on the primary clip on the Android clipboard.
82     *
83     * @return a Java string with the html text if any, or null if there is no html
84     *         text or no entries on the primary clip.
85     */
86    @CalledByNative
87    private String getHTMLText() {
88        if (isHTMLClipboardSupported()) {
89            final ClipData clip = mClipboardManager.getPrimaryClip();
90            if (clip != null && clip.getItemCount() > 0) {
91                return clip.getItemAt(0).getHtmlText();
92            }
93        }
94        return null;
95    }
96
97    /**
98     * Emulates the behavior of the now-deprecated
99     * {@link android.text.ClipboardManager#setText(CharSequence)}, setting the
100     * clipboard's current primary clip to a plain-text clip that consists of
101     * the specified string.
102     *
103     * @param label will become the label of the clipboard's primary clip
104     * @param text  will become the content of the clipboard's primary clip
105     */
106    public void setText(final String label, final String text) {
107        setPrimaryClipNoException(ClipData.newPlainText(label, text));
108    }
109
110    /**
111     * Emulates the behavior of the now-deprecated
112     * {@link android.text.ClipboardManager#setText(CharSequence)}, setting the
113     * clipboard's current primary clip to a plain-text clip that consists of
114     * the specified string.
115     *
116     * @param text will become the content of the clipboard's primary clip
117     */
118    @CalledByNative
119    public void setText(final String text) {
120        setText(null, text);
121    }
122
123    /**
124     * Writes HTML to the clipboard, together with a plain-text representation
125     * of that very data. This API is only available in Android JellyBean+ and
126     * will be a no-operation in older versions.
127     *
128     * @param html  The HTML content to be pasted to the clipboard.
129     * @param label The Plain-text label for the HTML content.
130     * @param text  Plain-text representation of the HTML content.
131     */
132    public void setHTMLText(final String html, final String label, final String text) {
133        if (isHTMLClipboardSupported()) {
134            setPrimaryClipNoException(ClipData.newHtmlText(label, text, html));
135        }
136    }
137
138    /**
139     * Writes HTML to the clipboard, together with a plain-text representation
140     * of that very data. This API is only available in Android JellyBean+ and
141     * will be a no-operation in older versions.
142     *
143     * @param html The HTML content to be pasted to the clipboard.
144     * @param text Plain-text representation of the HTML content.
145     */
146    @CalledByNative
147    public void setHTMLText(final String html, final String text) {
148        setHTMLText(html, null, text);
149    }
150
151    @CalledByNative
152    private static boolean isHTMLClipboardSupported() {
153        return ApiCompatibilityUtils.isHTMLClipboardSupported();
154    }
155
156    private void setPrimaryClipNoException(ClipData clip) {
157        try {
158            mClipboardManager.setPrimaryClip(clip);
159        } catch (Exception ex) {
160            // Ignore any exceptions here as certain devices have bugs and will fail.
161            String text = mContext.getString(R.string.copy_to_clipboard_failure_message);
162            Toast.makeText(mContext, text, Toast.LENGTH_SHORT).show();
163        }
164    }
165}
166