1c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath/*
2c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath * Copyright (C) 2010 The Android Open Source Project
3c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath *
4c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath * Licensed under the Apache License, Version 2.0 (the "License");
5c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath * you may not use this file except in compliance with the License.
6c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath * You may obtain a copy of the License at
7c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath *
8c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath *      http://www.apache.org/licenses/LICENSE-2.0
9c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath *
10c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath * Unless required by applicable law or agreed to in writing, software
11c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath * distributed under the License is distributed on an "AS IS" BASIS,
12c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath * See the License for the specific language governing permissions and
14c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath * limitations under the License.
15c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath */
16c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath
17c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamathpackage com.android.quicksearchbox.google;
18c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath
19c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamathimport com.android.quicksearchbox.R;
20c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamathimport com.android.quicksearchbox.SearchSettings;
211259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamathimport com.android.quicksearchbox.SearchSettingsImpl;
22c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamathimport com.android.quicksearchbox.util.HttpHelper;
23c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath
24c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamathimport android.content.Context;
251259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamathimport android.content.SharedPreferences;
26c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamathimport android.os.AsyncTask;
27964deade9111e08f22dce9c3c97b84e9c0f5b27aBjorn Bringertimport android.text.TextUtils;
28c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamathimport android.util.Log;
29c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath
30c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamathimport java.util.Locale;
31c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath
32c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath/**
33c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath * Helper to build the base URL for all search requests.
34c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath */
351259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamathpublic class SearchBaseUrlHelper implements SharedPreferences.OnSharedPreferenceChangeListener {
36c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath    private static final boolean DBG = false;
37c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath    private static final String TAG = "QSB.SearchBaseUrlHelper";
38c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath
39c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath    private static final String DOMAIN_CHECK_URL =
40c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath            "https://www.google.com/searchdomaincheck?format=domain";
41c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath
42c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath    private static final long SEARCH_BASE_URL_EXPIRY_MS = 24 * 3600 * 1000L;
43c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath
44c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath    private final HttpHelper mHttpHelper;
45c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath    private final Context mContext;
46c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath    private final SearchSettings mSearchSettings;
47c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath
48c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath    /**
49c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath     * Note that this constructor will spawn a thread to issue a HTTP
50c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath     * request if shouldUseGoogleCom is false.
51c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath     */
52c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath    public SearchBaseUrlHelper(Context context, HttpHelper helper,
531259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath            SearchSettings searchSettings, SharedPreferences prefs) {
54c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath        mHttpHelper = helper;
55c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath        mContext = context;
56c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath        mSearchSettings = searchSettings;
57c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath
581259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath        // Note: This earlier used an inner class, but that causes issues
591259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath        // because SharedPreferencesImpl uses a WeakHashMap< > and the listener
601259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath        // will be GC'ed unless we keep a reference to it here.
611259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath        prefs.registerOnSharedPreferenceChangeListener(this);
621259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath
63c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath        maybeUpdateBaseUrlSetting(false);
64c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath    }
65c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath
66c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath    /**
67c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath     * Update the base search url, either:
68c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath     * (a) it has never been set (first run)
69c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath     * (b) it has expired
70c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath     * (c) if the caller forces an update by setting the "force" parameter.
71c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath     *
72c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath     * @param force if true, then the URL is reset whether or not it has
73c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath     *     expired.
74c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath     */
75c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath    public void maybeUpdateBaseUrlSetting(boolean force) {
768f65daa6e6df644d6cc6a9a2e100c01feb33bbd2Narayan Kamath        long lastUpdateTime = mSearchSettings.getSearchBaseDomainApplyTime();
77c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath        long currentTime = System.currentTimeMillis();
78c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath
79c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath        if (force || lastUpdateTime == -1 ||
80c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath                currentTime - lastUpdateTime >= SEARCH_BASE_URL_EXPIRY_MS) {
81c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath            if (mSearchSettings.shouldUseGoogleCom()) {
828f65daa6e6df644d6cc6a9a2e100c01feb33bbd2Narayan Kamath                setSearchBaseDomain(getDefaultBaseDomain());
83c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath            } else {
84c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath                checkSearchDomain();
85c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath            }
86c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath        }
87c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath    }
88c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath
89c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath    /**
90c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath     * @return the base url for searches.
91c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath     */
92b1cdcbce5d9c5edb20309c59b7cb52f76bbfac72Narayan Kamath    public String getSearchBaseUrl() {
938f65daa6e6df644d6cc6a9a2e100c01feb33bbd2Narayan Kamath        return mContext.getResources().getString(R.string.google_search_base_pattern,
948f65daa6e6df644d6cc6a9a2e100c01feb33bbd2Narayan Kamath                getSearchDomain(), GoogleSearch.getLanguage(Locale.getDefault()));
95c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath    }
96c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath
97c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath    /**
981259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath     * @return the search domain. This is of the form "google.co.xx" or "google.com",
991259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath     *     used by UI code.
1001259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath     */
1011259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath    public String getSearchDomain() {
1028f65daa6e6df644d6cc6a9a2e100c01feb33bbd2Narayan Kamath        String domain = mSearchSettings.getSearchBaseDomain();
103f4e4f922ad11b7d5bdc6347436f1e129fbae9041Narayan Kamath
104f4e4f922ad11b7d5bdc6347436f1e129fbae9041Narayan Kamath        if (domain == null) {
105f4e4f922ad11b7d5bdc6347436f1e129fbae9041Narayan Kamath            if (DBG) {
106f4e4f922ad11b7d5bdc6347436f1e129fbae9041Narayan Kamath                Log.w(TAG, "Search base domain was null, last apply time=" +
107f4e4f922ad11b7d5bdc6347436f1e129fbae9041Narayan Kamath                        mSearchSettings.getSearchBaseDomainApplyTime());
108f4e4f922ad11b7d5bdc6347436f1e129fbae9041Narayan Kamath            }
109f4e4f922ad11b7d5bdc6347436f1e129fbae9041Narayan Kamath
110f4e4f922ad11b7d5bdc6347436f1e129fbae9041Narayan Kamath            // This is required to deal with the case wherein getSearchDomain
111f4e4f922ad11b7d5bdc6347436f1e129fbae9041Narayan Kamath            // is called before checkSearchDomain returns a valid URL. This will
112f4e4f922ad11b7d5bdc6347436f1e129fbae9041Narayan Kamath            // happen *only* on the first run of the app when the "use google.com"
113f4e4f922ad11b7d5bdc6347436f1e129fbae9041Narayan Kamath            // option is unchecked. In other cases, the previously set domain (or
114f4e4f922ad11b7d5bdc6347436f1e129fbae9041Narayan Kamath            // the default) will be returned.
115f4e4f922ad11b7d5bdc6347436f1e129fbae9041Narayan Kamath            //
116f4e4f922ad11b7d5bdc6347436f1e129fbae9041Narayan Kamath            // We have no choice in this case but to use the default search domain.
117f4e4f922ad11b7d5bdc6347436f1e129fbae9041Narayan Kamath            domain = getDefaultBaseDomain();
118f4e4f922ad11b7d5bdc6347436f1e129fbae9041Narayan Kamath        }
119f4e4f922ad11b7d5bdc6347436f1e129fbae9041Narayan Kamath
1208f65daa6e6df644d6cc6a9a2e100c01feb33bbd2Narayan Kamath        if (domain.startsWith(".")) {
1218f65daa6e6df644d6cc6a9a2e100c01feb33bbd2Narayan Kamath            if (DBG) Log.d(TAG, "Prepending www to " + domain);
1228f65daa6e6df644d6cc6a9a2e100c01feb33bbd2Narayan Kamath            domain = "www" + domain;
1231259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath        }
1248f65daa6e6df644d6cc6a9a2e100c01feb33bbd2Narayan Kamath        return domain;
1251259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath    }
1261259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath
1271259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath    /**
128c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath     * Issue a request to google.com/searchdomaincheck to retrieve the base
129c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath     * URL for search requests.
130c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath     */
131c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath    private void checkSearchDomain() {
132c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath        final HttpHelper.GetRequest request = new HttpHelper.GetRequest(DOMAIN_CHECK_URL);
133c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath
134c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath        new AsyncTask<Void, Void, Void>() {
135c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath            @Override
136c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath            protected Void doInBackground(Void ... params) {
137c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath                if (DBG) Log.d(TAG, "Starting request to /searchdomaincheck");
138c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath                String domain;
139c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath                try {
140c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath                    domain = mHttpHelper.get(request);
141c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath                } catch (Exception e) {
142c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath                    if (DBG) Log.d(TAG, "Request to /searchdomaincheck failed : " + e);
143c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath                    // Swallow any exceptions thrown by the HTTP helper, in
144c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath                    // this rare case, we just use the default URL.
1458f65daa6e6df644d6cc6a9a2e100c01feb33bbd2Narayan Kamath                    domain = getDefaultBaseDomain();
146c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath
147c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath                    return null;
148c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath                }
149c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath
1501259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath                if (DBG) Log.d(TAG, "Request to /searchdomaincheck succeeded");
1518f65daa6e6df644d6cc6a9a2e100c01feb33bbd2Narayan Kamath                setSearchBaseDomain(domain);
152f4e4f922ad11b7d5bdc6347436f1e129fbae9041Narayan Kamath
153c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath                return null;
154c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath            }
155c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath        }.execute();
156c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath    }
157c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath
1588f65daa6e6df644d6cc6a9a2e100c01feb33bbd2Narayan Kamath    private String getDefaultBaseDomain() {
1598f65daa6e6df644d6cc6a9a2e100c01feb33bbd2Narayan Kamath        return mContext.getResources().getString(R.string.default_search_domain);
160c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath    }
161c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath
1628f65daa6e6df644d6cc6a9a2e100c01feb33bbd2Narayan Kamath    private void setSearchBaseDomain(String domain) {
1638f65daa6e6df644d6cc6a9a2e100c01feb33bbd2Narayan Kamath        if (DBG) Log.d(TAG, "Setting search domain to : " + domain);
164c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath
1658f65daa6e6df644d6cc6a9a2e100c01feb33bbd2Narayan Kamath        mSearchSettings.setSearchBaseDomain(domain);
166c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath    }
1671259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath
1681259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath    @Override
1691259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath    public void onSharedPreferenceChanged(SharedPreferences pref, String key) {
1701259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath        // Listen for changes only to the SEARCH_BASE_URL preference.
1711259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath        if (DBG) Log.d(TAG, "Handling changed preference : " + key);
1721259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath        if (SearchSettingsImpl.USE_GOOGLE_COM_PREF.equals(key)) {
1731259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath            maybeUpdateBaseUrlSetting(true);
1741259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath        }
1751259cf7175b0eacac403fb5298684bdbf3ef672cNarayan Kamath    }
176b1cdcbce5d9c5edb20309c59b7cb52f76bbfac72Narayan Kamath}