1 /*
2  * Copyright 2018 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.media.update;
18 
19 import android.annotation.Nullable;
20 import android.content.Context;
21 import android.content.ContextWrapper;
22 import android.content.pm.ApplicationInfo;
23 import android.content.pm.PackageManager.NameNotFoundException;
24 import android.content.res.Resources;
25 import android.content.res.Resources.Theme;
26 import android.content.res.XmlResourceParser;
27 import android.support.annotation.GuardedBy;
28 import android.support.v4.widget.Space;
29 import android.support.v7.widget.ButtonBarLayout;
30 import android.util.AttributeSet;
31 import android.view.ContextThemeWrapper;
32 import android.view.LayoutInflater;
33 import android.view.View;
34 import android.view.ViewGroup;
35 
36 import com.android.support.mediarouter.app.MediaRouteButton;
37 import com.android.support.mediarouter.app.MediaRouteExpandCollapseButton;
38 import com.android.support.mediarouter.app.MediaRouteVolumeSlider;
39 import com.android.support.mediarouter.app.OverlayListView;
40 
41 public final class ApiHelper {
42     private static ApplicationInfo sUpdatableInfo;
43 
44     @GuardedBy("this")
45     private static Theme sLibTheme;
46 
ApiHelper()47     private ApiHelper() { }
48 
initialize(ApplicationInfo updatableInfo)49     static void initialize(ApplicationInfo updatableInfo) {
50         if (sUpdatableInfo != null) {
51             throw new IllegalStateException("initialize should only be called once");
52         }
53 
54         sUpdatableInfo = updatableInfo;
55     }
56 
getLibResources(Context context)57     public static Resources getLibResources(Context context) {
58         return getLibTheme(context).getResources();
59     }
60 
getLibTheme(Context context)61     public static Theme getLibTheme(Context context) {
62         if (sLibTheme != null) return sLibTheme;
63 
64         return getLibThemeSynchronized(context);
65     }
66 
getLibTheme(Context context, int themeId)67     public static Theme getLibTheme(Context context, int themeId) {
68         Theme theme = getLibResources(context).newTheme();
69         theme.applyStyle(themeId, true);
70         return theme;
71     }
72 
getLayoutInflater(Context context)73     public static LayoutInflater getLayoutInflater(Context context) {
74         return getLayoutInflater(context, null);
75     }
76 
getLayoutInflater(Context context, Theme theme)77     public static LayoutInflater getLayoutInflater(Context context, Theme theme) {
78         if (theme == null) {
79             theme = getLibTheme(context);
80         }
81 
82         // TODO (b/72975976): Avoid to use ContextThemeWrapper with app context and lib theme.
83         LayoutInflater layoutInflater = LayoutInflater.from(context).cloneInContext(
84                 new ContextThemeWrapper(context, theme));
85         layoutInflater.setFactory2(new LayoutInflater.Factory2() {
86             @Override
87             public View onCreateView(
88                     View parent, String name, Context context, AttributeSet attrs) {
89                 if (MediaRouteButton.class.getCanonicalName().equals(name)) {
90                     return new MediaRouteButton(context, attrs);
91                 } else if (MediaRouteVolumeSlider.class.getCanonicalName().equals(name)) {
92                     return new MediaRouteVolumeSlider(context, attrs);
93                 } else if (MediaRouteExpandCollapseButton.class.getCanonicalName().equals(name)) {
94                     return new MediaRouteExpandCollapseButton(context, attrs);
95                 } else if (OverlayListView.class.getCanonicalName().equals(name)) {
96                     return new OverlayListView(context, attrs);
97                 } else if (ButtonBarLayout.class.getCanonicalName().equals(name)) {
98                     return new ButtonBarLayout(context, attrs);
99                 } else if (Space.class.getCanonicalName().equals(name)) {
100                     return new Space(context, attrs);
101                 }
102                 return null;
103             }
104 
105             @Override
106             public View onCreateView(String name, Context context, AttributeSet attrs) {
107                 return onCreateView(null, name, context, attrs);
108             }
109         });
110         return layoutInflater;
111     }
112 
inflateLibLayout(Context context, int libResId)113     public static View inflateLibLayout(Context context, int libResId) {
114         return inflateLibLayout(context, getLibTheme(context), libResId, null, false);
115     }
116 
inflateLibLayout(Context context, Theme theme, int libResId)117     public static View inflateLibLayout(Context context, Theme theme, int libResId) {
118         return inflateLibLayout(context, theme, libResId, null, false);
119     }
120 
inflateLibLayout(Context context, Theme theme, int libResId, @Nullable ViewGroup root, boolean attachToRoot)121     public static View inflateLibLayout(Context context, Theme theme, int libResId,
122             @Nullable ViewGroup root, boolean attachToRoot) {
123         try (XmlResourceParser parser = getLibResources(context).getLayout(libResId)) {
124             return getLayoutInflater(context, theme).inflate(parser, root, attachToRoot);
125         }
126     }
127 
getLibThemeSynchronized(Context context)128     private static synchronized Theme getLibThemeSynchronized(Context context) {
129         if (sLibTheme != null) return sLibTheme;
130 
131         if (sUpdatableInfo == null) {
132             throw new IllegalStateException("initialize hasn't been called yet");
133         }
134 
135         try {
136             return sLibTheme = context.getPackageManager()
137                     .getResourcesForApplication(sUpdatableInfo).newTheme();
138         } catch (NameNotFoundException e) {
139             throw new RuntimeException(e);
140         }
141     }
142 }
143