Added settings to WebSearchProvider

WebSearchProvider now has its own settings UI, with a
"Show web suggestions" preference. This is disabled
if we do not support suggestions for the selected
search engine.

This is the WebSearchProvider part of http://b/issue?id=1996992
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index c221054..386a3d2 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -33,6 +33,12 @@
                 android:excludeFromRecents="true">
         </activity>
 
+        <!-- This is launched by .WebSearch in response to the
+             android.search.action.WEB_SEARCH_SETTINGS intent. -->
+        <activity android:name=".Settings"
+                android:excludeFromRecents="true">
+        </activity>
+
         <!--
         /*
          * The following activity aliases act as the various built-in web search and suggest
@@ -54,6 +60,9 @@
                 <action android:name="android.intent.action.WEB_SEARCH" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.search.action.WEB_SEARCH_SETTINGS" />
+            </intent-filter>
             <meta-data android:name="android.app.searchable"
                 android:resource="@xml/searchable_1" />
         </activity-alias>
@@ -70,6 +79,9 @@
                 <action android:name="android.intent.action.WEB_SEARCH" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.search.action.WEB_SEARCH_SETTINGS" />
+            </intent-filter>
             <meta-data android:name="android.app.searchable"
                 android:resource="@xml/searchable_2" />
         </activity-alias>
@@ -86,6 +98,9 @@
                 <action android:name="android.intent.action.WEB_SEARCH" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.search.action.WEB_SEARCH_SETTINGS" />
+            </intent-filter>
             <meta-data android:name="android.app.searchable"
                 android:resource="@xml/searchable_3" />
         </activity-alias>
@@ -102,6 +117,9 @@
                 <action android:name="android.intent.action.WEB_SEARCH" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.search.action.WEB_SEARCH_SETTINGS" />
+            </intent-filter>
             <meta-data android:name="android.app.searchable"
                 android:resource="@xml/searchable_4" />
         </activity-alias>
@@ -118,6 +136,9 @@
                 <action android:name="android.intent.action.WEB_SEARCH" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.search.action.WEB_SEARCH_SETTINGS" />
+            </intent-filter>
             <meta-data android:name="android.app.searchable"
                 android:resource="@xml/searchable_5" />
         </activity-alias>
@@ -134,6 +155,9 @@
                 <action android:name="android.intent.action.WEB_SEARCH" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.search.action.WEB_SEARCH_SETTINGS" />
+            </intent-filter>
             <meta-data android:name="android.app.searchable"
                 android:resource="@xml/searchable_6" />
         </activity-alias>
@@ -150,6 +174,9 @@
                 <action android:name="android.intent.action.WEB_SEARCH" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.search.action.WEB_SEARCH_SETTINGS" />
+            </intent-filter>
             <meta-data android:name="android.app.searchable"
                 android:resource="@xml/searchable_7" />
         </activity-alias>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index d32cfc1..0bcced4 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -20,5 +20,12 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Title of the web search provider activity. -->
     <string name="app_label">Web Search Provider</string>
+    <!-- Settings category title for 'Search engine settings' settings activity -->
+    <string name="search_engine_settings">%1$s search settings</string>
+    <!-- Title and summaries for 'show web suggestions' check box setting -->
+    <string name="show_web_suggestions">Show web suggestions</string>
+    <string name="show_web_suggestions_summary_enabled">Show suggestions from %1$s as you type</string>
+    <string name="show_web_suggestions_summary_disabled">Don\'t show suggestions from %1$s as you type</string>
+    <string name="show_web_suggestions_not_supported">Can\'t get suggestions from %1$s</string>
 </resources>
 
diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml
new file mode 100644
index 0000000..7752674
--- /dev/null
+++ b/res/xml/preferences.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<PreferenceScreen
+        xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <CheckBoxPreference
+            android:key="show_web_suggestions"
+            android:title="@string/show_web_suggestions"
+            android:defaultValue="true" />
+
+</PreferenceScreen>
diff --git a/src/com/android/websearch/SearchEngineInfo.java b/src/com/android/websearch/SearchEngineInfo.java
index 8a38ff7..86015f0 100644
--- a/src/com/android/websearch/SearchEngineInfo.java
+++ b/src/com/android/websearch/SearchEngineInfo.java
@@ -16,15 +16,14 @@
 
 package com.android.websearch;
 
-import java.net.URLEncoder;
-import java.util.Locale;
-
 import android.content.Context;
-import android.content.ComponentName;
 import android.content.res.Resources;
 import android.text.TextUtils;
 import android.util.Log;
 
+import java.net.URLEncoder;
+import java.util.Locale;
+
 
 /**
  * Loads and holds data for a given web search engine.
@@ -127,6 +126,10 @@
         return getFormattedUri(suggestUri(), query);
     }
 
+    public boolean supportsSuggestions() {
+        return !TextUtils.isEmpty(suggestUri());
+    }
+
     public String faviconUri() {
         return mSearchEngineData[FIELD_FAVICON_URI];
     }
diff --git a/src/com/android/websearch/Settings.java b/src/com/android/websearch/Settings.java
new file mode 100644
index 0000000..0ce54e4
--- /dev/null
+++ b/src/com/android/websearch/Settings.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.websearch;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.provider.Settings.SettingNotFoundException;
+import android.provider.Settings.System;
+import android.util.Log;
+
+/**
+ * Activity for setting web search engine preferences.
+ */
+public class Settings extends PreferenceActivity implements OnPreferenceClickListener {
+
+    private static final boolean DBG = false;
+    private static final String TAG = "WebSearchProvider";
+
+    private static final String SHOW_WEB_SUGGESTIONS_PREF = "show_web_suggestions";
+
+    private SearchEngineInfo mEngine;
+    private String mEngineLabel;
+
+    private CheckBoxPreference mShowWebSuggestionsPreference;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Intent intent = getIntent();
+        if (DBG) Log.d(TAG, "onCreate(), intent=" + intent);
+        ComponentName component = WebSearch.componentNameFromUri(intent.getData());
+        mEngine = WebSearch.getSearchEngine(this, component);
+        if (mEngine == null) {
+            Log.e(TAG, "Unknown search engine " + intent.getData());
+            finish();
+            return;
+        }
+        mEngineLabel = getActivityLabel(component);
+        if (mEngineLabel == null) {
+            Log.w(TAG, "No label for search engine " + intent.getData());
+            finish();
+            return;
+        }
+
+        setTitle(getString(R.string.search_engine_settings, mEngineLabel));
+
+        addPreferencesFromResource(R.xml.preferences);
+
+        mShowWebSuggestionsPreference = (CheckBoxPreference) findPreference(
+                SHOW_WEB_SUGGESTIONS_PREF);
+        mShowWebSuggestionsPreference.setOnPreferenceClickListener(this);
+
+        updateShowWebSuggestionsPreference();
+    }
+
+    private String getActivityLabel(ComponentName component) {
+        if (component == null) return null;
+        try {
+            PackageManager pm = getPackageManager();
+            ActivityInfo activityInfo = pm.getActivityInfo(component, 0);
+            int labelRes = activityInfo.labelRes;
+            if (labelRes == 0) labelRes = activityInfo.applicationInfo.labelRes;
+            if (labelRes == 0) return null;
+            return getString(labelRes);
+        } catch (PackageManager.NameNotFoundException exception) {
+            Log.e(TAG, "Error loading web search source from activity "
+                    + component, exception);
+            return null;
+        }
+    }
+
+    public synchronized boolean onPreferenceClick(Preference preference) {
+        if (preference == mShowWebSuggestionsPreference) {
+            System.putInt(
+                    getContentResolver(),
+                    System.SHOW_WEB_SUGGESTIONS,
+                    mShowWebSuggestionsPreference.isChecked() ? 1 : 0);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Updates the "show web suggestions" preference from the value in system settings.
+     */
+    private void updateShowWebSuggestionsPreference() {
+        boolean enabled = mEngine.supportsSuggestions();
+        mShowWebSuggestionsPreference.setEnabled(enabled);
+        if (!enabled) {
+            String summary =
+                getString(R.string.show_web_suggestions_not_supported, mEngineLabel);
+            mShowWebSuggestionsPreference.setSummary(summary);
+            return;
+        }
+        String summaryOn =
+            getString(R.string.show_web_suggestions_summary_enabled, mEngineLabel);
+        String summaryOff =
+            getString(R.string.show_web_suggestions_summary_disabled, mEngineLabel);
+        mShowWebSuggestionsPreference.setSummaryOn(summaryOn);
+        mShowWebSuggestionsPreference.setSummaryOff(summaryOff);
+        int value;
+        try {
+            value = System.getInt(
+                    getContentResolver(), System.SHOW_WEB_SUGGESTIONS);
+        } catch (SettingNotFoundException e) {
+            // No setting found, create one.
+            System.putInt(getContentResolver(), System.SHOW_WEB_SUGGESTIONS, 1);
+            value = 1;
+        }
+        mShowWebSuggestionsPreference.setChecked(value == 1);
+    }
+}
diff --git a/src/com/android/websearch/WebSearch.java b/src/com/android/websearch/WebSearch.java
index a625f06..423c38c 100644
--- a/src/com/android/websearch/WebSearch.java
+++ b/src/com/android/websearch/WebSearch.java
@@ -18,14 +18,11 @@
 
 import android.app.Activity;
 import android.app.SearchManager;
-import android.content.Context;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
-import android.content.res.Resources;
 import android.net.Uri;
 import android.os.Bundle;
-import android.provider.SearchRecentSuggestions;
-import android.text.TextUtils;
 import android.util.Log;
 
 /**
@@ -40,28 +37,68 @@
         super.onCreate(savedInstanceState);
 
         Intent intent = getIntent();
-        if (intent != null && Intent.ACTION_WEB_SEARCH.equals(intent.getAction())) {
-            // Fetch the provider index for which this action was launched and load the provider
-            // data.
-            String[] parts = intent.getComponent().getClassName().split("\\.");
-            String engine_index = parts[parts.length - 1];
-            SearchEngineInfo engine = null;
-            try {
-                engine = new SearchEngineInfo((Context)this, engine_index);
+        if (intent == null) {
+            finish();
+            return;
+        }
 
+        String action = intent.getAction();
+        if (Intent.ACTION_WEB_SEARCH.equals(action)) {
+            SearchEngineInfo engine = getSearchEngine(this, intent.getComponent());
+            if (engine != null) {
                 // Format the URI to launch and open it in the browser.
                 String query = intent.getStringExtra(SearchManager.QUERY);
                 String launchUri = engine.getSearchUriForQuery(query);
                 if (launchUri == null) {
-                    Log.e(LOG_TAG, "Unable to get search URI for engine index " + engine_index);
+                    Log.e(LOG_TAG, "Unable to get search URI for engine "
+                            + intent.getComponent());
                 } else {
                     intent = new Intent(Intent.ACTION_VIEW, Uri.parse(launchUri));
                     startActivity(intent);
                 }
-            } catch (IllegalArgumentException exception) {
-                Log.e(LOG_TAG, "Cannot load search engine index " + engine_index, exception);
             }
+        } else if (SearchManager.INTENT_ACTION_WEB_SEARCH_SETTINGS.equals(action)) {
+            Intent settingsIntent = new Intent(SearchManager.INTENT_ACTION_WEB_SEARCH_SETTINGS);
+            settingsIntent.setClass(this, Settings.class);
+            ComponentName component = intent.getComponent();
+            settingsIntent.setData(componentNameToUri(component));
+            startActivity(settingsIntent);
         }
         finish();
     }
+
+    public static SearchEngineInfo getSearchEngine(Context context, ComponentName component) {
+        if (component == null) return null;
+        String[] parts = component.getClassName().split("\\.");
+        String engine_index = parts[parts.length - 1];
+        try {
+            return new SearchEngineInfo(context, engine_index);
+        } catch (IllegalArgumentException exception) {
+            Log.e(LOG_TAG, "Cannot load search engine index " + engine_index, exception);
+            return null;
+        }
+    }
+
+    // TODO: Maybe this should live in ComponentName?
+    public static Uri componentNameToUri(ComponentName component) {
+        if (component == null) return null;
+        return new Uri.Builder()
+                .scheme("package")
+                .authority(component.getPackageName())
+                .path(component.getClassName())
+                .query("")
+                .fragment("")
+                .build();
+    }
+
+    // TODO: Maybe this should live in ComponentName?
+    public static ComponentName componentNameFromUri(Uri activityUri) {
+        if (activityUri == null) return null;
+        String packageName = activityUri.getAuthority();
+        String className = activityUri.getLastPathSegment();
+        if (packageName == null || className == null) return null;
+        return new ComponentName(packageName, className);
+    }
+
+
 }