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