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