package org.robobinding;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.LayoutInflater.Factory;
import android.view.LayoutInflater.Factory2;
import android.view.View;
/**
* @since 1.0
* @author Cheng Wei
*
*/
public class LayoutInflaterHoneyComb extends LayoutInflaterPreHoneyComb {
LayoutInflaterHoneyComb() {}
public static LayoutInflater create(LayoutInflater original, ViewCreationListener listener,
ViewNameResolver viewNameResolver) {
LayoutInflater newLayoutInflater = clone(original);
Factory factory = wrapFactory(original, listener);
if (factory != null) {
newLayoutInflater.setFactory(factory);
}
Factory2 factory2 = wrapFactory2(original.getFactory2(), listener);
if(factory2 != null) {
newLayoutInflater.setFactory2(factory2);
forceSetFactory2IfRequired(newLayoutInflater);
}
Factory2 privateFactory2 = wrapPrivateFactory2(original, viewNameResolver, listener, newLayoutInflater);
setPrivateFactory2(newLayoutInflater, privateFactory2);
return newLayoutInflater;
}
/**
* A fix to android.view.LayoutInflater.setFactory2 bug in early versions
* - https://code.google.com/p/android/issues/detail?id=73779
*/
private static void forceSetFactory2IfRequired(LayoutInflater layoutInflater) {
if(layoutInflater.getFactory() != layoutInflater.getFactory2()) {
ReflectionUtils.setField(layoutInflater, "mFactory2",
Factory2.class, (Factory2)layoutInflater.getFactory());
}
}
/**
* Fix for proguard warning.
*/
private static void setPrivateFactory2(LayoutInflater layoutInflater, Factory2 factory2) {
ReflectionUtils.setField(layoutInflater, "mPrivateFactory", Factory2.class, factory2);
}
private static Factory wrapFactory(LayoutInflater original, ViewCreationListener listener) {
Factory factory = original.getFactory();
if((original.getFactory2() != null) || (factory == null)) {
return null;
} else {
return new WithViewCreatedNotification(factory, listener);
}
}
private static Factory2 wrapFactory2(Factory2 factory2, ViewCreationListener listener) {
if(factory2 == null) {
return null;
} else {
invokeCompatEnsureSubDecorPre5_0(factory2);
return new WithViewCreatedNotification2(factory2, listener);
}
}
private static void invokeCompatEnsureSubDecorPre5_0(Factory2 factory2) {
if (Build.VERSION.SDK_INT < Build_VERSION_CODES_LOLLIPOP) {
Object mDelegateFactory = ReflectionUtils.tryToGetCompatibleField(factory2, "mDelegateFactory", Object.class);
if(mDelegateFactory != null) {
ReflectionUtils.tryToInvokeMethod(mDelegateFactory, "ensureSubDecor", new Object[0]);
}
}
}
private static class MConstructorArgs2Sync {
private LayoutInflater original;
private LayoutInflater newLayoutInflater;
public MConstructorArgs2Sync(LayoutInflater original, LayoutInflater newLayoutInflater) {
this.original = original;
this.newLayoutInflater = newLayoutInflater;
}
/**
* Sync mConstructorArgs[1], as it is weirdly assigned from some Class under same package.
*/
public void sync() {
mConstructorArgsOf(newLayoutInflater)[1] = mConstructorArgsOf(original)[1];
}
}
private static Object[] mConstructorArgsOf(LayoutInflater layoutInflater) {
return ReflectionUtils.getField(layoutInflater, "mConstructorArgs", Object[].class);
}
private static int Build_VERSION_CODES_LOLLIPOP = 21;
private static Factory2 wrapPrivateFactory2(LayoutInflater original, ViewNameResolver viewNameResolver,
ViewCreationListener listener, LayoutInflater newLayoutInflater) {
Factory2 privateFactory = getPrivateFactory(original);
Factory2 factory2 = (privateFactory != null) ? privateFactory : EMPTY_FACTORY2;
MConstructorArgs2Sync mConstructorArgs2Sync = new MConstructorArgs2Sync(original, newLayoutInflater);
if (Build.VERSION.SDK_INT >= Build_VERSION_CODES_LOLLIPOP) {
return new WithViewCreatedNotification2(
new WithViewCreationIfNull5_0(factory2, viewNameResolver, newLayoutInflater, mConstructorArgs2Sync),
listener);
} else {
return new WithViewCreatedNotification2(
new WithViewCreationIfNullPre5_0(factory2, viewNameResolver, newLayoutInflater, mConstructorArgs2Sync),
listener);
}
}
/*================Start:Workaround code for LayoutInflater===================*/
private static Factory2 getPrivateFactory(LayoutInflater layoutInflater) {
return ReflectionUtils.getField(layoutInflater, "mPrivateFactory", LayoutInflater.Factory2.class);
}
/*================End:Workaround code for LayoutInflater=====================*/
private static class WithViewCreatedNotification2 extends WithViewCreatedNotification implements Factory2 {
private final Factory2 delegate;
public WithViewCreatedNotification2(Factory2 delegate, ViewCreationListener listener) {
super(delegate, listener);
this.delegate = delegate;
}
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
View view = delegate.onCreateView(parent, name, context, attrs);
notifyViewCreatedIfNotNull(view, attrs);
return view;
}
}
private static class WithViewCreationIfNull5_0 extends WithViewCreationIfNull implements Factory2 {
private final Factory2 delegate;
private final ViewNameResolver viewNameResolver;
private final LayoutInflater layoutInflater;
private final MConstructorArgs2Sync mConstructorArgs2Sync;
public WithViewCreationIfNull5_0(Factory2 delegate, ViewNameResolver viewNameResolver,
LayoutInflater layoutInflater, MConstructorArgs2Sync mConstructorArgs2Sync) {
super(delegate, viewNameResolver, layoutInflater);
this.delegate = delegate;
this.viewNameResolver = viewNameResolver;
this.layoutInflater = layoutInflater;
this.mConstructorArgs2Sync = mConstructorArgs2Sync;
}
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
View view = delegate.onCreateView(parent, name, context, attrs);
return createViewIfNull(view, name, context, attrs);
}
private View createViewIfNull(View viewOrNull, String name, Context context, AttributeSet attrs) {
if(viewOrNull != null) return viewOrNull;
String viewFullName = viewNameResolver.getViewNameFromLayoutTag(name);
mConstructorArgs2Sync.sync();
final Object lastContext = mConstructorArgsOf(layoutInflater)[0];
mConstructorArgsOf(layoutInflater)[0] = context;
try {
return layoutInflater.createView(viewFullName, null, attrs);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} finally {
mConstructorArgsOf(layoutInflater)[0] = lastContext;
}
}
}
private static class WithViewCreationIfNullPre5_0 extends WithViewCreationIfNull implements Factory2 {
private final Factory2 delegate;
private final ViewNameResolver viewNameResolver;
private final LayoutInflater layoutInflater;
private final MConstructorArgs2Sync mConstructorArgs2Sync;
public WithViewCreationIfNullPre5_0(Factory2 delegate, ViewNameResolver viewNameResolver,
LayoutInflater layoutInflater, MConstructorArgs2Sync mConstructorArgs2Sync) {
super(delegate, viewNameResolver, layoutInflater);
this.delegate = delegate;
this.viewNameResolver = viewNameResolver;
this.layoutInflater = layoutInflater;
this.mConstructorArgs2Sync = mConstructorArgs2Sync;
}
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
View view = delegate.onCreateView(parent, name, context, attrs);
return createViewIfNull(view, name, context, attrs);
}
private View createViewIfNull(View viewOrNull, String name, Context context, AttributeSet attrs) {
if(viewOrNull != null) return viewOrNull;
String viewFullName = viewNameResolver.getViewNameFromLayoutTag(name);
mConstructorArgs2Sync.sync();
try {
return layoutInflater.createView(viewFullName, null, attrs);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
private static Factory2 EMPTY_FACTORY2 = new Factory2() {
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
return null;
}
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
return null;
}
};
}