Home | History | Annotate | Download | only in view
      1 /*
      2  * Copyright (C) 2006 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 android.view;
     18 
     19 import android.annotation.StyleRes;
     20 import android.content.Context;
     21 import android.content.ContextWrapper;
     22 import android.content.res.AssetManager;
     23 import android.content.res.Configuration;
     24 import android.content.res.Resources;
     25 
     26 /**
     27  * A context wrapper that allows you to modify or replace the theme of the
     28  * wrapped context.
     29  */
     30 public class ContextThemeWrapper extends ContextWrapper {
     31     private int mThemeResource;
     32     private Resources.Theme mTheme;
     33     private LayoutInflater mInflater;
     34     private Configuration mOverrideConfiguration;
     35     private Resources mResources;
     36 
     37     /**
     38      * Creates a new context wrapper with no theme and no base context.
     39      * <p class="note">
     40      * <strong>Note:</strong> A base context <strong>must</strong> be attached
     41      * using {@link #attachBaseContext(Context)} before calling any other
     42      * method on the newly constructed context wrapper.
     43      */
     44     public ContextThemeWrapper() {
     45         super(null);
     46     }
     47 
     48     /**
     49      * Creates a new context wrapper with the specified theme.
     50      * <p>
     51      * The specified theme will be applied on top of the base context's theme.
     52      * Any attributes not explicitly defined in the theme identified by
     53      * <var>themeResId</var> will retain their original values.
     54      *
     55      * @param base the base context
     56      * @param themeResId the resource ID of the theme to be applied on top of
     57      *                   the base context's theme
     58      */
     59     public ContextThemeWrapper(Context base, @StyleRes int themeResId) {
     60         super(base);
     61         mThemeResource = themeResId;
     62     }
     63 
     64     /**
     65      * Creates a new context wrapper with the specified theme.
     66      * <p>
     67      * Unlike {@link #ContextThemeWrapper(Context, int)}, the theme passed to
     68      * this constructor will completely replace the base context's theme.
     69      *
     70      * @param base the base context
     71      * @param theme the theme against which resources should be inflated
     72      */
     73     public ContextThemeWrapper(Context base, Resources.Theme theme) {
     74         super(base);
     75         mTheme = theme;
     76     }
     77 
     78     @Override
     79     protected void attachBaseContext(Context newBase) {
     80         super.attachBaseContext(newBase);
     81     }
     82 
     83     /**
     84      * Call to set an "override configuration" on this context -- this is
     85      * a configuration that replies one or more values of the standard
     86      * configuration that is applied to the context.  See
     87      * {@link Context#createConfigurationContext(Configuration)} for more
     88      * information.
     89      *
     90      * <p>This method can only be called once, and must be called before any
     91      * calls to {@link #getResources()} or {@link #getAssets()} are made.
     92      */
     93     public void applyOverrideConfiguration(Configuration overrideConfiguration) {
     94         if (mResources != null) {
     95             throw new IllegalStateException(
     96                     "getResources() or getAssets() has already been called");
     97         }
     98         if (mOverrideConfiguration != null) {
     99             throw new IllegalStateException("Override configuration has already been set");
    100         }
    101         mOverrideConfiguration = new Configuration(overrideConfiguration);
    102     }
    103 
    104     /**
    105      * Used by ActivityThread to apply the overridden configuration to onConfigurationChange
    106      * callbacks.
    107      * @hide
    108      */
    109     public Configuration getOverrideConfiguration() {
    110         return mOverrideConfiguration;
    111     }
    112 
    113     @Override
    114     public AssetManager getAssets() {
    115         // Ensure we're returning assets with the correct configuration.
    116         return getResourcesInternal().getAssets();
    117     }
    118 
    119     @Override
    120     public Resources getResources() {
    121         return getResourcesInternal();
    122     }
    123 
    124     private Resources getResourcesInternal() {
    125         if (mResources == null) {
    126             if (mOverrideConfiguration == null) {
    127                 mResources = super.getResources();
    128             } else {
    129                 final Context resContext = createConfigurationContext(mOverrideConfiguration);
    130                 mResources = resContext.getResources();
    131             }
    132         }
    133         return mResources;
    134     }
    135 
    136     @Override
    137     public void setTheme(int resid) {
    138         if (mThemeResource != resid) {
    139             mThemeResource = resid;
    140             initializeTheme();
    141         }
    142     }
    143 
    144     /** @hide */
    145     @Override
    146     public int getThemeResId() {
    147         return mThemeResource;
    148     }
    149 
    150     @Override
    151     public Resources.Theme getTheme() {
    152         if (mTheme != null) {
    153             return mTheme;
    154         }
    155 
    156         mThemeResource = Resources.selectDefaultTheme(mThemeResource,
    157                 getApplicationInfo().targetSdkVersion);
    158         initializeTheme();
    159 
    160         return mTheme;
    161     }
    162 
    163     @Override
    164     public Object getSystemService(String name) {
    165         if (LAYOUT_INFLATER_SERVICE.equals(name)) {
    166             if (mInflater == null) {
    167                 mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
    168             }
    169             return mInflater;
    170         }
    171         return getBaseContext().getSystemService(name);
    172     }
    173 
    174     /**
    175      * Called by {@link #setTheme} and {@link #getTheme} to apply a theme
    176      * resource to the current Theme object. May be overridden to change the
    177      * default (simple) behavior. This method will not be called in multiple
    178      * threads simultaneously.
    179      *
    180      * @param theme the theme being modified
    181      * @param resId the style resource being applied to <var>theme</var>
    182      * @param first {@code true} if this is the first time a style is being
    183      *              applied to <var>theme</var>
    184      */
    185     protected void onApplyThemeResource(Resources.Theme theme, int resId, boolean first) {
    186         theme.applyStyle(resId, true);
    187     }
    188 
    189     private void initializeTheme() {
    190         final boolean first = mTheme == null;
    191         if (first) {
    192             mTheme = getResources().newTheme();
    193             final Resources.Theme theme = getBaseContext().getTheme();
    194             if (theme != null) {
    195                 mTheme.setTo(theme);
    196             }
    197         }
    198         onApplyThemeResource(mTheme, mThemeResource, first);
    199     }
    200 }
    201 
    202