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