QsbApplication.java revision 54af8b26e73f9aa2a01473517cf6d9980a95fe55
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.ui.CorpusViewFactory;
20import com.android.quicksearchbox.ui.CorpusViewInflater;
21import com.android.quicksearchbox.ui.DelayingSuggestionsAdapter;
22import com.android.quicksearchbox.ui.SuggestionViewFactory;
23import com.android.quicksearchbox.ui.SuggestionViewInflater;
24import com.android.quicksearchbox.ui.SuggestionsAdapter;
25import com.android.quicksearchbox.ui.SuggestionsDecoration;
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.app.Application;
34import android.os.Handler;
35import android.os.Looper;
36import android.os.Process;
37
38import java.util.concurrent.Executor;
39import java.util.concurrent.Executors;
40import java.util.concurrent.ThreadFactory;
41
42public class QsbApplication extends Application {
43
44    private Handler mUiThreadHandler;
45    private Config mConfig;
46    private Corpora mCorpora;
47    private CorpusRanker mCorpusRanker;
48    private ShortcutRepository mShortcutRepository;
49    private ShortcutRefresher mShortcutRefresher;
50    private NamedTaskExecutor mSourceTaskExecutor;
51    private ThreadFactory mQueryThreadFactory;
52    private SuggestionsProvider mSuggestionsProvider;
53    private SuggestionViewFactory mSuggestionViewFactory;
54    private CorpusViewFactory mCorpusViewFactory;
55    private Logger mLogger;
56
57    @Override
58    public void onTerminate() {
59        close();
60        super.onTerminate();
61    }
62
63    protected void checkThread() {
64        if (Looper.myLooper() != Looper.getMainLooper()) {
65            throw new IllegalStateException("Accessed Application object from thread "
66                    + Thread.currentThread().getName());
67        }
68    }
69
70    protected void close() {
71        checkThread();
72        if (mConfig != null) {
73            mConfig.close();
74            mConfig = null;
75        }
76        if (mShortcutRepository != null) {
77            mShortcutRepository.close();
78            mShortcutRepository = null;
79        }
80        if (mSourceTaskExecutor != null) {
81            mSourceTaskExecutor.close();
82            mSourceTaskExecutor = null;
83        }
84        if (mSuggestionsProvider != null) {
85            mSuggestionsProvider.close();
86            mSuggestionsProvider = null;
87        }
88    }
89
90    public synchronized Handler getMainThreadHandler() {
91        if (mUiThreadHandler == null) {
92            mUiThreadHandler = new Handler(Looper.getMainLooper());
93        }
94        return mUiThreadHandler;
95    }
96
97    public void runOnUiThread(Runnable action) {
98        getMainThreadHandler().post(action);
99    }
100
101    /**
102     * Gets the QSB configuration object.
103     * May be called from any thread.
104     */
105    public synchronized Config getConfig() {
106        if (mConfig == null) {
107            mConfig = createConfig();
108        }
109        return mConfig;
110    }
111
112    protected Config createConfig() {
113        return new Config(this);
114    }
115
116    /**
117     * Gets the corpora.
118     * May only be called from the main thread.
119     */
120    public Corpora getCorpora() {
121        checkThread();
122        if (mCorpora == null) {
123            mCorpora = createCorpora();
124        }
125        return mCorpora;
126    }
127
128    protected Corpora createCorpora() {
129        SearchableCorpora corpora = new SearchableCorpora(this, getConfig(), createSources(),
130                createCorpusFactory());
131        corpora.update();
132        return corpora;
133    }
134
135    /**
136     * Updates the corpora, if they are loaded.
137     * May only be called from the main thread.
138     */
139    public void updateCorpora() {
140        checkThread();
141        if (mCorpora != null) {
142            mCorpora.update();
143        }
144    }
145
146    protected Sources createSources() {
147        return new SearchableSources(this);
148    }
149
150    protected CorpusFactory createCorpusFactory() {
151        int numWebCorpusThreads = getConfig().getNumWebCorpusThreads();
152        return new SearchableCorpusFactory(this, createExecutorFactory(numWebCorpusThreads));
153    }
154
155    protected Factory<Executor> createExecutorFactory(final int numThreads) {
156        final ThreadFactory threadFactory = getQueryThreadFactory();
157        return new Factory<Executor>() {
158            public Executor create() {
159                return Executors.newFixedThreadPool(numThreads, threadFactory);
160            }
161        };
162    }
163
164    /**
165     * Gets the corpus ranker.
166     * May only be called from the main thread.
167     */
168    public CorpusRanker getCorpusRanker() {
169        checkThread();
170        if (mCorpusRanker == null) {
171            mCorpusRanker = createCorpusRanker();
172        }
173        return mCorpusRanker;
174    }
175
176    protected CorpusRanker createCorpusRanker() {
177        return new DefaultCorpusRanker(getCorpora(), getShortcutRepository());
178    }
179
180    /**
181     * Gets the shortcut repository.
182     * May only be called from the main thread.
183     */
184    public ShortcutRepository getShortcutRepository() {
185        checkThread();
186        if (mShortcutRepository == null) {
187            mShortcutRepository = createShortcutRepository();
188        }
189        return mShortcutRepository;
190    }
191
192    protected ShortcutRepository createShortcutRepository() {
193        ThreadFactory logThreadFactory = new NamingThreadFactory("ShortcutRepositoryWriter #%d",
194                new PriorityThreadFactory(Process.THREAD_PRIORITY_BACKGROUND));
195        Executor logExecutor = Executors.newSingleThreadExecutor(logThreadFactory);
196        return ShortcutRepositoryImplLog.create(this, getConfig(), getCorpora(),
197            getShortcutRefresher(), getMainThreadHandler(), logExecutor);
198    }
199
200    /**
201     * Gets the shortcut refresher.
202     * May only be called from the main thread.
203     */
204    public ShortcutRefresher getShortcutRefresher() {
205        checkThread();
206        if (mShortcutRefresher == null) {
207            mShortcutRefresher = createShortcutRefresher();
208        }
209        return mShortcutRefresher;
210    }
211
212    protected ShortcutRefresher createShortcutRefresher() {
213        // For now, ShortcutRefresher gets its own SourceTaskExecutor
214        return new SourceShortcutRefresher(createSourceTaskExecutor());
215    }
216
217    /**
218     * Gets the source task executor.
219     * May only be called from the main thread.
220     */
221    public NamedTaskExecutor getSourceTaskExecutor() {
222        checkThread();
223        if (mSourceTaskExecutor == null) {
224            mSourceTaskExecutor = createSourceTaskExecutor();
225        }
226        return mSourceTaskExecutor;
227    }
228
229    protected NamedTaskExecutor createSourceTaskExecutor() {
230        Config config = getConfig();
231        ThreadFactory queryThreadFactory = getQueryThreadFactory();
232        return new PerNameExecutor(SingleThreadNamedTaskExecutor.factory(queryThreadFactory));
233    }
234
235    /**
236     * Gets the query thread factory.
237     * May only be called from the main thread.
238     */
239    protected ThreadFactory getQueryThreadFactory() {
240        checkThread();
241        if (mQueryThreadFactory == null) {
242            mQueryThreadFactory = createQueryThreadFactory();
243        }
244        return mQueryThreadFactory;
245    }
246
247    protected ThreadFactory createQueryThreadFactory() {
248        String nameFormat = "QSB #%d";
249        int priority = getConfig().getQueryThreadPriority();
250        return new NamingThreadFactory(nameFormat,
251                new PriorityThreadFactory(priority));
252    }
253
254    /**
255     * Gets the suggestion provider.
256     * May only be called from the main thread.
257     */
258    protected SuggestionsProvider getSuggestionsProvider() {
259        checkThread();
260        if (mSuggestionsProvider == null) {
261            mSuggestionsProvider = createSuggestionsProvider();
262        }
263        return mSuggestionsProvider;
264    }
265
266    protected SuggestionsProvider createSuggestionsProvider() {
267        Promoter promoter =  new ShortcutPromoter(
268                new RankAwarePromoter(getConfig(), getCorpora()));
269        SuggestionsProvider provider = new SuggestionsProviderImpl(getConfig(),
270                getSourceTaskExecutor(),
271                getMainThreadHandler(),
272                promoter,
273                getShortcutRepository(),
274                getCorpora(),
275                getCorpusRanker(),
276                getLogger());
277        return provider;
278    }
279
280    /**
281     * Gets the suggestion view factory.
282     * May only be called from the main thread.
283     */
284    public SuggestionViewFactory getSuggestionViewFactory() {
285        checkThread();
286        if (mSuggestionViewFactory == null) {
287            mSuggestionViewFactory = createSuggestionViewFactory();
288        }
289        return mSuggestionViewFactory;
290    }
291
292    protected SuggestionViewFactory createSuggestionViewFactory() {
293        return new SuggestionViewInflater(this);
294    }
295
296    /**
297     * Gets the corpus view factory.
298     * May only be called from the main thread.
299     */
300    public CorpusViewFactory getCorpusViewFactory() {
301        checkThread();
302        if (mCorpusViewFactory == null) {
303            mCorpusViewFactory = createCorpusViewFactory();
304        }
305        return mCorpusViewFactory;
306    }
307
308    protected CorpusViewFactory createCorpusViewFactory() {
309        return new CorpusViewInflater(this);
310    }
311
312    /**
313     * Creates a suggestions adapter.
314     * May only be called from the main thread.
315     */
316    public SuggestionsAdapter createSuggestionsAdapter() {
317        Config config = getConfig();
318        SuggestionViewFactory viewFactory = getSuggestionViewFactory();
319        DelayingSuggestionsAdapter adapter = new DelayingSuggestionsAdapter(viewFactory);
320        return adapter;
321    }
322
323    /**
324     * Creates a header view to add at the top of the search activity.
325     */
326    public SuggestionsDecoration createSuggestionsHeader() {
327        return new SuggestionsDecoration(this);
328    }
329
330    /**
331     * Creates a footer view to add at the bottom of the search activity.
332     */
333    public SuggestionsDecoration createSuggestionsFooter() {
334        return new SuggestionsDecoration(this);
335    }
336
337    /**
338     * Gets the event logger.
339     * May only be called from the main thread.
340     */
341    public Logger getLogger() {
342        checkThread();
343        if (mLogger == null) {
344            mLogger = createLogger();
345        }
346        return mLogger;
347    }
348
349    protected Logger createLogger() {
350        return new EventLogLogger(this, getConfig());
351    }
352}
353