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