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