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