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