QsbApplication.java revision 69494b842a3f907164a457852c385f86dbe71d15
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.CorpusViewFactory; 22import com.android.quicksearchbox.ui.CorpusViewInflater; 23import com.android.quicksearchbox.ui.DelayingSuggestionsAdapter; 24import com.android.quicksearchbox.ui.SuggestionViewFactory; 25import com.android.quicksearchbox.ui.SuggestionViewInflater; 26import com.android.quicksearchbox.ui.SuggestionsAdapter; 27import com.android.quicksearchbox.util.Factory; 28import com.android.quicksearchbox.util.NamedTaskExecutor; 29import com.android.quicksearchbox.util.PerNameExecutor; 30import com.android.quicksearchbox.util.PriorityThreadFactory; 31import com.android.quicksearchbox.util.SingleThreadNamedTaskExecutor; 32import com.google.common.util.concurrent.NamingThreadFactory; 33 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; 41 42import java.util.concurrent.Executor; 43import java.util.concurrent.Executors; 44import java.util.concurrent.ThreadFactory; 45 46public class QsbApplication { 47 48 private final Context mContext; 49 50 private int mVersionCode; 51 private Handler mUiThreadHandler; 52 private Config mConfig; 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 /** 159 * Gets the corpora. 160 * May only be called from the main thread. 161 */ 162 public Corpora getCorpora() { 163 checkThread(); 164 if (mCorpora == null) { 165 mCorpora = createCorpora(); 166 } 167 return mCorpora; 168 } 169 170 protected Corpora createCorpora() { 171 SearchableCorpora corpora = new SearchableCorpora(getContext(), createSources(), 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 createSources() { 189 return new SearchableSources(getContext()); 190 } 191 192 protected CorpusFactory createCorpusFactory() { 193 int numWebCorpusThreads = getConfig().getNumWebCorpusThreads(); 194 return new SearchableCorpusFactory(getContext(), getConfig(), 195 createExecutorFactory(numWebCorpusThreads)); 196 } 197 198 protected Factory<Executor> createExecutorFactory(final int numThreads) { 199 final ThreadFactory threadFactory = getQueryThreadFactory(); 200 return new Factory<Executor>() { 201 public Executor create() { 202 return Executors.newFixedThreadPool(numThreads, threadFactory); 203 } 204 }; 205 } 206 207 /** 208 * Gets the corpus ranker. 209 * May only be called from the main thread. 210 */ 211 public CorpusRanker getCorpusRanker() { 212 checkThread(); 213 if (mCorpusRanker == null) { 214 mCorpusRanker = createCorpusRanker(); 215 } 216 return mCorpusRanker; 217 } 218 219 protected CorpusRanker createCorpusRanker() { 220 return new DefaultCorpusRanker(getCorpora(), getShortcutRepository()); 221 } 222 223 /** 224 * Gets the shortcut repository. 225 * May only be called from the main thread. 226 */ 227 public ShortcutRepository getShortcutRepository() { 228 checkThread(); 229 if (mShortcutRepository == null) { 230 mShortcutRepository = createShortcutRepository(); 231 } 232 return mShortcutRepository; 233 } 234 235 protected ShortcutRepository createShortcutRepository() { 236 ThreadFactory logThreadFactory = new NamingThreadFactory("ShortcutRepositoryWriter #%d", 237 new PriorityThreadFactory(Process.THREAD_PRIORITY_BACKGROUND)); 238 Executor logExecutor = Executors.newSingleThreadExecutor(logThreadFactory); 239 return ShortcutRepositoryImplLog.create(getContext(), getConfig(), getCorpora(), 240 getShortcutRefresher(), getMainThreadHandler(), logExecutor); 241 } 242 243 /** 244 * Gets the shortcut refresher. 245 * May only be called from the main thread. 246 */ 247 public ShortcutRefresher getShortcutRefresher() { 248 checkThread(); 249 if (mShortcutRefresher == null) { 250 mShortcutRefresher = createShortcutRefresher(); 251 } 252 return mShortcutRefresher; 253 } 254 255 protected ShortcutRefresher createShortcutRefresher() { 256 // For now, ShortcutRefresher gets its own SourceTaskExecutor 257 return new SourceShortcutRefresher(createSourceTaskExecutor()); 258 } 259 260 /** 261 * Gets the source task executor. 262 * May only be called from the main thread. 263 */ 264 public NamedTaskExecutor getSourceTaskExecutor() { 265 checkThread(); 266 if (mSourceTaskExecutor == null) { 267 mSourceTaskExecutor = createSourceTaskExecutor(); 268 } 269 return mSourceTaskExecutor; 270 } 271 272 protected NamedTaskExecutor createSourceTaskExecutor() { 273 ThreadFactory queryThreadFactory = getQueryThreadFactory(); 274 return new PerNameExecutor(SingleThreadNamedTaskExecutor.factory(queryThreadFactory)); 275 } 276 277 /** 278 * Gets the query thread factory. 279 * May only be called from the main thread. 280 */ 281 protected ThreadFactory getQueryThreadFactory() { 282 checkThread(); 283 if (mQueryThreadFactory == null) { 284 mQueryThreadFactory = createQueryThreadFactory(); 285 } 286 return mQueryThreadFactory; 287 } 288 289 protected ThreadFactory createQueryThreadFactory() { 290 String nameFormat = "QSB #%d"; 291 int priority = getConfig().getQueryThreadPriority(); 292 return new NamingThreadFactory(nameFormat, 293 new PriorityThreadFactory(priority)); 294 } 295 296 /** 297 * Gets the suggestion provider. 298 * May only be called from the main thread. 299 */ 300 protected SuggestionsProvider getSuggestionsProvider() { 301 checkThread(); 302 if (mSuggestionsProvider == null) { 303 mSuggestionsProvider = createSuggestionsProvider(); 304 } 305 return mSuggestionsProvider; 306 } 307 308 protected SuggestionsProvider createSuggestionsProvider() { 309 int maxShortcutsPerWebSource = getConfig().getMaxShortcutsPerWebSource(); 310 int maxShortcutsPerNonWebSource = getConfig().getMaxShortcutsPerNonWebSource(); 311 Promoter allPromoter = new ShortcutLimitingPromoter( 312 maxShortcutsPerWebSource, 313 maxShortcutsPerNonWebSource, 314 new ShortcutPromoter( 315 new RankAwarePromoter(getConfig(), getCorpora()))); 316 Promoter singleCorpusPromoter = new ShortcutPromoter(new ConcatPromoter()); 317 SuggestionsProviderImpl provider = new SuggestionsProviderImpl(getConfig(), 318 getSourceTaskExecutor(), 319 getMainThreadHandler(), 320 getShortcutRepository(), 321 getCorpora(), 322 getCorpusRanker(), 323 getLogger()); 324 provider.setAllPromoter(allPromoter); 325 provider.setSingleCorpusPromoter(singleCorpusPromoter); 326 return provider; 327 } 328 329 /** 330 * Gets the suggestion view factory. 331 * May only be called from the main thread. 332 */ 333 public SuggestionViewFactory getSuggestionViewFactory() { 334 checkThread(); 335 if (mSuggestionViewFactory == null) { 336 mSuggestionViewFactory = createSuggestionViewFactory(); 337 } 338 return mSuggestionViewFactory; 339 } 340 341 protected SuggestionViewFactory createSuggestionViewFactory() { 342 return new SuggestionViewInflater(getContext()); 343 } 344 345 /** 346 * Gets the corpus view factory. 347 * May only be called from the main thread. 348 */ 349 public CorpusViewFactory getCorpusViewFactory() { 350 checkThread(); 351 if (mCorpusViewFactory == null) { 352 mCorpusViewFactory = createCorpusViewFactory(); 353 } 354 return mCorpusViewFactory; 355 } 356 357 protected CorpusViewFactory createCorpusViewFactory() { 358 return new CorpusViewInflater(getContext()); 359 } 360 361 /** 362 * Creates a suggestions adapter. 363 * May only be called from the main thread. 364 */ 365 public SuggestionsAdapter createSuggestionsAdapter() { 366 SuggestionViewFactory viewFactory = getSuggestionViewFactory(); 367 DelayingSuggestionsAdapter adapter = new DelayingSuggestionsAdapter(viewFactory); 368 return adapter; 369 } 370 371 /** 372 * Gets the Google source. 373 * May only be called from the main thread. 374 */ 375 public GoogleSource getGoogleSource() { 376 checkThread(); 377 if (mGoogleSource == null) { 378 mGoogleSource = createGoogleSource(); 379 } 380 return mGoogleSource; 381 } 382 383 protected GoogleSource createGoogleSource() { 384 return new GoogleSuggestClient(getContext()); 385 } 386 387 /** 388 * Gets Voice Search utilities. 389 */ 390 public VoiceSearch getVoiceSearch() { 391 checkThread(); 392 if (mVoiceSearch == null) { 393 mVoiceSearch = createVoiceSearch(); 394 } 395 return mVoiceSearch; 396 } 397 398 protected VoiceSearch createVoiceSearch() { 399 return new VoiceSearch(getContext()); 400 } 401 402 /** 403 * Gets the event logger. 404 * May only be called from the main thread. 405 */ 406 public Logger getLogger() { 407 checkThread(); 408 if (mLogger == null) { 409 mLogger = createLogger(); 410 } 411 return mLogger; 412 } 413 414 protected Logger createLogger() { 415 return new EventLogLogger(getContext(), getConfig()); 416 } 417 418 public SuggestionFormatter getSuggestionFormatter() { 419 if (mSuggestionFormatter == null) { 420 mSuggestionFormatter = createSuggestionFormatter(); 421 } 422 return mSuggestionFormatter; 423 } 424 425 protected SuggestionFormatter createSuggestionFormatter() { 426 return new LevenshteinSuggestionFormatter(getTextAppearanceFactory()); 427 } 428 429 public TextAppearanceFactory getTextAppearanceFactory() { 430 if (mTextAppearanceFactory == null) { 431 mTextAppearanceFactory = createTextAppearanceFactory(); 432 } 433 return mTextAppearanceFactory; 434 } 435 436 protected TextAppearanceFactory createTextAppearanceFactory() { 437 return new TextAppearanceFactory(getContext()); 438 } 439} 440