QsbApplication.java revision 2fb3a129925a42e72944b836e85a1a2d55a0d950
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.ui.CorpusViewFactory; 21import com.android.quicksearchbox.ui.CorpusViewInflater; 22import com.android.quicksearchbox.ui.DelayingSuggestionsAdapter; 23import com.android.quicksearchbox.ui.SuggestionViewFactory; 24import com.android.quicksearchbox.ui.SuggestionViewInflater; 25import com.android.quicksearchbox.ui.SuggestionsAdapter; 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.graphics.Typeface; 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 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 * Gets the QSB configuration object. 138 * May be called from any thread. 139 */ 140 public synchronized Config getConfig() { 141 if (mConfig == null) { 142 mConfig = createConfig(); 143 } 144 return mConfig; 145 } 146 147 protected Config createConfig() { 148 return new Config(getContext()); 149 } 150 151 /** 152 * Gets the corpora. 153 * May only be called from the main thread. 154 */ 155 public Corpora getCorpora() { 156 checkThread(); 157 if (mCorpora == null) { 158 mCorpora = createCorpora(); 159 } 160 return mCorpora; 161 } 162 163 protected Corpora createCorpora() { 164 SearchableCorpora corpora = new SearchableCorpora(getContext(), createSources(), 165 createCorpusFactory()); 166 corpora.update(); 167 return corpora; 168 } 169 170 /** 171 * Updates the corpora, if they are loaded. 172 * May only be called from the main thread. 173 */ 174 public void updateCorpora() { 175 checkThread(); 176 if (mCorpora != null) { 177 mCorpora.update(); 178 } 179 } 180 181 protected Sources createSources() { 182 return new SearchableSources(getContext()); 183 } 184 185 protected CorpusFactory createCorpusFactory() { 186 int numWebCorpusThreads = getConfig().getNumWebCorpusThreads(); 187 return new SearchableCorpusFactory(getContext(), getConfig(), 188 createExecutorFactory(numWebCorpusThreads)); 189 } 190 191 protected Factory<Executor> createExecutorFactory(final int numThreads) { 192 final ThreadFactory threadFactory = getQueryThreadFactory(); 193 return new Factory<Executor>() { 194 public Executor create() { 195 return Executors.newFixedThreadPool(numThreads, threadFactory); 196 } 197 }; 198 } 199 200 /** 201 * Gets the corpus ranker. 202 * May only be called from the main thread. 203 */ 204 public CorpusRanker getCorpusRanker() { 205 checkThread(); 206 if (mCorpusRanker == null) { 207 mCorpusRanker = createCorpusRanker(); 208 } 209 return mCorpusRanker; 210 } 211 212 protected CorpusRanker createCorpusRanker() { 213 return new DefaultCorpusRanker(getCorpora(), getShortcutRepository()); 214 } 215 216 /** 217 * Gets the shortcut repository. 218 * May only be called from the main thread. 219 */ 220 public ShortcutRepository getShortcutRepository() { 221 checkThread(); 222 if (mShortcutRepository == null) { 223 mShortcutRepository = createShortcutRepository(); 224 } 225 return mShortcutRepository; 226 } 227 228 protected ShortcutRepository createShortcutRepository() { 229 ThreadFactory logThreadFactory = new NamingThreadFactory("ShortcutRepositoryWriter #%d", 230 new PriorityThreadFactory(Process.THREAD_PRIORITY_BACKGROUND)); 231 Executor logExecutor = Executors.newSingleThreadExecutor(logThreadFactory); 232 return ShortcutRepositoryImplLog.create(getContext(), getConfig(), getCorpora(), 233 getShortcutRefresher(), getMainThreadHandler(), logExecutor); 234 } 235 236 /** 237 * Gets the shortcut refresher. 238 * May only be called from the main thread. 239 */ 240 public ShortcutRefresher getShortcutRefresher() { 241 checkThread(); 242 if (mShortcutRefresher == null) { 243 mShortcutRefresher = createShortcutRefresher(); 244 } 245 return mShortcutRefresher; 246 } 247 248 protected ShortcutRefresher createShortcutRefresher() { 249 // For now, ShortcutRefresher gets its own SourceTaskExecutor 250 return new SourceShortcutRefresher(createSourceTaskExecutor()); 251 } 252 253 /** 254 * Gets the source task executor. 255 * May only be called from the main thread. 256 */ 257 public NamedTaskExecutor getSourceTaskExecutor() { 258 checkThread(); 259 if (mSourceTaskExecutor == null) { 260 mSourceTaskExecutor = createSourceTaskExecutor(); 261 } 262 return mSourceTaskExecutor; 263 } 264 265 protected NamedTaskExecutor createSourceTaskExecutor() { 266 ThreadFactory queryThreadFactory = getQueryThreadFactory(); 267 return new PerNameExecutor(SingleThreadNamedTaskExecutor.factory(queryThreadFactory)); 268 } 269 270 /** 271 * Gets the query thread factory. 272 * May only be called from the main thread. 273 */ 274 protected ThreadFactory getQueryThreadFactory() { 275 checkThread(); 276 if (mQueryThreadFactory == null) { 277 mQueryThreadFactory = createQueryThreadFactory(); 278 } 279 return mQueryThreadFactory; 280 } 281 282 protected ThreadFactory createQueryThreadFactory() { 283 String nameFormat = "QSB #%d"; 284 int priority = getConfig().getQueryThreadPriority(); 285 return new NamingThreadFactory(nameFormat, 286 new PriorityThreadFactory(priority)); 287 } 288 289 /** 290 * Gets the suggestion provider. 291 * May only be called from the main thread. 292 */ 293 protected SuggestionsProvider getSuggestionsProvider() { 294 checkThread(); 295 if (mSuggestionsProvider == null) { 296 mSuggestionsProvider = createSuggestionsProvider(); 297 } 298 return mSuggestionsProvider; 299 } 300 301 protected SuggestionsProvider createSuggestionsProvider() { 302 Promoter promoter = new ShortcutPromoter( 303 new RankAwarePromoter(getConfig(), getCorpora())); 304 SuggestionsProvider provider = new SuggestionsProviderImpl(getConfig(), 305 getSourceTaskExecutor(), 306 getMainThreadHandler(), 307 promoter, 308 getShortcutRepository(), 309 getCorpora(), 310 getCorpusRanker(), 311 getLogger()); 312 return provider; 313 } 314 315 /** 316 * Gets the suggestion view factory. 317 * May only be called from the main thread. 318 */ 319 public SuggestionViewFactory getSuggestionViewFactory() { 320 checkThread(); 321 if (mSuggestionViewFactory == null) { 322 mSuggestionViewFactory = createSuggestionViewFactory(); 323 } 324 return mSuggestionViewFactory; 325 } 326 327 protected SuggestionViewFactory createSuggestionViewFactory() { 328 return new SuggestionViewInflater(getContext()); 329 } 330 331 /** 332 * Gets the corpus view factory. 333 * May only be called from the main thread. 334 */ 335 public CorpusViewFactory getCorpusViewFactory() { 336 checkThread(); 337 if (mCorpusViewFactory == null) { 338 mCorpusViewFactory = createCorpusViewFactory(); 339 } 340 return mCorpusViewFactory; 341 } 342 343 protected CorpusViewFactory createCorpusViewFactory() { 344 return new CorpusViewInflater(getContext()); 345 } 346 347 /** 348 * Creates a suggestions adapter. 349 * May only be called from the main thread. 350 */ 351 public SuggestionsAdapter createSuggestionsAdapter() { 352 SuggestionViewFactory viewFactory = getSuggestionViewFactory(); 353 DelayingSuggestionsAdapter adapter = new DelayingSuggestionsAdapter(viewFactory); 354 return adapter; 355 } 356 357 /** 358 * Gets the Google source. 359 * May only be called from the main thread. 360 */ 361 public GoogleSource getGoogleSource() { 362 checkThread(); 363 if (mGoogleSource == null) { 364 mGoogleSource = createGoogleSource(); 365 } 366 return mGoogleSource; 367 } 368 369 protected GoogleSource createGoogleSource() { 370 return new GoogleSource(getContext()); 371 } 372 373 /** 374 * Gets Voice Search utilities. 375 */ 376 public VoiceSearch getVoiceSearch() { 377 checkThread(); 378 if (mVoiceSearch == null) { 379 mVoiceSearch = createVoiceSearch(); 380 } 381 return mVoiceSearch; 382 } 383 384 protected VoiceSearch createVoiceSearch() { 385 return new VoiceSearch(getContext()); 386 } 387 388 /** 389 * Gets the event logger. 390 * May only be called from the main thread. 391 */ 392 public Logger getLogger() { 393 checkThread(); 394 if (mLogger == null) { 395 mLogger = createLogger(); 396 } 397 return mLogger; 398 } 399 400 protected Logger createLogger() { 401 return new EventLogLogger(getContext(), getConfig()); 402 } 403 404 public SuggestionFormatter getSuggestionFormatter() { 405 if (mSuggestionFormatter == null) { 406 mSuggestionFormatter = createSuggestionFormatter(); 407 mSuggestionFormatter.setSuggestedTextFormat(Typeface.BOLD, 0); 408 } 409 return mSuggestionFormatter; 410 } 411 412 protected SuggestionFormatter createSuggestionFormatter() { 413 return new LevenshteinSuggestionFormatter(); 414 } 415} 416