diff --git a/CalligraphySample/build.gradle b/CalligraphySample/build.gradle index 3e10d6d..71327f4 100644 --- a/CalligraphySample/build.gradle +++ b/CalligraphySample/build.gradle @@ -5,7 +5,7 @@ android { buildToolsVersion "24.0.0" defaultConfig { - minSdkVersion 8 + minSdkVersion 14 targetSdkVersion 24 versionCode project.ext.versionCodeInt versionName version @@ -24,6 +24,7 @@ android { dependencies { compile project(':calligraphy') + compile 'io.github.inflationx:viewpump:0.1.0-SNAPSHOT' compile 'com.android.support:support-v4:24.0.0' compile 'com.android.support:appcompat-v7:24.0.0' diff --git a/CalligraphySample/src/main/java/uk/co/chrisjenx/calligraphy/sample/CalligraphyApplication.java b/CalligraphySample/src/main/java/uk/co/chrisjenx/calligraphy/sample/CalligraphyApplication.java index 826a408..2622d94 100644 --- a/CalligraphySample/src/main/java/uk/co/chrisjenx/calligraphy/sample/CalligraphyApplication.java +++ b/CalligraphySample/src/main/java/uk/co/chrisjenx/calligraphy/sample/CalligraphyApplication.java @@ -2,7 +2,9 @@ import android.app.Application; -import uk.co.chrisjenx.calligraphy.CalligraphyConfig; +import io.github.inflationx.viewpump.ViewPump; +import uk.co.chrisjenx.calligraphy3.CalligraphyConfig; +import uk.co.chrisjenx.calligraphy3.CalligraphyInterceptor; /** * Created by chris on 06/05/2014. @@ -13,12 +15,14 @@ public class CalligraphyApplication extends Application { @Override public void onCreate() { super.onCreate(); - CalligraphyConfig.initDefault(new CalligraphyConfig.Builder() - .setDefaultFontPath("fonts/Roboto-ThinItalic.ttf") - .setFontAttrId(R.attr.fontPath) - .addCustomViewWithSetTypeface(CustomViewWithTypefaceSupport.class) - .addCustomStyle(TextField.class, R.attr.textFieldStyle) - .build() - ); + ViewPump.init(ViewPump.builder() + .addInterceptor(new CalligraphyInterceptor( + new CalligraphyConfig.Builder() + .setDefaultFontPath("fonts/Roboto-ThinItalic.ttf") + .setFontAttrId(R.attr.fontPath) + .addCustomViewWithSetTypeface(CustomViewWithTypefaceSupport.class) + .addCustomStyle(TextField.class, R.attr.textFieldStyle) + .build())) + .build()); } } diff --git a/CalligraphySample/src/main/java/uk/co/chrisjenx/calligraphy/sample/MainActivity.java b/CalligraphySample/src/main/java/uk/co/chrisjenx/calligraphy/sample/MainActivity.java index 7131d30..ec99c5d 100644 --- a/CalligraphySample/src/main/java/uk/co/chrisjenx/calligraphy/sample/MainActivity.java +++ b/CalligraphySample/src/main/java/uk/co/chrisjenx/calligraphy/sample/MainActivity.java @@ -7,7 +7,7 @@ import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; -import uk.co.chrisjenx.calligraphy.CalligraphyContextWrapper; +import io.github.inflationx.viewpump.ViewPumpContextWrapper; import static butterknife.ButterKnife.findById; @@ -52,17 +52,17 @@ public void run() { } /* - Uncomment if you disable PrivateFactory injection. See CalligraphyConfig#disablePrivateFactoryInjection() + Uncomment if you disable PrivateFactory injection. See ViewPumpConfig#setPrivateFactoryInjectionEnabled(boolean) */ // @Override // @TargetApi(Build.VERSION_CODES.HONEYCOMB) // public View onCreateView(View parent, String name, @NonNull Context context, @NonNull AttributeSet attrs) { -// return CalligraphyContextWrapper.onActivityCreateView(this, parent, super.onCreateView(parent, name, context, attrs), name, context, attrs); +// return ViewPumpContextWrapper.onActivityCreateView(this, parent, super.onCreateView(parent, name, context, attrs), name, context, attrs); // } @Override protected void attachBaseContext(Context newBase) { - super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase)); + super.attachBaseContext(ViewPumpContextWrapper.wrap(newBase)); } } diff --git a/build.gradle b/build.gradle index 8009b4c..5f70a94 100644 --- a/build.gradle +++ b/build.gradle @@ -10,6 +10,7 @@ buildscript { allprojects { repositories { + mavenLocal() jcenter() } // Is Release Build? diff --git a/calligraphy/build.gradle b/calligraphy/build.gradle index 0180f5d..7e2de70 100644 --- a/calligraphy/build.gradle +++ b/calligraphy/build.gradle @@ -5,7 +5,7 @@ android { buildToolsVersion "24.0.0" defaultConfig { - minSdkVersion 7 + minSdkVersion 14 targetSdkVersion 24 versionCode project.ext.versionCodeInt versionName version @@ -21,6 +21,7 @@ android { } dependencies { + compile 'io.github.inflationx:viewpump:0.1.1-SNAPSHOT' compile 'com.android.support:appcompat-v7:24.0.0' } diff --git a/calligraphy/consumer-proguard-rules.txt b/calligraphy/consumer-proguard-rules.txt index 10691cb..5c6256a 100644 --- a/calligraphy/consumer-proguard-rules.txt +++ b/calligraphy/consumer-proguard-rules.txt @@ -16,6 +16,6 @@ # public *; #} --keep class uk.co.chrisjenx.calligraphy.* { *; } --keep class uk.co.chrisjenx.calligraphy.*$* { *; } +-keep class uk.co.chrisjenx.calligraphy3.* { *; } +-keep class uk.co.chrisjenx.calligraphy3.*$* { *; } diff --git a/calligraphy/gradle.properties b/calligraphy/gradle.properties index 9e72cf6..577ffbd 100644 --- a/calligraphy/gradle.properties +++ b/calligraphy/gradle.properties @@ -2,5 +2,5 @@ # See parent properties for global properties POM_NAME=Calligraphy -POM_ARTIFACT_ID=calligraphy +POM_ARTIFACT_ID=calligraphy3 POM_PACKAGING=aar \ No newline at end of file diff --git a/calligraphy/src/main/AndroidManifest.xml b/calligraphy/src/main/AndroidManifest.xml index 921769e..4773b40 100644 --- a/calligraphy/src/main/AndroidManifest.xml +++ b/calligraphy/src/main/AndroidManifest.xml @@ -1,4 +1,4 @@ + package="uk.co.chrisjenx.calligraphy3"> diff --git a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyActivityFactory.java b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyActivityFactory.java deleted file mode 100644 index e0214eb..0000000 --- a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyActivityFactory.java +++ /dev/null @@ -1,34 +0,0 @@ -package uk.co.chrisjenx.calligraphy; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; - -/** - * Created by chris on 09/11/14. - * For Calligraphy. - */ -interface CalligraphyActivityFactory { - - /** - * Used to Wrap the Activity onCreateView method. - * - * You implement this method like so in you base activity. - *
-     * {@code
-     * public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
-     *   return CalligraphyContextWrapper.get(getBaseContext()).onActivityCreateView(super.onCreateView(parent, name, context, attrs), attrs);
-     * }
-     * }
-     * 
- * - * @param parent parent view, can be null. - * @param view result of {@code super.onCreateView(parent, name, context, attrs)}, this might be null, which is fine. - * @param name Name of View we are trying to inflate - * @param context current context (normally the Activity's) - * @param attrs see {@link android.view.LayoutInflater.Factory2#onCreateView(android.view.View, String, android.content.Context, android.util.AttributeSet)} @return the result from the activities {@code onCreateView()} - * @return The view passed in, or null if nothing was passed in. - * @see android.view.LayoutInflater.Factory2 - */ - View onActivityCreateView(View parent, View view, String name, Context context, AttributeSet attrs); -} diff --git a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyContextWrapper.java b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyContextWrapper.java deleted file mode 100644 index 8650208..0000000 --- a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyContextWrapper.java +++ /dev/null @@ -1,118 +0,0 @@ -package uk.co.chrisjenx.calligraphy; - -import android.app.Activity; -import android.content.Context; -import android.content.ContextWrapper; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.View; - -/** - * Created by chris on 19/12/2013 - * Project: Calligraphy - */ -public class CalligraphyContextWrapper extends ContextWrapper { - - private CalligraphyLayoutInflater mInflater; - - private final int mAttributeId; - - /** - * Uses the default configuration from {@link uk.co.chrisjenx.calligraphy.CalligraphyConfig} - * - * Remember if you are defining default in the - * {@link uk.co.chrisjenx.calligraphy.CalligraphyConfig} make sure this is initialised before - * the activity is created. - * - * @param base ContextBase to Wrap. - * @return ContextWrapper to pass back to the activity. - */ - public static ContextWrapper wrap(Context base) { - return new CalligraphyContextWrapper(base); - } - - /** - * You only need to call this IF you call - * {@link uk.co.chrisjenx.calligraphy.CalligraphyConfig.Builder#disablePrivateFactoryInjection()} - * This will need to be called from the - * {@link android.app.Activity#onCreateView(android.view.View, String, android.content.Context, android.util.AttributeSet)} - * method to enable view font injection if the view is created inside the activity onCreateView. - * - * You would implement this method like so in you base activity. - *
-     * {@code
-     * public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
-     *   return CalligraphyContextWrapper.onActivityCreateView(this, parent, super.onCreateView(parent, name, context, attrs), name, context, attrs);
-     * }
-     * }
-     * 
- * - * @param activity The activity the original that the ContextWrapper was attached too. - * @param parent Parent view from onCreateView - * @param view The View Created inside onCreateView or from super.onCreateView - * @param name The View name from onCreateView - * @param context The context from onCreateView - * @param attr The AttributeSet from onCreateView - * @return The same view passed in, or null if null passed in. - */ - public static View onActivityCreateView(Activity activity, View parent, View view, String name, Context context, AttributeSet attr) { - return get(activity).onActivityCreateView(parent, view, name, context, attr); - } - - /** - * Get the Calligraphy Activity Fragment Instance to allow callbacks for when views are created. - * - * @param activity The activity the original that the ContextWrapper was attached too. - * @return Interface allowing you to call onActivityViewCreated - */ - static CalligraphyActivityFactory get(Activity activity) { - if (!(activity.getLayoutInflater() instanceof CalligraphyLayoutInflater)) { - throw new RuntimeException("This activity does not wrap the Base Context! See CalligraphyContextWrapper.wrap(Context)"); - } - return (CalligraphyActivityFactory) activity.getLayoutInflater(); - } - - /** - * Uses the default configuration from {@link uk.co.chrisjenx.calligraphy.CalligraphyConfig} - * - * Remember if you are defining default in the - * {@link uk.co.chrisjenx.calligraphy.CalligraphyConfig} make sure this is initialised before - * the activity is created. - * - * @param base ContextBase to Wrap - */ - CalligraphyContextWrapper(Context base) { - super(base); - mAttributeId = CalligraphyConfig.get().getAttrId(); - } - - /** - * Override the default AttributeId, this will always take the custom attribute defined here - * and ignore the one set in {@link uk.co.chrisjenx.calligraphy.CalligraphyConfig}. - * - * Remember if you are defining default in the - * {@link uk.co.chrisjenx.calligraphy.CalligraphyConfig} make sure this is initialised before - * the activity is created. - * - * @param base ContextBase to Wrap - * @param attributeId Attribute to lookup. - * @deprecated use {@link #wrap(android.content.Context)} - */ - @Deprecated - public CalligraphyContextWrapper(Context base, int attributeId) { - super(base); - mAttributeId = attributeId; - } - - @Override - public Object getSystemService(String name) { - if (LAYOUT_INFLATER_SERVICE.equals(name)) { - if (mInflater == null) { - mInflater = new CalligraphyLayoutInflater(LayoutInflater.from(getBaseContext()), this, mAttributeId, false); - } - return mInflater; - } - return super.getSystemService(name); - } - -} diff --git a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyLayoutInflater.java b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyLayoutInflater.java deleted file mode 100644 index 6f9cfc1..0000000 --- a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyLayoutInflater.java +++ /dev/null @@ -1,311 +0,0 @@ -package uk.co.chrisjenx.calligraphy; - -import android.annotation.TargetApi; -import android.content.Context; -import android.os.Build; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import org.xmlpull.v1.XmlPullParser; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; - -/** - * Created by chris on 19/12/2013 - * Project: Calligraphy - */ -class CalligraphyLayoutInflater extends LayoutInflater implements CalligraphyActivityFactory { - - private static final String[] sClassPrefixList = { - "android.widget.", - "android.webkit." - }; - - private final int mAttributeId; - private final CalligraphyFactory mCalligraphyFactory; - // Reflection Hax - private boolean mSetPrivateFactory = false; - private Field mConstructorArgs = null; - - protected CalligraphyLayoutInflater(Context context, int attributeId) { - super(context); - mAttributeId = attributeId; - mCalligraphyFactory = new CalligraphyFactory(attributeId); - setUpLayoutFactories(false); - } - - protected CalligraphyLayoutInflater(LayoutInflater original, Context newContext, int attributeId, final boolean cloned) { - super(original, newContext); - mAttributeId = attributeId; - mCalligraphyFactory = new CalligraphyFactory(attributeId); - setUpLayoutFactories(cloned); - } - - @Override - public LayoutInflater cloneInContext(Context newContext) { - return new CalligraphyLayoutInflater(this, newContext, mAttributeId, true); - } - - // === - // Wrapping goodies - // === - - - @Override - public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) { - setPrivateFactoryInternal(); - return super.inflate(parser, root, attachToRoot); - } - - /** - * We don't want to unnecessary create/set our factories if there are none there. We try to be - * as lazy as possible. - */ - private void setUpLayoutFactories(boolean cloned) { - if (cloned) return; - // If we are HC+ we get and set Factory2 otherwise we just wrap Factory1 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - if (getFactory2() != null && !(getFactory2() instanceof WrapperFactory2)) { - // Sets both Factory/Factory2 - setFactory2(getFactory2()); - } - } - // We can do this as setFactory2 is used for both methods. - if (getFactory() != null && !(getFactory() instanceof WrapperFactory)) { - setFactory(getFactory()); - } - } - - @Override - public void setFactory(Factory factory) { - // Only set our factory and wrap calls to the Factory trying to be set! - if (!(factory instanceof WrapperFactory)) { - super.setFactory(new WrapperFactory(factory, this, mCalligraphyFactory)); - } else { - super.setFactory(factory); - } - } - - @Override - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - public void setFactory2(Factory2 factory2) { - // Only set our factory and wrap calls to the Factory2 trying to be set! - if (!(factory2 instanceof WrapperFactory2)) { -// LayoutInflaterCompat.setFactory(this, new WrapperFactory2(factory2, mCalligraphyFactory)); - super.setFactory2(new WrapperFactory2(factory2, mCalligraphyFactory)); - } else { - super.setFactory2(factory2); - } - } - - private void setPrivateFactoryInternal() { - // Already tried to set the factory. - if (mSetPrivateFactory) return; - // Reflection (Or Old Device) skip. - if (!CalligraphyConfig.get().isReflection()) return; - // Skip if not attached to an activity. - if (!(getContext() instanceof Factory2)) { - mSetPrivateFactory = true; - return; - } - - final Method setPrivateFactoryMethod = ReflectionUtils - .getMethod(LayoutInflater.class, "setPrivateFactory"); - - if (setPrivateFactoryMethod != null) { - ReflectionUtils.invokeMethod(this, - setPrivateFactoryMethod, - new PrivateWrapperFactory2((Factory2) getContext(), this, mCalligraphyFactory)); - } - mSetPrivateFactory = true; - } - - // === - // LayoutInflater ViewCreators - // Works in order of inflation - // === - - /** - * The Activity onCreateView (PrivateFactory) is the third port of call for LayoutInflation. - * We opted to manual injection over aggressive reflection, this should be less fragile. - */ - @Override - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - public View onActivityCreateView(View parent, View view, String name, Context context, AttributeSet attrs) { - return mCalligraphyFactory.onViewCreated(createCustomViewInternal(parent, view, name, context, attrs), context, attrs); - } - - /** - * The LayoutInflater onCreateView is the fourth port of call for LayoutInflation. - * BUT only for none CustomViews. - */ - @Override - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - protected View onCreateView(View parent, String name, AttributeSet attrs) throws ClassNotFoundException { - return mCalligraphyFactory.onViewCreated(super.onCreateView(parent, name, attrs), - getContext(), attrs); - } - - /** - * The LayoutInflater onCreateView is the fourth port of call for LayoutInflation. - * BUT only for none CustomViews. - * Basically if this method doesn't inflate the View nothing probably will. - */ - @Override - protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException { - // This mimics the {@code PhoneLayoutInflater} in the way it tries to inflate the base - // classes, if this fails its pretty certain the app will fail at this point. - View view = null; - for (String prefix : sClassPrefixList) { - try { - view = createView(name, prefix, attrs); - } catch (ClassNotFoundException ignored) { - } - } - // In this case we want to let the base class take a crack - // at it. - if (view == null) view = super.onCreateView(name, attrs); - - return mCalligraphyFactory.onViewCreated(view, view.getContext(), attrs); - } - - /** - * Nasty method to inflate custom layouts that haven't been handled else where. If this fails it - * will fall back through to the PhoneLayoutInflater method of inflating custom views where - * Calligraphy will NOT have a hook into. - * - * @param parent parent view - * @param view view if it has been inflated by this point, if this is not null this method - * just returns this value. - * @param name name of the thing to inflate. - * @param viewContext Context to inflate by if parent is null - * @param attrs Attr for this view which we can steal fontPath from too. - * @return view or the View we inflate in here. - */ - private View createCustomViewInternal(View parent, View view, String name, Context viewContext, AttributeSet attrs) { - // I by no means advise anyone to do this normally, but Google have locked down access to - // the createView() method, so we never get a callback with attributes at the end of the - // createViewFromTag chain (which would solve all this unnecessary rubbish). - // We at the very least try to optimise this as much as possible. - // We only call for customViews (As they are the ones that never go through onCreateView(...)). - // We also maintain the Field reference and make it accessible which will make a pretty - // significant difference to performance on Android 4.0+. - - // If CustomViewCreation is off skip this. - if (!CalligraphyConfig.get().isCustomViewCreation()) return view; - if (view == null && name.indexOf('.') > -1) { - if (mConstructorArgs == null) - mConstructorArgs = ReflectionUtils.getField(LayoutInflater.class, "mConstructorArgs"); - - final Object[] mConstructorArgsArr = (Object[]) ReflectionUtils.getValue(mConstructorArgs, this); - final Object lastContext = mConstructorArgsArr[0]; - // The LayoutInflater actually finds out the correct context to use. We just need to set - // it on the mConstructor for the internal method. - // Set the constructor ars up for the createView, not sure why we can't pass these in. - mConstructorArgsArr[0] = viewContext; - ReflectionUtils.setValue(mConstructorArgs, this, mConstructorArgsArr); - try { - view = createView(name, null, attrs); - } catch (ClassNotFoundException ignored) { - } finally { - mConstructorArgsArr[0] = lastContext; - ReflectionUtils.setValue(mConstructorArgs, this, mConstructorArgsArr); - } - } - return view; - } - - // === - // Wrapper Factories for Pre/Post HC - // === - - /** - * Factory 1 is the first port of call for LayoutInflation - */ - private static class WrapperFactory implements Factory { - - private final Factory mFactory; - private final CalligraphyLayoutInflater mInflater; - private final CalligraphyFactory mCalligraphyFactory; - - public WrapperFactory(Factory factory, CalligraphyLayoutInflater inflater, CalligraphyFactory calligraphyFactory) { - mFactory = factory; - mInflater = inflater; - mCalligraphyFactory = calligraphyFactory; - } - - @Override - public View onCreateView(String name, Context context, AttributeSet attrs) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { - return mCalligraphyFactory.onViewCreated( - mInflater.createCustomViewInternal( - null, mFactory.onCreateView(name, context, attrs), name, context, attrs - ), - context, attrs - ); - } - return mCalligraphyFactory.onViewCreated( - mFactory.onCreateView(name, context, attrs), - context, attrs - ); - } - } - - /** - * Factory 2 is the second port of call for LayoutInflation - */ - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - private static class WrapperFactory2 implements Factory2 { - protected final Factory2 mFactory2; - protected final CalligraphyFactory mCalligraphyFactory; - - public WrapperFactory2(Factory2 factory2, CalligraphyFactory calligraphyFactory) { - mFactory2 = factory2; - mCalligraphyFactory = calligraphyFactory; - } - - @Override - public View onCreateView(String name, Context context, AttributeSet attrs) { - return mCalligraphyFactory.onViewCreated( - mFactory2.onCreateView(name, context, attrs), - context, attrs); - } - - @Override - public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { - return mCalligraphyFactory.onViewCreated( - mFactory2.onCreateView(parent, name, context, attrs), - context, attrs); - } - } - - /** - * Private factory is step three for Activity Inflation, this is what is attached to the - * Activity on HC+ devices. - */ - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - private static class PrivateWrapperFactory2 extends WrapperFactory2 { - - private final CalligraphyLayoutInflater mInflater; - - public PrivateWrapperFactory2(Factory2 factory2, CalligraphyLayoutInflater inflater, CalligraphyFactory calligraphyFactory) { - super(factory2, calligraphyFactory); - mInflater = inflater; - } - - @Override - public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { - return mCalligraphyFactory.onViewCreated( - mInflater.createCustomViewInternal(parent, - mFactory2.onCreateView(parent, name, context, attrs), - name, context, attrs - ), - context, attrs - ); - } - } - -} diff --git a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/ReflectionUtils.java b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/ReflectionUtils.java deleted file mode 100644 index 99c0d86..0000000 --- a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/ReflectionUtils.java +++ /dev/null @@ -1,61 +0,0 @@ -package uk.co.chrisjenx.calligraphy; - -import android.util.Log; - -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -/** - * Created by chris on 17/12/14. - * For Calligraphy. - */ -class ReflectionUtils { - - private static final String TAG = ReflectionUtils.class.getSimpleName(); - - static Field getField(Class clazz, String fieldName) { - try { - final Field f = clazz.getDeclaredField(fieldName); - f.setAccessible(true); - return f; - } catch (NoSuchFieldException ignored) { - } - return null; - } - - static Object getValue(Field field, Object obj) { - try { - return field.get(obj); - } catch (IllegalAccessException ignored) { - } - return null; - } - - static void setValue(Field field, Object obj, Object value) { - try { - field.set(obj, value); - } catch (IllegalAccessException ignored) { - } - } - - static Method getMethod(Class clazz, String methodName) { - final Method[] methods = clazz.getMethods(); - for (Method method : methods) { - if (method.getName().equals(methodName)) { - method.setAccessible(true); - return method; - } - } - return null; - } - - static void invokeMethod(Object object, Method method, Object... args) { - try { - if (method == null) return; - method.invoke(object, args); - } catch (IllegalAccessException | InvocationTargetException e) { - Log.d(TAG, "Can't invoke method using reflection", e); - } - } -} diff --git a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyFactory.java b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy3/Calligraphy.java similarity index 89% rename from calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyFactory.java rename to calligraphy/src/main/java/uk/co/chrisjenx/calligraphy3/Calligraphy.java index abc43dc..6b43fbf 100644 --- a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyFactory.java +++ b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy3/Calligraphy.java @@ -1,4 +1,4 @@ -package uk.co.chrisjenx.calligraphy; +package uk.co.chrisjenx.calligraphy3; import android.annotation.SuppressLint; import android.annotation.TargetApi; @@ -15,7 +15,9 @@ import java.lang.ref.WeakReference; import java.lang.reflect.Method; -class CalligraphyFactory { +import io.github.inflationx.viewpump.ReflectionUtils; + +class Calligraphy { private static final String ACTION_BAR_TITLE = "action_bar_title"; private static final String ACTION_BAR_SUBTITLE = "action_bar_subtitle"; @@ -26,7 +28,7 @@ class CalligraphyFactory { * @param view view to check. * @return 2 element array, default to -1 unless a style has been found. */ - protected static int[] getStyleForTextView(TextView view) { + protected int[] getStyleForTextView(TextView view) { final int[] styleIds = new int[]{-1, -1}; // Try to find the specific actionbar styles if (isActionBarTitle(view)) { @@ -38,8 +40,8 @@ protected static int[] getStyleForTextView(TextView view) { } if (styleIds[0] == -1) { // Use TextAppearance as default style - styleIds[0] = CalligraphyConfig.get().getClassStyles().containsKey(view.getClass()) - ? CalligraphyConfig.get().getClassStyles().get(view.getClass()) + styleIds[0] = mCalligraphyConfig.getClassStyles().containsKey(view.getClass()) + ? mCalligraphyConfig.getClassStyles().get(view.getClass()) : android.R.attr.textAppearance; } return styleIds; @@ -94,10 +96,12 @@ protected static boolean matchesResourceIdName(View view, String matches) { return resourceEntryName.equalsIgnoreCase(matches); } + private final CalligraphyConfig mCalligraphyConfig; private final int[] mAttributeId; - public CalligraphyFactory(int attributeId) { - this.mAttributeId = new int[]{attributeId}; + public Calligraphy(CalligraphyConfig calligraphyConfig) { + mCalligraphyConfig = calligraphyConfig; + this.mAttributeId = new int[]{calligraphyConfig.getAttrId()}; } /** @@ -143,7 +147,7 @@ void onViewCreatedInternal(View view, final Context context, AttributeSet attrs) // Still need to defer the Native action bar, appcompat-v7:21+ uses the Toolbar underneath. But won't match these anyway. final boolean deferred = matchesResourceIdName(view, ACTION_BAR_TITLE) || matchesResourceIdName(view, ACTION_BAR_SUBTITLE); - CalligraphyUtils.applyFontToTextView(context, (TextView) view, CalligraphyConfig.get(), textViewFont, deferred); + CalligraphyUtils.applyFontToTextView(context, (TextView) view, mCalligraphyConfig, textViewFont, deferred); } // AppCompat API21+ The ActionBar doesn't inflate default Title/SubTitle, we need to scan the @@ -155,11 +159,12 @@ void onViewCreatedInternal(View view, final Context context, AttributeSet attrs) // Try to set typeface for custom views using interface method or via reflection if available if (view instanceof HasTypeface) { - Typeface typeface = getDefaultTypeface(context, resolveFontPath(context, attrs)); + String textViewFont = resolveFontPath(context, attrs); + Typeface typeface = getDefaultTypeface(context, textViewFont); if (typeface != null) { ((HasTypeface) view).setTypeface(typeface); } - } else if (CalligraphyConfig.get().isCustomViewTypefaceSupport() && CalligraphyConfig.get().isCustomViewHasTypeface(view)) { + } else if (mCalligraphyConfig.isCustomViewTypefaceSupport() && mCalligraphyConfig.isCustomViewHasTypeface(view)) { final Method setTypeface = ReflectionUtils.getMethod(view.getClass(), "setTypeface"); String fontPath = resolveFontPath(context, attrs); Typeface typeface = getDefaultTypeface(context, fontPath); @@ -172,7 +177,7 @@ void onViewCreatedInternal(View view, final Context context, AttributeSet attrs) private Typeface getDefaultTypeface(Context context, String fontPath) { if (TextUtils.isEmpty(fontPath)) { - fontPath = CalligraphyConfig.get().getFontPath(); + fontPath = mCalligraphyConfig.getFontPath(); } if (!TextUtils.isEmpty(fontPath)) { return TypefaceUtils.load(context.getAssets(), fontPath); @@ -204,14 +209,14 @@ private static class ToolbarLayoutListener implements ViewTreeObserver.OnGlobalL static String BLANK = " "; - private final WeakReference mCalligraphyFactory; + private final WeakReference mCalligraphyFactory; private final WeakReference mContextRef; private final WeakReference mToolbarReference; private final CharSequence originalSubTitle; - private ToolbarLayoutListener(final CalligraphyFactory calligraphyFactory, + private ToolbarLayoutListener(final Calligraphy calligraphy, final Context context, Toolbar toolbar) { - mCalligraphyFactory = new WeakReference<>(calligraphyFactory); + mCalligraphyFactory = new WeakReference<>(calligraphy); mContextRef = new WeakReference<>(context); mToolbarReference = new WeakReference<>(toolbar); originalSubTitle = toolbar.getSubtitle(); @@ -222,7 +227,7 @@ private ToolbarLayoutListener(final CalligraphyFactory calligraphyFactory, @Override public void onGlobalLayout() { final Toolbar toolbar = mToolbarReference.get(); final Context context = mContextRef.get(); - final CalligraphyFactory factory = mCalligraphyFactory.get(); + final Calligraphy factory = mCalligraphyFactory.get(); if (toolbar == null) return; if (factory == null || context == null) { removeSelf(toolbar); diff --git a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyConfig.java b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy3/CalligraphyConfig.java similarity index 63% rename from calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyConfig.java rename to calligraphy/src/main/java/uk/co/chrisjenx/calligraphy3/CalligraphyConfig.java index 2fb8f63..d5d9642 100644 --- a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyConfig.java +++ b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy3/CalligraphyConfig.java @@ -1,6 +1,5 @@ -package uk.co.chrisjenx.calligraphy; +package uk.co.chrisjenx.calligraphy3; -import android.os.Build; import android.text.TextUtils; import android.view.View; import android.widget.AutoCompleteTextView; @@ -61,28 +60,6 @@ private static void addAppCompatViews() { DEFAULT_STYLES.put(android.support.v7.widget.AppCompatCheckedTextView.class, android.R.attr.checkedTextViewStyle); } - private static CalligraphyConfig sInstance; - - /** - * Set the default Calligraphy Config - * - * @param calligraphyConfig the config build using the builder. - * @see uk.co.chrisjenx.calligraphy.CalligraphyConfig.Builder - */ - public static void initDefault(CalligraphyConfig calligraphyConfig) { - sInstance = calligraphyConfig; - } - - /** - * The current Calligraphy Config. - * If not set it will create a default config. - */ - public static CalligraphyConfig get() { - if (sInstance == null) - sInstance = new CalligraphyConfig(new Builder()); - return sInstance; - } - /** * Is a default font set? */ @@ -95,14 +72,6 @@ public static CalligraphyConfig get() { * Default Font Path Attr Id to lookup */ private final int mAttrId; - /** - * Use Reflection to inject the private factory. - */ - private final boolean mReflection; - /** - * Use Reflection to intercept CustomView inflation with the correct Context. - */ - private final boolean mCustomViewCreation; /** * Use Reflection to try to set typeface for custom views if they has setTypeface method */ @@ -113,16 +82,14 @@ public static CalligraphyConfig get() { private final Map, Integer> mClassStyleAttributeMap; /** * Collection of custom non-{@code TextView}'s registered for applying typeface during inflation - * @see uk.co.chrisjenx.calligraphy.CalligraphyConfig.Builder#addCustomViewWithSetTypeface(Class) + * @see CalligraphyConfig.Builder#addCustomViewWithSetTypeface(Class) */ private final Set> hasTypefaceViews; - protected CalligraphyConfig(Builder builder) { + private CalligraphyConfig(Builder builder) { mIsFontSet = builder.isFontSet; mFontPath = builder.fontAssetPath; mAttrId = builder.attrId; - mReflection = builder.reflection; - mCustomViewCreation = builder.customViewCreation; mCustomViewTypefaceSupport = builder.customViewTypefaceSupport; final Map, Integer> tempMap = new HashMap<>(DEFAULT_STYLES); tempMap.putAll(builder.mStyleClassMap); @@ -144,14 +111,6 @@ boolean isFontSet() { return mIsFontSet; } - public boolean isReflection() { - return mReflection; - } - - public boolean isCustomViewCreation() { - return mCustomViewCreation; - } - public boolean isCustomViewTypefaceSupport() { return mCustomViewTypefaceSupport; } @@ -176,14 +135,6 @@ public static class Builder { * Default AttrID if not set. */ public static final int INVALID_ATTR_ID = -1; - /** - * Use Reflection to inject the private factory. Doesn't exist pre HC. so defaults to false. - */ - private boolean reflection = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB; - /** - * Use Reflection to intercept CustomView inflation with the correct Context. - */ - private boolean customViewCreation = true; /** * Use Reflection during view creation to try change typeface via setTypeface method if it exists */ @@ -231,56 +182,6 @@ public Builder setDefaultFontPath(String defaultFontAssetPath) { return this; } - /** - *

Turn of the use of Reflection to inject the private factory. - * This has operational consequences! Please read and understand before disabling. - * This is already disabled on pre Honeycomb devices. (API 11)

- * - *

If you disable this you will need to override your {@link android.app.Activity#onCreateView(android.view.View, String, android.content.Context, android.util.AttributeSet)} - * as this is set as the {@link android.view.LayoutInflater} private factory.

- *
- * Use the following code in the Activity if you disable FactoryInjection: - *

-         * {@literal @}Override
-         * {@literal @}TargetApi(Build.VERSION_CODES.HONEYCOMB)
-         * public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
-         *   return CalligraphyContextWrapper.onActivityCreateView(this, parent, super.onCreateView(parent, name, context, attrs), name, context, attrs);
-         * }
-         * 
- */ - public Builder disablePrivateFactoryInjection() { - this.reflection = false; - return this; - } - - /** - * Due to the poor inflation order where custom views are created and never returned inside an - * {@code onCreateView(...)} method. We have to create CustomView's at the latest point in the - * overrideable injection flow. - * - * On HoneyComb+ this is inside the {@link android.app.Activity#onCreateView(android.view.View, String, android.content.Context, android.util.AttributeSet)} - * Pre HoneyComb this is in the {@link android.view.LayoutInflater.Factory#onCreateView(String, android.util.AttributeSet)} - * - * We wrap base implementations, so if you LayoutInflater/Factory/Activity creates the - * custom view before we get to this point, your view is used. (Such is the case with the - * TintEditText etc) - * - * The problem is, the native methods pass there parents context to the constructor in a really - * specific place. We have to mimic this in {@link uk.co.chrisjenx.calligraphy.CalligraphyLayoutInflater#createCustomViewInternal(android.view.View, android.view.View, String, android.content.Context, android.util.AttributeSet)} - * To mimic this we have to use reflection as the Class constructor args are hidden to us. - * - * We have discussed other means of doing this but this is the only semi-clean way of doing it. - * (Without having to do proxy classes etc). - * - * Calling this will of course speed up inflation by turning off reflection, but not by much, - * But if you want Calligraphy to inject the correct typeface then you will need to make sure your CustomView's - * are created before reaching the LayoutInflater onViewCreated. - */ - public Builder disableCustomViewInflation() { - this.customViewCreation = false; - return this; - } - /** * Add a custom style to get looked up. If you use a custom class that has a parent style * which is not part of the default android styles you will need to add it here. diff --git a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy3/CalligraphyInterceptor.java b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy3/CalligraphyInterceptor.java new file mode 100644 index 0000000..f683740 --- /dev/null +++ b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy3/CalligraphyInterceptor.java @@ -0,0 +1,22 @@ +package uk.co.chrisjenx.calligraphy3; + +import android.view.View; + +import io.github.inflationx.viewpump.InflateResult; +import io.github.inflationx.viewpump.Interceptor; + +public class CalligraphyInterceptor implements Interceptor { + + private final Calligraphy calligraphy; + + public CalligraphyInterceptor(CalligraphyConfig calligraphyConfig) { + this.calligraphy = new Calligraphy(calligraphyConfig); + } + + @Override + public InflateResult intercept(Chain chain) { + InflateResult result = chain.proceed(chain.request()); + View viewWithTypeface = calligraphy.onViewCreated(result.view(), result.context(), result.attrs()); + return result.toBuilder().view(viewWithTypeface).build(); + } +} diff --git a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyTypefaceSpan.java b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy3/CalligraphyTypefaceSpan.java similarity index 96% rename from calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyTypefaceSpan.java rename to calligraphy/src/main/java/uk/co/chrisjenx/calligraphy3/CalligraphyTypefaceSpan.java index debdd3e..8994a7c 100644 --- a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyTypefaceSpan.java +++ b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy3/CalligraphyTypefaceSpan.java @@ -1,4 +1,4 @@ -package uk.co.chrisjenx.calligraphy; +package uk.co.chrisjenx.calligraphy3; import android.graphics.Paint; import android.graphics.Typeface; diff --git a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyUtils.java b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy3/CalligraphyUtils.java similarity index 99% rename from calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyUtils.java rename to calligraphy/src/main/java/uk/co/chrisjenx/calligraphy3/CalligraphyUtils.java index ab71cd9..be990b3 100644 --- a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/CalligraphyUtils.java +++ b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy3/CalligraphyUtils.java @@ -1,4 +1,4 @@ -package uk.co.chrisjenx.calligraphy; +package uk.co.chrisjenx.calligraphy3; import android.content.Context; import android.content.res.AssetManager; @@ -97,7 +97,7 @@ public void afterTextChanged(Editable s) { /** * Useful for manually fonts to a TextView. Will not default back to font - * set in {@link uk.co.chrisjenx.calligraphy.CalligraphyConfig} + * set in {@link CalligraphyConfig} * * @param context Context * @param textView Not null, TextView to apply to. diff --git a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/HasTypeface.java b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy3/HasTypeface.java similarity index 81% rename from calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/HasTypeface.java rename to calligraphy/src/main/java/uk/co/chrisjenx/calligraphy3/HasTypeface.java index 48950f9..e3ca0c6 100644 --- a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/HasTypeface.java +++ b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy3/HasTypeface.java @@ -1,4 +1,4 @@ -package uk.co.chrisjenx.calligraphy; +package uk.co.chrisjenx.calligraphy3; import android.graphics.Typeface; @@ -8,7 +8,7 @@ *
  • Implementing this interface. You should only implements {@link #setTypeface(Typeface)} method.
  • *
  • Or via reflection. If custom view already has setTypeface method you can * register it during Calligraphy configuration - * {@link uk.co.chrisjenx.calligraphy.CalligraphyConfig.Builder#addCustomViewWithSetTypeface(Class)}
  • + * {@link CalligraphyConfig.Builder#addCustomViewWithSetTypeface(Class)} * * First way is faster but encourage more effort from the developer to implements interface. Second one * requires less effort but works slowly cause reflection calls. diff --git a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/TypefaceUtils.java b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy3/TypefaceUtils.java similarity index 98% rename from calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/TypefaceUtils.java rename to calligraphy/src/main/java/uk/co/chrisjenx/calligraphy3/TypefaceUtils.java index 8ad3a8c..bad759f 100644 --- a/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy/TypefaceUtils.java +++ b/calligraphy/src/main/java/uk/co/chrisjenx/calligraphy3/TypefaceUtils.java @@ -1,4 +1,4 @@ -package uk.co.chrisjenx.calligraphy; +package uk.co.chrisjenx.calligraphy3; import android.content.res.AssetManager; import android.graphics.Typeface;