Home | History | Annotate | Download | only in quicksearchbox
      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 
     17 package com.android.quicksearchbox;
     18 
     19 import android.content.Context;
     20 import android.content.pm.PackageInfo;
     21 import android.content.pm.PackageManager;
     22 import android.os.Build;
     23 import android.os.Handler;
     24 import android.os.Looper;
     25 import android.os.Process;
     26 import android.view.ContextThemeWrapper;
     27 
     28 import com.android.quicksearchbox.google.GoogleSource;
     29 import com.android.quicksearchbox.google.GoogleSuggestClient;
     30 import com.android.quicksearchbox.google.SearchBaseUrlHelper;
     31 import com.android.quicksearchbox.ui.DefaultSuggestionViewFactory;
     32 import com.android.quicksearchbox.ui.SuggestionViewFactory;
     33 import com.android.quicksearchbox.util.Factory;
     34 import com.android.quicksearchbox.util.HttpHelper;
     35 import com.android.quicksearchbox.util.JavaNetHttpHelper;
     36 import com.android.quicksearchbox.util.NamedTaskExecutor;
     37 import com.android.quicksearchbox.util.PerNameExecutor;
     38 import com.android.quicksearchbox.util.PriorityThreadFactory;
     39 import com.android.quicksearchbox.util.SingleThreadNamedTaskExecutor;
     40 import com.google.common.util.concurrent.ThreadFactoryBuilder;
     41 
     42 import java.util.concurrent.Executor;
     43 import java.util.concurrent.Executors;
     44 import java.util.concurrent.ThreadFactory;
     45 
     46 public class QsbApplication {
     47     private final Context mContext;
     48 
     49     private int mVersionCode;
     50     private Handler mUiThreadHandler;
     51     private Config mConfig;
     52     private SearchSettings mSettings;
     53     private NamedTaskExecutor mSourceTaskExecutor;
     54     private ThreadFactory mQueryThreadFactory;
     55     private SuggestionsProvider mSuggestionsProvider;
     56     private SuggestionViewFactory mSuggestionViewFactory;
     57     private GoogleSource mGoogleSource;
     58     private VoiceSearch mVoiceSearch;
     59     private Logger mLogger;
     60     private SuggestionFormatter mSuggestionFormatter;
     61     private TextAppearanceFactory mTextAppearanceFactory;
     62     private NamedTaskExecutor mIconLoaderExecutor;
     63     private HttpHelper mHttpHelper;
     64     private SearchBaseUrlHelper mSearchBaseUrlHelper;
     65 
     66     public QsbApplication(Context context) {
     67         // the application context does not use the theme from the <application> tag
     68         mContext = new ContextThemeWrapper(context, R.style.Theme_QuickSearchBox);
     69     }
     70 
     71     public static boolean isFroyoOrLater() {
     72         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO;
     73     }
     74 
     75     public static boolean isHoneycombOrLater() {
     76         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
     77     }
     78 
     79     public static QsbApplication get(Context context) {
     80         return ((QsbApplicationWrapper) context.getApplicationContext()).getApp();
     81     }
     82 
     83     protected Context getContext() {
     84         return mContext;
     85     }
     86 
     87     public int getVersionCode() {
     88         if (mVersionCode == 0) {
     89             try {
     90                 PackageManager pm = getContext().getPackageManager();
     91                 PackageInfo pkgInfo = pm.getPackageInfo(getContext().getPackageName(), 0);
     92                 mVersionCode = pkgInfo.versionCode;
     93             } catch (PackageManager.NameNotFoundException ex) {
     94                 // The current package should always exist, how else could we
     95                 // run code from it?
     96                 throw new RuntimeException(ex);
     97             }
     98         }
     99         return mVersionCode;
    100     }
    101 
    102     protected void checkThread() {
    103         if (Looper.myLooper() != Looper.getMainLooper()) {
    104             throw new IllegalStateException("Accessed Application object from thread "
    105                     + Thread.currentThread().getName());
    106         }
    107     }
    108 
    109     protected void close() {
    110         checkThread();
    111         if (mConfig != null) {
    112             mConfig.close();
    113             mConfig = null;
    114         }
    115         if (mSuggestionsProvider != null) {
    116             mSuggestionsProvider.close();
    117             mSuggestionsProvider = null;
    118         }
    119     }
    120 
    121     public synchronized Handler getMainThreadHandler() {
    122         if (mUiThreadHandler == null) {
    123             mUiThreadHandler = new Handler(Looper.getMainLooper());
    124         }
    125         return mUiThreadHandler;
    126     }
    127 
    128     public void runOnUiThread(Runnable action) {
    129         getMainThreadHandler().post(action);
    130     }
    131 
    132     public synchronized NamedTaskExecutor getIconLoaderExecutor() {
    133         if (mIconLoaderExecutor == null) {
    134             mIconLoaderExecutor = createIconLoaderExecutor();
    135         }
    136         return mIconLoaderExecutor;
    137     }
    138 
    139     protected NamedTaskExecutor createIconLoaderExecutor() {
    140         ThreadFactory iconThreadFactory = new PriorityThreadFactory(
    141                     Process.THREAD_PRIORITY_BACKGROUND);
    142         return new PerNameExecutor(SingleThreadNamedTaskExecutor.factory(iconThreadFactory));
    143     }
    144 
    145     /**
    146      * Indicates that construction of the QSB UI is now complete.
    147      */
    148     public void onStartupComplete() {
    149     }
    150 
    151     /**
    152      * Gets the QSB configuration object.
    153      * May be called from any thread.
    154      */
    155     public synchronized Config getConfig() {
    156         if (mConfig == null) {
    157             mConfig = createConfig();
    158         }
    159         return mConfig;
    160     }
    161 
    162     protected Config createConfig() {
    163         return new Config(getContext());
    164     }
    165 
    166     public synchronized SearchSettings getSettings() {
    167         if (mSettings == null) {
    168             mSettings = createSettings();
    169             mSettings.upgradeSettingsIfNeeded();
    170         }
    171         return mSettings;
    172     }
    173 
    174     protected SearchSettings createSettings() {
    175         return new SearchSettingsImpl(getContext(), getConfig());
    176     }
    177 
    178     protected Factory<Executor> createExecutorFactory(final int numThreads) {
    179         final ThreadFactory threadFactory = getQueryThreadFactory();
    180         return new Factory<Executor>() {
    181             @Override
    182             public Executor create() {
    183                 return Executors.newFixedThreadPool(numThreads, threadFactory);
    184             }
    185         };
    186     }
    187 
    188     /**
    189     /**
    190      * Gets the source task executor.
    191      * May only be called from the main thread.
    192      */
    193     public NamedTaskExecutor getSourceTaskExecutor() {
    194         checkThread();
    195         if (mSourceTaskExecutor == null) {
    196             mSourceTaskExecutor = createSourceTaskExecutor();
    197         }
    198         return mSourceTaskExecutor;
    199     }
    200 
    201     protected NamedTaskExecutor createSourceTaskExecutor() {
    202         ThreadFactory queryThreadFactory = getQueryThreadFactory();
    203         return new PerNameExecutor(SingleThreadNamedTaskExecutor.factory(queryThreadFactory));
    204     }
    205 
    206     /**
    207      * Gets the query thread factory.
    208      * May only be called from the main thread.
    209      */
    210     protected ThreadFactory getQueryThreadFactory() {
    211         checkThread();
    212         if (mQueryThreadFactory == null) {
    213             mQueryThreadFactory = createQueryThreadFactory();
    214         }
    215         return mQueryThreadFactory;
    216     }
    217 
    218     protected ThreadFactory createQueryThreadFactory() {
    219         String nameFormat = "QSB #%d";
    220         int priority = getConfig().getQueryThreadPriority();
    221         return new ThreadFactoryBuilder()
    222                 .setNameFormat(nameFormat)
    223                 .setThreadFactory(new PriorityThreadFactory(priority))
    224                 .build();
    225     }
    226 
    227     /**
    228      * Gets the suggestion provider.
    229      *
    230      * May only be called from the main thread.
    231      */
    232     protected SuggestionsProvider getSuggestionsProvider() {
    233         checkThread();
    234         if (mSuggestionsProvider == null) {
    235             mSuggestionsProvider = createSuggestionsProvider();
    236         }
    237         return mSuggestionsProvider;
    238     }
    239 
    240     protected SuggestionsProvider createSuggestionsProvider() {
    241         return new SuggestionsProviderImpl(getConfig(),
    242               getSourceTaskExecutor(),
    243               getMainThreadHandler(),
    244               getLogger());
    245     }
    246 
    247     /**
    248      * Gets the default suggestion view factory.
    249      * May only be called from the main thread.
    250      */
    251     public SuggestionViewFactory getSuggestionViewFactory() {
    252         checkThread();
    253         if (mSuggestionViewFactory == null) {
    254             mSuggestionViewFactory = createSuggestionViewFactory();
    255         }
    256         return mSuggestionViewFactory;
    257     }
    258 
    259     protected SuggestionViewFactory createSuggestionViewFactory() {
    260         return new DefaultSuggestionViewFactory(getContext());
    261     }
    262 
    263     /**
    264      * Gets the Google source.
    265      * May only be called from the main thread.
    266      */
    267     public GoogleSource getGoogleSource() {
    268         checkThread();
    269         if (mGoogleSource == null) {
    270             mGoogleSource = createGoogleSource();
    271         }
    272         return mGoogleSource;
    273     }
    274 
    275     protected GoogleSource createGoogleSource() {
    276         return new GoogleSuggestClient(getContext(), getMainThreadHandler(),
    277                 getIconLoaderExecutor(), getConfig());
    278     }
    279 
    280     /**
    281      * Gets Voice Search utilities.
    282      */
    283     public VoiceSearch getVoiceSearch() {
    284         checkThread();
    285         if (mVoiceSearch == null) {
    286             mVoiceSearch = createVoiceSearch();
    287         }
    288         return mVoiceSearch;
    289     }
    290 
    291     protected VoiceSearch createVoiceSearch() {
    292         return new VoiceSearch(getContext());
    293     }
    294 
    295     /**
    296      * Gets the event logger.
    297      * May only be called from the main thread.
    298      */
    299     public Logger getLogger() {
    300         checkThread();
    301         if (mLogger == null) {
    302             mLogger = createLogger();
    303         }
    304         return mLogger;
    305     }
    306 
    307     protected Logger createLogger() {
    308         return new EventLogLogger(getContext(), getConfig());
    309     }
    310 
    311     public SuggestionFormatter getSuggestionFormatter() {
    312         if (mSuggestionFormatter == null) {
    313             mSuggestionFormatter = createSuggestionFormatter();
    314         }
    315         return mSuggestionFormatter;
    316     }
    317 
    318     protected SuggestionFormatter createSuggestionFormatter() {
    319         return new LevenshteinSuggestionFormatter(getTextAppearanceFactory());
    320     }
    321 
    322     public TextAppearanceFactory getTextAppearanceFactory() {
    323         if (mTextAppearanceFactory == null) {
    324             mTextAppearanceFactory = createTextAppearanceFactory();
    325         }
    326         return mTextAppearanceFactory;
    327     }
    328 
    329     protected TextAppearanceFactory createTextAppearanceFactory() {
    330         return new TextAppearanceFactory(getContext());
    331     }
    332 
    333     public synchronized HttpHelper getHttpHelper() {
    334         if (mHttpHelper == null) {
    335             mHttpHelper = createHttpHelper();
    336         }
    337         return mHttpHelper;
    338     }
    339 
    340     protected HttpHelper createHttpHelper() {
    341         return new JavaNetHttpHelper(
    342                 new JavaNetHttpHelper.PassThroughRewriter(),
    343                 getConfig().getUserAgent());
    344     }
    345 
    346     public synchronized SearchBaseUrlHelper getSearchBaseUrlHelper() {
    347         if (mSearchBaseUrlHelper == null) {
    348             mSearchBaseUrlHelper = createSearchBaseUrlHelper();
    349         }
    350 
    351         return mSearchBaseUrlHelper;
    352     }
    353 
    354     protected SearchBaseUrlHelper createSearchBaseUrlHelper() {
    355         // This cast to "SearchSettingsImpl" is somewhat ugly.
    356         return new SearchBaseUrlHelper(getContext(), getHttpHelper(),
    357                 getSettings(), ((SearchSettingsImpl)getSettings()).getSearchPreferences());
    358     }
    359 
    360     public Help getHelp() {
    361         // No point caching this, it's super cheap.
    362         return new Help(getContext(), getConfig());
    363     }
    364 }
    365