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