QsbApplication.java revision 96c7058210699c82445169048b7c0fdfb16f59ee
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.util.Factory;
26import com.android.quicksearchbox.util.NamedTaskExecutor;
27import com.android.quicksearchbox.util.PerNameExecutor;
28import com.android.quicksearchbox.util.PriorityThreadFactory;
29import com.android.quicksearchbox.util.SingleThreadNamedTaskExecutor;
30import com.google.common.util.concurrent.NamingThreadFactory;
31
32import android.app.Application;
33import android.os.Handler;
34import android.os.Looper;
35import android.os.Process;
36
37import java.util.concurrent.Executor;
38import java.util.concurrent.Executors;
39import java.util.concurrent.ThreadFactory;
40
41public class QsbApplication extends Application {
42
43    private Handler mUiThreadHandler;
44    private Config mConfig;
45    private Corpora mCorpora;
46    private CorpusRanker mCorpusRanker;
47    private ShortcutRepository mShortcutRepository;
48    private ShortcutRefresher mShortcutRefresher;
49    private NamedTaskExecutor mSourceTaskExecutor;
50    private ThreadFactory mQueryThreadFactory;
51    private SuggestionsProvider mSuggestionsProvider;
52    private SuggestionViewFactory mSuggestionViewFactory;
53    private CorpusViewFactory mCorpusViewFactory;
54    private Logger mLogger;
55
56    @Override
57    public void onTerminate() {
58        close();
59        super.onTerminate();
60    }
61
62    protected void checkThread() {
63        if (Looper.myLooper() != Looper.getMainLooper()) {
64            throw new IllegalStateException("Accessed Application object from thread "
65                    + Thread.currentThread().getName());
66        }
67    }
68
69    protected void close() {
70        checkThread();
71        if (mConfig != null) {
72            mConfig.close();
73            mConfig = null;
74        }
75        if (mShortcutRepository != null) {
76            mShortcutRepository.close();
77            mShortcutRepository = null;
78        }
79        if (mSourceTaskExecutor != null) {
80            mSourceTaskExecutor.close();
81            mSourceTaskExecutor = null;
82        }
83        if (mSuggestionsProvider != null) {
84            mSuggestionsProvider.close();
85            mSuggestionsProvider = null;
86        }
87    }
88
89    public synchronized Handler getMainThreadHandler() {
90        if (mUiThreadHandler == null) {
91            mUiThreadHandler = new Handler(Looper.getMainLooper());
92        }
93        return mUiThreadHandler;
94    }
95
96    public void runOnUiThread(Runnable action) {
97        getMainThreadHandler().post(action);
98    }
99
100    /**
101     * Gets the QSB configuration object.
102     * May be called from any thread.
103     */
104    public synchronized Config getConfig() {
105        if (mConfig == null) {
106            mConfig = createConfig();
107        }
108        return mConfig;
109    }
110
111    protected Config createConfig() {
112        return new Config(this);
113    }
114
115    /**
116     * Gets the corpora.
117     * May only be called from the main thread.
118     */
119    public Corpora getCorpora() {
120        checkThread();
121        if (mCorpora == null) {
122            mCorpora = createCorpora();
123        }
124        return mCorpora;
125    }
126
127    protected Corpora createCorpora() {
128        SearchableCorpora corpora = new SearchableCorpora(this, createSources(),
129                createCorpusFactory());
130        corpora.update();
131        return corpora;
132    }
133
134    /**
135     * Updates the corpora, if they are loaded.
136     * May only be called from the main thread.
137     */
138    public void updateCorpora() {
139        checkThread();
140        if (mCorpora != null) {
141            mCorpora.update();
142        }
143    }
144
145    protected Sources createSources() {
146        return new SearchableSources(this);
147    }
148
149    protected CorpusFactory createCorpusFactory() {
150        int numWebCorpusThreads = getConfig().getNumWebCorpusThreads();
151        return new SearchableCorpusFactory(this, getConfig(),
152                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     * Gets the event logger.
325     * May only be called from the main thread.
326     */
327    public Logger getLogger() {
328        checkThread();
329        if (mLogger == null) {
330            mLogger = createLogger();
331        }
332        return mLogger;
333    }
334
335    protected Logger createLogger() {
336        return new EventLogLogger(this, getConfig());
337    }
338}
339