1/*
2 * Copyright (C) 2011 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.content.res;
18
19import com.android.annotations.Nullable;
20import com.android.ide.common.rendering.api.ResourceReference;
21import com.android.ide.common.rendering.api.StyleResourceValue;
22import com.android.layoutlib.bridge.android.BridgeContext;
23import com.android.layoutlib.bridge.impl.DelegateManager;
24import com.android.layoutlib.bridge.impl.RenderSessionImpl;
25import com.android.resources.ResourceType;
26import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
27
28import android.content.res.Resources.NotFoundException;
29import android.content.res.Resources.Theme;
30import android.util.AttributeSet;
31import android.util.TypedValue;
32
33/**
34 * Delegate used to provide new implementation of a select few methods of {@link Resources.Theme}
35 *
36 * Through the layoutlib_create tool, the original  methods of Theme have been replaced
37 * by calls to methods of the same name in this delegate class.
38 *
39 */
40public class Resources_Theme_Delegate {
41
42    // ---- delegate manager ----
43
44    private static final DelegateManager<Resources_Theme_Delegate> sManager =
45            new DelegateManager<Resources_Theme_Delegate>(Resources_Theme_Delegate.class);
46
47    public static DelegateManager<Resources_Theme_Delegate> getDelegateManager() {
48        return sManager;
49    }
50
51    // ---- delegate methods. ----
52
53    @LayoutlibDelegate
54    /*package*/ static TypedArray obtainStyledAttributes(
55            Resources thisResources, Theme thisTheme,
56            int[] attrs) {
57        boolean changed = setupResources(thisTheme);
58        BridgeTypedArray ta = RenderSessionImpl.getCurrentContext().obtainStyledAttributes(attrs);
59        ta.setTheme(thisTheme);
60        restoreResources(changed);
61        return ta;
62    }
63
64    @LayoutlibDelegate
65    /*package*/ static TypedArray obtainStyledAttributes(
66            Resources thisResources, Theme thisTheme,
67            int resid, int[] attrs)
68            throws NotFoundException {
69        boolean changed = setupResources(thisTheme);
70        BridgeTypedArray ta = RenderSessionImpl.getCurrentContext().obtainStyledAttributes(resid,
71                attrs);
72        ta.setTheme(thisTheme);
73        restoreResources(changed);
74        return ta;
75    }
76
77    @LayoutlibDelegate
78    /*package*/ static TypedArray obtainStyledAttributes(
79            Resources thisResources, Theme thisTheme,
80            AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) {
81        boolean changed = setupResources(thisTheme);
82        BridgeTypedArray ta = RenderSessionImpl.getCurrentContext().obtainStyledAttributes(set,
83                attrs, defStyleAttr, defStyleRes);
84        ta.setTheme(thisTheme);
85        restoreResources(changed);
86        return ta;
87    }
88
89    @LayoutlibDelegate
90    /*package*/ static boolean resolveAttribute(
91            Resources thisResources, Theme thisTheme,
92            int resid, TypedValue outValue,
93            boolean resolveRefs) {
94        boolean changed = setupResources(thisTheme);
95        boolean found =  RenderSessionImpl.getCurrentContext().resolveThemeAttribute(resid,
96                outValue, resolveRefs);
97        restoreResources(changed);
98        return found;
99    }
100
101    @LayoutlibDelegate
102    /*package*/ static TypedArray resolveAttributes(Resources thisResources, Theme thisTheme,
103            int[] values, int[] attrs) {
104        // FIXME
105        return null;
106    }
107
108    // ---- private helper methods ----
109
110    private static boolean setupResources(Theme thisTheme) {
111        // Key is a space-separated list of theme ids applied that have been merged into the
112        // BridgeContext's theme to make thisTheme.
113        String[] appliedStyles = thisTheme.getKey().split(" ");
114        boolean changed = false;
115        for (String s : appliedStyles) {
116            if (s.isEmpty()) {
117                continue;
118            }
119            // See the definition of force parameter in Theme.applyStyle().
120            boolean force = false;
121            if (s.charAt(s.length() - 1) == '!') {
122                force = true;
123                s = s.substring(0, s.length() - 1);
124            }
125            int styleId = Integer.parseInt(s, 16);
126            StyleResourceValue style = resolveStyle(styleId);
127            if (style != null) {
128                RenderSessionImpl.getCurrentContext().getRenderResources().applyStyle(style, force);
129                changed = true;
130            }
131
132        }
133        return changed;
134    }
135
136    private static void restoreResources(boolean changed) {
137        if (changed) {
138            RenderSessionImpl.getCurrentContext().getRenderResources().clearStyles();
139        }
140    }
141
142    @Nullable
143    private static StyleResourceValue resolveStyle(int nativeResid) {
144        if (nativeResid == 0) {
145            return null;
146        }
147        BridgeContext context = RenderSessionImpl.getCurrentContext();
148        ResourceReference theme = context.resolveId(nativeResid);
149        if (theme.isFramework()) {
150            return (StyleResourceValue) context.getRenderResources()
151                    .getFrameworkResource(ResourceType.STYLE, theme.getName());
152        } else {
153            return (StyleResourceValue) context.getRenderResources()
154                    .getProjectResource(ResourceType.STYLE, theme.getName());
155        }
156    }
157}
158