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