Index: content/public/android/java/src/org/chromium/content/browser/SelectActionModeCallback.java |
diff --git a/content/public/android/java/src/org/chromium/content/browser/SelectActionModeCallback.java b/content/public/android/java/src/org/chromium/content/browser/SelectActionModeCallback.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ba7c4e15d865093d39807ebcc0138873f0f815b1 |
--- /dev/null |
+++ b/content/public/android/java/src/org/chromium/content/browser/SelectActionModeCallback.java |
@@ -0,0 +1,258 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.content.browser; |
+ |
+import android.app.SearchManager; |
+import android.content.ClipboardManager; |
+import android.content.Context; |
+import android.content.Intent; |
+import android.content.pm.PackageManager; |
+import android.content.res.TypedArray; |
+import android.provider.Browser; |
+import android.text.TextUtils; |
+import android.view.ActionMode; |
+import android.view.Menu; |
+import android.view.MenuItem; |
+ |
+import org.chromium.content.app.AppResource; |
+ |
+/** |
+ * An ActionMode.Callback for in-page selection. This class handles both the editable and |
+ * non-editable cases. |
+ */ |
+public class SelectActionModeCallback implements ActionMode.Callback { |
+ private static final int SELECT_ALL_ATTR_INDEX = 0; |
+ private static final int CUT_ATTR_INDEX = 1; |
+ private static final int COPY_ATTR_INDEX = 2; |
+ private static final int PASTE_ATTR_INDEX = 3; |
+ private static final int[] ACTION_MODE_ATTRS = { |
+ android.R.attr.actionModeSelectAllDrawable, |
+ android.R.attr.actionModeCutDrawable, |
+ android.R.attr.actionModeCopyDrawable, |
+ android.R.attr.actionModePasteDrawable, |
+ }; |
+ |
+ private static final int ID_SELECTALL = 0; |
+ private static final int ID_COPY = 1; |
+ private static final int ID_SHARE = 2; |
+ private static final int ID_SEARCH = 3; |
+ private static final int ID_CUT = 4; |
+ private static final int ID_PASTE = 5; |
+ |
+ /** |
+ * An interface to retrieve information about the current selection, and also to perform |
+ * actions based on the selection or when the action bar is dismissed. |
+ */ |
+ public interface ActionHandler { |
+ /** |
+ * Perform a select all action. |
+ * @return true iff the action was successful. |
+ */ |
+ boolean selectAll(); |
+ |
+ /** |
+ * Perform a copy (to clipboard) action. |
+ * @return true iff the action was successful. |
+ */ |
+ boolean copy(); |
+ |
+ /** |
+ * Perform a cut (to clipboard) action. |
+ * @return true iff the action was successful. |
+ */ |
+ boolean cut(); |
+ |
+ /** |
+ * Perform a paste action. |
+ * @return true iff the action was successful. |
+ */ |
+ boolean paste(); |
+ |
+ /** |
+ * @return true iff the current selection is editable (e.g. text within an input field). |
+ */ |
+ boolean isSelectionEditable(); |
+ |
+ /** |
+ * @return the currently selected text String. |
+ */ |
+ String getSelectedText(); |
+ |
+ /** |
+ * Called when the onDestroyActionMode of the SelectActionmodeCallback is called. |
+ */ |
+ void onDestroyActionMode(); |
+ } |
+ |
+ private Context mContext; |
+ private ActionHandler mActionHandler; |
+ private final boolean mIncognito; |
+ private boolean mEditable; |
+ |
+ protected SelectActionModeCallback( |
+ Context context, ActionHandler actionHandler, boolean incognito) { |
+ mContext = context; |
+ mActionHandler = actionHandler; |
+ mIncognito = incognito; |
+ } |
+ |
+ protected Context getContext() { |
+ return mContext; |
+ } |
+ |
+ @Override |
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) { |
+ mode.setSubtitle(null); |
+ mEditable = mActionHandler.isSelectionEditable(); |
+ createActionMenu(mode, menu); |
+ return true; |
+ } |
+ |
+ @Override |
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) { |
+ boolean isEditableNow = mActionHandler.isSelectionEditable(); |
+ if (mEditable != isEditableNow) { |
+ mEditable = isEditableNow; |
+ menu.clear(); |
+ createActionMenu(mode, menu); |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ private void createActionMenu(ActionMode mode, Menu menu) { |
+ TypedArray styledAttributes = getContext().obtainStyledAttributes(ACTION_MODE_ATTRS); |
+ |
+ menu.add(Menu.NONE, ID_SELECTALL, Menu.NONE, android.R.string.selectAll). |
+ setAlphabeticShortcut('a'). |
+ setIcon(styledAttributes.getResourceId(SELECT_ALL_ATTR_INDEX, 0)). |
+ setShowAsAction( |
+ MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); |
+ |
+ if (mEditable) { |
+ menu.add(Menu.NONE, ID_CUT, Menu.NONE, android.R.string.cut). |
+ setIcon(styledAttributes.getResourceId(CUT_ATTR_INDEX, 0)). |
+ setAlphabeticShortcut('x'). |
+ setShowAsAction( |
+ MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); |
+ } |
+ |
+ menu.add(Menu.NONE, ID_COPY, Menu.NONE, android.R.string.copy). |
+ setIcon(styledAttributes.getResourceId(COPY_ATTR_INDEX, 0)). |
+ setAlphabeticShortcut('c'). |
+ setShowAsAction( |
+ MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); |
+ |
+ if (mEditable && canPaste()) { |
+ menu.add(Menu.NONE, ID_PASTE, Menu.NONE, android.R.string.paste). |
+ setIcon(styledAttributes.getResourceId(PASTE_ATTR_INDEX, 0)). |
+ setAlphabeticShortcut('v'). |
+ setShowAsAction( |
+ MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); |
+ } |
+ |
+ if (!mEditable) { |
+ if (isShareHandlerAvailable()) { |
+ assert AppResource.STRING_ACTION_BAR_SHARE != 0; |
+ assert AppResource.DRAWABLE_ICON_ACTION_BAR_SHARE != 0; |
+ menu.add(Menu.NONE, ID_SHARE, Menu.NONE, AppResource.STRING_ACTION_BAR_SHARE). |
+ setIcon(AppResource.DRAWABLE_ICON_ACTION_BAR_SHARE). |
+ setShowAsAction( |
+ MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); |
+ } |
+ |
+ if (!mIncognito && isWebSearchAvailable()) { |
+ assert AppResource.STRING_ACTION_BAR_WEB_SEARCH != 0; |
+ assert AppResource.DRAWABLE_ICON_ACTION_BAR_WEB_SEARCH != 0; |
+ menu.add(Menu.NONE, ID_SEARCH, Menu.NONE, |
+ AppResource.STRING_ACTION_BAR_WEB_SEARCH). |
+ setIcon(AppResource.DRAWABLE_ICON_ACTION_BAR_WEB_SEARCH). |
+ setShowAsAction( |
+ MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); |
+ } |
+ } |
+ |
+ styledAttributes.recycle(); |
+ } |
+ |
+ @Override |
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) { |
+ String selection = mActionHandler.getSelectedText(); |
+ switch(item.getItemId()) { |
+ case ID_SELECTALL: |
+ mActionHandler.selectAll(); |
+ break; |
+ case ID_CUT: |
+ mActionHandler.cut(); |
+ break; |
+ case ID_COPY: |
+ mActionHandler.copy(); |
+ mode.finish(); |
+ break; |
+ case ID_PASTE: |
+ mActionHandler.paste(); |
+ break; |
+ case ID_SHARE: |
+ if (!TextUtils.isEmpty(selection)) { |
+ Intent send = new Intent(Intent.ACTION_SEND); |
+ send.setType("text/plain"); |
+ send.putExtra(Intent.EXTRA_TEXT, selection); |
+ try { |
+ assert AppResource.STRING_ACTION_BAR_SHARE != 0; |
+ Intent i = Intent.createChooser(send, getContext().getString( |
+ AppResource.STRING_ACTION_BAR_SHARE)); |
+ i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
+ getContext().startActivity(i); |
+ } catch (android.content.ActivityNotFoundException ex) { |
+ // If no app handles it, do nothing. |
+ } |
+ } |
+ mode.finish(); |
+ break; |
+ case ID_SEARCH: |
+ if (!TextUtils.isEmpty(selection)) { |
+ Intent i = new Intent(Intent.ACTION_WEB_SEARCH); |
+ i.putExtra(SearchManager.EXTRA_NEW_SEARCH, true); |
+ i.putExtra(SearchManager.QUERY, selection); |
+ i.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName()); |
+ try { |
+ getContext().startActivity(i); |
+ } catch (android.content.ActivityNotFoundException ex) { |
+ // If no app handles it, do nothing. |
+ } |
+ } |
+ mode.finish(); |
+ break; |
+ default: |
+ return false; |
+ } |
+ return true; |
+ } |
+ |
+ @Override |
+ public void onDestroyActionMode(ActionMode mode) { |
+ mActionHandler.onDestroyActionMode(); |
+ } |
+ |
+ private boolean canPaste() { |
+ ClipboardManager clipMgr = (ClipboardManager) |
+ getContext().getSystemService(Context.CLIPBOARD_SERVICE); |
+ return clipMgr.hasPrimaryClip(); |
+ } |
+ |
+ private boolean isShareHandlerAvailable() { |
+ Intent intent = new Intent(Intent.ACTION_SEND); |
+ intent.setType("text/plain"); |
+ return getContext().getPackageManager() |
+ .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0; |
+ } |
+ |
+ private boolean isWebSearchAvailable() { |
+ Intent intent = new Intent(Intent.ACTION_WEB_SEARCH); |
+ intent.putExtra(SearchManager.EXTRA_NEW_SEARCH, true); |
+ return getContext().getPackageManager() |
+ .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0; |
+ } |
+} |