QsbApplication.java revision ef6dbc776667219ac89bd5f80c181f65260457ac
1/* 2 * Copyright (C) 2009 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 17package com.android.quicksearchbox; 18 19import com.android.quicksearchbox.google.GoogleSource; 20import com.android.quicksearchbox.google.GoogleSuggestClient; 21import com.android.quicksearchbox.preferences.PreferenceControllerFactory; 22import com.android.quicksearchbox.ui.DefaultSuggestionViewFactory; 23import com.android.quicksearchbox.ui.SuggestionViewFactory; 24import com.android.quicksearchbox.util.Factory; 25import com.android.quicksearchbox.util.HttpHelper; 26import com.android.quicksearchbox.util.JavaNetHttpHelper; 27import com.android.quicksearchbox.util.NamedTaskExecutor; 28import com.android.quicksearchbox.util.PerNameExecutor; 29import com.android.quicksearchbox.util.PriorityThreadFactory; 30import com.android.quicksearchbox.util.SingleThreadNamedTaskExecutor; 31import com.google.common.util.concurrent.NamingThreadFactory; 32 33import android.app.Activity; 34import android.content.Context; 35import android.content.pm.PackageInfo; 36import android.content.pm.PackageManager; 37import android.os.Build; 38import android.os.Handler; 39import android.os.Looper; 40import android.os.Process; 41import android.view.ContextThemeWrapper; 42 43import java.util.concurrent.Executor; 44import java.util.concurrent.Executors; 45import java.util.concurrent.ThreadFactory; 46 47public class QsbApplication { 48 private final Context mContext; 49 50 private int mVersionCode; 51 private Handler mUiThreadHandler; 52 private Config mConfig; 53 private SearchSettings mSettings; 54 private Sources mSources; 55 private Corpora mCorpora; 56 private CorpusRanker mCorpusRanker; 57 private ShortcutRepository mShortcutRepository; 58 private ShortcutRefresher mShortcutRefresher; 59 private NamedTaskExecutor mSourceTaskExecutor; 60 private ThreadFactory mQueryThreadFactory; 61 private SuggestionsProvider mSuggestionsProvider; 62 private SuggestionViewFactory mSuggestionViewFactory; 63 private GoogleSource mGoogleSource; 64 private VoiceSearch mVoiceSearch; 65 private Logger mLogger; 66 private SuggestionFormatter mSuggestionFormatter; 67 private TextAppearanceFactory mTextAppearanceFactory; 68 private NamedTaskExecutor mIconLoaderExecutor; 69 private HttpHelper mHttpHelper; 70 71 public QsbApplication(Context context) { 72 // the application context does not use the theme from the <application> tag 73 mContext = new ContextThemeWrapper(context, R.style.Theme_QuickSearchBox); 74 } 75 76 public static boolean isFroyoOrLater() { 77 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO; 78 } 79 80 public static boolean isHoneycombOrLater() { 81 // TODO while Honeycomb is still under development, this doesn't work. When honeycomb is 82 // done, this needs to be changed, 83 //return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; 84 return true; 85 } 86 87 public static QsbApplication get(Context context) { 88 return ((QsbApplicationWrapper) context.getApplicationContext()).getApp(); 89 } 90 91 protected Context getContext() { 92 return mContext; 93 } 94 95 public int getVersionCode() { 96 if (mVersionCode == 0) { 97 try { 98 PackageManager pm = getContext().getPackageManager(); 99 PackageInfo pkgInfo = pm.getPackageInfo(getContext().getPackageName(), 0); 100 mVersionCode = pkgInfo.versionCode; 101 } catch (PackageManager.NameNotFoundException ex) { 102 // The current package should always exist, how else could we 103 // run code from it? 104 throw new RuntimeException(ex); 105 } 106 } 107 return mVersionCode; 108 } 109 110 protected void checkThread() { 111 if (Looper.myLooper() != Looper.getMainLooper()) { 112 throw new IllegalStateException("Accessed Application object from thread " 113 + Thread.currentThread().getName()); 114 } 115 } 116 117 protected void close() { 118 checkThread(); 119 if (mConfig != null) { 120 mConfig.close(); 121 mConfig = null; 122 } 123 if (mShortcutRepository != null) { 124 mShortcutRepository.close(); 125 mShortcutRepository = null; 126 } 127 if (mSourceTaskExecutor != null) { 128 mSourceTaskExecutor.close(); 129 mSourceTaskExecutor = null; 130 } 131 if (mSuggestionsProvider != null) { 132 mSuggestionsProvider.close(); 133 mSuggestionsProvider = null; 134 } 135 } 136 137 public synchronized Handler getMainThreadHandler() { 138 if (mUiThreadHandler == null) { 139 mUiThreadHandler = new Handler(Looper.getMainLooper()); 140 } 141 return mUiThreadHandler; 142 } 143 144 public void runOnUiThread(Runnable action) { 145 getMainThreadHandler().post(action); 146 } 147 148 public synchronized NamedTaskExecutor getIconLoaderExecutor() { 149 if (mIconLoaderExecutor == null) { 150 mIconLoaderExecutor = createIconLoaderExecutor(); 151 } 152 return mIconLoaderExecutor; 153 } 154 155 protected NamedTaskExecutor createIconLoaderExecutor() { 156 ThreadFactory iconThreadFactory = new PriorityThreadFactory( 157 Process.THREAD_PRIORITY_BACKGROUND); 158 return new PerNameExecutor(SingleThreadNamedTaskExecutor.factory(iconThreadFactory)); 159 } 160 161 /** 162 * Indicates that construction of the QSB UI is now complete. 163 */ 164 public void onStartupComplete() { 165 } 166 167 /** 168 * Gets the QSB configuration object. 169 * May be called from any thread. 170 */ 171 public synchronized Config getConfig() { 172 if (mConfig == null) { 173 mConfig = createConfig(); 174 } 175 return mConfig; 176 } 177 178 protected Config createConfig() { 179 return new Config(getContext()); 180 } 181 182 public synchronized SearchSettings getSettings() { 183 if (mSettings == null) { 184 mSettings = createSettings(); 185 mSettings.upgradeSettingsIfNeeded(); 186 } 187 return mSettings; 188 } 189 190 protected SearchSettings createSettings() { 191 return new SearchSettingsImpl(getContext(), getConfig()); 192 } 193 194 /** 195 * Gets all corpora. 196 * 197 * May only be called from the main thread. 198 */ 199 public Corpora getCorpora() { 200 checkThread(); 201 if (mCorpora == null) { 202 mCorpora = createCorpora(getSources()); 203 } 204 return mCorpora; 205 } 206 207 protected Corpora createCorpora(Sources sources) { 208 SearchableCorpora corpora = new SearchableCorpora(getContext(), getSettings(), sources, 209 createCorpusFactory()); 210 corpora.update(); 211 return corpora; 212 } 213 214 /** 215 * Updates the corpora, if they are loaded. 216 * May only be called from the main thread. 217 */ 218 public void updateCorpora() { 219 checkThread(); 220 if (mCorpora != null) { 221 mCorpora.update(); 222 } 223 } 224 225 protected Sources getSources() { 226 checkThread(); 227 if (mSources == null) { 228 mSources = createSources(); 229 } 230 return mSources; 231 } 232 233 protected Sources createSources() { 234 return new SearchableSources(getContext(), getMainThreadHandler(), 235 getIconLoaderExecutor(), getConfig()); 236 } 237 238 protected CorpusFactory createCorpusFactory() { 239 int numWebCorpusThreads = getConfig().getNumWebCorpusThreads(); 240 return new SearchableCorpusFactory(getContext(), getConfig(), getSettings(), 241 createExecutorFactory(numWebCorpusThreads)); 242 } 243 244 protected Factory<Executor> createExecutorFactory(final int numThreads) { 245 final ThreadFactory threadFactory = getQueryThreadFactory(); 246 return new Factory<Executor>() { 247 public Executor create() { 248 return Executors.newFixedThreadPool(numThreads, threadFactory); 249 } 250 }; 251 } 252 253 /** 254 * Gets the corpus ranker. 255 * May only be called from the main thread. 256 */ 257 public CorpusRanker getCorpusRanker() { 258 checkThread(); 259 if (mCorpusRanker == null) { 260 mCorpusRanker = createCorpusRanker(); 261 } 262 return mCorpusRanker; 263 } 264 265 protected CorpusRanker createCorpusRanker() { 266 return new DefaultCorpusRanker(getCorpora(), getShortcutRepository()); 267 } 268 269 /** 270 * Gets the shortcut repository. 271 * May only be called from the main thread. 272 */ 273 public ShortcutRepository getShortcutRepository() { 274 checkThread(); 275 if (mShortcutRepository == null) { 276 mShortcutRepository = createShortcutRepository(); 277 } 278 return mShortcutRepository; 279 } 280 281 protected ShortcutRepository createShortcutRepository() { 282 ThreadFactory logThreadFactory = new NamingThreadFactory("ShortcutRepositoryWriter #%d", 283 new PriorityThreadFactory(Process.THREAD_PRIORITY_BACKGROUND)); 284 Executor logExecutor = Executors.newSingleThreadExecutor(logThreadFactory); 285 return ShortcutRepositoryImplLog.create(getContext(), getConfig(), getCorpora(), 286 getShortcutRefresher(), getMainThreadHandler(), logExecutor); 287 } 288 289 /** 290 * Gets the shortcut refresher. 291 * May only be called from the main thread. 292 */ 293 public ShortcutRefresher getShortcutRefresher() { 294 checkThread(); 295 if (mShortcutRefresher == null) { 296 mShortcutRefresher = createShortcutRefresher(); 297 } 298 return mShortcutRefresher; 299 } 300 301 protected ShortcutRefresher createShortcutRefresher() { 302 // For now, ShortcutRefresher gets its own SourceTaskExecutor 303 return new SourceShortcutRefresher(createSourceTaskExecutor()); 304 } 305 306 /** 307 * Gets the source task executor. 308 * May only be called from the main thread. 309 */ 310 public NamedTaskExecutor getSourceTaskExecutor() { 311 checkThread(); 312 if (mSourceTaskExecutor == null) { 313 mSourceTaskExecutor = createSourceTaskExecutor(); 314 } 315 return mSourceTaskExecutor; 316 } 317 318 protected NamedTaskExecutor createSourceTaskExecutor() { 319 ThreadFactory queryThreadFactory = getQueryThreadFactory(); 320 return new PerNameExecutor(SingleThreadNamedTaskExecutor.factory(queryThreadFactory)); 321 } 322 323 /** 324 * Gets the query thread factory. 325 * May only be called from the main thread. 326 */ 327 protected ThreadFactory getQueryThreadFactory() { 328 checkThread(); 329 if (mQueryThreadFactory == null) { 330 mQueryThreadFactory = createQueryThreadFactory(); 331 } 332 return mQueryThreadFactory; 333 } 334 335 protected ThreadFactory createQueryThreadFactory() { 336 String nameFormat = "QSB #%d"; 337 int priority = getConfig().getQueryThreadPriority(); 338 return new NamingThreadFactory(nameFormat, 339 new PriorityThreadFactory(priority)); 340 } 341 342 /** 343 * Gets the suggestion provider. 344 * 345 * May only be called from the main thread. 346 */ 347 protected SuggestionsProvider getSuggestionsProvider() { 348 checkThread(); 349 if (mSuggestionsProvider == null) { 350 mSuggestionsProvider = createSuggestionsProvider(); 351 } 352 return mSuggestionsProvider; 353 } 354 355 protected SuggestionsProvider createSuggestionsProvider() { 356 return new SuggestionsProviderImpl(getConfig(), 357 getSourceTaskExecutor(), 358 getMainThreadHandler(), 359 getLogger()); 360 } 361 362 /** 363 * Gets the default suggestion view factory. 364 * May only be called from the main thread. 365 */ 366 public SuggestionViewFactory getSuggestionViewFactory() { 367 checkThread(); 368 if (mSuggestionViewFactory == null) { 369 mSuggestionViewFactory = createSuggestionViewFactory(); 370 } 371 return mSuggestionViewFactory; 372 } 373 374 protected SuggestionViewFactory createSuggestionViewFactory() { 375 return new DefaultSuggestionViewFactory(getContext()); 376 } 377 378 public Promoter createBlendingPromoter() { 379 return new ShortcutPromoter(getConfig(), 380 new RankAwarePromoter(getConfig(), null, null), null); 381 } 382 383 public Promoter createSingleCorpusPromoter(Corpus corpus) { 384 return new SingleCorpusPromoter(corpus, Integer.MAX_VALUE); 385 } 386 387 public Promoter createSingleCorpusResultsPromoter(Corpus corpus) { 388 return new SingleCorpusResultsPromoter(corpus, Integer.MAX_VALUE); 389 } 390 391 public Promoter createWebPromoter() { 392 return new WebPromoter(getConfig().getMaxShortcutsPerWebSource()); 393 } 394 395 public Promoter createResultsPromoter() { 396 SuggestionFilter resultFilter = new ResultFilter(); 397 return new ShortcutPromoter(getConfig(), null, resultFilter); 398 } 399 400 /** 401 * Gets the Google source. 402 * May only be called from the main thread. 403 */ 404 public GoogleSource getGoogleSource() { 405 checkThread(); 406 if (mGoogleSource == null) { 407 mGoogleSource = createGoogleSource(); 408 } 409 return mGoogleSource; 410 } 411 412 protected GoogleSource createGoogleSource() { 413 return new GoogleSuggestClient(getContext(), getMainThreadHandler(), 414 getIconLoaderExecutor(), getConfig()); 415 } 416 417 /** 418 * Gets Voice Search utilities. 419 */ 420 public VoiceSearch getVoiceSearch() { 421 checkThread(); 422 if (mVoiceSearch == null) { 423 mVoiceSearch = createVoiceSearch(); 424 } 425 return mVoiceSearch; 426 } 427 428 protected VoiceSearch createVoiceSearch() { 429 return new VoiceSearch(getContext()); 430 } 431 432 /** 433 * Gets the event logger. 434 * May only be called from the main thread. 435 */ 436 public Logger getLogger() { 437 checkThread(); 438 if (mLogger == null) { 439 mLogger = createLogger(); 440 } 441 return mLogger; 442 } 443 444 protected Logger createLogger() { 445 return new EventLogLogger(getContext(), getConfig()); 446 } 447 448 public SuggestionFormatter getSuggestionFormatter() { 449 if (mSuggestionFormatter == null) { 450 mSuggestionFormatter = createSuggestionFormatter(); 451 } 452 return mSuggestionFormatter; 453 } 454 455 protected SuggestionFormatter createSuggestionFormatter() { 456 return new LevenshteinSuggestionFormatter(getTextAppearanceFactory()); 457 } 458 459 public TextAppearanceFactory getTextAppearanceFactory() { 460 if (mTextAppearanceFactory == null) { 461 mTextAppearanceFactory = createTextAppearanceFactory(); 462 } 463 return mTextAppearanceFactory; 464 } 465 466 protected TextAppearanceFactory createTextAppearanceFactory() { 467 return new TextAppearanceFactory(getContext()); 468 } 469 470 public PreferenceControllerFactory createPreferenceControllerFactory(Activity activity) { 471 return new PreferenceControllerFactory(getSettings(), activity); 472 } 473 474 public HttpHelper getHttpHelper() { 475 if (mHttpHelper == null) { 476 mHttpHelper = createHttpHelper(); 477 } 478 return mHttpHelper; 479 } 480 481 protected HttpHelper createHttpHelper() { 482 return new JavaNetHttpHelper( 483 new JavaNetHttpHelper.PassThroughRewriter(), 484 getConfig().getUserAgent()); 485 } 486} 487