SelectActionModeCallback.java revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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.SearchManager; 8import android.content.ClipboardManager; 9import android.content.Context; 10import android.content.Intent; 11import android.content.pm.PackageManager; 12import android.content.res.TypedArray; 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 private static final int SELECT_ALL_ATTR_INDEX = 0; 27 private static final int CUT_ATTR_INDEX = 1; 28 private static final int COPY_ATTR_INDEX = 2; 29 private static final int PASTE_ATTR_INDEX = 3; 30 private static final int[] ACTION_MODE_ATTRS = { 31 android.R.attr.actionModeSelectAllDrawable, 32 android.R.attr.actionModeCutDrawable, 33 android.R.attr.actionModeCopyDrawable, 34 android.R.attr.actionModePasteDrawable, 35 }; 36 37 private static final int ID_SELECTALL = 0; 38 private static final int ID_COPY = 1; 39 private static final int ID_SHARE = 2; 40 private static final int ID_SEARCH = 3; 41 private static final int ID_CUT = 4; 42 private static final int ID_PASTE = 5; 43 44 /** 45 * An interface to retrieve information about the current selection, and also to perform 46 * actions based on the selection or when the action bar is dismissed. 47 */ 48 public interface ActionHandler { 49 /** 50 * Perform a select all action. 51 * @return true iff the action was successful. 52 */ 53 boolean selectAll(); 54 55 /** 56 * Perform a copy (to clipboard) action. 57 * @return true iff the action was successful. 58 */ 59 boolean copy(); 60 61 /** 62 * Perform a cut (to clipboard) action. 63 * @return true iff the action was successful. 64 */ 65 boolean cut(); 66 67 /** 68 * Perform a paste action. 69 * @return true iff the action was successful. 70 */ 71 boolean paste(); 72 73 /** 74 * @return true iff the current selection is editable (e.g. text within an input field). 75 */ 76 boolean isSelectionEditable(); 77 78 /** 79 * @return the currently selected text String. 80 */ 81 String getSelectedText(); 82 83 /** 84 * Called when the onDestroyActionMode of the SelectActionmodeCallback is called. 85 */ 86 void onDestroyActionMode(); 87 } 88 89 private Context mContext; 90 private ActionHandler mActionHandler; 91 private final boolean mIncognito; 92 private boolean mEditable; 93 94 protected SelectActionModeCallback( 95 Context context, ActionHandler actionHandler, boolean incognito) { 96 mContext = context; 97 mActionHandler = actionHandler; 98 mIncognito = incognito; 99 } 100 101 protected Context getContext() { 102 return mContext; 103 } 104 105 @Override 106 public boolean onCreateActionMode(ActionMode mode, Menu menu) { 107 mode.setSubtitle(null); 108 mEditable = mActionHandler.isSelectionEditable(); 109 createActionMenu(mode, menu); 110 return true; 111 } 112 113 @Override 114 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 115 boolean isEditableNow = mActionHandler.isSelectionEditable(); 116 if (mEditable != isEditableNow) { 117 mEditable = isEditableNow; 118 menu.clear(); 119 createActionMenu(mode, menu); 120 return true; 121 } 122 return false; 123 } 124 125 private void createActionMenu(ActionMode mode, Menu menu) { 126 TypedArray styledAttributes = getContext().obtainStyledAttributes(ACTION_MODE_ATTRS); 127 128 menu.add(Menu.NONE, ID_SELECTALL, Menu.NONE, android.R.string.selectAll). 129 setAlphabeticShortcut('a'). 130 setIcon(styledAttributes.getResourceId(SELECT_ALL_ATTR_INDEX, 0)). 131 setShowAsAction( 132 MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); 133 134 if (mEditable) { 135 menu.add(Menu.NONE, ID_CUT, Menu.NONE, android.R.string.cut). 136 setIcon(styledAttributes.getResourceId(CUT_ATTR_INDEX, 0)). 137 setAlphabeticShortcut('x'). 138 setShowAsAction( 139 MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); 140 } 141 142 menu.add(Menu.NONE, ID_COPY, Menu.NONE, android.R.string.copy). 143 setIcon(styledAttributes.getResourceId(COPY_ATTR_INDEX, 0)). 144 setAlphabeticShortcut('c'). 145 setShowAsAction( 146 MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); 147 148 if (mEditable && canPaste()) { 149 menu.add(Menu.NONE, ID_PASTE, Menu.NONE, android.R.string.paste). 150 setIcon(styledAttributes.getResourceId(PASTE_ATTR_INDEX, 0)). 151 setAlphabeticShortcut('v'). 152 setShowAsAction( 153 MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); 154 } 155 156 if (!mEditable) { 157 if (isShareHandlerAvailable()) { 158 menu.add(Menu.NONE, ID_SHARE, Menu.NONE, R.string.actionbar_share). 159 setIcon(R.drawable.ic_menu_share_holo_light). 160 setShowAsAction( 161 MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); 162 } 163 164 if (!mIncognito && isWebSearchAvailable()) { 165 menu.add(Menu.NONE, ID_SEARCH, Menu.NONE, R.string.actionbar_web_search). 166 setIcon(R.drawable.ic_menu_search_holo_light). 167 setShowAsAction( 168 MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); 169 } 170 } 171 172 styledAttributes.recycle(); 173 } 174 175 @Override 176 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 177 String selection = mActionHandler.getSelectedText(); 178 switch(item.getItemId()) { 179 case ID_SELECTALL: 180 mActionHandler.selectAll(); 181 break; 182 case ID_CUT: 183 mActionHandler.cut(); 184 break; 185 case ID_COPY: 186 mActionHandler.copy(); 187 mode.finish(); 188 break; 189 case ID_PASTE: 190 mActionHandler.paste(); 191 break; 192 case ID_SHARE: 193 if (!TextUtils.isEmpty(selection)) { 194 Intent send = new Intent(Intent.ACTION_SEND); 195 send.setType("text/plain"); 196 send.putExtra(Intent.EXTRA_TEXT, selection); 197 try { 198 Intent i = Intent.createChooser(send, getContext().getString( 199 R.string.actionbar_share)); 200 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 201 getContext().startActivity(i); 202 } catch (android.content.ActivityNotFoundException ex) { 203 // If no app handles it, do nothing. 204 } 205 } 206 mode.finish(); 207 break; 208 case ID_SEARCH: 209 if (!TextUtils.isEmpty(selection)) { 210 Intent i = new Intent(Intent.ACTION_WEB_SEARCH); 211 i.putExtra(SearchManager.EXTRA_NEW_SEARCH, true); 212 i.putExtra(SearchManager.QUERY, selection); 213 i.putExtra(Browser.EXTRA_APPLICATION_ID, getContext().getPackageName()); 214 try { 215 getContext().startActivity(i); 216 } catch (android.content.ActivityNotFoundException ex) { 217 // If no app handles it, do nothing. 218 } 219 } 220 mode.finish(); 221 break; 222 default: 223 return false; 224 } 225 return true; 226 } 227 228 @Override 229 public void onDestroyActionMode(ActionMode mode) { 230 mActionHandler.onDestroyActionMode(); 231 } 232 233 private boolean canPaste() { 234 ClipboardManager clipMgr = (ClipboardManager) 235 getContext().getSystemService(Context.CLIPBOARD_SERVICE); 236 return clipMgr.hasPrimaryClip(); 237 } 238 239 private boolean isShareHandlerAvailable() { 240 Intent intent = new Intent(Intent.ACTION_SEND); 241 intent.setType("text/plain"); 242 return getContext().getPackageManager() 243 .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0; 244 } 245 246 private boolean isWebSearchAvailable() { 247 Intent intent = new Intent(Intent.ACTION_WEB_SEARCH); 248 intent.putExtra(SearchManager.EXTRA_NEW_SEARCH, true); 249 return getContext().getPackageManager() 250 .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0; 251 } 252} 253