// 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.Activity;
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.R;
/**
* 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.setTitle(null);
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()) {
menu.add(Menu.NONE, ID_SHARE, Menu.NONE, R.string.actionbar_share).
setIcon(R.drawable.ic_menu_share_holo_light).
setShowAsAction(
MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
}
if (!mIncognito && isWebSearchAvailable()) {
menu.add(Menu.NONE, ID_SEARCH, Menu.NONE, R.string.actionbar_web_search).
setIcon(R.drawable.ic_menu_search_holo_light).
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 {
Intent i = Intent.createChooser(send, getContext().getString(
R.string.actionbar_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());
if (!(getContext() instanceof Activity)) {
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
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;
}
}