blob: 4888877cb28be6f80c674406ffd1bf55539083b0 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17package android.view;
18
Tor Norbye417ee5b2015-03-10 20:57:37 -070019import android.annotation.StyleRes;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import android.content.Context;
21import android.content.ContextWrapper;
Alan Viverette99bf63f2015-08-25 13:45:38 -040022import android.content.res.AssetManager;
Dianne Hackborn756220b2012-08-14 16:45:30 -070023import android.content.res.Configuration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.content.res.Resources;
25
26/**
Alan Viverette04067f32015-08-25 14:07:02 -040027 * A context wrapper that allows you to modify or replace the theme of the
28 * wrapped context.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029 */
30public class ContextThemeWrapper extends ContextWrapper {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031 private int mThemeResource;
32 private Resources.Theme mTheme;
33 private LayoutInflater mInflater;
Dianne Hackborn756220b2012-08-14 16:45:30 -070034 private Configuration mOverrideConfiguration;
35 private Resources mResources;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036
Alan Viverette04067f32015-08-25 14:07:02 -040037 /**
38 * Creates a new context wrapper with no theme and no base context.
39 * <p>
40 * <stong>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 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044 public ContextThemeWrapper() {
45 super(null);
46 }
Alan Viveretteb9ead4a2015-01-14 10:43:31 -080047
Alan Viverette04067f32015-08-25 14:07:02 -040048 /**
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 */
Tor Norbye417ee5b2015-03-10 20:57:37 -070059 public ContextThemeWrapper(Context base, @StyleRes int themeResId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060 super(base);
Alan Viveretteb9ead4a2015-01-14 10:43:31 -080061 mThemeResource = themeResId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062 }
63
Alan Viverette04067f32015-08-25 14:07:02 -040064 /**
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 */
Alan Viveretteb9ead4a2015-01-14 10:43:31 -080073 public ContextThemeWrapper(Context base, Resources.Theme theme) {
74 super(base);
75 mTheme = theme;
76 }
77
78 @Override
79 protected void attachBaseContext(Context newBase) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080 super.attachBaseContext(newBase);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 }
Dianne Hackborn756220b2012-08-14 16:45:30 -070082
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
Alan Viverette99bf63f2015-08-25 13:45:38 -040091 * calls to {@link #getResources()} or {@link #getAssets()} are made.
Dianne Hackborn756220b2012-08-14 16:45:30 -070092 */
93 public void applyOverrideConfiguration(Configuration overrideConfiguration) {
94 if (mResources != null) {
Alan Viverette99bf63f2015-08-25 13:45:38 -040095 throw new IllegalStateException(
96 "getResources() or getAssets() has already been called");
Dianne Hackborn756220b2012-08-14 16:45:30 -070097 }
98 if (mOverrideConfiguration != null) {
99 throw new IllegalStateException("Override configuration has already been set");
100 }
101 mOverrideConfiguration = new Configuration(overrideConfiguration);
102 }
103
104 @Override
Alan Viverette99bf63f2015-08-25 13:45:38 -0400105 public AssetManager getAssets() {
106 // Ensure we're returning assets with the correct configuration.
Alan Viverette713a5cd2015-12-16 15:46:32 -0500107 return getResourcesInternal().getAssets();
Alan Viverette99bf63f2015-08-25 13:45:38 -0400108 }
109
110 @Override
Dianne Hackborn756220b2012-08-14 16:45:30 -0700111 public Resources getResources() {
Alan Viverette713a5cd2015-12-16 15:46:32 -0500112 return getResourcesInternal();
113 }
114
115 private Resources getResourcesInternal() {
Alan Viverette04067f32015-08-25 14:07:02 -0400116 if (mResources == null) {
117 if (mOverrideConfiguration == null) {
118 mResources = super.getResources();
119 } else {
120 final Context resContext = createConfigurationContext(mOverrideConfiguration);
121 mResources = resContext.getResources();
122 }
Dianne Hackborn756220b2012-08-14 16:45:30 -0700123 }
Alan Viverette04067f32015-08-25 14:07:02 -0400124 return mResources;
Dianne Hackborn756220b2012-08-14 16:45:30 -0700125 }
Alan Viverette04067f32015-08-25 14:07:02 -0400126
Alan Viverettecc2a1d42015-05-01 11:28:37 -0700127 @Override
128 public void setTheme(int resid) {
129 if (mThemeResource != resid) {
130 mThemeResource = resid;
131 initializeTheme();
132 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 }
Alan Viverette04067f32015-08-25 14:07:02 -0400134
Dianne Hackborn247fe742011-01-08 17:25:57 -0800135 /** @hide */
136 @Override
137 public int getThemeResId() {
138 return mThemeResource;
139 }
140
Alan Viverette04067f32015-08-25 14:07:02 -0400141 @Override
142 public Resources.Theme getTheme() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 if (mTheme != null) {
144 return mTheme;
145 }
146
Alan Viverette5effd7e2014-05-05 12:25:33 -0700147 mThemeResource = Resources.selectDefaultTheme(mThemeResource,
Dianne Hackbornd922ae02011-01-14 11:43:24 -0800148 getApplicationInfo().targetSdkVersion);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 initializeTheme();
150
151 return mTheme;
152 }
153
Alan Viverette04067f32015-08-25 14:07:02 -0400154 @Override
155 public Object getSystemService(String name) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156 if (LAYOUT_INFLATER_SERVICE.equals(name)) {
157 if (mInflater == null) {
Fabrice Di Meglioebb9f3a2014-03-07 17:04:52 -0800158 mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800159 }
160 return mInflater;
161 }
Fabrice Di Meglioebb9f3a2014-03-07 17:04:52 -0800162 return getBaseContext().getSystemService(name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 }
Alan Viverette04067f32015-08-25 14:07:02 -0400164
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 /**
166 * Called by {@link #setTheme} and {@link #getTheme} to apply a theme
Alan Viverette04067f32015-08-25 14:07:02 -0400167 * resource to the current Theme object. May be overridden to change the
168 * default (simple) behavior. This method will not be called in multiple
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 * threads simultaneously.
170 *
Alan Viverette04067f32015-08-25 14:07:02 -0400171 * @param theme the theme being modified
172 * @param resId the style resource being applied to <var>theme</var>
173 * @param first {@code true} if this is the first time a style is being
174 * applied to <var>theme</var>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175 */
Alan Viverette04067f32015-08-25 14:07:02 -0400176 protected void onApplyThemeResource(Resources.Theme theme, int resId, boolean first) {
177 theme.applyStyle(resId, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 }
179
180 private void initializeTheme() {
181 final boolean first = mTheme == null;
182 if (first) {
183 mTheme = getResources().newTheme();
Alan Viverette04067f32015-08-25 14:07:02 -0400184 final Resources.Theme theme = getBaseContext().getTheme();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185 if (theme != null) {
186 mTheme.setTo(theme);
187 }
188 }
189 onApplyThemeResource(mTheme, mThemeResource, first);
190 }
191}
192