/******************************************************************************* * Copyright 2012-present Pixate, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.pixate.freestyle; import java.lang.reflect.Field; import java.util.Map; import java.util.WeakHashMap; import android.app.Activity; import android.content.Context; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.LayoutInflater.Factory; import android.view.View; import com.pixate.freestyle.util.PXLog; import com.pixate.freestyle.util.ViewUtil; class WrappedInflaterFactory implements Factory { private static final String TAG = WrappedInflaterFactory.class.getSimpleName(); /* * (non-Javadoc) * @see <a href= * "http://github.com/android/platform_frameworks_policies_base/blob/c5d93b3b6a/phone/com/android/internal/policy/impl/PhoneLayoutInflater.java#L27" * > Android source for these prefixes. </a> */ private static final String[] sKnownClassPrefixes = new String[] { "android.widget.", "android.webkit.", "android.view." }; private static Field sFactorySetField = null; private Factory mOriginalFactory = null; private LayoutInflater mInflater = null; WrappedInflaterFactory(Activity activity) { mInflater = activity.getLayoutInflater(); mOriginalFactory = mInflater.getFactory(); } public View onCreateView(String name, Context context, AttributeSet attrs) { View view = null; if (mOriginalFactory != null) { view = mOriginalFactory.onCreateView(name, context, attrs); } else { try { if (-1 == name.indexOf(".")) { for (int i = 0; i < sKnownClassPrefixes.length && view == null; i++) { try { view = mInflater.createView(name, sKnownClassPrefixes[i], attrs); } catch (Exception e) { // We do null check on view later. } } } else { view = mInflater.createView(name, null, attrs); } } catch (Exception e) { return null; } } if (view != null) { ViewUtil.initView(view, attrs); } return view; } private static void initReflection() { try { sFactorySetField = LayoutInflater.class.getDeclaredField("mFactorySet"); sFactorySetField.setAccessible(true); } catch (Exception e) { PXLog.e(TAG, e, "Unable to get LayoutInflater's mFactorySet field."); } } private static Map<Object, Boolean> activitiesWithWrappedInflater = new WeakHashMap<Object, Boolean>(); static void applyTo(Activity activity) { if (activitiesWithWrappedInflater.containsKey(activity)) { // Already done this, so exit. return; } WrappedInflaterFactory newFactory = new WrappedInflaterFactory(activity); if (newFactory.mOriginalFactory != null) { /* * Need reflection because we need to change the mFactorySet boolean * field inside LayoutInflater. It'll be true if a factory is * already there, and if we call .setFactory() again we'll get an * exception indicating that a factory has already been set. So we * need to change the field's value to false first before passing * our special Factory to setFactory(); */ if (sFactorySetField == null) { initReflection(); if (sFactorySetField == null) { PXLog.w(TAG, "Unable to replace the LayoutInflater's Factory. " + "This view will likely not be styled with CSS."); return; } } try { sFactorySetField.setBoolean(activity.getLayoutInflater(), false); } catch (Exception e) { PXLog.e(TAG, e, "Unable to replace the LayoutInflater's Factory. " + "This view will likely not be styled with CSS."); return; } } activity.getLayoutInflater().setFactory(newFactory); activitiesWithWrappedInflater.put(activity, Boolean.TRUE); } }