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