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