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