Bug 1455740 - Make GeckoViewExample more useful r=esawin,jchen,droeh
This adds a location bar so you can enter arbitrary URLs, and also
adds a menu for toggling various settings such as usage of E10s or
tracking protection.
MozReview-Commit-ID: FgBPR92dsfm
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TestRunnerActivity.java
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/TestRunnerActivity.java
@@ -89,20 +89,16 @@ public class TestRunnerActivity extends
private GeckoSession createSession() {
return createSession(null);
}
private GeckoSession createSession(GeckoSessionSettings settings) {
if (settings == null) {
settings = new GeckoSessionSettings();
-
- // We can't use e10s because we get deadlocked when quickly creating and
- // destroying sessions. Bug 1348361.
- settings.setBoolean(GeckoSessionSettings.USE_MULTIPROCESS, false);
}
final GeckoSession session = new GeckoSession(settings);
session.setNavigationDelegate(mNavigationDelegate);
return session;
}
@Override
--- a/mobile/android/geckoview_example/build.gradle
+++ b/mobile/android/geckoview_example/build.gradle
@@ -28,16 +28,17 @@ android {
project.configureProductFlavors.delegate = it
project.configureProductFlavors()
}
dependencies {
testImplementation 'junit:junit:4.12'
implementation "com.android.support:support-annotations:$support_library_version"
+ implementation "com.android.support:appcompat-v7:$support_library_version"
androidTestImplementation 'com.android.support.test.espresso:espresso-core:2.2.2'
androidTestImplementation 'com.android.support.test:runner:0.5'
// Not defining this library again results in test-app assuming 23.1.1, and the following errors:
// "Conflict with dependency 'com.android.support:support-annotations'. Resolved versions for app (23.4.0) and test app (23.1.1) differ."
androidTestImplementation "com.android.support:support-annotations:$support_library_version"
implementation project(path: ':geckoview')
--- a/mobile/android/geckoview_example/src/main/AndroidManifest.xml
+++ b/mobile/android/geckoview_example/src/main/AndroidManifest.xml
@@ -2,19 +2,20 @@
package="org.mozilla.geckoview_example">
<application android:allowBackup="true"
android:label="@string/app_name"
android:supportsRtl="true">
<uses-library android:name="android.test.runner" />
- <activity android:name="org.mozilla.geckoview_example.GeckoViewActivity"
- android:label="GeckoViewActivity"
+ <activity android:name=".GeckoViewActivity"
+ android:label="@string/activity_label"
android:windowSoftInputMode="stateUnspecified|adjustResize"
+ android:theme="@style/Theme.AppCompat.Light.NoActionBar"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER"/>
<category android:name="android.intent.category.APP_BROWSER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
@@ -25,11 +26,17 @@
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:scheme="about" />
<data android:scheme="javascript" />
</intent-filter>
</activity>
+ <activity android:name=".SessionActivity"
+ android:label="@string/activity_label"
+ android:windowSoftInputMode="stateUnspecified|adjustResize"
+ android:theme="@style/Theme.AppCompat.Light.NoActionBar"
+ android:exported="false">
+ </activity>
</application>
</manifest>
--- a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
+++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
@@ -1,62 +1,94 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.geckoview_example;
-import android.app.Activity;
+import org.mozilla.gecko.GeckoThread;
+import org.mozilla.gecko.process.GeckoProcessManager;
+import org.mozilla.geckoview.GeckoResponse;
+import org.mozilla.geckoview.GeckoRuntime;
+import org.mozilla.geckoview.GeckoRuntimeSettings;
+import org.mozilla.geckoview.GeckoSession;
+import org.mozilla.geckoview.GeckoSession.TrackingProtectionDelegate;
+import org.mozilla.geckoview.GeckoSessionSettings;
+import org.mozilla.geckoview.GeckoView;
+
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.SystemClock;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.WindowManager;
import java.util.Locale;
-import org.mozilla.geckoview.GeckoResponse;
-import org.mozilla.geckoview.GeckoSession;
-import org.mozilla.geckoview.GeckoSessionSettings;
-import org.mozilla.geckoview.GeckoSession.TrackingProtectionDelegate;
-import org.mozilla.geckoview.GeckoView;
-import org.mozilla.geckoview.GeckoRuntime;
-import org.mozilla.geckoview.GeckoRuntimeSettings;
-
-public class GeckoViewActivity extends Activity {
+public class GeckoViewActivity extends AppCompatActivity {
private static final String LOGTAG = "GeckoViewActivity";
private static final String DEFAULT_URL = "https://mozilla.org";
private static final String USE_MULTIPROCESS_EXTRA = "use_multiprocess";
- private static final String USE_REMOTE_DEBUGGER_EXTRA = "use_remote_debugger";
- private static final String ACTION_SHUTDOWN =
- "org.mozilla.geckoview_example.SHUTDOWN";
- private boolean mKillProcessOnDestroy;
-
- /* package */ static final int REQUEST_FILE_PICKER = 1;
+ private static final String SEARCH_URI_BASE = "https://www.google.com/search?q=";
+ private static final String ACTION_SHUTDOWN = "org.mozilla.geckoview_example.SHUTDOWN";
+ private static final int REQUEST_FILE_PICKER = 1;
private static final int REQUEST_PERMISSIONS = 2;
private static GeckoRuntime sGeckoRuntime;
private GeckoSession mGeckoSession;
private GeckoView mGeckoView;
+ private boolean mUseMultiprocess = true;
+ private boolean mUseTrackingProtection = false;
+ private boolean mUsePrivateBrowsing = false;
+ private boolean mKillProcessOnDestroy;
+
+ private LocationView mLocationView;
+ private String mCurrentUri;
+ private boolean mCanGoBack;
+ private boolean mFullScreen;
+
+ private LocationView.CommitListener mCommitListener = new LocationView.CommitListener() {
+ @Override
+ public void onCommit(String text) {
+ if (text.contains(".") && !text.contains(" ")) {
+ mGeckoSession.loadUri(text);
+ } else {
+ mGeckoSession.loadUri(SEARCH_URI_BASE + text);
+ }
+ mGeckoView.requestFocus();
+ }
+ };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(LOGTAG, "zerdatime " + SystemClock.elapsedRealtime() +
" - application start");
setContentView(R.layout.geckoview_activity);
mGeckoView = (GeckoView) findViewById(R.id.gecko_view);
- final boolean useMultiprocess =
- getIntent().getBooleanExtra(USE_MULTIPROCESS_EXTRA, true);
+ setSupportActionBar((Toolbar)findViewById(R.id.toolbar));
+
+ mLocationView = new LocationView(this);
+ getSupportActionBar().setCustomView(mLocationView,
+ new ActionBar.LayoutParams(ActionBar.LayoutParams.MATCH_PARENT,
+ ActionBar.LayoutParams.WRAP_CONTENT));
+ getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
+
+ mUseMultiprocess = getIntent().getBooleanExtra(USE_MULTIPROCESS_EXTRA, true);
if (sGeckoRuntime == null) {
final GeckoRuntimeSettings.Builder runtimeSettingsBuilder =
new GeckoRuntimeSettings.Builder();
if (BuildConfig.DEBUG) {
// In debug builds, we want to load JavaScript resources fresh with
// each build.
@@ -64,94 +96,194 @@ public class GeckoViewActivity extends A
}
final Bundle extras = getIntent().getExtras();
if (extras != null) {
runtimeSettingsBuilder.extras(extras);
}
runtimeSettingsBuilder
- .useContentProcessHint(useMultiprocess)
+ .useContentProcessHint(mUseMultiprocess)
+ .remoteDebuggingEnabled(true)
.nativeCrashReportingEnabled(true)
.javaCrashReportingEnabled(true);
sGeckoRuntime = GeckoRuntime.create(this, runtimeSettingsBuilder.build());
}
- final GeckoSessionSettings sessionSettings = new GeckoSessionSettings();
- sessionSettings.setBoolean(GeckoSessionSettings.USE_MULTIPROCESS,
- useMultiprocess);
- mGeckoSession = new GeckoSession(sessionSettings);
+ mGeckoSession = (GeckoSession)getIntent().getParcelableExtra("session");
+ if (mGeckoSession != null) {
+ connectSession(mGeckoSession);
+
+ if (!mGeckoSession.isOpen()) {
+ mGeckoSession.open(sGeckoRuntime);
+ }
+
+ mUseMultiprocess = mGeckoSession.getSettings().getBoolean(GeckoSessionSettings.USE_MULTIPROCESS);
+
+ mGeckoView.setSession(mGeckoSession);
+ } else {
+ mGeckoSession = createSession();
+ mGeckoView.setSession(mGeckoSession, sGeckoRuntime);
+ loadFromIntent(getIntent());
+ }
- mGeckoView.setSession(mGeckoSession, sGeckoRuntime);
+ mLocationView.setCommitListener(mCommitListener);
+ }
+
+ private GeckoSession createSession() {
+ GeckoSession session = new GeckoSession();
+ session.getSettings().setBoolean(GeckoSessionSettings.USE_MULTIPROCESS, mUseMultiprocess);
+ session.getSettings().setBoolean(GeckoSessionSettings.USE_PRIVATE_MODE, mUsePrivateBrowsing);
+
+ connectSession(session);
- mGeckoSession.setContentDelegate(new MyGeckoViewContent());
- final MyTrackingProtection tp = new MyTrackingProtection();
- mGeckoSession.setTrackingProtectionDelegate(tp);
- mGeckoSession.setProgressDelegate(new MyGeckoViewProgress(tp));
- mGeckoSession.setNavigationDelegate(new Navigation());
+ return session;
+ }
+
+ private void connectSession(GeckoSession session) {
+ session.setContentDelegate(new ExampleContentDelegate());
+ final ExampleTrackingProtectionDelegate tp = new ExampleTrackingProtectionDelegate();
+ session.setTrackingProtectionDelegate(tp);
+ session.setProgressDelegate(new ExampleProgressDelegate(tp));
+ session.setNavigationDelegate(new ExampleNavigationDelegate());
final BasicGeckoViewPrompt prompt = new BasicGeckoViewPrompt(this);
prompt.filePickerRequestCode = REQUEST_FILE_PICKER;
- mGeckoSession.setPromptDelegate(prompt);
+ session.setPromptDelegate(prompt);
+
+ final ExamplePermissionDelegate permission = new ExamplePermissionDelegate();
+ permission.androidPermissionRequestCode = REQUEST_PERMISSIONS;
+ session.setPermissionDelegate(permission);
+
+ updateTrackingProtection(session);
+ }
+
+ private void recreateSession() {
+ mGeckoSession.close();
+
+ mGeckoSession = createSession();
+ mGeckoSession.open(sGeckoRuntime);
+ mGeckoView.setSession(mGeckoSession);
+ mGeckoSession.loadUri(mCurrentUri != null ? mCurrentUri : DEFAULT_URL);
+ }
- final MyGeckoViewPermission permission = new MyGeckoViewPermission();
- permission.androidPermissionRequestCode = REQUEST_PERMISSIONS;
- mGeckoSession.setPermissionDelegate(permission);
+ private void updateTrackingProtection(GeckoSession session) {
+ if (mUseTrackingProtection) {
+ session.enableTrackingProtection(
+ TrackingProtectionDelegate.CATEGORY_AD |
+ TrackingProtectionDelegate.CATEGORY_ANALYTIC |
+ TrackingProtectionDelegate.CATEGORY_SOCIAL
+ );
+ } else {
+ session.disableTrackingProtection();
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (mFullScreen) {
+ mGeckoSession.exitFullScreen();
+ return;
+ }
+
+ if (mCanGoBack && mGeckoSession != null) {
+ mGeckoSession.goBack();
+ return;
+ }
+
+ super.onBackPressed();
+ }
- mGeckoSession.enableTrackingProtection(
- TrackingProtectionDelegate.CATEGORY_AD |
- TrackingProtectionDelegate.CATEGORY_ANALYTIC |
- TrackingProtectionDelegate.CATEGORY_SOCIAL
- );
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.actions, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ menu.findItem(R.id.action_e10s).setChecked(mUseMultiprocess);
+ menu.findItem(R.id.action_tp).setChecked(mUseTrackingProtection);
+ menu.findItem(R.id.action_pb).setChecked(mUsePrivateBrowsing);
+ return true;
+ }
- loadSettings(getIntent());
- loadFromIntent(getIntent());
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_reload:
+ mGeckoSession.reload();
+ break;
+ case R.id.action_forward:
+ mGeckoSession.goForward();
+ break;
+ case R.id.action_e10s:
+ mUseMultiprocess = !mUseMultiprocess;
+ recreateSession();
+ break;
+ case R.id.action_tp:
+ mUseTrackingProtection = !mUseTrackingProtection;
+ updateTrackingProtection(mGeckoSession);
+ mGeckoSession.reload();
+ break;
+ case R.id.action_pb:
+ mUsePrivateBrowsing = !mUsePrivateBrowsing;
+ recreateSession();
+ break;
+ case R.id.action_crash_native:
+ GeckoThread.crash();
+ break;
+ case R.id.action_crash_java:
+ throw new RuntimeException("Crash via menu");
+ case R.id.action_crash_content_native:
+ GeckoProcessManager.getInstance().crashChild();
+ break;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+
+ return true;
}
@Override
public void onDestroy() {
- super.onDestroy();
-
if (mKillProcessOnDestroy) {
android.os.Process.killProcess(android.os.Process.myPid());
}
+
+ super.onDestroy();
}
- @Override
protected void onNewIntent(final Intent intent) {
super.onNewIntent(intent);
if (ACTION_SHUTDOWN.equals(intent.getAction())) {
mKillProcessOnDestroy = true;
if (sGeckoRuntime != null) {
sGeckoRuntime.shutdown();
}
finish();
return;
}
setIntent(intent);
- loadSettings(intent);
if (intent.getData() != null) {
loadFromIntent(intent);
}
}
- private void loadFromIntent(final Intent intent) {
+
+ private void loadFromIntent(final Intent intent) {
final Uri uri = intent.getData();
mGeckoSession.loadUri(uri != null ? uri.toString() : DEFAULT_URL);
}
- private void loadSettings(final Intent intent) {
- sGeckoRuntime.getSettings().setRemoteDebuggingEnabled(
- intent.getBooleanExtra(USE_REMOTE_DEBUGGER_EXTRA, false));
- }
-
@Override
protected void onActivityResult(final int requestCode, final int resultCode,
final Intent data) {
if (requestCode == REQUEST_FILE_PICKER) {
final BasicGeckoViewPrompt prompt = (BasicGeckoViewPrompt)
mGeckoSession.getPromptDelegate();
prompt.onFileCallbackResult(resultCode, data);
} else {
@@ -159,38 +291,39 @@ public class GeckoViewActivity extends A
}
}
@Override
public void onRequestPermissionsResult(final int requestCode,
final String[] permissions,
final int[] grantResults) {
if (requestCode == REQUEST_PERMISSIONS) {
- final MyGeckoViewPermission permission = (MyGeckoViewPermission)
+ final ExamplePermissionDelegate permission = (ExamplePermissionDelegate)
mGeckoSession.getPermissionDelegate();
permission.onRequestPermissionsResult(permissions, grantResults);
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
- private class MyGeckoViewContent implements GeckoSession.ContentDelegate {
+ private class ExampleContentDelegate implements GeckoSession.ContentDelegate {
@Override
public void onTitleChange(GeckoSession session, String title) {
Log.i(LOGTAG, "Content title changed to " + title);
}
@Override
public void onFullScreen(final GeckoSession session, final boolean fullScreen) {
getWindow().setFlags(fullScreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ mFullScreen = fullScreen;
if (fullScreen) {
- getActionBar().hide();
+ getSupportActionBar().hide();
} else {
- getActionBar().show();
+ getSupportActionBar().show();
}
}
@Override
public void onFocusRequest(final GeckoSession session) {
Log.i(LOGTAG, "Content requesting focus");
}
@@ -210,20 +343,20 @@ public class GeckoViewActivity extends A
" elementSrc=" + elementSrc);
}
@Override
public void onExternalResponse(GeckoSession session, GeckoSession.WebResponseInfo request) {
}
}
- private class MyGeckoViewProgress implements GeckoSession.ProgressDelegate {
- private MyTrackingProtection mTp;
+ private class ExampleProgressDelegate implements GeckoSession.ProgressDelegate {
+ private ExampleTrackingProtectionDelegate mTp;
- private MyGeckoViewProgress(final MyTrackingProtection tp) {
+ private ExampleProgressDelegate(final ExampleTrackingProtectionDelegate tp) {
mTp = tp;
}
@Override
public void onPageStart(GeckoSession session, String url) {
Log.i(LOGTAG, "Starting to load page at " + url);
Log.i(LOGTAG, "zerdatime " + SystemClock.elapsedRealtime() +
" - page load start");
@@ -239,17 +372,17 @@ public class GeckoViewActivity extends A
}
@Override
public void onSecurityChange(GeckoSession session, SecurityInformation securityInfo) {
Log.i(LOGTAG, "Security status changed to " + securityInfo.securityMode);
}
}
- private class MyGeckoViewPermission implements GeckoSession.PermissionDelegate {
+ private class ExamplePermissionDelegate implements GeckoSession.PermissionDelegate {
public int androidPermissionRequestCode = 1;
private Callback mCallback;
public void onRequestPermissionsResult(final String[] permissions,
final int[] grantResults) {
if (mCallback == null) {
return;
@@ -345,23 +478,26 @@ public class GeckoViewActivity extends A
String[] audioNames = normalizeMediaName(audio);
final BasicGeckoViewPrompt prompt = (BasicGeckoViewPrompt)
mGeckoSession.getPromptDelegate();
prompt.onMediaPrompt(session, title, video, audio, videoNames, audioNames, callback);
}
}
- private class Navigation implements GeckoSession.NavigationDelegate {
+ private class ExampleNavigationDelegate implements GeckoSession.NavigationDelegate {
@Override
public void onLocationChange(GeckoSession session, final String url) {
+ mLocationView.setText(url);
+ mCurrentUri = url;
}
@Override
public void onCanGoBack(GeckoSession session, boolean canGoBack) {
+ mCanGoBack = canGoBack;
}
@Override
public void onCanGoForward(GeckoSession session, boolean value) {
}
@Override
public void onLoadRequest(final GeckoSession session, final String uri,
@@ -369,21 +505,29 @@ public class GeckoViewActivity extends A
GeckoResponse<Boolean> response) {
Log.d(LOGTAG, "onLoadRequest=" + uri + " where=" + target +
" flags=" + flags);
response.respond(false);
}
@Override
public void onNewSession(final GeckoSession session, final String uri, GeckoResponse<GeckoSession> response) {
- response.respond(null);
+ GeckoSession newSession = new GeckoSession(session.getSettings());
+ response.respond(newSession);
+
+ Intent intent = new Intent(GeckoViewActivity.this, SessionActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+ intent.setAction(Intent.ACTION_VIEW);
+ intent.setData(Uri.parse(uri));
+ intent.putExtra("session", newSession);
+ startActivity(intent);
}
}
- private class MyTrackingProtection implements GeckoSession.TrackingProtectionDelegate {
+ private class ExampleTrackingProtectionDelegate implements GeckoSession.TrackingProtectionDelegate {
private int mBlockedAds = 0;
private int mBlockedAnalytics = 0;
private int mBlockedSocial = 0;
private void clearCounters() {
mBlockedAds = 0;
mBlockedAnalytics = 0;
mBlockedSocial = 0;
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/LocationView.java
@@ -0,0 +1,61 @@
+package org.mozilla.geckoview_example;
+
+import org.mozilla.geckoview.GeckoSession;
+
+import android.content.Context;
+import android.support.v7.widget.AppCompatEditText;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.widget.TextView;
+
+public class LocationView extends AppCompatEditText {
+
+ private CommitListener mCommitListener;
+ private FocusAndCommitListener mFocusCommitListener = new FocusAndCommitListener();
+
+ public interface CommitListener {
+ void onCommit(String text);
+ }
+
+ public LocationView(Context context) {
+ super(context);
+
+ this.setInputType(EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_URI);
+ this.setSingleLine(true);
+ this.setSelectAllOnFocus(true);
+ this.setHint(R.string.location_hint);
+
+ setOnFocusChangeListener(mFocusCommitListener);
+ setOnEditorActionListener(mFocusCommitListener);
+ }
+
+ public void setCommitListener(CommitListener listener) {
+ mCommitListener = listener;
+ }
+
+ private class FocusAndCommitListener implements OnFocusChangeListener, OnEditorActionListener {
+ private String mInitialText;
+ private boolean mCommitted;
+
+ @Override
+ public void onFocusChange(View view, boolean focused) {
+ if (focused) {
+ mInitialText = ((TextView)view).getText().toString();
+ mCommitted = false;
+ } else if (!mCommitted) {
+ setText(mInitialText);
+ }
+ }
+
+ @Override
+ public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) {
+ if (mCommitListener != null) {
+ mCommitListener.onCommit(textView.getText().toString());
+ }
+
+ mCommitted = true;
+ return true;
+ }
+ }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/SessionActivity.java
@@ -0,0 +1,4 @@
+package org.mozilla.geckoview_example;
+
+public class SessionActivity extends GeckoViewActivity {
+}
--- a/mobile/android/geckoview_example/src/main/res/layout/geckoview_activity.xml
+++ b/mobile/android/geckoview_example/src/main/res/layout/geckoview_activity.xml
@@ -1,13 +1,20 @@
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical">
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/main"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
<org.mozilla.geckoview.GeckoView
android:id="@+id/gecko_view"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:layout_above="@id/toolbar"
android:scrollbars="none"
/>
-</LinearLayout>
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?android:actionBarSize"
+ android:layout_alignParentBottom="true"/>
+</RelativeLayout>
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/main/res/menu/actions.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:title="Crash" android:icon="@android:drawable/ic_menu_more">
+ <menu>
+ <item android:title="Native" android:id="@+id/action_crash_native"/>
+ <item android:title="Java" android:id="@+id/action_crash_java"/>
+ <item android:title="Content Native" android:id="@+id/action_crash_content_native"/>
+ </menu>
+ </item>
+ <item android:title="Electrolysis" android:id="@+id/action_e10s" android:checkable="true"
+ android:showAsAction="never"/>
+ <item android:title="TP" android:id="@+id/action_tp" android:showAsAction="never"
+ android:checkable="true"/>
+ <item android:title="Private Browsing" android:checkable="true" android:id="@+id/action_pb"/>
+ <item android:title="Forward" android:id="@+id/action_forward"/>
+ <item android:title="Reload" android:id="@+id/action_reload"/>
+</menu>
\ No newline at end of file
--- a/mobile/android/geckoview_example/src/main/res/values/strings.xml
+++ b/mobile/android/geckoview_example/src/main/res/values/strings.xml
@@ -1,10 +1,12 @@
<resources>
<string name="app_name">geckoview_example</string>
+ <string name="activity_label">GeckoView Example</string>
+ <string name="location_hint">Enter URL or search keywords...</string>
<string name="username">Username</string>
<string name="password">Password</string>
<string name="clear_field">Clear</string>
<string name="request_geolocation">Share location with "%1$s"?</string>
<string name="request_notification">Allow notifications for "%1$s"?</string>
<string name="request_video">Share video with "%1$s"</string>
<string name="request_audio">Share audio with "%1$s"</string>
<string name="request_media">Share video and audio with "%1$s"</string>