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