blob: b3bccd37d5c76263a18a229b69edcd089470d667 [file] [log] [blame]
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.quicksearchbox;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Process;
import android.view.ContextThemeWrapper;
import com.android.quicksearchbox.google.GoogleSource;
import com.android.quicksearchbox.google.GoogleSuggestClient;
import com.android.quicksearchbox.google.SearchBaseUrlHelper;
import com.android.quicksearchbox.ui.DefaultSuggestionViewFactory;
import com.android.quicksearchbox.ui.SuggestionViewFactory;
import com.android.quicksearchbox.util.Factory;
import com.android.quicksearchbox.util.HttpHelper;
import com.android.quicksearchbox.util.JavaNetHttpHelper;
import com.android.quicksearchbox.util.NamedTaskExecutor;
import com.android.quicksearchbox.util.PerNameExecutor;
import com.android.quicksearchbox.util.PriorityThreadFactory;
import com.android.quicksearchbox.util.SingleThreadNamedTaskExecutor;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
public class QsbApplication {
private final Context mContext;
private int mVersionCode;
private Handler mUiThreadHandler;
private Config mConfig;
private SearchSettings mSettings;
private NamedTaskExecutor mSourceTaskExecutor;
private ThreadFactory mQueryThreadFactory;
private SuggestionsProvider mSuggestionsProvider;
private SuggestionViewFactory mSuggestionViewFactory;
private GoogleSource mGoogleSource;
private VoiceSearch mVoiceSearch;
private Logger mLogger;
private SuggestionFormatter mSuggestionFormatter;
private TextAppearanceFactory mTextAppearanceFactory;
private NamedTaskExecutor mIconLoaderExecutor;
private HttpHelper mHttpHelper;
private SearchBaseUrlHelper mSearchBaseUrlHelper;
public QsbApplication(Context context) {
// the application context does not use the theme from the <application> tag
mContext = new ContextThemeWrapper(context, R.style.Theme_QuickSearchBox);
}
public static boolean isFroyoOrLater() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO;
}
public static boolean isHoneycombOrLater() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
}
public static QsbApplication get(Context context) {
return ((QsbApplicationWrapper) context.getApplicationContext()).getApp();
}
protected Context getContext() {
return mContext;
}
public int getVersionCode() {
if (mVersionCode == 0) {
try {
PackageManager pm = getContext().getPackageManager();
PackageInfo pkgInfo = pm.getPackageInfo(getContext().getPackageName(), 0);
mVersionCode = pkgInfo.versionCode;
} catch (PackageManager.NameNotFoundException ex) {
// The current package should always exist, how else could we
// run code from it?
throw new RuntimeException(ex);
}
}
return mVersionCode;
}
protected void checkThread() {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new IllegalStateException("Accessed Application object from thread "
+ Thread.currentThread().getName());
}
}
protected void close() {
checkThread();
if (mConfig != null) {
mConfig.close();
mConfig = null;
}
if (mSuggestionsProvider != null) {
mSuggestionsProvider.close();
mSuggestionsProvider = null;
}
}
public synchronized Handler getMainThreadHandler() {
if (mUiThreadHandler == null) {
mUiThreadHandler = new Handler(Looper.getMainLooper());
}
return mUiThreadHandler;
}
public void runOnUiThread(Runnable action) {
getMainThreadHandler().post(action);
}
public synchronized NamedTaskExecutor getIconLoaderExecutor() {
if (mIconLoaderExecutor == null) {
mIconLoaderExecutor = createIconLoaderExecutor();
}
return mIconLoaderExecutor;
}
protected NamedTaskExecutor createIconLoaderExecutor() {
ThreadFactory iconThreadFactory = new PriorityThreadFactory(
Process.THREAD_PRIORITY_BACKGROUND);
return new PerNameExecutor(SingleThreadNamedTaskExecutor.factory(iconThreadFactory));
}
/**
* Indicates that construction of the QSB UI is now complete.
*/
public void onStartupComplete() {
}
/**
* Gets the QSB configuration object.
* May be called from any thread.
*/
public synchronized Config getConfig() {
if (mConfig == null) {
mConfig = createConfig();
}
return mConfig;
}
protected Config createConfig() {
return new Config(getContext());
}
public synchronized SearchSettings getSettings() {
if (mSettings == null) {
mSettings = createSettings();
mSettings.upgradeSettingsIfNeeded();
}
return mSettings;
}
protected SearchSettings createSettings() {
return new SearchSettingsImpl(getContext(), getConfig());
}
protected Factory<Executor> createExecutorFactory(final int numThreads) {
final ThreadFactory threadFactory = getQueryThreadFactory();
return new Factory<Executor>() {
@Override
public Executor create() {
return Executors.newFixedThreadPool(numThreads, threadFactory);
}
};
}
/**
/**
* Gets the source task executor.
* May only be called from the main thread.
*/
public NamedTaskExecutor getSourceTaskExecutor() {
checkThread();
if (mSourceTaskExecutor == null) {
mSourceTaskExecutor = createSourceTaskExecutor();
}
return mSourceTaskExecutor;
}
protected NamedTaskExecutor createSourceTaskExecutor() {
ThreadFactory queryThreadFactory = getQueryThreadFactory();
return new PerNameExecutor(SingleThreadNamedTaskExecutor.factory(queryThreadFactory));
}
/**
* Gets the query thread factory.
* May only be called from the main thread.
*/
protected ThreadFactory getQueryThreadFactory() {
checkThread();
if (mQueryThreadFactory == null) {
mQueryThreadFactory = createQueryThreadFactory();
}
return mQueryThreadFactory;
}
protected ThreadFactory createQueryThreadFactory() {
String nameFormat = "QSB #%d";
int priority = getConfig().getQueryThreadPriority();
return new ThreadFactoryBuilder()
.setNameFormat(nameFormat)
.setThreadFactory(new PriorityThreadFactory(priority))
.build();
}
/**
* Gets the suggestion provider.
*
* May only be called from the main thread.
*/
protected SuggestionsProvider getSuggestionsProvider() {
checkThread();
if (mSuggestionsProvider == null) {
mSuggestionsProvider = createSuggestionsProvider();
}
return mSuggestionsProvider;
}
protected SuggestionsProvider createSuggestionsProvider() {
return new SuggestionsProviderImpl(getConfig(),
getSourceTaskExecutor(),
getMainThreadHandler(),
getLogger());
}
/**
* Gets the default suggestion view factory.
* May only be called from the main thread.
*/
public SuggestionViewFactory getSuggestionViewFactory() {
checkThread();
if (mSuggestionViewFactory == null) {
mSuggestionViewFactory = createSuggestionViewFactory();
}
return mSuggestionViewFactory;
}
protected SuggestionViewFactory createSuggestionViewFactory() {
return new DefaultSuggestionViewFactory(getContext());
}
/**
* Gets the Google source.
* May only be called from the main thread.
*/
public GoogleSource getGoogleSource() {
checkThread();
if (mGoogleSource == null) {
mGoogleSource = createGoogleSource();
}
return mGoogleSource;
}
protected GoogleSource createGoogleSource() {
return new GoogleSuggestClient(getContext(), getMainThreadHandler(),
getIconLoaderExecutor(), getConfig());
}
/**
* Gets Voice Search utilities.
*/
public VoiceSearch getVoiceSearch() {
checkThread();
if (mVoiceSearch == null) {
mVoiceSearch = createVoiceSearch();
}
return mVoiceSearch;
}
protected VoiceSearch createVoiceSearch() {
return new VoiceSearch(getContext());
}
/**
* Gets the event logger.
* May only be called from the main thread.
*/
public Logger getLogger() {
checkThread();
if (mLogger == null) {
mLogger = createLogger();
}
return mLogger;
}
protected Logger createLogger() {
return new EventLogLogger(getContext(), getConfig());
}
public SuggestionFormatter getSuggestionFormatter() {
if (mSuggestionFormatter == null) {
mSuggestionFormatter = createSuggestionFormatter();
}
return mSuggestionFormatter;
}
protected SuggestionFormatter createSuggestionFormatter() {
return new LevenshteinSuggestionFormatter(getTextAppearanceFactory());
}
public TextAppearanceFactory getTextAppearanceFactory() {
if (mTextAppearanceFactory == null) {
mTextAppearanceFactory = createTextAppearanceFactory();
}
return mTextAppearanceFactory;
}
protected TextAppearanceFactory createTextAppearanceFactory() {
return new TextAppearanceFactory(getContext());
}
public synchronized HttpHelper getHttpHelper() {
if (mHttpHelper == null) {
mHttpHelper = createHttpHelper();
}
return mHttpHelper;
}
protected HttpHelper createHttpHelper() {
return new JavaNetHttpHelper(
new JavaNetHttpHelper.PassThroughRewriter(),
getConfig().getUserAgent());
}
public synchronized SearchBaseUrlHelper getSearchBaseUrlHelper() {
if (mSearchBaseUrlHelper == null) {
mSearchBaseUrlHelper = createSearchBaseUrlHelper();
}
return mSearchBaseUrlHelper;
}
protected SearchBaseUrlHelper createSearchBaseUrlHelper() {
// This cast to "SearchSettingsImpl" is somewhat ugly.
return new SearchBaseUrlHelper(getContext(), getHttpHelper(),
getSettings(), ((SearchSettingsImpl)getSettings()).getSearchPreferences());
}
public Help getHelp() {
// No point caching this, it's super cheap.
return new Help(getContext(), getConfig());
}
}