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 5 package org.chromium.content_shell_apk; 6 7 import android.content.BroadcastReceiver; 8 import android.content.Context; 9 import android.content.Intent; 10 import android.content.IntentFilter; 11 import android.os.Bundle; 12 import android.text.TextUtils; 13 import android.util.Log; 14 import android.view.KeyEvent; 15 16 import org.chromium.base.ChromiumActivity; 17 import org.chromium.base.MemoryPressureListener; 18 import org.chromium.content.app.LibraryLoader; 19 import org.chromium.content.browser.ActivityContentVideoViewClient; 20 import org.chromium.content.browser.AndroidBrowserProcess; 21 import org.chromium.content.browser.BrowserStartupConfig; 22 import org.chromium.content.browser.ContentVideoViewClient; 23 import org.chromium.content.browser.ContentView; 24 import org.chromium.content.browser.ContentViewClient; 25 import org.chromium.content.browser.DeviceUtils; 26 import org.chromium.content.browser.TracingIntentHandler; 27 import org.chromium.content.common.CommandLine; 28 import org.chromium.content.common.ProcessInitException; 29 import org.chromium.content_shell.Shell; 30 import org.chromium.content_shell.ShellManager; 31 import org.chromium.ui.ActivityWindowAndroid; 32 import org.chromium.ui.WindowAndroid; 33 34 /** 35 * Activity for managing the Content Shell. 36 */ 37 public class ContentShellActivity extends ChromiumActivity { 38 39 public static final String COMMAND_LINE_FILE = "/data/local/tmp/content-shell-command-line"; 40 private static final String TAG = "ContentShellActivity"; 41 42 private static final String ACTIVE_SHELL_URL_KEY = "activeUrl"; 43 private static final String ACTION_START_TRACE = 44 "org.chromium.content_shell.action.PROFILE_START"; 45 private static final String ACTION_STOP_TRACE = 46 "org.chromium.content_shell.action.PROFILE_STOP"; 47 public static final String COMMAND_LINE_ARGS_KEY = "commandLineArgs"; 48 49 /** 50 * Sending an intent with this action will simulate a memory pressure signal 51 * at a critical level. 52 */ 53 private static final String ACTION_LOW_MEMORY = 54 "org.chromium.content_shell.action.ACTION_LOW_MEMORY"; 55 56 /** 57 * Sending an intent with this action will simulate a memory pressure signal 58 * at a moderate level. 59 */ 60 private static final String ACTION_TRIM_MEMORY_MODERATE = 61 "org.chromium.content_shell.action.ACTION_TRIM_MEMORY_MODERATE"; 62 63 64 private ShellManager mShellManager; 65 private WindowAndroid mWindowAndroid; 66 private BroadcastReceiver mReceiver; 67 68 @Override 69 protected void onCreate(Bundle savedInstanceState) { 70 super.onCreate(savedInstanceState); 71 72 // Initializing the command line must occur before loading the library. 73 if (!CommandLine.isInitialized()) { 74 CommandLine.initFromFile(COMMAND_LINE_FILE); 75 String[] commandLineParams = getCommandLineParamsFromIntent(getIntent()); 76 if (commandLineParams != null) { 77 CommandLine.getInstance().appendSwitchesAndArguments(commandLineParams); 78 } 79 } 80 waitForDebuggerIfNeeded(); 81 82 DeviceUtils.addDeviceSpecificUserAgentSwitch(this); 83 try { 84 LibraryLoader.ensureInitialized(); 85 86 setContentView(R.layout.content_shell_activity); 87 mShellManager = (ShellManager) findViewById(R.id.shell_container); 88 mWindowAndroid = new ActivityWindowAndroid(this); 89 mWindowAndroid.restoreInstanceState(savedInstanceState); 90 mShellManager.setWindow(mWindowAndroid); 91 92 String startupUrl = getUrlFromIntent(getIntent()); 93 if (!TextUtils.isEmpty(startupUrl)) { 94 mShellManager.setStartupUrl(Shell.sanitizeUrl(startupUrl)); 95 } 96 97 if (!CommandLine.getInstance().hasSwitch(CommandLine.DUMP_RENDER_TREE)) { 98 BrowserStartupConfig.setAsync(new BrowserStartupConfig.StartupCallback() { 99 100 @Override 101 public void run(int startupResult) { 102 if (startupResult > 0) { 103 // TODO: Show error message. 104 Log.e(TAG, "ContentView initialization failed."); 105 finish(); 106 } else { 107 finishInitialization(); 108 } 109 } 110 }); 111 } 112 113 if (!AndroidBrowserProcess.init(this, AndroidBrowserProcess.MAX_RENDERERS_LIMIT)) { 114 String shellUrl = ShellManager.DEFAULT_SHELL_URL; 115 if (savedInstanceState != null 116 && savedInstanceState.containsKey(ACTIVE_SHELL_URL_KEY)) { 117 shellUrl = savedInstanceState.getString(ACTIVE_SHELL_URL_KEY); 118 } 119 mShellManager.launchShell(shellUrl); 120 finishInitialization(); 121 } 122 } catch (ProcessInitException e) { 123 Log.e(TAG, "ContentView initialization failed.", e); 124 finish(); 125 } 126 } 127 128 private void finishInitialization() { 129 getActiveContentView().setContentViewClient(new ContentViewClient() { 130 @Override 131 public ContentVideoViewClient getContentVideoViewClient() { 132 return new ActivityContentVideoViewClient(ContentShellActivity.this); 133 } 134 }); 135 } 136 137 @Override 138 protected void onSaveInstanceState(Bundle outState) { 139 super.onSaveInstanceState(outState); 140 Shell activeShell = getActiveShell(); 141 if (activeShell != null) { 142 outState.putString(ACTIVE_SHELL_URL_KEY, activeShell.getContentView().getUrl()); 143 } 144 145 mWindowAndroid.saveInstanceState(outState); 146 } 147 148 private void waitForDebuggerIfNeeded() { 149 if (CommandLine.getInstance().hasSwitch(CommandLine.WAIT_FOR_JAVA_DEBUGGER)) { 150 Log.e(TAG, "Waiting for Java debugger to connect..."); 151 android.os.Debug.waitForDebugger(); 152 Log.e(TAG, "Java debugger connected. Resuming execution."); 153 } 154 } 155 156 @Override 157 public boolean onKeyUp(int keyCode, KeyEvent event) { 158 if (keyCode != KeyEvent.KEYCODE_BACK) return super.onKeyUp(keyCode, event); 159 160 Shell activeView = getActiveShell(); 161 if (activeView != null && activeView.getContentView().canGoBack()) { 162 activeView.getContentView().goBack(); 163 return true; 164 } 165 166 return super.onKeyUp(keyCode, event); 167 } 168 169 @Override 170 protected void onNewIntent(Intent intent) { 171 if (getCommandLineParamsFromIntent(intent) != null) { 172 Log.i(TAG, "Ignoring command line params: can only be set when creating the activity."); 173 } 174 175 if (ACTION_LOW_MEMORY.equals(intent.getAction())) { 176 MemoryPressureListener.simulateMemoryPressureSignal(TRIM_MEMORY_COMPLETE); 177 return; 178 } else if (ACTION_TRIM_MEMORY_MODERATE.equals(intent.getAction())) { 179 MemoryPressureListener.simulateMemoryPressureSignal(TRIM_MEMORY_MODERATE); 180 return; 181 } 182 183 String url = getUrlFromIntent(intent); 184 if (!TextUtils.isEmpty(url)) { 185 Shell activeView = getActiveShell(); 186 if (activeView != null) { 187 activeView.loadUrl(url); 188 } 189 } 190 } 191 192 @Override 193 protected void onPause() { 194 ContentView view = getActiveContentView(); 195 if (view != null) view.onActivityPause(); 196 197 super.onPause(); 198 unregisterReceiver(mReceiver); 199 } 200 201 @Override 202 protected void onResume() { 203 super.onResume(); 204 205 ContentView view = getActiveContentView(); 206 if (view != null) view.onActivityResume(); 207 IntentFilter intentFilter = new IntentFilter(ACTION_START_TRACE); 208 intentFilter.addAction(ACTION_STOP_TRACE); 209 mReceiver = new BroadcastReceiver() { 210 @Override 211 public void onReceive(Context context, Intent intent) { 212 String action = intent.getAction(); 213 String extra = intent.getStringExtra("file"); 214 if (ACTION_START_TRACE.equals(action)) { 215 if (extra.isEmpty()) { 216 Log.e(TAG, "Can not start tracing without specifing saving location"); 217 } else { 218 TracingIntentHandler.beginTracing(extra); 219 Log.i(TAG, "start tracing"); 220 } 221 } else if (ACTION_STOP_TRACE.equals(action)) { 222 Log.i(TAG, "stop tracing"); 223 TracingIntentHandler.endTracing(); 224 } 225 } 226 }; 227 registerReceiver(mReceiver, intentFilter); 228 } 229 230 @Override 231 public void onActivityResult(int requestCode, int resultCode, Intent data) { 232 super.onActivityResult(requestCode, resultCode, data); 233 mWindowAndroid.onActivityResult(requestCode, resultCode, data); 234 } 235 236 private static String getUrlFromIntent(Intent intent) { 237 return intent != null ? intent.getDataString() : null; 238 } 239 240 private static String[] getCommandLineParamsFromIntent(Intent intent) { 241 return intent != null ? intent.getStringArrayExtra(COMMAND_LINE_ARGS_KEY) : null; 242 } 243 244 /** 245 * @return The {@link ShellManager} configured for the activity or null if it has not been 246 * created yet. 247 */ 248 public ShellManager getShellManager() { 249 return mShellManager; 250 } 251 252 /** 253 * @return The currently visible {@link Shell} or null if one is not showing. 254 */ 255 public Shell getActiveShell() { 256 return mShellManager != null ? mShellManager.getActiveShell() : null; 257 } 258 259 /** 260 * @return The {@link ContentView} owned by the currently visible {@link Shell} or null if one 261 * is not showing. 262 */ 263 public ContentView getActiveContentView() { 264 Shell shell = getActiveShell(); 265 return shell != null ? shell.getContentView() : null; 266 } 267 } 268