// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.content_shell;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.Toast;
import org.chromium.base.ChromiumActivity;
import org.chromium.base.MemoryPressureListener;
import org.chromium.content.app.LibraryLoader;
import org.chromium.content.browser.ActivityContentVideoViewClient;
import org.chromium.content.browser.BrowserStartupController;
import org.chromium.content.browser.ContentVideoViewClient;
import org.chromium.content.browser.ContentView;
import org.chromium.content.browser.ContentViewClient;
import org.chromium.content.browser.DeviceUtils;
import org.chromium.content.browser.TracingIntentHandler;
import org.chromium.content.common.CommandLine;
import org.chromium.content.common.ProcessInitException;
import org.chromium.ui.WindowAndroid;
/**
* Activity for managing the Content Shell.
*/
public class ContentShellActivity extends ChromiumActivity {
public static final String COMMAND_LINE_FILE = "/data/local/tmp/content-shell-command-line";
private static final String TAG = "ContentShellActivity";
private static final String ACTIVE_SHELL_URL_KEY = "activeUrl";
private static final String ACTION_START_TRACE =
"org.chromium.content_shell.action.PROFILE_START";
private static final String ACTION_STOP_TRACE =
"org.chromium.content_shell.action.PROFILE_STOP";
public static final String COMMAND_LINE_ARGS_KEY = "commandLineArgs";
/**
* Sending an intent with this action will simulate a memory pressure signal at a critical
* level.
*/
private static final String ACTION_LOW_MEMORY =
"org.chromium.content_shell.action.ACTION_LOW_MEMORY";
/**
* Sending an intent with this action will simulate a memory pressure signal at a moderate
* level.
*/
private static final String ACTION_TRIM_MEMORY_MODERATE =
"org.chromium.content_shell.action.ACTION_TRIM_MEMORY_MODERATE";
private ShellManager mShellManager;
private WindowAndroid mWindowAndroid;
private BroadcastReceiver mReceiver;
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Initializing the command line must occur before loading the library.
if (!CommandLine.isInitialized()) {
CommandLine.initFromFile(COMMAND_LINE_FILE);
String[] commandLineParams = getCommandLineParamsFromIntent(getIntent());
if (commandLineParams != null) {
CommandLine.getInstance().appendSwitchesAndArguments(commandLineParams);
}
}
waitForDebuggerIfNeeded();
DeviceUtils.addDeviceSpecificUserAgentSwitch(this);
try {
LibraryLoader.ensureInitialized();
} catch (ProcessInitException e) {
Log.e(TAG, "ContentView initialization failed.", e);
finish();
return;
}
setContentView(R.layout.content_shell_activity);
mShellManager = (ShellManager) findViewById(R.id.shell_container);
mWindowAndroid = new WindowAndroid(this);
mWindowAndroid.restoreInstanceState(savedInstanceState);
mShellManager.setWindow(mWindowAndroid);
String startupUrl = getUrlFromIntent(getIntent());
if (!TextUtils.isEmpty(startupUrl)) {
mShellManager.setStartupUrl(Shell.sanitizeUrl(startupUrl));
}
if (CommandLine.getInstance().hasSwitch(CommandLine.DUMP_RENDER_TREE)) {
if(BrowserStartupController.get(this).startBrowserProcessesSync(
BrowserStartupController.MAX_RENDERERS_LIMIT)) {
finishInitialization(savedInstanceState);
} else {
initializationFailed();
}
} else {
BrowserStartupController.get(this).startBrowserProcessesAsync(
new BrowserStartupController.StartupCallback() {
@Override
public void onSuccess(boolean alreadyStarted) {
finishInitialization(savedInstanceState);
}
@Override
public void onFailure() {
initializationFailed();
}
});
}
}
private void finishInitialization(Bundle savedInstanceState) {
String shellUrl = ShellManager.DEFAULT_SHELL_URL;
if (savedInstanceState != null
&& savedInstanceState.containsKey(ACTIVE_SHELL_URL_KEY)) {
shellUrl = savedInstanceState.getString(ACTIVE_SHELL_URL_KEY);
}
mShellManager.launchShell(shellUrl);
getActiveContentView().setContentViewClient(new ContentViewClient() {
@Override
public ContentVideoViewClient getContentVideoViewClient() {
return new ActivityContentVideoViewClient(ContentShellActivity.this);
}
});
}
private void initializationFailed() {
Log.e(TAG, "ContentView initialization failed.");
Toast.makeText(ContentShellActivity.this,
R.string.browser_process_initialization_failed,
Toast.LENGTH_SHORT).show();
finish();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Shell activeShell = getActiveShell();
if (activeShell != null) {
outState.putString(ACTIVE_SHELL_URL_KEY, activeShell.getContentView().getUrl());
}
mWindowAndroid.saveInstanceState(outState);
}
private void waitForDebuggerIfNeeded() {
if (CommandLine.getInstance().hasSwitch(CommandLine.WAIT_FOR_JAVA_DEBUGGER)) {
Log.e(TAG, "Waiting for Java debugger to connect...");
android.os.Debug.waitForDebugger();
Log.e(TAG, "Java debugger connected. Resuming execution.");
}
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode != KeyEvent.KEYCODE_BACK) return super.onKeyUp(keyCode, event);
Shell activeView = getActiveShell();
if (activeView != null && activeView.getContentView().canGoBack()) {
activeView.getContentView().goBack();
return true;
}
return super.onKeyUp(keyCode, event);
}
@Override
protected void onNewIntent(Intent intent) {
if (getCommandLineParamsFromIntent(intent) != null) {
Log.i(TAG, "Ignoring command line params: can only be set when creating the activity.");
}
if (ACTION_LOW_MEMORY.equals(intent.getAction())) {
MemoryPressureListener.simulateMemoryPressureSignal(TRIM_MEMORY_COMPLETE);
return;
} else if (ACTION_TRIM_MEMORY_MODERATE.equals(intent.getAction())) {
MemoryPressureListener.simulateMemoryPressureSignal(TRIM_MEMORY_MODERATE);
return;
}
String url = getUrlFromIntent(intent);
if (!TextUtils.isEmpty(url)) {
Shell activeView = getActiveShell();
if (activeView != null) {
activeView.loadUrl(url);
}
}
}
@Override
protected void onPause() {
ContentView view = getActiveContentView();
if (view != null) view.onActivityPause();
super.onPause();
unregisterReceiver(mReceiver);
}
@Override
protected void onResume() {
super.onResume();
ContentView view = getActiveContentView();
if (view != null) view.onActivityResume();
IntentFilter intentFilter = new IntentFilter(ACTION_START_TRACE);
intentFilter.addAction(ACTION_STOP_TRACE);
mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
String extra = intent.getStringExtra("file");
if (ACTION_START_TRACE.equals(action)) {
if (extra.isEmpty()) {
Log.e(TAG, "Can not start tracing without specifing saving location");
} else {
TracingIntentHandler.beginTracing(extra);
Log.i(TAG, "start tracing");
}
} else if (ACTION_STOP_TRACE.equals(action)) {
Log.i(TAG, "stop tracing");
TracingIntentHandler.endTracing();
}
}
};
registerReceiver(mReceiver, intentFilter);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
mWindowAndroid.onActivityResult(requestCode, resultCode, data);
}
private static String getUrlFromIntent(Intent intent) {
return intent != null ? intent.getDataString() : null;
}
private static String[] getCommandLineParamsFromIntent(Intent intent) {
return intent != null ? intent.getStringArrayExtra(COMMAND_LINE_ARGS_KEY) : null;
}
/**
* @return The {@link ShellManager} configured for the activity or null if it has not been
* created yet.
*/
public ShellManager getShellManager() {
return mShellManager;
}
/**
* @return The currently visible {@link Shell} or null if one is not showing.
*/
public Shell getActiveShell() {
return mShellManager != null ? mShellManager.getActiveShell() : null;
}
/**
* @return The {@link ContentView} owned by the currently visible {@link Shell} or null if one
* is not showing.
*/
public ContentView getActiveContentView() {
Shell shell = getActiveShell();
return shell != null ? shell.getContentView() : null;
}
}