diff --git a/mobile/android/search/java/org/mozilla/search/SearchWidget.java b/mobile/android/search/java/org/mozilla/search/SearchWidget.java
new file mode 100644
index 00000000000..88d8ea02247
--- /dev/null
+++ b/mobile/android/search/java/org/mozilla/search/SearchWidget.java
@@ -0,0 +1,124 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; 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.search;
+
+import org.mozilla.gecko.AppConstants;
+
+import android.annotation.SuppressLint;
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.widget.RemoteViews;
+import android.util.Log;
+
+/* Provides a really simple widget with two buttons, one to launch Fennec
+ * and one to launch the search activity. All intents are actually sent back
+ * here and then forwarded on to start the real activity. */
+public class SearchWidget extends AppWidgetProvider {
+ final private static String LOGTAG = "GeckoSearchWidget";
+
+ final public static String ACTION_LAUNCH_BROWSER = "org.mozilla.widget.LAUNCH_BROWSER";
+ final public static String ACTION_LAUNCH_SEARCH = "org.mozilla.widget.LAUNCH_SEARCH";
+
+ @SuppressLint("NewApi")
+ @Override
+ public void onUpdate(final Context context, final AppWidgetManager manager, final int[] ids) {
+ for (int id : ids) {
+ final Bundle bundle;
+ if (AppConstants.Versions.feature16Plus) {
+ bundle = manager.getAppWidgetOptions(id);
+ } else {
+ bundle = null;
+ }
+ addView(manager, context, id, bundle);
+ }
+
+ super.onUpdate(context, manager, ids);
+ }
+
+ @SuppressLint("NewApi")
+ @Override
+ public void onAppWidgetOptionsChanged(final Context context,
+ final AppWidgetManager manager,
+ final int id,
+ final Bundle options) {
+ addView(manager, context, id, options);
+ if (AppConstants.Versions.feature16Plus) {
+ super.onAppWidgetOptionsChanged(context, manager, id, options);
+ }
+ }
+
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+ // This will hold the intent to redispatch
+ final Intent redirect;
+ Log.i(LOGTAG, "Got intent " + intent.getAction());
+ if (intent.getAction().equals(ACTION_LAUNCH_BROWSER)) {
+ redirect = buildRedirectIntent(Intent.ACTION_VIEW,
+ AppConstants.ANDROID_PACKAGE_NAME,
+ AppConstants.BROWSER_INTENT_CLASS_NAME,
+ intent);
+ } else if (intent.getAction().equals(ACTION_LAUNCH_SEARCH)) {
+ redirect = buildRedirectIntent(Intent.ACTION_VIEW,
+ AppConstants.SEARCH_PACKAGE_NAME,
+ AppConstants.SEARCH_INTENT_CLASS_NAME,
+ intent);
+ } else {
+ redirect = null;
+ }
+
+ if (redirect != null) {
+ try {
+ context.startActivity(redirect);
+ } catch(Exception ex) {
+ // When this is built stand alone, its hardcoded to try and launch nightly.
+ // If that fails, just fire a generic VIEW intent.
+ Intent redirect2 = buildRedirectIntent(Intent.ACTION_VIEW, null, null, intent);
+ context.startActivity(redirect2);
+ }
+ }
+
+ super.onReceive(context, intent);
+ }
+
+ // Utility to create the view for this widget and attach any event listeners to it
+ private void addView(final AppWidgetManager manager, final Context context, final int id, final Bundle options) {
+ final RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.search_widget);
+
+ addClickIntent(context, views, R.id.search_button, ACTION_LAUNCH_SEARCH);
+ addClickIntent(context, views, R.id.new_tab_button, ACTION_LAUNCH_BROWSER);
+ // Clicking the logo also launches the browser
+ addClickIntent(context, views, R.id.logo_button, ACTION_LAUNCH_BROWSER);
+
+ manager.updateAppWidget(id, views);
+ }
+
+ // Utility for adding a pending intent to be fired when a View is clicked.
+ private void addClickIntent(final Context context, final RemoteViews views, final int viewId, final String action) {
+ final Intent intent = new Intent(context, SearchWidget.class);
+ intent.setAction(action);
+ intent.setData(Uri.parse("about:home"));
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
+ views.setOnClickPendingIntent(viewId, pendingIntent);
+ }
+
+ // Utility for building an intent to be redispatched (i.e. to launch the browser or the search intent).
+ private Intent buildRedirectIntent(final String action, final String pkg, final String className, final Intent source) {
+ final Intent activity = new Intent(action);
+ if (pkg != null && className != null) {
+ activity.setClassName(pkg, className);
+ }
+ activity.setData(source.getData());
+ activity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return activity;
+ }
+
+}
diff --git a/mobile/android/search/manifests/SearchAndroidManifest_activities.xml.in b/mobile/android/search/manifests/SearchAndroidManifest_activities.xml.in
index 12589f76224..672e6353b9d 100644
--- a/mobile/android/search/manifests/SearchAndroidManifest_activities.xml.in
+++ b/mobile/android/search/manifests/SearchAndroidManifest_activities.xml.in
@@ -13,8 +13,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mobile/android/search/res/layout/search_widget.xml b/mobile/android/search/res/layout/search_widget.xml
new file mode 100644
index 00000000000..2ad9d50d27d
--- /dev/null
+++ b/mobile/android/search/res/layout/search_widget.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mobile/android/search/res/values/search_dimens.xml b/mobile/android/search/res/values/search_dimens.xml
index 80e4deadcac..444807eb973 100644
--- a/mobile/android/search/res/values/search_dimens.xml
+++ b/mobile/android/search/res/values/search_dimens.xml
@@ -23,4 +23,11 @@
38dp
23dp
+
+ 70dp
+ -50dp
+ 45dp
+ 15sp
+ 2dp
+
diff --git a/mobile/android/search/res/xml/search_widget_info.xml b/mobile/android/search/res/xml/search_widget_info.xml
new file mode 100644
index 00000000000..f837c4a208e
--- /dev/null
+++ b/mobile/android/search/res/xml/search_widget_info.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/mobile/android/search/search_activity_sources.mozbuild b/mobile/android/search/search_activity_sources.mozbuild
index 517cd0ba13f..d84d243d201 100644
--- a/mobile/android/search/search_activity_sources.mozbuild
+++ b/mobile/android/search/search_activity_sources.mozbuild
@@ -16,4 +16,5 @@ search_activity_sources = [
'java/org/mozilla/search/PostSearchFragment.java',
'java/org/mozilla/search/PreSearchFragment.java',
'java/org/mozilla/search/SearchPreferenceActivity.java',
+ 'java/org/mozilla/search/SearchWidget.java',
]
diff --git a/mobile/android/search/strings/search_strings.dtd b/mobile/android/search/strings/search_strings.dtd
index 425ed073ef9..1b4d2e251a9 100644
--- a/mobile/android/search/strings/search_strings.dtd
+++ b/mobile/android/search/strings/search_strings.dtd
@@ -4,7 +4,7 @@
-
+
@@ -12,3 +12,6 @@
+
+
+
diff --git a/mobile/android/search/strings/search_strings.xml.in b/mobile/android/search/strings/search_strings.xml.in
index ee0b866166e..6c8e45b0b73 100644
--- a/mobile/android/search/strings/search_strings.xml.in
+++ b/mobile/android/search/strings/search_strings.xml.in
@@ -8,3 +8,6 @@
&search_pref_clear_history_dialog_message;
&search_pref_clear_history_title;
&search_pref_button_content_description;
+
+ &search_app_name;
+ &search_widget_button_label;