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