ChromeShellActivity.java revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
1// Copyright 2014 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.chrome.shell; 6 7import android.app.Activity; 8import android.content.Intent; 9import android.content.res.TypedArray; 10import android.os.Bundle; 11import android.text.TextUtils; 12import android.util.Log; 13import android.view.KeyEvent; 14import android.view.Menu; 15import android.view.MenuItem; 16import android.widget.Toast; 17 18import com.google.common.annotations.VisibleForTesting; 19 20import org.chromium.base.ApiCompatibilityUtils; 21import org.chromium.base.BaseSwitches; 22import org.chromium.base.CommandLine; 23import org.chromium.base.MemoryPressureListener; 24import org.chromium.base.library_loader.ProcessInitException; 25import org.chromium.chrome.browser.DevToolsServer; 26import org.chromium.chrome.browser.appmenu.AppMenuHandler; 27import org.chromium.chrome.browser.appmenu.AppMenuPropertiesDelegate; 28import org.chromium.chrome.browser.printing.PrintingControllerFactory; 29import org.chromium.chrome.browser.printing.TabPrinter; 30import org.chromium.chrome.shell.sync.SyncController; 31import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils; 32import org.chromium.content.browser.ActivityContentVideoViewClient; 33import org.chromium.content.browser.BrowserStartupController; 34import org.chromium.content.browser.ContentView; 35import org.chromium.content.browser.DeviceUtils; 36import org.chromium.printing.PrintManagerDelegateImpl; 37import org.chromium.printing.PrintingController; 38import org.chromium.sync.signin.ChromeSigninController; 39import org.chromium.ui.base.ActivityWindowAndroid; 40import org.chromium.ui.base.WindowAndroid; 41 42/** 43 * The {@link android.app.Activity} component of a basic test shell to test Chrome features. 44 */ 45public class ChromeShellActivity extends Activity implements AppMenuPropertiesDelegate { 46 private static final String TAG = "ChromeShellActivity"; 47 private static final String CHROME_DISTILLER_SCHEME = "chrome-distiller"; 48 49 /** 50 * Factory used to set up a mock ActivityWindowAndroid for testing. 51 */ 52 public interface WindowAndroidFactoryForTest { 53 /** 54 * @return ActivityWindowAndroid for the given activity. 55 */ 56 public ActivityWindowAndroid getActivityWindowAndroid(Activity activity); 57 } 58 59 private static WindowAndroidFactoryForTest sWindowAndroidFactory = 60 new WindowAndroidFactoryForTest() { 61 @Override 62 public ActivityWindowAndroid getActivityWindowAndroid(Activity activity) { 63 return new ActivityWindowAndroid(activity); 64 } 65 }; 66 private WindowAndroid mWindow; 67 private TabManager mTabManager; 68 private DevToolsServer mDevToolsServer; 69 private SyncController mSyncController; 70 private PrintingController mPrintingController; 71 72 private AppMenuHandler mAppMenuHandler; 73 74 @Override 75 protected void onCreate(final Bundle savedInstanceState) { 76 super.onCreate(savedInstanceState); 77 78 ChromeShellApplication.initCommandLine(); 79 waitForDebuggerIfNeeded(); 80 81 DeviceUtils.addDeviceSpecificUserAgentSwitch(this); 82 83 BrowserStartupController.StartupCallback callback = 84 new BrowserStartupController.StartupCallback() { 85 @Override 86 public void onSuccess(boolean alreadyStarted) { 87 finishInitialization(savedInstanceState); 88 } 89 90 @Override 91 public void onFailure() { 92 Toast.makeText(ChromeShellActivity.this, 93 R.string.browser_process_initialization_failed, 94 Toast.LENGTH_SHORT).show(); 95 Log.e(TAG, "Chromium browser process initialization failed"); 96 finish(); 97 } 98 }; 99 try { 100 BrowserStartupController.get(this).startBrowserProcessesAsync(callback); 101 } catch (ProcessInitException e) { 102 Log.e(TAG, "Unable to load native library.", e); 103 System.exit(-1); 104 } 105 } 106 107 private void finishInitialization(final Bundle savedInstanceState) { 108 setContentView(R.layout.chrome_shell_activity); 109 mTabManager = (TabManager) findViewById(R.id.tab_manager); 110 111 mWindow = sWindowAndroidFactory.getActivityWindowAndroid(this); 112 mWindow.restoreInstanceState(savedInstanceState); 113 mTabManager.initialize(mWindow, new ActivityContentVideoViewClient(this)); 114 115 String startupUrl = getUrlFromIntent(getIntent()); 116 if (!TextUtils.isEmpty(startupUrl)) { 117 mTabManager.setStartupUrl(startupUrl); 118 } 119 ChromeShellToolbar mToolbar = (ChromeShellToolbar) findViewById(R.id.toolbar); 120 mAppMenuHandler = new AppMenuHandler(this, this, R.menu.main_menu); 121 mToolbar.setMenuHandler(mAppMenuHandler); 122 123 mDevToolsServer = new DevToolsServer("chrome_shell"); 124 mDevToolsServer.setRemoteDebuggingEnabled(true); 125 126 mPrintingController = PrintingControllerFactory.create(this); 127 128 mSyncController = SyncController.get(this); 129 // In case this method is called after the first onStart(), we need to inform the 130 // SyncController that we have started. 131 mSyncController.onStart(); 132 } 133 134 @Override 135 protected void onDestroy() { 136 super.onDestroy(); 137 138 if (mDevToolsServer != null) mDevToolsServer.destroy(); 139 mDevToolsServer = null; 140 } 141 142 @Override 143 protected void onSaveInstanceState(Bundle outState) { 144 // TODO(dtrainor): Save/restore the tab state. 145 if (mWindow != null) mWindow.saveInstanceState(outState); 146 } 147 148 @Override 149 public boolean onKeyUp(int keyCode, KeyEvent event) { 150 if (keyCode == KeyEvent.KEYCODE_BACK) { 151 ChromeShellTab tab = getActiveTab(); 152 if (tab != null && tab.getContentView().canGoBack()) { 153 tab.getContentView().goBack(); 154 return true; 155 } 156 } 157 158 return super.onKeyUp(keyCode, event); 159 } 160 161 @Override 162 protected void onNewIntent(Intent intent) { 163 if (MemoryPressureListener.handleDebugIntent(this, intent.getAction())) return; 164 165 String url = getUrlFromIntent(intent); 166 if (!TextUtils.isEmpty(url)) { 167 ChromeShellTab tab = getActiveTab(); 168 if (tab != null) tab.loadUrlWithSanitization(url); 169 } 170 } 171 172 @Override 173 protected void onStop() { 174 super.onStop(); 175 176 ContentView view = getActiveContentView(); 177 if (view != null) view.onHide(); 178 } 179 180 @Override 181 protected void onStart() { 182 super.onStart(); 183 184 ContentView view = getActiveContentView(); 185 if (view != null) view.onShow(); 186 187 if (mSyncController != null) { 188 mSyncController.onStart(); 189 } 190 } 191 192 @Override 193 public void onActivityResult(int requestCode, int resultCode, Intent data) { 194 mWindow.onActivityResult(requestCode, resultCode, data); 195 } 196 197 /** 198 * @return The {@link WindowAndroid} associated with this activity. 199 */ 200 public WindowAndroid getWindowAndroid() { 201 return mWindow; 202 } 203 204 /** 205 * @return The {@link ChromeShellTab} that is currently visible. 206 */ 207 public ChromeShellTab getActiveTab() { 208 return mTabManager != null ? mTabManager.getCurrentTab() : null; 209 } 210 211 /** 212 * @return The ContentView of the active tab. 213 */ 214 public ContentView getActiveContentView() { 215 ChromeShellTab tab = getActiveTab(); 216 return tab != null ? tab.getContentView() : null; 217 } 218 219 /** 220 * Creates a {@link ChromeShellTab} with a URL specified by {@code url}. 221 * 222 * @param url The URL the new {@link ChromeShellTab} should start with. 223 */ 224 @VisibleForTesting 225 public void createTab(String url) { 226 mTabManager.createTab(url); 227 } 228 229 /** 230 * Override the menu key event to show AppMenu. 231 */ 232 @Override 233 public boolean onKeyDown(int keyCode, KeyEvent event) { 234 if (keyCode == KeyEvent.KEYCODE_MENU && event.getRepeatCount() == 0) { 235 mAppMenuHandler.showAppMenu(findViewById(R.id.menu_button), true, false); 236 return true; 237 } 238 return super.onKeyDown(keyCode, event); 239 } 240 241 @Override 242 public boolean onOptionsItemSelected(MenuItem item) { 243 switch (item.getItemId()) { 244 case R.id.signin: 245 if (ChromeSigninController.get(this).isSignedIn()) 246 SyncController.openSignOutDialog(getFragmentManager()); 247 else 248 SyncController.openSigninDialog(getFragmentManager()); 249 return true; 250 case R.id.print: 251 if (getActiveTab() != null) { 252 mPrintingController.startPrint(new TabPrinter(getActiveTab()), 253 new PrintManagerDelegateImpl(this)); 254 } 255 return true; 256 case R.id.distill_page: 257 ChromeShellTab activeTab = getActiveTab(); 258 String viewUrl = DomDistillerUrlUtils.getDistillerViewUrlFromUrl( 259 CHROME_DISTILLER_SCHEME, getActiveTab().getUrl()); 260 activeTab.loadUrlWithSanitization(viewUrl); 261 return true; 262 case R.id.back_menu_id: 263 if (getActiveTab().canGoBack()) getActiveTab().goBack(); 264 return true; 265 case R.id.forward_menu_id: 266 if (getActiveTab().canGoForward()) getActiveTab().goForward(); 267 return true; 268 default: 269 return super.onOptionsItemSelected(item); 270 } 271 } 272 273 private void waitForDebuggerIfNeeded() { 274 if (CommandLine.getInstance().hasSwitch(BaseSwitches.WAIT_FOR_JAVA_DEBUGGER)) { 275 Log.e(TAG, "Waiting for Java debugger to connect..."); 276 android.os.Debug.waitForDebugger(); 277 Log.e(TAG, "Java debugger connected. Resuming execution."); 278 } 279 } 280 281 private static String getUrlFromIntent(Intent intent) { 282 return intent != null ? intent.getDataString() : null; 283 } 284 285 @Override 286 public boolean shouldShowAppMenu() { 287 return true; 288 } 289 290 @Override 291 public void prepareMenu(Menu menu) { 292 // Disable the "Back" menu item if there is no page to go to. 293 MenuItem backMenuItem = menu.findItem(R.id.back_menu_id); 294 backMenuItem.setEnabled(getActiveTab().canGoBack()); 295 296 // Disable the "Forward" menu item if there is no page to go to. 297 MenuItem forwardMenuItem = menu.findItem(R.id.forward_menu_id); 298 forwardMenuItem.setEnabled(getActiveTab().canGoForward()); 299 300 // ChromeShell does not know about bookmarks yet 301 menu.findItem(R.id.bookmark_this_page_id).setEnabled(false); 302 303 MenuItem signinItem = menu.findItem(R.id.signin); 304 if (ChromeSigninController.get(this).isSignedIn()) { 305 signinItem.setTitle(ChromeSigninController.get(this).getSignedInAccountName()); 306 } else { 307 signinItem.setTitle(R.string.signin_sign_in); 308 } 309 310 menu.findItem(R.id.print).setVisible(ApiCompatibilityUtils.isPrintingSupported()); 311 312 menu.findItem(R.id.distill_page).setVisible( 313 CommandLine.getInstance().hasSwitch(ChromeShellSwitches.ENABLE_DOM_DISTILLER)); 314 315 menu.setGroupVisible(R.id.MAIN_MENU, true); 316 } 317 318 @Override 319 public boolean shouldShowIconRow() { 320 return true; 321 } 322 323 @Override 324 public int getMenuThemeResourceId() { 325 return android.R.style.Theme_Holo_Light; 326 } 327 328 @Override 329 public int getItemRowHeight() { 330 TypedArray a = obtainStyledAttributes( 331 new int[] {android.R.attr.listPreferredItemHeightSmall}); 332 int itemRowHeight = a.getDimensionPixelSize(0, 0); 333 a.recycle(); 334 return itemRowHeight; 335 } 336 337 @VisibleForTesting 338 public static void setActivityWindowAndroidFactory(WindowAndroidFactoryForTest factory) { 339 sWindowAndroidFactory = factory; 340 } 341} 342