Bug 876485 - Moved clipboard code from GeckoAppShell to a separate Clipboard class. Changed jni calls to new methods. r=cpeterson

This commit is contained in:
Federico Paolinelli 2013-06-07 13:39:37 -07:00
parent d775786d2c
commit 55bcb1df9b
9 changed files with 138 additions and 99 deletions

View File

@ -15,6 +15,7 @@ import org.mozilla.gecko.gfx.LayerView;
import org.mozilla.gecko.gfx.PanZoomController;
import org.mozilla.gecko.health.BrowserHealthReporter;
import org.mozilla.gecko.menu.GeckoMenu;
import org.mozilla.gecko.util.Clipboard;
import org.mozilla.gecko.util.FloatUtils;
import org.mozilla.gecko.util.GamepadUtils;
import org.mozilla.gecko.util.HardwareUtils;
@ -510,7 +511,7 @@ abstract public class BrowserApp extends GeckoApp
public boolean onContextItemSelected(MenuItem item) {
switch(item.getItemId()) {
case R.id.pasteandgo: {
String text = GeckoAppShell.getClipboardText();
String text = Clipboard.getText();
if (!TextUtils.isEmpty(text)) {
Tabs.getInstance().loadUrl(text);
}
@ -521,7 +522,7 @@ abstract public class BrowserApp extends GeckoApp
return true;
}
case R.id.paste: {
String text = GeckoAppShell.getClipboardText();
String text = Clipboard.getText();
if (!TextUtils.isEmpty(text)) {
showAwesomebar(AwesomeBar.Target.CURRENT_TAB, text);
}
@ -549,7 +550,7 @@ abstract public class BrowserApp extends GeckoApp
if (tab != null) {
String url = tab.getURL();
if (url != null) {
GeckoAppShell.setClipboardText(url);
Clipboard.setText(url);
}
}
return true;

View File

@ -11,6 +11,7 @@ import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
import org.mozilla.gecko.gfx.LayerView;
import org.mozilla.gecko.menu.GeckoMenu;
import org.mozilla.gecko.menu.MenuPopup;
import org.mozilla.gecko.util.Clipboard;
import org.mozilla.gecko.util.StringUtils;
import org.mozilla.gecko.util.HardwareUtils;
@ -198,7 +199,7 @@ public class BrowserToolbar implements Tabs.OnTabsChangedListener,
MenuInflater inflater = mActivity.getMenuInflater();
inflater.inflate(R.menu.titlebar_contextmenu, menu);
String clipboard = GeckoAppShell.getClipboardText();
String clipboard = Clipboard.getText();
if (TextUtils.isEmpty(clipboard)) {
menu.findItem(R.id.pasteandgo).setVisible(false);
menu.findItem(R.id.paste).setVisible(false);

View File

@ -20,7 +20,6 @@ import android.app.Activity;
import android.app.ActivityManager;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@ -102,7 +101,6 @@ import java.util.Map;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.concurrent.SynchronousQueue;
public class GeckoAppShell
{
@ -1231,85 +1229,6 @@ public class GeckoAppShell
return intent;
}
/* On some devices, access to the clipboard service needs to happen
* on a thread with a looper, so this function requires a looper is
* present on the thread. */
private static String getClipboardTextImpl() {
Context context = getContext();
if (android.os.Build.VERSION.SDK_INT >= 11) {
android.content.ClipboardManager cm = (android.content.ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE);
if (cm.hasPrimaryClip()) {
ClipData clip = cm.getPrimaryClip();
if (clip != null) {
ClipData.Item item = clip.getItemAt(0);
return item.coerceToText(context).toString();
}
}
} else {
android.text.ClipboardManager cm = (android.text.ClipboardManager)context.getSystemService(Context.CLIPBOARD_SERVICE);
if (cm.hasText()) {
return cm.getText().toString();
}
}
return null;
}
private static SynchronousQueue<String> sClipboardQueue = new SynchronousQueue<String>();
static String getClipboardText() {
// If we're on the UI thread or the background thread, we have a looper on the thread
// and can just call this directly. For any other threads, post the call to the
// background thread.
if (ThreadUtils.isOnUiThread() || ThreadUtils.isOnBackgroundThread()) {
return getClipboardTextImpl();
}
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
String text = getClipboardTextImpl();
try {
sClipboardQueue.put(text != null ? text : "");
} catch (InterruptedException ie) {}
}
});
try {
return sClipboardQueue.take();
} catch (InterruptedException ie) {
return "";
}
}
static void setClipboardText(String copiedText) {
// Copy an empty string instead of null to avoid clipboard crashes.
// AndroidBridge::EmptyClipboard() passes null to clear the clipboard's current contents.
final String text = (copiedText != null) ? copiedText : "";
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
Context context = getContext();
if (android.os.Build.VERSION.SDK_INT >= 11) {
android.content.ClipboardManager cm = (android.content.ClipboardManager)
context.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("Text", text);
try {
cm.setPrimaryClip(clip);
} catch (NullPointerException e) {
// Bug 776223: This is a Samsung clipboard bug. setPrimaryClip() can throw
// a NullPointerException if Samsung's /data/clipboard directory is full.
// Fortunately, the text is still successfully copied to the clipboard.
}
} else {
android.text.ClipboardManager cm = (android.text.ClipboardManager)
context.getSystemService(Context.CLIPBOARD_SERVICE);
cm.setText(text);
}
}
});
}
public static void setNotificationClient(NotificationClient client) {
if (sNotificationClient == null) {
sNotificationClient = client;

View File

@ -7,6 +7,7 @@ package org.mozilla.gecko;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.mozglue.GeckoLoader;
import org.mozilla.gecko.util.Clipboard;
import org.mozilla.gecko.util.HardwareUtils;
import org.mozilla.gecko.util.ThreadUtils;
@ -79,6 +80,7 @@ public class GeckoApplication extends Application {
@Override
public void onCreate() {
HardwareUtils.init(getApplicationContext());
Clipboard.init(getApplicationContext());
GeckoLoader.loadMozGlue(getApplicationContext());
super.onCreate();
}

View File

@ -6,6 +6,7 @@
package org.mozilla.gecko;
import org.mozilla.gecko.gfx.InputConnectionHandler;
import org.mozilla.gecko.util.Clipboard;
import org.mozilla.gecko.util.GamepadUtils;
import org.mozilla.gecko.util.ThreadUtils;
@ -271,10 +272,10 @@ class GeckoInputConnection
// If selection is empty, we'll select everything
if (selStart == selEnd) {
// Fill the clipboard
GeckoAppShell.setClipboardText(editable.toString());
Clipboard.setText(editable);
editable.clear();
} else {
GeckoAppShell.setClipboardText(
Clipboard.setText(
editable.toString().substring(
Math.min(selStart, selEnd),
Math.max(selStart, selEnd)));
@ -282,7 +283,7 @@ class GeckoInputConnection
}
break;
case R.id.paste:
commitText(GeckoAppShell.getClipboardText(), 1);
commitText(Clipboard.getText(), 1);
break;
case R.id.copy:
// Copy the current selection or the empty string if nothing is selected.
@ -290,7 +291,7 @@ class GeckoInputConnection
editable.toString().substring(
Math.min(selStart, selEnd),
Math.max(selStart, selEnd));
GeckoAppShell.setClipboardText(copiedText);
Clipboard.setText(copiedText);
break;
}
return true;

View File

@ -27,6 +27,7 @@ MOZGLUE_PP_JAVA_FILES := \
UTIL_JAVA_FILES := \
util/ActivityResultHandler.java \
util/ActivityResultHandlerMap.java \
util/Clipboard.java \
util/EventDispatcher.java \
util/FloatUtils.java \
util/GamepadUtils.java \

View File

@ -0,0 +1,110 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.util;
import android.content.ClipData;
import android.content.Context;
import android.os.Build;
import android.util.Log;
import java.util.concurrent.SynchronousQueue;
public final class Clipboard {
private static Context mContext;
private final static String LOG_TAG = "Clipboard";
private final static SynchronousQueue<String> sClipboardQueue = new SynchronousQueue<String>();
private Clipboard() {
}
public static void init(Context c) {
if (mContext != null) {
Log.w(LOG_TAG, "Clipboard.init() called twice!");
return;
}
mContext = c;
}
public static String getText() {
// If we're on the UI thread or the background thread, we have a looper on the thread
// and can just call this directly. For any other threads, post the call to the
// background thread.
if (ThreadUtils.isOnUiThread() || ThreadUtils.isOnBackgroundThread()) {
return getClipboardTextImpl();
}
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
String text = getClipboardTextImpl();
try {
sClipboardQueue.put(text != null ? text : "");
} catch (InterruptedException ie) {}
}
});
try {
return sClipboardQueue.take();
} catch (InterruptedException ie) {
return "";
}
}
public static void setText(final CharSequence text) {
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
@SuppressWarnings("deprecation")
public void run() {
if (Build.VERSION.SDK_INT >= 11) {
android.content.ClipboardManager cm = getClipboardManager11(mContext);
ClipData clip = ClipData.newPlainText("Text", text);
try {
cm.setPrimaryClip(clip);
} catch (NullPointerException e) {
// Bug 776223: This is a Samsung clipboard bug. setPrimaryClip() can throw
// a NullPointerException if Samsung's /data/clipboard directory is full.
// Fortunately, the text is still successfully copied to the clipboard.
}
} else {
android.text.ClipboardManager cm = getClipboardManager(mContext);
cm.setText(text);
}
}
});
}
private static android.content.ClipboardManager getClipboardManager11(Context context) {
// In API Level 11 and above, CLIPBOARD_SERVICE returns android.content.ClipboardManager,
// which is a subclass of android.text.ClipboardManager.
return (android.content.ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
}
private static android.text.ClipboardManager getClipboardManager(Context context) {
return (android.text.ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
}
/* On some devices, access to the clipboard service needs to happen
* on a thread with a looper, so this function requires a looper is
* present on the thread. */
@SuppressWarnings("deprecation")
private static String getClipboardTextImpl() {
if (Build.VERSION.SDK_INT >= 11) {
android.content.ClipboardManager cm = getClipboardManager11(mContext);
if (cm.hasPrimaryClip()) {
ClipData clip = cm.getPrimaryClip();
if (clip != null) {
ClipData.Item item = clip.getItemAt(0);
return item.coerceToText(mContext).toString();
}
}
} else {
android.text.ClipboardManager cm = getClipboardManager(mContext);
if (cm.hasText()) {
return cm.getText().toString();
}
}
return null;
}
}

View File

@ -123,8 +123,6 @@ AndroidBridge::Init(JNIEnv *jEnv,
jGetMimeTypeFromExtensions = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getMimeTypeFromExtensions", "(Ljava/lang/String;)Ljava/lang/String;");
jGetExtensionFromMimeType = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getExtensionFromMimeType", "(Ljava/lang/String;)Ljava/lang/String;");
jMoveTaskToBack = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "moveTaskToBack", "()V");
jGetClipboardText = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getClipboardText", "()Ljava/lang/String;");
jSetClipboardText = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "setClipboardText", "(Ljava/lang/String;)V");
jShowAlertNotification = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "showAlertNotification", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
jShowFilePickerForExtensions = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "showFilePickerForExtensions", "(Ljava/lang/String;)Ljava/lang/String;");
jShowFilePickerForMimeType = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "showFilePickerForMimeType", "(Ljava/lang/String;)Ljava/lang/String;");
@ -242,6 +240,10 @@ AndroidBridge::Init(JNIEnv *jEnv,
jGetContext = (jmethodID)jEnv->GetStaticMethodID(jGeckoAppShellClass, "getContext", "()Landroid/content/Context;");
jClipboardClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("org/mozilla/gecko/util/Clipboard"));
jClipboardGetText = (jmethodID) jEnv->GetStaticMethodID(jClipboardClass, "getText", "()Ljava/lang/String;");
jClipboardSetText = (jmethodID) jEnv->GetStaticMethodID(jClipboardClass, "setText", "(Ljava/lang/CharSequence;)V");
InitAndroidJavaWrappers(jEnv);
// jEnv should NOT be cached here by anything -- the jEnv here
@ -632,8 +634,8 @@ AndroidBridge::GetClipboardText(nsAString& aText)
AutoLocalJNIFrame jniFrame(env);
jstring jstrType = static_cast<jstring>(
env->CallStaticObjectMethod(mGeckoAppShellClass,
jGetClipboardText));
env->CallStaticObjectMethod(jClipboardClass,
jClipboardGetText));
if (jniFrame.CheckForException() || !jstrType)
return false;
@ -653,7 +655,7 @@ AndroidBridge::SetClipboardText(const nsAString& aText)
AutoLocalJNIFrame jniFrame(env);
jstring jstr = NewJavaString(&jniFrame, aText);
env->CallStaticVoidMethod(mGeckoAppShellClass, jSetClipboardText, jstr);
env->CallStaticVoidMethod(jClipboardClass, jClipboardSetText, jstr);
}
bool
@ -667,8 +669,8 @@ AndroidBridge::ClipboardHasText()
AutoLocalJNIFrame jniFrame(env);
jstring jstrType = static_cast<jstring>(
env->CallStaticObjectMethod(mGeckoAppShellClass,
jGetClipboardText));
env->CallStaticObjectMethod(jClipboardClass,
jClipboardGetText));
if (jniFrame.CheckForException() || !jstrType)
return false;
@ -685,7 +687,7 @@ AndroidBridge::EmptyClipboard()
return;
AutoLocalJNIFrame jniFrame(env, 0);
env->CallStaticVoidMethod(mGeckoAppShellClass, jSetClipboardText, nullptr);
env->CallStaticVoidMethod(jClipboardClass, jClipboardSetText, nullptr);
}
void

View File

@ -473,8 +473,6 @@ protected:
jmethodID jGetMimeTypeFromExtensions;
jmethodID jGetExtensionFromMimeType;
jmethodID jMoveTaskToBack;
jmethodID jGetClipboardText;
jmethodID jSetClipboardText;
jmethodID jShowAlertNotification;
jmethodID jShowFilePickerForExtensions;
jmethodID jShowFilePickerForMimeType;
@ -562,6 +560,10 @@ protected:
jmethodID jRequestContentRepaint;
jmethodID jPostDelayedCallback;
jclass jClipboardClass;
jmethodID jClipboardGetText;
jmethodID jClipboardSetText;
// some convinient types to have around
jclass jStringClass;