diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 7a54cf92040..cec0d22884d 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -234,6 +234,7 @@ FENNEC_JAVA_FILES = \ menu/MenuPanel.java \ menu/MenuPopup.java \ preferences/SearchPreferenceCategory.java \ + preferences/SearchEnginePreference.java \ widget/AboutHome.java \ widget/AboutHomeView.java \ widget/AboutHomeSection.java \ diff --git a/mobile/android/base/awesomebar/AllPagesTab.java b/mobile/android/base/awesomebar/AllPagesTab.java index 17149c090d6..3e004dea26d 100644 --- a/mobile/android/base/awesomebar/AllPagesTab.java +++ b/mobile/android/base/awesomebar/AllPagesTab.java @@ -90,7 +90,7 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener { mSearchEngines = new ArrayList(); registerEventListener("SearchEngines:Data"); - GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("SearchEngines:Get", null)); + GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("SearchEngines:GetVisible", null)); mHandler = new AllPagesHandler(); } diff --git a/mobile/android/base/locales/en-US/android_strings.dtd b/mobile/android/base/locales/en-US/android_strings.dtd index 004ad7572ed..94a4d3c3ae0 100644 --- a/mobile/android/base/locales/en-US/android_strings.dtd +++ b/mobile/android/base/locales/en-US/android_strings.dtd @@ -144,6 +144,10 @@ size. --> + + + + diff --git a/mobile/android/base/preferences/SearchEnginePreference.java b/mobile/android/base/preferences/SearchEnginePreference.java new file mode 100644 index 00000000000..598e63de810 --- /dev/null +++ b/mobile/android/base/preferences/SearchEnginePreference.java @@ -0,0 +1,229 @@ +/* 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.gecko.preferences; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.os.Build; +import android.preference.Preference; +import android.text.SpannableString; +import android.util.Log; +import android.widget.TextView; +import android.widget.Toast; +import org.json.JSONException; +import org.json.JSONObject; +import org.mozilla.gecko.R; +import org.mozilla.gecko.gfx.BitmapUtils; +import org.mozilla.gecko.util.ThreadUtils; + +/** + * Represents an element in the list of search engines on the preferences menu. + */ +public class SearchEnginePreference extends Preference { + private static final String LOGTAG = "SearchEnginePreference"; + + // Dimensions, in dp, of the icon to display for this engine. + public static int sIconSize; + + // Indices in button array of the AlertDialog of the three buttons. + public static final int INDEX_SET_DEFAULT_BUTTON = 0; + public static final int INDEX_REMOVE_BUTTON = 1; + + // Cache label to avoid repeated use of the resource system. + public final String LABEL_IS_DEFAULT; + + // Specifies if this engine is configured as the default search engine. + private boolean mIsDefaultEngine; + // Specifies if this engine is one of the ones bundled with the app, which cannot be deleted. + private boolean mIsImmutableEngine; + + // Dialog element labels. + private String[] mDialogItems; + + // The popup displayed when this element is tapped. + private AlertDialog mDialog; + + private final SearchPreferenceCategory mParentCategory; + + /** + * Create a preference object to represent a search engine that is attached to category + * containingCategory. + * @param context The activity context we operate under. + * @param parentCategory The PreferenceCategory this object exists within. + * @see this.setSearchEngine + */ + public SearchEnginePreference(Context context, SearchPreferenceCategory parentCategory) { + super(context); + mParentCategory = parentCategory; + + Resources res = getContext().getResources(); + + // Fetch the icon dimensions from the resource file. + sIconSize = res.getDimensionPixelSize(R.dimen.searchpreferences_icon_size); + setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + SearchEnginePreference sPref = (SearchEnginePreference) preference; + sPref.showDialog(); + + return true; + } + }); + + // Fetch this resource now, instead of every time we ever want to relabel a button. + LABEL_IS_DEFAULT = res.getString(R.string.pref_search_default); + + // Set up default dialog items. + mDialogItems = new String[] { res.getString(R.string.pref_search_set_default), + res.getString(R.string.pref_search_remove) }; + } + + /** + * Configure this Preference object from the Gecko search engine JSON object. + * @param geckoEngineJSON The Gecko-formatted JSON object representing the search engine. + * @throws JSONException If the JSONObject is invalid. + */ + public void setSearchEngineFromJSON(JSONObject geckoEngineJSON) throws JSONException { + final String engineName = geckoEngineJSON.getString("name"); + SpannableString titleSpannable = new SpannableString(engineName); + mIsImmutableEngine = geckoEngineJSON.getBoolean("immutable"); + + if (mIsImmutableEngine) { + // Delete the "Remove" option from the menu. + mDialogItems = new String[] { getContext().getResources().getString(R.string.pref_search_set_default) }; + } + setTitle(titleSpannable); + + // setIcon is only available on Honeycomb and up. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + // Create a drawable from the iconURI and assign it to this Preference for display. + String iconURI = geckoEngineJSON.getString("iconURI"); + Bitmap iconBitmap = BitmapUtils.getBitmapFromDataURI(iconURI); + Bitmap scaledIconBitmap = Bitmap.createScaledBitmap(iconBitmap, sIconSize, sIconSize, false); + BitmapDrawable drawable = new BitmapDrawable(scaledIconBitmap); + setIcon(drawable); + } + } + + /** + * Set if this object's UI should show that this is the default engine. + * @param isDefault Flag indicating if this represents the default engine. + */ + public void setIsDefaultEngine(boolean isDefault) { + mIsDefaultEngine = isDefault; + if (isDefault) { + setOrder(0); + setSummary(LABEL_IS_DEFAULT); + } else { + setOrder(1); + setSummary(""); + } + } + + /** + * Display the AlertDialog providing options to reconfigure this search engine. Sets an event + * listener to disable buttons in the dialog as appropriate after they have been constructed by + * Android. + * @see this.configureShownDialog + * @see this.hideDialog + */ + public void showDialog() { + // If we are the only engine left, then we are the default engine, and none of the options + // on this menu can do anything. + if (mParentCategory.getPreferenceCount() == 1) { + ThreadUtils.postToUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(getContext(), R.string.pref_search_last_toast, Toast.LENGTH_SHORT).show(); + } + }); + return; + } + + // If we are both default and immutable, we have no enabled items to show on the menu - abort. + if (mIsDefaultEngine && mIsImmutableEngine) { + return; + } + + final AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setTitle(getTitle().toString()); + builder.setItems(mDialogItems, new DialogInterface.OnClickListener() { + // Forward the various events that we care about to the container class for handling. + @Override + public void onClick(DialogInterface dialog, int indexClicked) { + hideDialog(); + switch (indexClicked) { + case INDEX_SET_DEFAULT_BUTTON: + mParentCategory.setDefault(SearchEnginePreference.this); + break; + case INDEX_REMOVE_BUTTON: + mParentCategory.uninstall(SearchEnginePreference.this); + break; + default: + Log.w(LOGTAG, "Selected index out of range."); + break; + } + } + }); + + // Copy the icon, if any, from this object to the prompt we produce. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + builder.setIcon(getIcon()); + } + + // We have to construct the dialog itself on the UI thread. + ThreadUtils.postToUiThread(new Runnable() { + @Override + public void run() { + mDialog = builder.create(); + mDialog.setOnShowListener(new DialogInterface.OnShowListener() { + // Called when the dialog is shown (so we're finally able to manipulate button enabledness). + @Override + public void onShow(DialogInterface dialog) { + configureShownDialog(); + } + }); + mDialog.show(); + } + }); + } + + /** + * Hide the dialog we previously created, if any. + */ + public void hideDialog() { + ThreadUtils.postToUiThread(new Runnable() { + @Override + public void run() { + // Null check so we can chain engine-mutating methods up in SearchPreferenceCategory + // without consequence. + if (mDialog != null && mDialog.isShowing()) { + mDialog.dismiss(); + } + } + }); + } + + /** + * Disables buttons in the shown AlertDialog as required. The button elements are not created + * until after we call show, so this method has to be called from the onShowListener above. + * @see this.showDialog + */ + private void configureShownDialog() { + // If we are the default engine, disable the "Set as default" button. + TextView defaultButton = (TextView) mDialog.getListView().getChildAt(INDEX_SET_DEFAULT_BUTTON); + // Disable "Set as default" button if we are already the default. + if (mIsDefaultEngine) { + defaultButton.setEnabled(false); + // Failure to unregister this listener leads to tapping the button dismissing the dialog + // without doing anything. + defaultButton.setOnClickListener(null); + } + } +} diff --git a/mobile/android/base/preferences/SearchPreferenceCategory.java b/mobile/android/base/preferences/SearchPreferenceCategory.java index 31378748591..35bdc2a5f71 100644 --- a/mobile/android/base/preferences/SearchPreferenceCategory.java +++ b/mobile/android/base/preferences/SearchPreferenceCategory.java @@ -1,9 +1,10 @@ +/* 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.gecko.preferences; import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; -import android.os.Build; import android.preference.Preference; import android.preference.PreferenceCategory; import android.util.AttributeSet; @@ -13,38 +14,36 @@ import org.json.JSONException; import org.json.JSONObject; import org.mozilla.gecko.GeckoAppShell; import org.mozilla.gecko.GeckoEvent; -import org.mozilla.gecko.R; -import org.mozilla.gecko.gfx.BitmapUtils; import org.mozilla.gecko.util.GeckoEventListener; public class SearchPreferenceCategory extends PreferenceCategory implements GeckoEventListener { public static final String LOGTAG = "SearchPrefCategory"; - private static int sIconSize; + private SearchEnginePreference mDefaultEngineReference; + + // These seemingly redundant constructors are mandated by the Android system, else it fails to + // inflate this object. public SearchPreferenceCategory(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - init(); } + public SearchPreferenceCategory(Context context, AttributeSet attrs) { super(context, attrs); - init(); } public SearchPreferenceCategory(Context context) { super(context); - init(); - } - - private void init() { - sIconSize = getContext().getResources().getDimensionPixelSize(R.dimen.searchpreferences_icon_size); } @Override protected void onAttachedToActivity() { super.onAttachedToActivity(); - // Request list of search engines from Gecko + // Ensures default engine remains at top of list. + setOrderingAsAdded(false); + + // Request list of search engines from Gecko. GeckoAppShell.registerEventListener("SearchEngines:Data", this); GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("SearchEngines:Get", null)); } @@ -52,9 +51,17 @@ public class SearchPreferenceCategory extends PreferenceCategory implements Geck @Override public void handleMessage(String event, final JSONObject data) { if (event.equals("SearchEngines:Data")) { + // Parse engines array from JSON. The first element in the array is the default engine. JSONArray engines; + JSONObject defaultEngine; + final String defaultEngineName; try { engines = data.getJSONArray("searchEngines"); + if (engines.length() == 0) { + return; + } + defaultEngine = engines.getJSONObject(0); + defaultEngineName = defaultEngine.getString("name"); } catch (JSONException e) { Log.e(LOGTAG, "Unable to decode search engine data from Gecko.", e); return; @@ -66,24 +73,90 @@ public class SearchPreferenceCategory extends PreferenceCategory implements Geck JSONObject engineJSON = engines.getJSONObject(i); final String engineName = engineJSON.getString("name"); - Preference engine = new Preference(getContext()); - engine.setTitle(engineName); - engine.setKey(engineName); - - // The setIcon feature is not available prior to API 11. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - String iconURI = engineJSON.getString("iconURI"); - Bitmap iconBitmap = BitmapUtils.getBitmapFromDataURI(iconURI); - Bitmap scaledIconBitmap = Bitmap.createScaledBitmap(iconBitmap, sIconSize, sIconSize, false); - BitmapDrawable drawable = new BitmapDrawable(scaledIconBitmap); - engine.setIcon(drawable); + SearchEnginePreference enginePreference = new SearchEnginePreference(getContext(), this); + enginePreference.setSearchEngineFromJSON(engineJSON); + if (engineName.equals(defaultEngineName)) { + // We set this here, not in setSearchEngineFromJSON, because it allows us to + // keep a reference to the default engine to use when the AlertDialog + // callbacks are used. + enginePreference.setIsDefaultEngine(true); + mDefaultEngineReference = enginePreference; } - addPreference(engine); - // TODO: Bug 892113 - Add event listener here for tapping on each element. Produce a dialog to provide options. + + enginePreference.setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + SearchEnginePreference sPref = (SearchEnginePreference) preference; + // Display the configuration dialog associated with the tapped engine. + sPref.showDialog(); + return true; + } + }); + + addPreference(enginePreference); } catch (JSONException e) { Log.e(LOGTAG, "JSONException parsing engine at index " + i, e); } } } + + // We are no longer interested in this event from Gecko, as we do not request it again with + // this instance. + GeckoAppShell.unregisterEventListener("SearchEngines:Data", this); + } + + /** + * Set the default engine to any available engine. Used if the current default is removed or + * disabled. + */ + private void setFallbackDefaultEngine() { + if (getPreferenceCount() > 0) { + SearchEnginePreference aEngine = (SearchEnginePreference) getPreference(0); + setDefault(aEngine); + } + } + + /** + * Helper method to send a particular event string to Gecko with an associated engine name. + * @param event The type of event to send. + * @param engine The engine to which the event relates. + */ + private void sendGeckoEngineEvent(String event, SearchEnginePreference engine) { + JSONObject json = new JSONObject(); + try { + json.put("engine", engine.getTitle()); + } catch (JSONException e) { + Log.e(LOGTAG, "JSONException creating search engine configuration change message for Gecko.", e); + return; + } + GeckoAppShell.notifyGeckoOfEvent(GeckoEvent.createBroadcastEvent(event, json.toString())); + } + + // Methods called by tapping items on the submenus for each search engine are below. + + /** + * Removes the given engine from the set of available engines. + * @param engine The engine to remove. + */ + public void uninstall(SearchEnginePreference engine) { + removePreference(engine); + if (engine == mDefaultEngineReference) { + // If they're deleting their default engine, get them a new default engine. + setFallbackDefaultEngine(); + } + + sendGeckoEngineEvent("SearchEngines:Remove", engine); + } + + /** + * Sets the given engine as the current default engine. + * @param engine The intended new default engine. + */ + public void setDefault(SearchEnginePreference engine) { + engine.setIsDefaultEngine(true); + mDefaultEngineReference.setIsDefaultEngine(false); + mDefaultEngineReference = engine; + + sendGeckoEngineEvent("SearchEngines:SetDefault", engine); } } diff --git a/mobile/android/base/strings.xml.in b/mobile/android/base/strings.xml.in index 9008c1fff07..5454aa5ba47 100644 --- a/mobile/android/base/strings.xml.in +++ b/mobile/android/base/strings.xml.in @@ -151,6 +151,12 @@ &pref_vendor_faqs; &pref_vendor_feedback; + + &pref_search_set_default; + &pref_search_default; + &pref_search_remove; + &pref_search_last_toast; + &datareporting_notification_title; &datareporting_notification_action_long; &datareporting_notification_action; diff --git a/mobile/android/chrome/content/aboutAddons.js b/mobile/android/chrome/content/aboutAddons.js index 2a2f06d173b..9c27dc56abb 100644 --- a/mobile/android/chrome/content/aboutAddons.js +++ b/mobile/android/chrome/content/aboutAddons.js @@ -61,12 +61,7 @@ var ContextMenus = { document.getElementById("contextmenu-disable").setAttribute("hidden", "true"); } - // Only show the "Set as Default" menuitem for enabled non-default search engines. - if (addon.type == "search" && enabled && addon.id != Services.search.defaultEngine.name) { - document.getElementById("contextmenu-default").removeAttribute("hidden"); - } else { - document.getElementById("contextmenu-default").setAttribute("hidden", "true"); - } + document.getElementById("contextmenu-default").setAttribute("hidden", "true"); }, enable: function(event) { @@ -82,17 +77,11 @@ var ContextMenus = { uninstall: function (event) { Addons.uninstall(this.target.addon); this.target = null; - }, - - setDefaultSearch: function(event) { - Addons.setDefaultSearch(this.target.addon); - this.target = null; } } function init() { window.addEventListener("popstate", onPopState, false); - Services.obs.addObserver(Addons, "browser-search-engine-modified", false); AddonManager.addInstallListener(Addons); AddonManager.addAddonListener(Addons); @@ -102,7 +91,6 @@ function init() { } function uninit() { - Services.obs.removeObserver(Addons, "browser-search-engine-modified"); AddonManager.removeInstallListener(Addons); AddonManager.removeAddonListener(Addons); } @@ -361,16 +349,7 @@ var Addons = { uninstallBtn.removeAttribute("disabled"); let defaultButton = document.getElementById("default-btn"); - if (addon.type == "search") { - if (addon.id == Services.search.defaultEngine.name) - defaultButton.setAttribute("disabled", "true"); - else - defaultButton.removeAttribute("disabled"); - - defaultButton.removeAttribute("hidden"); - } else { - defaultButton.setAttribute("hidden", "true"); - } + defaultButton.setAttribute("hidden", "true"); let box = document.querySelector("#addons-details > .addon-item .options-box"); box.innerHTML = ""; @@ -424,10 +403,7 @@ var Addons = { let listItem = this._getElementForAddon(addon.id); let opType; - if (addon.type == "search") { - addon.engine.hidden = !aValue; - opType = aValue ? "needs-enable" : "needs-disable"; - } else if (addon.type == "theme") { + if (addon.type == "theme") { if (aValue) { // We can have only one theme enabled, so disable the current one if any let list = document.getElementById("addons-list"); @@ -492,30 +468,21 @@ var Addons = { let listItem = this._getElementForAddon(addon.id); - if (addon.type == "search") { - // Make sure the engine isn't hidden before removing it, to make sure it's - // visible if the user later re-adds it (works around bug 341833) - addon.engine.hidden = false; - Services.search.removeEngine(addon.engine); - // the search-engine-modified observer will take care of updating the list - history.back(); + addon.uninstall(); + if (addon.pendingOperations & AddonManager.PENDING_UNINSTALL) { + this.showRestart(); + + // A disabled addon doesn't need a restart so it has no pending ops and + // can't be cancelled + let opType = this._getOpTypeForOperations(addon.pendingOperations); + if (!addon.isActive && opType == "") + opType = "needs-uninstall"; + + detailItem.setAttribute("opType", opType); + listItem.setAttribute("opType", opType); } else { - addon.uninstall(); - if (addon.pendingOperations & AddonManager.PENDING_UNINSTALL) { - this.showRestart(); - - // A disabled addon doesn't need a restart so it has no pending ops and - // can't be cancelled - let opType = this._getOpTypeForOperations(addon.pendingOperations); - if (!addon.isActive && opType == "") - opType = "needs-uninstall"; - - detailItem.setAttribute("opType", opType); - listItem.setAttribute("opType", opType); - } else { - list.removeChild(listItem); - history.back(); - } + list.removeChild(listItem); + history.back(); } }, @@ -535,20 +502,6 @@ var Addons = { listItem.setAttribute("opType", opType); }, - setDefaultSearch: function setDefaultSearch(aAddon) { - let addon = aAddon || document.querySelector("#addons-details > .addon-item").addon; - if (addon.type != "search") - return; - - let engine = Services.search.getEngineByName(addon.id); - - // Move the new default search engine to the top of the search engine list. - Services.search.moveEngine(engine, 0); - Services.search.defaultEngine = engine; - - document.getElementById("default-btn").setAttribute("disabled", "true"); - }, - showRestart: function showRestart() { this._restartCount++; gChromeWin.XPInstallObserver.showRestartPrompt(); @@ -590,18 +543,6 @@ var Addons = { element.setAttribute("opType", "needs-restart"); }, - observe: function observe(aSubject, aTopic, aData) { - if (aTopic == "browser-search-engine-modified") { - switch (aData) { - case "engine-added": - case "engine-removed": - case "engine-changed": - this.getAddons(); - break; - } - } - }, - onInstallFailed: function(aInstall) { }, diff --git a/mobile/android/chrome/content/aboutAddons.xhtml b/mobile/android/chrome/content/aboutAddons.xhtml index e3c34587103..04615a49c40 100644 --- a/mobile/android/chrome/content/aboutAddons.xhtml +++ b/mobile/android/chrome/content/aboutAddons.xhtml @@ -57,7 +57,6 @@ -
@@ -84,7 +83,6 @@ -
&aboutAddons.options;
diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 2b91d37fb0c..9b49befcc34 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -6421,6 +6421,9 @@ var SearchEngines = { init: function init() { Services.obs.addObserver(this, "SearchEngines:Get", false); + Services.obs.addObserver(this, "SearchEngines:GetVisible", false); + Services.obs.addObserver(this, "SearchEngines:SetDefault", false); + Services.obs.addObserver(this, "SearchEngines:Remove", false); let contextName = Strings.browser.GetStringFromName("contextmenu.addSearchEngine"); let filter = { matches: function (aElement) { @@ -6432,21 +6435,36 @@ var SearchEngines = { uninit: function uninit() { Services.obs.removeObserver(this, "SearchEngines:Get"); + Services.obs.removeObserver(this, "SearchEngines:GetVisible"); + Services.obs.removeObserver(this, "SearchEngines:SetDefault"); + Services.obs.removeObserver(this, "SearchEngines:Remove"); if (this._contextMenuId != null) NativeWindow.contextmenus.remove(this._contextMenuId); }, - _handleSearchEnginesGet: function _handleSearchEnginesGet(rv) { + // Fetch list of search engines. all ? All engines : Visible engines only. + _handleSearchEnginesGet: function _handleSearchEnginesGet(rv, all) { if (!Components.isSuccessCode(rv)) { Cu.reportError("Could not initialize search service, bailing out."); return; } - let engineData = Services.search.getVisibleEngines({}); + let engineData; + if (all) { + engineData = Services.search.getEngines({}); + } else { + engineData = Services.search.getVisibleEngines({}); + } + + // These engines are the bundled ones - they may not be uninstalled. + let immutableEngines = Services.search.getDefaultEngines(); + let searchEngines = engineData.map(function (engine) { return { name: engine.name, identifier: engine.identifier, - iconURI: (engine.iconURI ? engine.iconURI.spec : null) + iconURI: (engine.iconURI ? engine.iconURI.spec : null), + hidden: engine.hidden, + immutable: immutableEngines.indexOf(engine) != -1 }; }); @@ -6458,6 +6476,8 @@ var SearchEngines = { suggestTemplate = engine.getSubmission("__searchTerms__", "application/x-suggestions+json").uri.spec; } + + // By convention, the currently configured default engine is at position zero in searchEngines. sendMessageToJava({ type: "SearchEngines:Data", searchEngines: searchEngines, @@ -6470,9 +6490,45 @@ var SearchEngines = { }); }, + _handleSearchEnginesGetAll: function _handleSearchEnginesGetAll(rv) { + this._handleSearchEnginesGet(rv, true); + }, + _handleSearchEnginesGetVisible: function _handleSearchEnginesGetVisible(rv) { + this._handleSearchEnginesGet(rv, false) + }, + + // Helper method to extract the engine name from a JSON. Simplifies the observe function. + _extractEngineFromJSON: function _extractEngineFromJSON(aData) { + let data = JSON.parse(aData); + return Services.search.getEngineByName(data.engine); + }, + observe: function observe(aSubject, aTopic, aData) { - if (aTopic == "SearchEngines:Get") { - Services.search.init(this._handleSearchEnginesGet.bind(this)); + let engine; + switch(aTopic) { + case "SearchEngines:GetVisible": + Services.search.init(this._handleSearchEnginesGetVisible.bind(this)); + break; + case "SearchEngines:Get": + // Return a list of all engines, including "Hidden" ones. + Services.search.init(this._handleSearchEnginesGetAll.bind(this)); + break; + case "SearchEngines:SetDefault": + engine = this._extractEngineFromJSON(aData); + // Move the new default search engine to the top of the search engine list. + Services.search.moveEngine(engine, 0); + Services.search.defaultEngine = engine; + break; + case "SearchEngines:Remove": + // Make sure the engine isn't hidden before removing it, to make sure it's + // visible if the user later re-adds it (works around bug 341833) + engine = this._extractEngineFromJSON(aData); + engine.hidden = false; + Services.search.removeEngine(engine); + break; + default: + dump("Unexpected message type observed: " + aTopic); + break; } }, diff --git a/mobile/android/locales/en-US/chrome/aboutAddons.dtd b/mobile/android/locales/en-US/chrome/aboutAddons.dtd index 4630892dd68..05405bbb2cc 100644 --- a/mobile/android/locales/en-US/chrome/aboutAddons.dtd +++ b/mobile/android/locales/en-US/chrome/aboutAddons.dtd @@ -10,4 +10,3 @@ - diff --git a/mobile/android/locales/en-US/chrome/aboutAddons.properties b/mobile/android/locales/en-US/chrome/aboutAddons.properties index b3aca7f41fa..41ea1a80e0c 100644 --- a/mobile/android/locales/en-US/chrome/aboutAddons.properties +++ b/mobile/android/locales/en-US/chrome/aboutAddons.properties @@ -2,12 +2,9 @@ # 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/. -addonsSearchEngine.description=Integrated Search - addonType.extension=Extension addonType.theme=Theme addonType.locale=Locale -addonType.search=Search addonStatus.uninstalled=%S will be uninstalled after restart.