Skip to content

Commit 33cad2d

Browse files
committed
新增试验性feature
1 parent afeba53 commit 33cad2d

File tree

5 files changed

+352
-0
lines changed

5 files changed

+352
-0
lines changed

PluginCore/src/com/plugin/core/PluginInstrumentionWrapper.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import android.os.IBinder;
1313
import android.os.UserHandle;
1414

15+
import com.plugin.core.viewfactory.PluginViewFactory;
1516
import com.plugin.util.LogUtil;
1617
import com.plugin.util.RefInvoker;
1718

@@ -113,6 +114,11 @@ public void callActivityOnCreate(Activity activity, Bundle icicle) {
113114
intent.setExtrasClassLoader(activity.getClassLoader());
114115
}
115116

117+
//TODO 试验性功能,暂时关闭
118+
if (false) {
119+
new PluginViewFactory(activity, activity.getWindow()).installViewFactory();
120+
}
121+
116122
super.callActivityOnCreate(activity, icicle);
117123
}
118124

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package com.plugin.core.viewfactory;
2+
3+
import android.annotation.TargetApi;
4+
import android.content.Context;
5+
import android.os.Build;
6+
import android.util.AttributeSet;
7+
import android.util.Log;
8+
import android.view.LayoutInflater;
9+
import android.view.View;
10+
11+
import java.lang.reflect.Field;
12+
13+
/**
14+
* Created by Administrator on 2015/12/13.
15+
*
16+
* Porting From SupportV7
17+
*/
18+
public class PluginFactoryCompat {
19+
private static final String TAG = "FactoryCompat";
20+
21+
private static Field sLayoutInflaterFactory2Field;
22+
private static boolean sCheckedField;
23+
24+
@TargetApi(11)
25+
static class FactoryWrapper implements LayoutInflater.Factory, LayoutInflater.Factory2 {
26+
final PluginFactoryInterface mDelegateFactory;
27+
28+
FactoryWrapper(PluginFactoryInterface delegateFactory) {
29+
mDelegateFactory = delegateFactory;
30+
}
31+
32+
@Override
33+
public View onCreateView(String name, Context context, AttributeSet attrs) {
34+
return mDelegateFactory.onCreateView(null, name, context, attrs);
35+
}
36+
37+
@Override
38+
public View onCreateView(View parent, String name, Context context,
39+
AttributeSet attributeSet) {
40+
return mDelegateFactory.onCreateView(parent, name, context, attributeSet);
41+
}
42+
}
43+
44+
@TargetApi(11)
45+
static void setFactory(LayoutInflater inflater, PluginFactoryInterface factory) {
46+
final LayoutInflater.Factory2 factory2 = factory != null
47+
? new FactoryWrapper(factory) : null;
48+
inflater.setFactory2(factory2);
49+
50+
if (Build.VERSION.SDK_INT < 21) {
51+
final LayoutInflater.Factory f = inflater.getFactory();
52+
if (f instanceof LayoutInflater.Factory2) {
53+
// The merged factory is now set to getFactory(), but not getFactory2() (pre-v21).
54+
// We will now try and force set the merged factory to mFactory2
55+
forceSetFactory2(inflater, (LayoutInflater.Factory2) f);
56+
} else {
57+
// Else, we will force set the original wrapped Factory2
58+
forceSetFactory2(inflater, factory2);
59+
}
60+
}
61+
}
62+
63+
/**
64+
* For APIs >= 11 && < 21, there was a framework bug that prevented a LayoutInflater's
65+
* Factory2 from being merged properly if set after a cloneInContext from a LayoutInflater
66+
* that already had a Factory2 registered. We work around that bug here. If we can't we
67+
* log an error.
68+
*/
69+
static void forceSetFactory2(LayoutInflater inflater, LayoutInflater.Factory2 factory) {
70+
if (!sCheckedField) {
71+
try {
72+
sLayoutInflaterFactory2Field = LayoutInflater.class.getDeclaredField("mFactory2");
73+
sLayoutInflaterFactory2Field.setAccessible(true);
74+
} catch (NoSuchFieldException e) {
75+
Log.e(TAG, "forceSetFactory2 Could not find field 'mFactory2' on class "
76+
+ LayoutInflater.class.getName()
77+
+ "; inflation may have unexpected results.", e);
78+
}
79+
sCheckedField = true;
80+
}
81+
if (sLayoutInflaterFactory2Field != null) {
82+
try {
83+
sLayoutInflaterFactory2Field.set(inflater, factory);
84+
} catch (IllegalAccessException e) {
85+
Log.e(TAG, "forceSetFactory2 could not set the Factory2 on LayoutInflater "
86+
+ inflater + "; inflation may have unexpected results.", e);
87+
}
88+
}
89+
}
90+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.plugin.core.viewfactory;
2+
3+
import android.content.Context;
4+
import android.util.AttributeSet;
5+
import android.view.View;
6+
7+
/**
8+
* Created by Administrator on 2015/12/13.
9+
*
10+
* Porting From SupportV7
11+
*/
12+
public interface PluginFactoryInterface {
13+
14+
/**
15+
* Hook you can supply that is called when inflating from a LayoutInflater.
16+
* You can use this to customize the tag names available in your XML
17+
* layout files.
18+
*
19+
* @param parent The parent that the created view will be placed
20+
* in; <em>note that this may be null</em>.
21+
* @param name Tag name to be inflated.
22+
* @param context The context the view is being created in.
23+
* @param attrs Inflation attributes as specified in XML file.
24+
*
25+
* @return View Newly created view. Return null for the default
26+
* behavior.
27+
*/
28+
public View onCreateView(View parent, String name, Context context, AttributeSet attrs);
29+
30+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package com.plugin.core.viewfactory;
2+
3+
import android.annotation.TargetApi;
4+
import android.content.Context;
5+
import android.os.Build;
6+
import android.util.AttributeSet;
7+
import android.view.LayoutInflater;
8+
import android.view.View;
9+
import android.view.Window;
10+
11+
import com.plugin.util.LogUtil;
12+
13+
/**
14+
* Created by Administrator on 2015/12/13.
15+
*
16+
* Porting From SupportV7
17+
*/
18+
public class PluginViewFactory implements PluginFactoryInterface {
19+
20+
final Context mContext;
21+
final Window mWindow;
22+
final Window.Callback mOriginalWindowCallback;
23+
24+
PluginViewInflater mPluginViewInflater;
25+
26+
public PluginViewFactory(Context context, Window window) {
27+
mContext = context;
28+
mWindow = window;
29+
mOriginalWindowCallback = window.getCallback();
30+
}
31+
32+
public void installViewFactory() {
33+
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
34+
if (layoutInflater.getFactory() == null) {
35+
PluginFactoryCompat.setFactory(layoutInflater, this);
36+
} else {
37+
LogUtil.d("The Activity's LayoutInflater already has a Factory installed"
38+
+ " so we can not install plugin's");
39+
}
40+
}
41+
42+
@Override
43+
public final View onCreateView(View parent, String name,
44+
Context context, AttributeSet attrs) {
45+
// First let the Activity's Factory try and inflate the view
46+
final View view = callActivityOnCreateView(parent, name, context, attrs);
47+
if (view != null) {
48+
return view;
49+
}
50+
51+
// If the Factory didn't handle it, let our createView() method try
52+
return createView(parent, name, context, attrs);
53+
}
54+
55+
@TargetApi(11)
56+
private View callActivityOnCreateView(View parent, String name, Context context, AttributeSet attrs) {
57+
View view = null;
58+
if (mOriginalWindowCallback instanceof LayoutInflater.Factory) {
59+
view = ((LayoutInflater.Factory) mOriginalWindowCallback)
60+
.onCreateView(name, context, attrs);
61+
}
62+
63+
if (view != null) {
64+
return view;
65+
}
66+
67+
if (mOriginalWindowCallback instanceof LayoutInflater.Factory2) {
68+
return ((LayoutInflater.Factory2) mOriginalWindowCallback)
69+
.onCreateView(parent, name, context, attrs);
70+
}
71+
72+
return null;
73+
}
74+
75+
private View createView(View parent, final String name, Context context,
76+
AttributeSet attrs) {
77+
final boolean isPre21 = Build.VERSION.SDK_INT < 21;
78+
79+
if (mPluginViewInflater == null) {
80+
mPluginViewInflater = new PluginViewInflater(mContext);
81+
}
82+
83+
// We only want the View to inherit it's context from the parent if it is from the
84+
// apps content, and not part of our sub-decor
85+
final boolean inheritContext = isPre21 && parent != null
86+
&& parent.getId() != android.R.id.content;
87+
88+
return mPluginViewInflater.createView(parent, name, context, attrs,
89+
inheritContext, isPre21);
90+
}
91+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package com.plugin.core.viewfactory;
2+
3+
import android.content.Context;
4+
import android.content.res.TypedArray;
5+
import android.util.AttributeSet;
6+
import android.util.Log;
7+
import android.view.ContextThemeWrapper;
8+
import android.view.InflateException;
9+
import android.view.View;
10+
11+
import com.plugin.util.LogUtil;
12+
13+
import java.lang.reflect.Constructor;
14+
import java.util.HashMap;
15+
import java.util.Map;
16+
17+
/**
18+
* Created by Administrator on 2015/12/13.
19+
*
20+
* Porting From SupportV7
21+
*/
22+
public class PluginViewInflater {
23+
24+
static final Class<?>[] sConstructorSignature = new Class[] {
25+
Context.class, AttributeSet.class};
26+
27+
private static final Map<String, Constructor<? extends View>> sConstructorMap = new HashMap<>();
28+
29+
private final Context mContext;
30+
private final Object[] mConstructorArgs = new Object[2];
31+
32+
public PluginViewInflater(Context context) {
33+
mContext = context;
34+
}
35+
36+
public final View createView(View parent, final String name, Context context,
37+
AttributeSet attrs, boolean inheritContext, boolean themeContext) {
38+
final Context originalContext = context;
39+
40+
// We can emulate Lollipop's android:theme attribute propagating down the view hierarchy
41+
// by using the parent's context
42+
if (inheritContext && parent != null) {
43+
context = parent.getContext();
44+
}
45+
if (themeContext) {
46+
// We then apply the theme on the context, if specified
47+
context = themifyContext(context, attrs, true, true);
48+
}
49+
50+
// We need to 'inject' our tint aware Views in place of the standard framework versions
51+
View view = injectView(name, context, attrs);
52+
if (view != null) {
53+
return view;
54+
}
55+
56+
if (originalContext != context) {
57+
// If the original context does not equal our themed context, then we need to manually
58+
// inflate it using the name so that app:theme takes effect.
59+
return createViewFromTag(context, name, attrs);
60+
}
61+
62+
return null;
63+
}
64+
65+
private View injectView(String name, Context context, AttributeSet attrs) {
66+
//TODO
67+
LogUtil.d("TODO");
68+
return null;
69+
};
70+
71+
private View createViewFromTag(Context context, String name, AttributeSet attrs) {
72+
if (name.equals("view")) {
73+
name = attrs.getAttributeValue(null, "class");
74+
}
75+
76+
try {
77+
mConstructorArgs[0] = context;
78+
mConstructorArgs[1] = attrs;
79+
80+
if (-1 == name.indexOf('.')) {
81+
// try the android.widget prefix first...
82+
return createView(name, "android.widget.");
83+
} else {
84+
return createView(name, null);
85+
}
86+
} catch (Exception e) {
87+
// We do not want to catch these, lets return null and let the actual LayoutInflater
88+
// try
89+
return null;
90+
} finally {
91+
// Don't retain static reference on context.
92+
mConstructorArgs[0] = null;
93+
mConstructorArgs[1] = null;
94+
}
95+
}
96+
97+
private View createView(String name, String prefix)
98+
throws ClassNotFoundException, InflateException {
99+
Constructor<? extends View> constructor = sConstructorMap.get(name);
100+
101+
try {
102+
if (constructor == null) {
103+
// Class not found in the cache, see if it's real, and try to add it
104+
Class<? extends View> clazz = getClassLoader(mContext, name, prefix).loadClass(
105+
prefix != null ? (prefix + name) : name).asSubclass(View.class);
106+
107+
constructor = clazz.getConstructor(sConstructorSignature);
108+
sConstructorMap.put(name, constructor);
109+
}
110+
constructor.setAccessible(true);
111+
return constructor.newInstance(mConstructorArgs);
112+
} catch (Exception e) {
113+
// We do not want to catch these, lets return null and let the actual LayoutInflater
114+
// try
115+
return null;
116+
}
117+
}
118+
119+
private ClassLoader getClassLoader(Context context, String name, String prefix) {
120+
//TODO
121+
LogUtil.d("TODO");
122+
return context.getClassLoader();
123+
}
124+
125+
/**
126+
* Allows us to emulate the {@code android:theme} attribute for devices before L.
127+
*/
128+
public static Context themifyContext(Context context, AttributeSet attrs,
129+
boolean useAndroidTheme, boolean useAppTheme) {
130+
//wrap it in a new wrapper
131+
//context = new ContextThemeWrapper(context, themeId);
132+
return context;
133+
}
134+
135+
}

0 commit comments

Comments
 (0)