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.
5 package org.chromium.chrome.shell;
7 import android.app.Activity;
8 import android.content.Intent;
9 import android.os.Bundle;
10 import android.text.TextUtils;
11 import android.util.Log;
12 import android.view.KeyEvent;
13 import android.view.Menu;
14 import android.view.MenuItem;
15 import android.view.View;
16 import android.widget.Toast;
18 import com.google.common.annotations.VisibleForTesting;
20 import org.chromium.base.ApiCompatibilityUtils;
21 import org.chromium.base.BaseSwitches;
22 import org.chromium.base.CommandLine;
23 import org.chromium.base.MemoryPressureListener;
24 import org.chromium.base.library_loader.ProcessInitException;
25 import org.chromium.chrome.browser.DevToolsServer;
26 import org.chromium.chrome.browser.appmenu.AppMenuHandler;
27 import org.chromium.chrome.browser.appmenu.AppMenuPropertiesDelegate;
28 import org.chromium.chrome.browser.printing.PrintingControllerFactory;
29 import org.chromium.chrome.browser.printing.TabPrinter;
30 import org.chromium.chrome.browser.share.ShareHelper;
31 import org.chromium.chrome.shell.sync.SyncController;
32 import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
33 import org.chromium.content.browser.ActivityContentVideoViewClient;
34 import org.chromium.content.browser.BrowserStartupController;
35 import org.chromium.content.browser.ContentViewCore;
36 import org.chromium.content.browser.DeviceUtils;
37 import org.chromium.content.common.ContentSwitches;
38 import org.chromium.printing.PrintManagerDelegateImpl;
39 import org.chromium.printing.PrintingController;
40 import org.chromium.sync.signin.AccountManagerHelper;
41 import org.chromium.sync.signin.ChromeSigninController;
42 import org.chromium.ui.base.ActivityWindowAndroid;
43 import org.chromium.ui.base.WindowAndroid;
46 * The {@link android.app.Activity} component of a basic test shell to test Chrome features.
48 public class ChromeShellActivity extends Activity implements AppMenuPropertiesDelegate {
49 private static final String TAG = "ChromeShellActivity";
50 private static final String CHROME_DISTILLER_SCHEME = "chrome-distiller";
53 * Factory used to set up a mock ActivityWindowAndroid for testing.
55 public interface ActivityWindowAndroidFactory {
57 * @return ActivityWindowAndroid for the given activity.
59 public ActivityWindowAndroid getActivityWindowAndroid(Activity activity);
62 private static ActivityWindowAndroidFactory sWindowAndroidFactory =
63 new ActivityWindowAndroidFactory() {
65 public ActivityWindowAndroid getActivityWindowAndroid(Activity activity) {
66 return new ActivityWindowAndroid(activity);
70 private WindowAndroid mWindow;
71 private TabManager mTabManager;
72 private DevToolsServer mDevToolsServer;
73 private SyncController mSyncController;
74 private PrintingController mPrintingController;
77 * Factory used to set up a mock AppMenuHandler for testing.
79 public interface AppMenuHandlerFactory {
81 * @return AppMenuHandler for the given activity and menu resource id.
83 public AppMenuHandler getAppMenuHandler(Activity activity,
84 AppMenuPropertiesDelegate delegate, int menuResourceId);
87 private static AppMenuHandlerFactory sAppMenuHandlerFactory =
88 new AppMenuHandlerFactory() {
90 public AppMenuHandler getAppMenuHandler(Activity activity,
91 AppMenuPropertiesDelegate delegate, int menuResourceId) {
92 return new AppMenuHandler(activity, delegate, menuResourceId);
95 private AppMenuHandler mAppMenuHandler;
98 protected void onCreate(final Bundle savedInstanceState) {
99 super.onCreate(savedInstanceState);
101 ChromeShellApplication.initCommandLine();
102 waitForDebuggerIfNeeded();
104 DeviceUtils.addDeviceSpecificUserAgentSwitch(this);
106 BrowserStartupController.StartupCallback callback =
107 new BrowserStartupController.StartupCallback() {
109 public void onSuccess(boolean alreadyStarted) {
110 finishInitialization(savedInstanceState);
114 public void onFailure() {
115 Toast.makeText(ChromeShellActivity.this,
116 R.string.browser_process_initialization_failed,
117 Toast.LENGTH_SHORT).show();
118 Log.e(TAG, "Chromium browser process initialization failed");
123 BrowserStartupController.get(this).startBrowserProcessesAsync(callback);
124 } catch (ProcessInitException e) {
125 Log.e(TAG, "Unable to load native library.", e);
130 private void finishInitialization(final Bundle savedInstanceState) {
131 setContentView(R.layout.chrome_shell_activity);
132 mTabManager = (TabManager) findViewById(R.id.tab_manager);
134 mWindow = sWindowAndroidFactory.getActivityWindowAndroid(this);
135 mWindow.restoreInstanceState(savedInstanceState);
136 mTabManager.initialize(mWindow, new ActivityContentVideoViewClient(this) {
138 public boolean onShowCustomView(View view) {
139 if (mTabManager == null) return false;
140 boolean success = super.onShowCustomView(view);
141 if (!CommandLine.getInstance().hasSwitch(
142 ContentSwitches.DISABLE_OVERLAY_FULLSCREEN_VIDEO_SUBTITLE)) {
143 mTabManager.setOverlayVideoMode(true);
149 public void onDestroyContentVideoView() {
150 super.onDestroyContentVideoView();
151 if (mTabManager != null && !CommandLine.getInstance().hasSwitch(
152 ContentSwitches.DISABLE_OVERLAY_FULLSCREEN_VIDEO_SUBTITLE)) {
153 mTabManager.setOverlayVideoMode(false);
158 String startupUrl = getUrlFromIntent(getIntent());
159 if (!TextUtils.isEmpty(startupUrl)) {
160 mTabManager.setStartupUrl(startupUrl);
162 ChromeShellToolbar mToolbar = (ChromeShellToolbar) findViewById(R.id.toolbar);
163 mAppMenuHandler = sAppMenuHandlerFactory.getAppMenuHandler(this, this, R.menu.main_menu);
164 mToolbar.setMenuHandler(mAppMenuHandler);
166 mDevToolsServer = new DevToolsServer("chrome_shell");
167 mDevToolsServer.setRemoteDebuggingEnabled(true);
169 mPrintingController = PrintingControllerFactory.create(this);
171 mSyncController = SyncController.get(this);
172 // In case this method is called after the first onStart(), we need to inform the
173 // SyncController that we have started.
174 mSyncController.onStart();
178 protected void onDestroy() {
181 if (mDevToolsServer != null) mDevToolsServer.destroy();
182 mDevToolsServer = null;
186 protected void onSaveInstanceState(Bundle outState) {
187 // TODO(dtrainor): Save/restore the tab state.
188 if (mWindow != null) mWindow.saveInstanceState(outState);
192 public boolean onKeyUp(int keyCode, KeyEvent event) {
193 if (keyCode == KeyEvent.KEYCODE_BACK) {
194 ChromeShellTab tab = getActiveTab();
195 if (tab != null && tab.canGoBack()) {
201 return super.onKeyUp(keyCode, event);
205 protected void onNewIntent(Intent intent) {
206 if (MemoryPressureListener.handleDebugIntent(this, intent.getAction())) return;
208 String url = getUrlFromIntent(intent);
209 if (!TextUtils.isEmpty(url)) {
210 ChromeShellTab tab = getActiveTab();
211 if (tab != null) tab.loadUrlWithSanitization(url);
216 protected void onStop() {
219 ContentViewCore viewCore = getActiveContentViewCore();
220 if (viewCore != null) viewCore.onHide();
224 protected void onStart() {
227 ContentViewCore viewCore = getActiveContentViewCore();
228 if (viewCore != null) viewCore.onShow();
230 if (mSyncController != null) {
231 mSyncController.onStart();
236 public void onActivityResult(int requestCode, int resultCode, Intent data) {
237 mWindow.onActivityResult(requestCode, resultCode, data);
241 * @return The {@link WindowAndroid} associated with this activity.
243 public WindowAndroid getWindowAndroid() {
248 * @return The {@link ChromeShellTab} that is currently visible.
250 public ChromeShellTab getActiveTab() {
251 return mTabManager != null ? mTabManager.getCurrentTab() : null;
255 * @return The ContentViewCore of the active tab.
257 public ContentViewCore getActiveContentViewCore() {
258 ChromeShellTab tab = getActiveTab();
259 return tab != null ? tab.getContentViewCore() : null;
263 * Creates a {@link ChromeShellTab} with a URL specified by {@code url}.
265 * @param url The URL the new {@link ChromeShellTab} should start with.
268 public void createTab(String url) {
269 mTabManager.createTab(url);
273 * Override the menu key event to show AppMenu.
276 public boolean onKeyDown(int keyCode, KeyEvent event) {
277 if (keyCode == KeyEvent.KEYCODE_MENU && event.getRepeatCount() == 0) {
278 mAppMenuHandler.showAppMenu(findViewById(R.id.menu_button), true, false);
281 return super.onKeyDown(keyCode, event);
285 public boolean onOptionsItemSelected(MenuItem item) {
286 ChromeShellTab activeTab = getActiveTab();
287 switch (item.getItemId()) {
289 if (ChromeSigninController.get(this).isSignedIn()) {
290 SyncController.openSignOutDialog(getFragmentManager());
291 } else if (AccountManagerHelper.get(this).hasGoogleAccounts()) {
292 SyncController.openSigninDialog(getFragmentManager());
294 Toast.makeText(this, R.string.signin_no_account, Toast.LENGTH_SHORT).show();
298 if (activeTab != null) {
299 mPrintingController.startPrint(new TabPrinter(activeTab),
300 new PrintManagerDelegateImpl(this));
303 case R.id.distill_page:
304 if (activeTab != null) {
305 String viewUrl = DomDistillerUrlUtils.getDistillerViewUrlFromUrl(
306 CHROME_DISTILLER_SCHEME, activeTab.getUrl());
307 activeTab.loadUrlWithSanitization(viewUrl);
310 case R.id.back_menu_id:
311 if (activeTab != null && activeTab.canGoBack()) {
315 case R.id.forward_menu_id:
316 if (activeTab != null && activeTab.canGoForward()) {
317 activeTab.goForward();
320 case R.id.share_menu_id:
321 case R.id.direct_share_menu_id:
322 ShareHelper.share(item.getItemId() == R.id.direct_share_menu_id, this,
323 activeTab.getTitle(), activeTab.getUrl(), null);
326 return super.onOptionsItemSelected(item);
330 private void waitForDebuggerIfNeeded() {
331 if (CommandLine.getInstance().hasSwitch(BaseSwitches.WAIT_FOR_JAVA_DEBUGGER)) {
332 Log.e(TAG, "Waiting for Java debugger to connect...");
333 android.os.Debug.waitForDebugger();
334 Log.e(TAG, "Java debugger connected. Resuming execution.");
338 private static String getUrlFromIntent(Intent intent) {
339 return intent != null ? intent.getDataString() : null;
343 public boolean shouldShowAppMenu() {
348 public void prepareMenu(Menu menu) {
349 menu.setGroupVisible(R.id.MAIN_MENU, true);
350 ChromeShellTab activeTab = getActiveTab();
352 // Disable the "Back" menu item if there is no page to go to.
353 MenuItem backMenuItem = menu.findItem(R.id.back_menu_id);
354 backMenuItem.setEnabled(activeTab != null ? activeTab.canGoBack() : false);
356 // Disable the "Forward" menu item if there is no page to go to.
357 MenuItem forwardMenuItem = menu.findItem(R.id.forward_menu_id);
358 forwardMenuItem.setEnabled(activeTab != null ? activeTab.canGoForward() : false);
360 // ChromeShell does not know about bookmarks yet
361 menu.findItem(R.id.bookmark_this_page_id).setEnabled(true);
363 MenuItem signinItem = menu.findItem(R.id.signin);
364 if (ChromeSigninController.get(this).isSignedIn()) {
365 signinItem.setTitle(ChromeSigninController.get(this).getSignedInAccountName());
367 signinItem.setTitle(R.string.signin_sign_in);
370 menu.findItem(R.id.print).setVisible(ApiCompatibilityUtils.isPrintingSupported());
372 MenuItem distillPageItem = menu.findItem(R.id.distill_page);
373 if (CommandLine.getInstance().hasSwitch(ChromeShellSwitches.ENABLE_DOM_DISTILLER)) {
374 String url = activeTab != null ? activeTab.getUrl() : null;
375 distillPageItem.setEnabled(!DomDistillerUrlUtils.isUrlReportable(
376 CHROME_DISTILLER_SCHEME, url));
377 distillPageItem.setVisible(true);
379 distillPageItem.setVisible(false);
381 ShareHelper.configureDirectShareMenuItem(this, menu.findItem(R.id.direct_share_menu_id));
385 public AppMenuHandler getAppMenuHandler() {
386 return mAppMenuHandler;
390 public int getMenuThemeResourceId() {
391 return android.R.style.Theme_Holo_Light;
395 public static void setActivityWindowAndroidFactory(ActivityWindowAndroidFactory factory) {
396 sWindowAndroidFactory = factory;
400 public static void setAppMenuHandlerFactory(AppMenuHandlerFactory factory) {
401 sAppMenuHandlerFactory = factory;