1// Copyright (c) 2012 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.content.browser; 6 7import android.app.Activity; 8import android.app.SearchManager; 9import android.content.ClipboardManager; 10import android.content.Context; 11import android.content.Intent; 12import android.content.pm.PackageManager; 13import android.provider.Browser; 14import android.text.TextUtils; 15import android.view.ActionMode; 16import android.view.Menu; 17import android.view.MenuItem; 18 19import org.chromium.content.R; 20 21/** 22 * An ActionMode.Callback for in-page selection. This class handles both the editable and 23 * non-editable cases. 24 */ 25public class SelectActionModeCallback implements ActionMode.Callback { 26 /** 27 * An interface to retrieve information about the current selection, and also to perform 28 * actions based on the selection or when the action bar is dismissed. 29 */ 30 public interface ActionHandler { 31 /** 32 * Perform a select all action. 33 * @return true iff the action was successful. 34 */ 35 boolean selectAll(); 36 37 /** 38 * Perform a copy (to clipboard) action. 39 * @return true iff the action was successful. 40 */ 41 boolean copy(); 42 43 /** 44 * Perform a cut (to clipboard) action. 45 * @return true iff the action was successful. 46 */ 47 boolean cut(); 48 49 /** 50 * Perform a paste action. 51 * @return true iff the action was successful. 52 */ 53 boolean paste(); 54 55 /** 56 * @return true iff the current selection is editable (e.g. text within an input field). 57 */ 58 boolean isSelectionEditable(); 59 60 /** 61 * @return the currently selected text String. 62 */ 63 String getSelectedText(); 64 65 /** 66 * Called when the onDestroyActionMode of the SelectActionmodeCallback is called. 67 */ 68 void onDestroyActionMode(); 69 } 70 71 private Context mContext; 72 private ActionHandler mActionHandler; 73 private final boolean mIncognito; 74 private boolean mEditable; 75 76 protected SelectActionModeCallback( 77 Context context, ActionHandler actionHandler, boolean incognito) { 78 mContext = context; 79 mActionHandler = actionHandler; 80 mIncognito = incognito; 81 } 82 83 protected Context getContext() { 84 return mContext; 85 } 86 87 @Override 88 public boolean onCreateActionMode(ActionMode mode, Menu menu) { 89 mode.setTitle(null); 90 mode.setSubtitle(null); 91 mEditable = mActionHandler.isSelectionEditable(); 92 createActionMenu(mode, menu); 93 return true; 94 } 95 96 @Override 97 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 98 boolean isEditableNow = mActionHandler.isSelectionEditable(); 99 if (mEditable != isEditableNow) { 100 mEditable = isEditableNow; 101 menu.clear(); 102 createActionMenu(mode, menu); 103 return true; 104 } 105 return false; 106 } 107 108 private void createActionMenu(ActionMode mode, Menu menu) { 109 mode.getMenuInflater().inflate(R.menu.select_action_menu, menu); 110 if (!mEditable || !canPaste()) { 111 menu.removeItem(R.id.select_action_menu_paste); 112 } 113 114 if (!mEditable) { 115 menu.removeItem(R.id.select_action_menu_cut); 116 } 117 118 if (mEditable || !isShareHandlerAvailable()) { 119 menu.removeItem(R.id.select_action_menu_share); 120 } 121 122 if (mEditable || mIncognito || !isWebSearchAvailable()) { 123 menu.removeItem(R.id.select_action_menu_web_search); 124 } 125 } 126 127 @Override 128 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 129 String selection = mActionHandler.getSelectedText(); 130 int id = item.getItemId(); 131 132 if (id == R.id.select_action_menu_select_all) { 133 mActionHandler.selectAll(); 134 } else if (id == R.id.select_action_menu_cut) { 135 mActionHandler.cut(); 136 } else if (id == R.id.select_action_menu_copy) { 137 mActionHandler.copy(); 138 mode.finish(); 139 } else if (id == R.id.select_action_menu_paste) { 140 mActionHandler.paste(); 141 } else if (id == R.id.select_action_menu_share) { 142 if (!TextUtils.isEmpty(selection)) { 143 Intent send = new Intent(Intent.ACTION_SEND); 144 send.setType("text/plain"); 145 send.putExtra(Intent.EXTRA_TEXT, selection); 146 try { 147 Intent i = Intent.createChooser(send, getContext().getString( 148 R.string.actionbar_share)); 149 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 150 getContext().startActivity(i); 151 } catch (android.content.ActivityNotFoundException ex) { 152 // If no app handles it, do nothing. 153 } 154 } 155 mode.finish(); 156 } else if (id == R.id.select_action_menu_web_search) { 157 if (!TextUtils.isEmpty(selection)) { 158 Intent i = new Intent(Intent.ACTION_WEB_SEARCH); 159 i.putExtra(SearchManager.EXTRA_NEW_SEARCH, true); 160 i.putExtra(SearchManager.QUERY, selection); 161 i.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName()); 162 if (!(getContext() instanceof Activity)) { 163 i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 164 } 165 try { 166 getContext().startActivity(i); 167 } catch (android.content.ActivityNotFoundException ex) { 168 // If no app handles it, do nothing. 169 } 170 } 171 mode.finish(); 172 } else { 173 return false; 174 } 175 return true; 176 } 177 178 @Override 179 public void onDestroyActionMode(ActionMode mode) { 180 mActionHandler.onDestroyActionMode(); 181 } 182 183 private boolean canPaste() { 184 ClipboardManager clipMgr = (ClipboardManager) 185 getContext().getSystemService(Context.CLIPBOARD_SERVICE); 186 return clipMgr.hasPrimaryClip(); 187 } 188 189 private boolean isShareHandlerAvailable() { 190 Intent intent = new Intent(Intent.ACTION_SEND); 191 intent.setType("text/plain"); 192 return getContext().getPackageManager() 193 .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0; 194 } 195 196 private boolean isWebSearchAvailable() { 197 Intent intent = new Intent(Intent.ACTION_WEB_SEARCH); 198 intent.putExtra(SearchManager.EXTRA_NEW_SEARCH, true); 199 return getContext().getPackageManager() 200 .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0; 201 } 202} 203