diff --git a/.idea/misc.xml b/.idea/misc.xml
index 6412c4d90..53dc4cb34 100755
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -14,6 +14,7 @@
+
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/XMLLayoutInflater.java b/xml-inflater/src/main/java/com/itsaky/inflater/XMLLayoutInflater.java
index 947ec8de4..6031f59bb 100644
--- a/xml-inflater/src/main/java/com/itsaky/inflater/XMLLayoutInflater.java
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/XMLLayoutInflater.java
@@ -1,29 +1,32 @@
/************************************************************************************
* This file is part of AndroidIDE.
- *
+ *
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
- *
+ *
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see .
*
-**************************************************************************************/
+ **************************************************************************************/
package com.itsaky.inflater;
import android.content.Context;
+import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
+
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.core.content.ContextCompat;
+
import com.itsaky.androidide.app.BaseApplication;
import com.itsaky.androidide.ui.util.Preconditions;
import com.itsaky.androidide.utils.Logger;
@@ -36,11 +39,13 @@ import com.itsaky.inflater.impl.UiView;
import com.itsaky.inflater.impl.UiViewGroup;
import com.itsaky.widgets.WidgetInfo;
import com.itsaky.widgets.models.Widget;
+
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.regex.Pattern;
+
import org.jsoup.Jsoup;
import org.jsoup.nodes.Attribute;
import org.jsoup.nodes.Attributes;
@@ -59,60 +64,60 @@ class XMLLayoutInflater extends BaseLayoutInflater {
private ContextProvider contextProvider;
- private static final Logger LOG = Logger.instance("XMLLayoutInflater");
+ private static final Logger LOG = Logger.instance ("XMLLayoutInflater");
public static final String ATTR_ADAPTER_SUFFIX = "AttrAdapter";
private XMLLayoutInflater () {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException ();
}
- XMLLayoutInflater(@NonNull LayoutInflaterConfiguration config) {
+ XMLLayoutInflater (@NonNull LayoutInflaterConfiguration config) {
this.resDirs = config.resDirs;
this.attrInfo = config.attrInfo;
this.widgetInfo = config.widgetInfo;
this.resFinder = config.resFinder;
this.contextProvider = config.contextProvider;
- Preconditions.assertAllNotNull("LayoutInflater parameters cannot be null", resDirs, attrInfo, widgetInfo, resFinder);
+ Preconditions.assertAllNotNull ("LayoutInflater parameters cannot be null", resDirs, attrInfo, widgetInfo, resFinder);
}
-
+
@Override
@NonNull
- protected IView doInflate(String layout, ViewGroup parent) throws InflateException {
+ protected IView doInflate (String layout, ViewGroup parent) throws InflateException {
try {
- IDTable.newRound();
+ IDTable.newRound ();
// Notify
- preInflate();
+ preInflate ();
- final Document doc = Jsoup.parse(layout, "", Parser.xmlParser());
+ final Document doc = Jsoup.parse (layout, "", Parser.xmlParser ());
- if (doc.childrenSize() <= 0) {
+ if (doc.childrenSize () <= 0) {
throw new InflateException ("No views added");
}
- if (doc.childrenSize() > 1) {
+ if (doc.childrenSize () > 1) {
throw new InflateException ("More than one root element was found. An XML layout can have only one root element.");
}
- IView root = onCreateView(doc.child(0), parent);
+ IView root = onCreateView (doc.child (0), parent);
if (root == null) {
- root = onCreateErrorView(TextView.class.getName(), "No views added");
+ root = onCreateErrorView (TextView.class.getName (), "No views added");
}
// Notify
- postInflate(root);
+ postInflate (root);
return root;
} catch (Throwable th) {
- throw new InflateException("Unable to inflate layout", th);
+ throw new InflateException ("Unable to inflate layout", th);
}
}
-
+
@Override
- public void resetContextProvider(ILayoutInflater.ContextProvider provider) {
+ public void resetContextProvider (ILayoutInflater.ContextProvider provider) {
this.contextProvider = provider;
}
@@ -127,203 +132,205 @@ class XMLLayoutInflater extends BaseLayoutInflater {
return null;
}
- final String name = tag.tagName().trim();
-
- if (name.equals("include")) {
- return createFromInclude (tag.attributes(), parent);
+ final String name = tag.tagName ().trim ();
+
+ if (name.equals ("include")) {
+ return createFromInclude (tag.attributes (), parent);
}
-
+
IView root = null;
- int style = tag.hasAttr("style") ? parseFrameworkStyle (tag.attr("style")) : 0;
- if (!name.contains(".")) {
+ int style = tag.hasAttr ("style") ? parseFrameworkStyle (tag.attr ("style")) : 0;
+ if (!name.contains (".")) {
root = createFromSimpleName (name, parent, style);
} else {
root = createFromQualifiedName (name, parent, style);
}
-
+
if (root == null) {
return null;
}
registerAttributeAdaptersTo (root);
-
- if (!tag.attributes().isEmpty()) {
- addAttributesTo(root, tag.attributes());
+
+ if (!tag.attributes ().isEmpty ()) {
+ addAttributesTo (root, tag.attributes ());
}
-
+
if (root instanceof IViewGroup) {
addChildren (tag, (IViewGroup) root, parent);
}
- postCreateView(root);
-
+ postCreateView (root);
+
return root;
}
-
- protected void addChildren(@NonNull Element tag, IViewGroup root, ViewGroup parent) {
- if (tag.childrenSize() > 0 && !root.isPlaceholder()) {
- for (Element child : tag.children()) {
- final BaseView created = (BaseView) onCreateView(child, (ViewGroup) root.asView());
- if(created != null) {
- root.addView(created);
- created.setParent(root);
+
+ protected void addChildren (@NonNull Element tag, IViewGroup root, ViewGroup parent) {
+ if (tag.childrenSize () > 0 && !root.isPlaceholder ()) {
+ for (Element child : tag.children ()) {
+ final BaseView created = (BaseView) onCreateView (child, (ViewGroup) root.asView ());
+ if (created != null) {
+ root.addView (created);
+ created.setParent (root);
}
}
}
}
-
- protected int parseFrameworkStyle (String value) throws InflateException{
-
+
+ protected int parseFrameworkStyle (String value) throws InflateException {
+
try {
- if (value.startsWith("?android:attr/")) {
- final String name = underscorize (value.substring("?android:attr/".length()));
- return (int) android.R.attr.class.getField(name).get(null);
- } else if (value.startsWith("@android:style/")) {
- final String name = underscorize (value.substring("@android:style/".length()));
- return (int) android.R.style.class.getField(name).get(null);
+ if (value.startsWith ("?android:attr/")) {
+ final String name = underscorize (value.substring ("?android:attr/".length ()));
+ return (int) android.R.attr.class.getField (name).get (null);
+ } else if (value.startsWith ("@android:style/")) {
+ final String name = underscorize (value.substring ("@android:style/".length ()));
+ return (int) android.R.style.class.getField (name).get (null);
}
} catch (Throwable th) {
throw new InflateException (th);
}
-
+
return 0;
}
-
+
@NonNull
- private String underscorize(@NonNull String substring) {
- return substring.replace(".", "_");
- }
-
- protected void registerAttributeAdaptersTo(@NonNull IView root) {
- root.registerAttributeAdapter(onCreateAttributeAdapter (root.asView()));
+ private String underscorize (@NonNull String substring) {
+ return substring.replace (".", "_");
}
- protected IAttributeAdapter onCreateAttributeAdapter(View view) {
+ protected void registerAttributeAdaptersTo (@NonNull IView root) {
+ root.registerAttributeAdapter (onCreateAttributeAdapter (root.asView ()));
+ }
+
+ protected IAttributeAdapter onCreateAttributeAdapter (View view) {
+ final var displayMetrics = contextProvider.getContext ().getResources ().getDisplayMetrics ();
try {
- final String adapterName = view.getClass().getName().concat(ATTR_ADAPTER_SUFFIX);
- final String name = "com.itsaky.inflater.adapters.".concat(adapterName);
- final Class extends IAttributeAdapter> adapterClass = getClass()
- .getClassLoader()
- .loadClass(name)
- .asSubclass(IAttributeAdapter.class);
- final var adapter = adapterClass.newInstance();
- adapter.setResourceFinder (resFinder);
- return adapter;
+ final String adapterName = view.getClass ().getName ().concat (ATTR_ADAPTER_SUFFIX);
+ final String name = "com.itsaky.inflater.adapters.".concat (adapterName);
+ final Class extends IAttributeAdapter> adapterClass = getClass ()
+ .getClassLoader ()
+ .loadClass (name)
+ .asSubclass (IAttributeAdapter.class);
+ final var constructor = adapterClass.getConstructor (IResourceFinder.class, DisplayMetrics.class);
+ final var adapter = constructor.newInstance (resFinder, displayMetrics);
+ adapter.setResourceFinder (resFinder);
+ return adapter;
} catch (Throwable th) {
- LOG.error (BaseApplication.getBaseInstance().getString(com.itsaky.inflater.R.string.err_no_attr_adapter, view.getClass().getName()), th);
+ LOG.error (BaseApplication.getBaseInstance ().getString (com.itsaky.inflater.R.string.err_no_attr_adapter, view.getClass ().getName ()), th);
}
// If we cannot find a suitable adapter, fall back to using common adapters
IAttributeAdapter adapter = null;
if (view instanceof ViewGroup) {
- adapter = new ViewGroupAttrAdapter ();
+ adapter = new ViewGroupAttrAdapter (resFinder, displayMetrics);
} else {
- adapter = new ViewAttrAdapter ();
+ adapter = new ViewAttrAdapter (resFinder, displayMetrics);
}
return adapter;
}
-
- protected void addAttributesTo(IView view, Attributes attributes) {
- assertNotnull(attributes, "Cannot apply null attributes!");
-
+
+ protected void addAttributesTo (IView view, Attributes attributes) {
+ assertNotnull (attributes, "Cannot apply null attributes!");
+
for (Attribute attr : attributes) {
- final String key = attr.getKey();
- final String[] split = !key.contains(":") ? new String[] {"", key} : key.split(Pattern.quote(":"), 2);
+ final String key = attr.getKey ();
+ final String[] split = !key.contains (":") ? new String[]{"", key} : key.split (Pattern.quote (":"), 2);
final String namespace = split[0];
final String name = split[1];
- final String value = attr.getValue();
-
+ final String value = attr.getValue ();
+
final IAttribute iAttr = asAttribute (namespace, name, value);
- view.addAttribute(iAttr);
-
- postApplyAttribute(iAttr, view);
+ view.addAttribute (iAttr);
+
+ postApplyAttribute (iAttr, view);
}
}
-
- protected IAttribute asAttribute(String namespace, String name, String value) {
+
+ protected IAttribute asAttribute (String namespace, String name, String value) {
return new UiAttribute (namespace, name, value);
}
-
+
protected IView create (@NonNull String name, ViewGroup parent, int style) throws InflateException {
- if (!name.contains(".")) {
+ if (!name.contains (".")) {
return createFromSimpleName (name, parent, style);
} else {
return createFromQualifiedName (name, parent, style);
}
}
-
+
protected IView createFromInclude (Attributes attrs, ViewGroup parent) {
throw new UnsupportedOperationException ("Inflating from is not supported yet!");
}
-
- protected IView createFromSimpleName(String name, ViewGroup parent, int style) throws InflateException {
- final Widget widget = widgetInfo.getWidgetBySimpleName(name);
+
+ protected IView createFromSimpleName (String name, ViewGroup parent, int style) throws InflateException {
+ final Widget widget = widgetInfo.getWidgetBySimpleName (name);
if (widget == null) {
- LOG.error("Unable to inflate view. widget == null");
- return onCreateErrorView(name, getString(com.itsaky.inflater.R.string.msg_cannot_create_view, name));
+ LOG.error ("Unable to inflate view. widget == null");
+ return onCreateErrorView (name, getString (com.itsaky.inflater.R.string.msg_cannot_create_view, name));
}
-
- return createFromQualifiedName(widget.name, parent, style);
+
+ return createFromQualifiedName (widget.name, parent, style);
}
-
- protected IView createFromQualifiedName(String name, ViewGroup parent, int style) throws InflateException {
- assertNotBlank(name, "Invalid tag name: " + name);
+
+ protected IView createFromQualifiedName (String name, ViewGroup parent, int style) throws InflateException {
+ assertNotBlank (name, "Invalid tag name: " + name);
// TODO Try to load classes directly from .class files if possible
try {
- final View created = createAndroidViewForName(name);
+ final View created = createAndroidViewForName (name);
final BaseView view =
- created instanceof ViewGroup
- ? new UiViewGroup (name, (ViewGroup) created)
- : new UiView (name, created);
- return applyLayoutParams(view, parent);
+ created instanceof ViewGroup
+ ? new UiViewGroup (name, (ViewGroup) created)
+ : new UiView (name, created);
+ return applyLayoutParams (view, parent);
} catch (Throwable th) {
- final var msg = getString(R.string.msg_cannot_create_view, name);
- LOG.error(msg, th);
- return onCreateErrorView(name, msg);
+ final var msg = getString (R.string.msg_cannot_create_view, name);
+ LOG.error (msg, th);
+ return onCreateErrorView (name, msg);
}
}
-
+
@NonNull
- protected View createAndroidViewForName(String name) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, java.lang.reflect.InvocationTargetException {
- final Class extends View> loaded = Class.forName(name).asSubclass(View.class);
- final Constructor extends View> constructor = loaded.getConstructor(Context.class /*, AttributeSet.class, int.class*/);
- final View created = constructor.newInstance(contextProvider.getContext()/*, null, style*/);
+ protected View createAndroidViewForName (String name) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, java.lang.reflect.InvocationTargetException {
+ final Class extends View> loaded = Class.forName (name).asSubclass (View.class);
+ final Constructor extends View> constructor = loaded.getConstructor (Context.class /*, AttributeSet.class, int.class*/);
+ final View created = constructor.newInstance (contextProvider.getContext ()/*, null, style*/);
return created;
}
-
- protected IView applyLayoutParams(@NonNull IView view, ViewGroup parent) {
- final ViewGroup.LayoutParams params = generateLayoutParams(parent);
- view.asView().setLayoutParams(params);
+
+ protected IView applyLayoutParams (@NonNull IView view, ViewGroup parent) {
+ final ViewGroup.LayoutParams params = generateLayoutParams (parent);
+ view.asView ().setLayoutParams (params);
return view;
}
-
+
protected IView onCreateErrorView (String name, String msg) throws InflateException {
- final Context ctx = contextProvider.getContext();
- assertNotnull(ctx, "Context is null!");
-
+ final Context ctx = contextProvider.getContext ();
+ assertNotnull (ctx, "Context is null!");
+
final TextView error = new TextView (ctx);
- error.setText(msg);
- error.setLayoutParams(new ViewGroup.LayoutParams (-2, -2));
- error.setTextColor(ContextCompat.getColor(ctx, android.R.color.black));
- error.setBackgroundColor(ContextCompat.getColor(ctx, android.R.color.white));
-
+ error.setText (msg);
+ error.setLayoutParams (new ViewGroup.LayoutParams (-2, -2));
+ error.setTextColor (ContextCompat.getColor (ctx, android.R.color.black));
+ error.setBackgroundColor (ContextCompat.getColor (ctx, android.R.color.white));
+
return new UiView (name, error, true);
}
-
+
private String getString (@StringRes int id, Object... format) throws InflateException {
- assertNotnull(contextProvider.getContext(), "Context is null!");
- return contextProvider.getContext().getString(id, format);
+ assertNotnull (contextProvider.getContext (), "Context is null!");
+ return contextProvider.getContext ().getString (id, format);
}
@NonNull
protected ViewGroup.LayoutParams generateLayoutParams (ViewGroup parent) {
try {
- final Class> clazz = parent.getClass();
+ final Class> clazz = parent.getClass ();
Method method = null;
try {
- method = clazz.getDeclaredMethod("generateDefaultLayoutParams");
+ method = clazz.getDeclaredMethod ("generateDefaultLayoutParams");
} catch (NoSuchMethodException | SecurityException e) {
// If the method is not implicitly declared in this ViewGroup,
// Then try to get the method from its superclass
@@ -333,8 +340,8 @@ class XMLLayoutInflater extends BaseLayoutInflater {
// No null check here
// If we reach here, then method is definitely not null
- method.setAccessible(true);
- return (ViewGroup.LayoutParams) method.invoke(parent);
+ method.setAccessible (true);
+ return (ViewGroup.LayoutParams) method.invoke (parent);
} catch (Throwable th) {
throw new InflateException ("Unable to create layout params for parent: " + parent, th);
}
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/view/ViewAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/view/ViewAttrAdapter.java
index 529a6cb6b..a1b7bf2f3 100644
--- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/view/ViewAttrAdapter.java
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/view/ViewAttrAdapter.java
@@ -19,13 +19,7 @@ package com.itsaky.inflater.adapters.android.view;
import android.content.Context;
import android.content.res.Resources;
-import android.graphics.Color;
-import android.graphics.NinePatch;
import android.graphics.PorterDuff;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.NinePatchDrawable;
import android.os.Build;
import android.util.DisplayMetrics;
import android.util.TypedValue;
@@ -36,22 +30,15 @@ import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.core.content.ContextCompat;
import androidx.core.view.GravityCompat;
-import com.blankj.utilcode.util.ImageUtils;
import com.itsaky.androidide.utils.Logger;
import com.itsaky.inflater.IAttribute;
import com.itsaky.inflater.IAttributeAdapter;
import com.itsaky.inflater.IDTable;
import com.itsaky.inflater.IResourceFinder;
-import com.sdsmdg.harjot.vectormaster.VectorMasterDrawable;
+import com.itsaky.inflater.util.CommonParseUtils;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.File;
-import java.lang.reflect.Field;
import java.util.regex.Pattern;
/**
@@ -61,18 +48,17 @@ import java.util.regex.Pattern;
*
* @author Akash Yadav
*/
-public class ViewAttrAdapter implements IAttributeAdapter {
-
- private static final Pattern HEX_COLOR = Pattern.compile("#[a-fA-F0-9]{6,8}");
+public class ViewAttrAdapter extends CommonParseUtils implements IAttributeAdapter {
protected static final Logger LOG = Logger.instance ("BaseViewAttrAdapter");
- public static final int ENUM_NOT_FOUND = -4116;
- protected IResourceFinder resFinder;
+ public ViewAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
+ super (resourceFinder, displayMetrics);
+ }
@Override
public void setResourceFinder (IResourceFinder resourceFinder) {
- this.resFinder = resourceFinder;
+ this.resourceFinder = resourceFinder;
}
@Override
@@ -87,7 +73,6 @@ public class ViewAttrAdapter implements IAttributeAdapter {
final String value = attribute.getValue();
final ViewGroup.LayoutParams params = view.getLayoutParams();
final Resources res = view.getResources();
- final DisplayMetrics dm = res.getDisplayMetrics();
final Context ctx = view.getContext();
if (!canHandleNamespace(namespace)) {
@@ -97,10 +82,10 @@ public class ViewAttrAdapter implements IAttributeAdapter {
boolean handled = true;
switch (name) {
case "layout_height" :
- params.height = parseDimension (value, -2, dm);
+ params.height = parseDimension (value, -2, displayMetrics);
break;
case "layout_width" :
- params.width = parseDimension (value, -2, dm);
+ params.width = parseDimension (value, -2, displayMetrics);
break;
case "alpha" :
view.setAlpha(parseFloat(value));
@@ -135,13 +120,13 @@ public class ViewAttrAdapter implements IAttributeAdapter {
view.setDuplicateParentStateEnabled(parseBoolean(value));
break;
case "elevation" :
- view.setElevation(parseDimension(value, 0, dm));
+ view.setElevation(parseDimension(value, 0, displayMetrics));
break;
case "fadeScrollbars" :
view.setScrollbarFadingEnabled(parseBoolean(value));
break;
case "fadingEdgeLength" :
- view.setFadingEdgeLength(parseDimension(value, 0, dm));
+ view.setFadingEdgeLength(parseDimension(value, 0, displayMetrics));
break;
case "filterTouchesWhenObscured" :
view.setFilterTouchesWhenObscured(parseBoolean(value));
@@ -162,37 +147,37 @@ public class ViewAttrAdapter implements IAttributeAdapter {
view.setId(parseId (value));
break;
case "minHeight" :
- view.setMinimumHeight(parseDimension(value, 0, dm));
+ view.setMinimumHeight(parseDimension(value, 0, displayMetrics));
break;
case "minWidth" :
- view.setMinimumWidth(parseDimension(value, 0, dm));
+ view.setMinimumWidth(parseDimension(value, 0, displayMetrics));
break;
case "padding" :
- final int padding = parseDimension(value, 0, dm);
+ final int padding = parseDimension(value, 0, displayMetrics);
view.setPaddingRelative(padding, padding, padding, padding);
break;
case "paddingLeft" :
- final int paddingLeft = parseDimension(value, 0, dm);
+ final int paddingLeft = parseDimension(value, 0, displayMetrics);
view.setPadding(paddingLeft, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom());
break;
case "paddingTop" :
- final int paddingTop = parseDimension(value, 0, dm);
+ final int paddingTop = parseDimension(value, 0, displayMetrics);
view.setPadding(view.getPaddingLeft(), paddingTop, view.getPaddingRight(), view.getPaddingBottom());
break;
case "paddingRight" :
- final int paddingRight = parseDimension(value, 0, dm);
+ final int paddingRight = parseDimension(value, 0, displayMetrics);
view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), paddingRight, view.getPaddingBottom());
break;
case "paddingBottom" :
- final int paddingBottom = parseDimension(value, 0, dm);
+ final int paddingBottom = parseDimension(value, 0, displayMetrics);
view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), view.getPaddingRight(), paddingBottom);
break;
case "paddingStart" :
- final int paddingStart = parseDimension(value, 0, dm);
+ final int paddingStart = parseDimension(value, 0, displayMetrics);
view.setPaddingRelative(paddingStart, view.getPaddingTop(), view.getPaddingEnd(), view.getPaddingBottom());
break;
case "paddingEnd" :
- final int paddingEnd = parseDimension(value, 0, dm);
+ final int paddingEnd = parseDimension(value, 0, displayMetrics);
view.setPaddingRelative(view.getPaddingStart(), view.getPaddingTop(), paddingEnd, view.getPaddingBottom());
break;
case "rotation" :
@@ -253,7 +238,7 @@ public class ViewAttrAdapter implements IAttributeAdapter {
// ----- Handle attributes related to parent ------
if (!handled && params instanceof LinearLayout.LayoutParams) {
- handled = handleLinearLayoutParams ((LinearLayout.LayoutParams) params, name, value, dm);
+ handled = handleLinearLayoutParams ((LinearLayout.LayoutParams) params, name, value, displayMetrics);
}
if (!handled && params instanceof RelativeLayout.LayoutParams) {
@@ -261,7 +246,7 @@ public class ViewAttrAdapter implements IAttributeAdapter {
}
if (!handled && params instanceof ViewGroup.MarginLayoutParams) {
- handled = handleMarginParams((ViewGroup.MarginLayoutParams) params, name, value, dm);
+ handled = handleMarginParams((ViewGroup.MarginLayoutParams) params, name, value, displayMetrics);
}
view.setLayoutParams(params);
@@ -535,24 +520,11 @@ public class ViewAttrAdapter implements IAttributeAdapter {
protected String parseString(@NonNull String value) {
if (value.startsWith("@")) {
- return parseString(resFinder.findString(value.substring("@string/".length())));
+ return parseString(resourceFinder.findString(value.substring("@string/".length())));
}
return value;
}
- protected boolean parseBoolean(@NonNull String value) {
- if (!value.startsWith("@")) {
- if ("true".equals(value)) {
- return true;
- } else if ("false".equals(value)) {
- return false;
- }
- } else {
- // TODO Find resource value in booleans.xml
- }
- return false;
- }
-
protected PorterDuff.Mode parsePorterDuffMode(@NonNull String mode) {
switch (mode) {
case "add" :
@@ -572,121 +544,6 @@ public class ViewAttrAdapter implements IAttributeAdapter {
}
}
- protected Drawable parseDrawable (String value, final Context ctx) {
- if (HEX_COLOR.matcher(value).matches()) {
- return drawableForColor(value);
- } else if (value.startsWith("@")) {
-
- // First check if this is a reference to an android resource
- if (value.startsWith("@android:")) {
- final String typeAndValue = value.substring("@android:".length());
- final String[] split = typeAndValue.split(Pattern.quote("/")); // For @android:color/white, it will be ["color", "white"]
- final String type = split[0];
- final String typeVal = split[1];
- final int id = findAndroidResId (type, typeVal);
-
- if (id != -1) {
- switch (type) {
- case "color" :
- return drawableForColor(ContextCompat.getColor(ctx, id));
- case "drawable" :
- return ContextCompat.getDrawable(ctx, id);
- default :
- return newTransparentDrawable();
- }
- }
-
- } else {
- // We found a reference to another resource
- if (value.startsWith("@drawable/")) {
- final File drawable = resFinder.findDrawable (value.substring("@drawable/".length()));
-
- if (drawable == null) {
- return null;
- }
-
- if (drawable.getName ().endsWith (".xml")) {
- // TODO This might not always be a vector drawable
- // Implement a drawable parser that can parse everything listed here: https://developer.android.com/guide/topics/resources/drawable-resource
- try {
- return VectorMasterDrawable.fromXMLFile (drawable);
- } catch (XmlPullParserException e) {
- LOG.error ("Failed to parse XML Drawable", e);
- return null;
- }
- } else {
- return parseImageDrawable (ctx, drawable);
- }
- } else if (value.startsWith("@color/")) {
- final String color = resFinder.findColor(value.substring("@color/".length()));
- // TODO Check if this color resource is a selector
- return parseDrawable(color, ctx);
- }
- }
- }
- return newTransparentDrawable();
- }
-
- // NinePatch has not been tested
- @Nullable
- private Drawable parseImageDrawable (final Context context, final File file) {
- final var bitmap = ImageUtils.getBitmap (file);
- if (bitmap == null) {
- return null;
- }
-
- final var chunk = bitmap.getNinePatchChunk ();
- if (NinePatch.isNinePatchChunk (chunk)) {
- return new NinePatchDrawable (context.getResources (), new NinePatch (bitmap, chunk));
- }
-
- return new BitmapDrawable (context.getResources (), bitmap);
- }
-
- private int findAndroidResId(String type, String name) {
- try {
- final Class> typeClass = android.R.class.getClassLoader().loadClass("android.R$" + type);
- final Field typeField = typeClass.getDeclaredField(name);
- typeField.setAccessible(true);
- return (int) typeField.get(null);
- } catch (Throwable th) {
- return -1;
- }
- }
-
- protected int parseColor (String color, final Context ctx) {
- if (HEX_COLOR.matcher(color).matches()) {
- try {
- return Color.parseColor(color);
- } catch (Throwable th) {
- // Ignored
- }
- } else if (color.startsWith("@color/")) {
- return parseColor(resFinder.findColor(color.substring("@color/".length())), ctx);
- } else if (color.startsWith("@android:color/")) {
- final int id = findAndroidResId("color", color.substring("@android:color/".length()));
- return ContextCompat.getColor(ctx, id);
- }
-
- return Color.parseColor("#00ffffff");
- }
-
- protected Drawable drawableForColor (String color) {
- try {
- return drawableForColor(Color.parseColor(color));
- } catch (Throwable th) {
- return newTransparentDrawable();
- }
- }
-
- protected Drawable drawableForColor (int color) {
- return new ColorDrawable (color);
- }
-
- protected Drawable newTransparentDrawable () {
- return new ColorDrawable (Color.TRANSPARENT);
- }
-
protected float parseFloat (String value) {
try {
return Float.parseFloat(value);
@@ -716,7 +573,7 @@ public class ViewAttrAdapter implements IAttributeAdapter {
return (int) TypedValue.applyDimension(getUnitForDimensionType(dimensionType), dimen, dm);
} else if (c == '@') {
String name = value.substring("@dimen/".length());
- return parseDimension(resFinder.findDimension(name), defaultValue, dm);
+ return parseDimension(resourceFinder.findDimension(name), defaultValue, dm);
} else if (Character.isLetter(c)) {
// This could be one of the following :
// 1. match_parent
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/view/ViewGroupAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/view/ViewGroupAttrAdapter.java
index dc56388bb..a3adfebe7 100644
--- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/view/ViewGroupAttrAdapter.java
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/view/ViewGroupAttrAdapter.java
@@ -18,12 +18,14 @@
package com.itsaky.inflater.adapters.android.view;
import android.animation.LayoutTransition;
+import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.itsaky.inflater.IAttribute;
+import com.itsaky.inflater.IResourceFinder;
import org.jetbrains.annotations.Contract;
@@ -34,6 +36,10 @@ import org.jetbrains.annotations.Contract;
*/
public class ViewGroupAttrAdapter extends ViewAttrAdapter {
+ public ViewGroupAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
+ super (resourceFinder, displayMetrics);
+ }
+
@Override
public boolean isApplicableTo(View view) {
return view instanceof ViewGroup;
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AbsSeekBarAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AbsSeekBarAttrAdapter.java
index 6c297251f..872cc745b 100644
--- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AbsSeekBarAttrAdapter.java
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AbsSeekBarAttrAdapter.java
@@ -17,10 +17,12 @@
package com.itsaky.inflater.adapters.android.widget;
+import android.util.DisplayMetrics;
import android.view.View;
import android.widget.AbsSeekBar;
import androidx.annotation.NonNull;
import com.itsaky.inflater.IAttribute;
+import com.itsaky.inflater.IResourceFinder;
/**
* Attribute adapter for handling attributes related to
@@ -29,7 +31,11 @@ import com.itsaky.inflater.IAttribute;
* @author Akash Yadav
*/
public abstract class AbsSeekBarAttrAdapter extends ProgressBarAttrAdapter {
-
+
+ public AbsSeekBarAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
+ super (resourceFinder, displayMetrics);
+ }
+
@Override
public boolean isApplicableTo(View view) {
return view instanceof AbsSeekBar;
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AbsSpinnerAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AbsSpinnerAttrAdapter.java
index f7ce2358c..15373b144 100644
--- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AbsSpinnerAttrAdapter.java
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AbsSpinnerAttrAdapter.java
@@ -17,13 +17,21 @@
package com.itsaky.inflater.adapters.android.widget;
+import android.util.DisplayMetrics;
import android.view.View;
import android.widget.AbsSpinner;
+import androidx.annotation.NonNull;
+
import com.itsaky.inflater.IAttribute;
+import com.itsaky.inflater.IResourceFinder;
public class AbsSpinnerAttrAdapter extends AdapterViewAttrAdapter {
-
+
+ public AbsSpinnerAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
+ super (resourceFinder, displayMetrics);
+ }
+
@Override
public boolean isApplicableTo(View view) {
return view instanceof AbsSpinner;
@@ -45,7 +53,7 @@ public class AbsSpinnerAttrAdapter extends AdapterViewAttrAdapter {
switch (name) {
case "entries" :
- final var adapter = newSimpleAdapter(context, resFinder.findArray(value));
+ final var adapter = newSimpleAdapter(context, resourceFinder.findArray(value));
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
break;
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AdapterViewAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AdapterViewAttrAdapter.java
index a4ebbb886..a3d6378a6 100644
--- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AdapterViewAttrAdapter.java
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/AdapterViewAttrAdapter.java
@@ -18,11 +18,15 @@
package com.itsaky.inflater.adapters.android.widget;
import android.content.Context;
+import android.util.DisplayMetrics;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
+import androidx.annotation.NonNull;
+
import com.itsaky.inflater.IAttribute;
+import com.itsaky.inflater.IResourceFinder;
import com.itsaky.inflater.adapters.android.view.ViewGroupAttrAdapter;
/**
@@ -32,7 +36,11 @@ import com.itsaky.inflater.adapters.android.view.ViewGroupAttrAdapter;
* @author Akash Yadav
*/
public abstract class AdapterViewAttrAdapter extends ViewGroupAttrAdapter {
-
+
+ public AdapterViewAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
+ super (resourceFinder, displayMetrics);
+ }
+
@Override
public boolean isApplicableTo(View view) {
return view instanceof AdapterView;
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ButtonAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ButtonAttrAdapter.java
index d704d7829..cb653f4bc 100644
--- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ButtonAttrAdapter.java
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ButtonAttrAdapter.java
@@ -17,10 +17,14 @@
package com.itsaky.inflater.adapters.android.widget;
+import android.util.DisplayMetrics;
import android.view.View;
import android.widget.Button;
+import androidx.annotation.NonNull;
+
import com.itsaky.inflater.IAttribute;
+import com.itsaky.inflater.IResourceFinder;
/**
* Adapter for handling attributes related to Button.
@@ -31,7 +35,11 @@ import com.itsaky.inflater.IAttribute;
* @author Akash Yadav
*/
public class ButtonAttrAdapter extends TextViewAttrAdapter {
-
+
+ public ButtonAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
+ super (resourceFinder, displayMetrics);
+ }
+
@Override
public boolean isApplicableTo(View view) {
return view instanceof Button;
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/CheckBoxAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/CheckBoxAttrAdapter.java
index 3ef08395d..216a730a7 100644
--- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/CheckBoxAttrAdapter.java
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/CheckBoxAttrAdapter.java
@@ -16,16 +16,24 @@
*/
package com.itsaky.inflater.adapters.android.widget;
+import android.util.DisplayMetrics;
import android.view.View;
import android.widget.CheckBox;
+import androidx.annotation.NonNull;
+
import com.itsaky.inflater.IAttribute;
+import com.itsaky.inflater.IResourceFinder;
/**
* Attribute handler for handling attributes related to CheckBox
*/
public class CheckBoxAttrAdapter extends CompondButtonAttrAdapter {
-
+
+ public CheckBoxAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
+ super (resourceFinder, displayMetrics);
+ }
+
@Override
public boolean isApplicableTo(View view) {
return view instanceof CheckBox;
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/CheckedTextViewAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/CheckedTextViewAttrAdapter.java
index 91d3d5ae4..816762e9b 100644
--- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/CheckedTextViewAttrAdapter.java
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/CheckedTextViewAttrAdapter.java
@@ -16,10 +16,14 @@
*/
package com.itsaky.inflater.adapters.android.widget;
+import android.util.DisplayMetrics;
import android.view.View;
import android.widget.CheckedTextView;
+import androidx.annotation.NonNull;
+
import com.itsaky.inflater.IAttribute;
+import com.itsaky.inflater.IResourceFinder;
/**
* Attribute handler for handling attributes related to
@@ -28,7 +32,11 @@ import com.itsaky.inflater.IAttribute;
* @author Akash Yadav
*/
public class CheckedTextViewAttrAdapter extends TextViewAttrAdapter {
-
+
+ public CheckedTextViewAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
+ super (resourceFinder, displayMetrics);
+ }
+
@Override
public boolean isApplicableTo(View view) {
return view instanceof CheckedTextView;
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/CompondButtonAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/CompondButtonAttrAdapter.java
index ad89d6122..84629c469 100644
--- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/CompondButtonAttrAdapter.java
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/CompondButtonAttrAdapter.java
@@ -1,10 +1,14 @@
package com.itsaky.inflater.adapters.android.widget;
import android.content.Context;
+import android.util.DisplayMetrics;
import android.view.View;
import android.widget.CompoundButton;
+import androidx.annotation.NonNull;
+
import com.itsaky.inflater.IAttribute;
+import com.itsaky.inflater.IResourceFinder;
/**
* Attribute handler for handling attibutes to CompoundButton
@@ -12,7 +16,11 @@ import com.itsaky.inflater.IAttribute;
* @author Akash Yadav
*/
public class CompondButtonAttrAdapter extends ButtonAttrAdapter {
-
+
+ public CompondButtonAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
+ super (resourceFinder, displayMetrics);
+ }
+
@Override
public boolean isApplicableTo(View view) {
return view instanceof CompoundButton;
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/EditTextAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/EditTextAttrAdapter.java
index e182b0284..81803be1e 100644
--- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/EditTextAttrAdapter.java
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/EditTextAttrAdapter.java
@@ -17,13 +17,21 @@
package com.itsaky.inflater.adapters.android.widget;
+import android.util.DisplayMetrics;
import android.view.View;
import android.widget.EditText;
+import androidx.annotation.NonNull;
+
import com.itsaky.inflater.IAttribute;
+import com.itsaky.inflater.IResourceFinder;
public class EditTextAttrAdapter extends TextViewAttrAdapter {
+ public EditTextAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
+ super (resourceFinder, displayMetrics);
+ }
+
@Override
public boolean isApplicableTo(View view) {
return view instanceof EditText;
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ImageButtonAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ImageButtonAttrAdapter.java
index 16caa6667..56505e92e 100644
--- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ImageButtonAttrAdapter.java
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ImageButtonAttrAdapter.java
@@ -16,10 +16,14 @@
*/
package com.itsaky.inflater.adapters.android.widget;
+import android.util.DisplayMetrics;
import android.view.View;
import android.widget.ImageButton;
+import androidx.annotation.NonNull;
+
import com.itsaky.inflater.IAttribute;
+import com.itsaky.inflater.IResourceFinder;
/**
* Attribute handler for handling attributes related to
@@ -29,6 +33,10 @@ import com.itsaky.inflater.IAttribute;
*/
public class ImageButtonAttrAdapter extends ImageViewAttrAdapter {
+ public ImageButtonAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
+ super (resourceFinder, displayMetrics);
+ }
+
@Override
public boolean isApplicableTo(View view) {
return view instanceof ImageButton;
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ImageViewAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ImageViewAttrAdapter.java
index 404144a16..34af8afce 100644
--- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ImageViewAttrAdapter.java
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ImageViewAttrAdapter.java
@@ -22,8 +22,11 @@ import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;
import android.view.View;
import android.widget.ImageView;
+
+import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import com.itsaky.inflater.IAttribute;
+import com.itsaky.inflater.IResourceFinder;
import com.itsaky.inflater.adapters.android.view.ViewAttrAdapter;
/**
@@ -33,7 +36,11 @@ import com.itsaky.inflater.adapters.android.view.ViewAttrAdapter;
* @author Akash Yadav
*/
public class ImageViewAttrAdapter extends ViewAttrAdapter {
-
+
+ public ImageViewAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
+ super (resourceFinder, displayMetrics);
+ }
+
@Override
public boolean isApplicableTo(View view) {
return view instanceof ImageView;
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/LinearLayoutAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/LinearLayoutAttrAdapter.java
index 6d5a6772b..748d452da 100644
--- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/LinearLayoutAttrAdapter.java
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/LinearLayoutAttrAdapter.java
@@ -16,12 +16,14 @@
*/
package com.itsaky.inflater.adapters.android.widget;
+import android.util.DisplayMetrics;
import android.view.View;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import com.itsaky.inflater.IAttribute;
+import com.itsaky.inflater.IResourceFinder;
import com.itsaky.inflater.adapters.android.view.ViewGroupAttrAdapter;
/**
@@ -30,7 +32,11 @@ import com.itsaky.inflater.adapters.android.view.ViewGroupAttrAdapter;
* @author Akash Yadav
*/
public class LinearLayoutAttrAdapter extends ViewGroupAttrAdapter {
-
+
+ public LinearLayoutAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
+ super (resourceFinder, displayMetrics);
+ }
+
@Override
public boolean isApplicableTo(View view) {
return view instanceof LinearLayout;
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ProgressBarAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ProgressBarAttrAdapter.java
index 551af7b93..b46cc3220 100644
--- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ProgressBarAttrAdapter.java
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/ProgressBarAttrAdapter.java
@@ -21,7 +21,11 @@ import android.content.Context;
import android.util.DisplayMetrics;
import android.view.View;
import android.widget.ProgressBar;
+
+import androidx.annotation.NonNull;
+
import com.itsaky.inflater.IAttribute;
+import com.itsaky.inflater.IResourceFinder;
import com.itsaky.inflater.adapters.android.view.ViewAttrAdapter;
/**
@@ -31,7 +35,11 @@ import com.itsaky.inflater.adapters.android.view.ViewAttrAdapter;
* @author Akash Yadav
*/
public class ProgressBarAttrAdapter extends ViewAttrAdapter {
-
+
+ public ProgressBarAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
+ super (resourceFinder, displayMetrics);
+ }
+
@Override
public boolean isApplicableTo(View view) {
return view instanceof ProgressBar;
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/RadioButtonAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/RadioButtonAttrAdapter.java
index 6934f29f3..8fe8532c2 100644
--- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/RadioButtonAttrAdapter.java
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/RadioButtonAttrAdapter.java
@@ -17,10 +17,14 @@
package com.itsaky.inflater.adapters.android.widget;
+import android.util.DisplayMetrics;
import android.view.View;
import android.widget.RadioButton;
+import androidx.annotation.NonNull;
+
import com.itsaky.inflater.IAttribute;
+import com.itsaky.inflater.IResourceFinder;
/**
* Attribute adapter for handling attributes related to
@@ -29,7 +33,11 @@ import com.itsaky.inflater.IAttribute;
* @author Akash Yadav
*/
public class RadioButtonAttrAdapter extends CompondButtonAttrAdapter {
-
+
+ public RadioButtonAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
+ super (resourceFinder, displayMetrics);
+ }
+
@Override
public boolean isApplicableTo(View view) {
return view instanceof RadioButton;
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/RelativeLayoutAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/RelativeLayoutAttrAdapter.java
index ebb1f3c6a..cc6481392 100644
--- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/RelativeLayoutAttrAdapter.java
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/RelativeLayoutAttrAdapter.java
@@ -16,9 +16,14 @@
*/
package com.itsaky.inflater.adapters.android.widget;
+import android.util.DisplayMetrics;
import android.view.View;
import android.widget.RelativeLayout;
+
+import androidx.annotation.NonNull;
+
import com.itsaky.inflater.IAttribute;
+import com.itsaky.inflater.IResourceFinder;
import com.itsaky.inflater.adapters.android.view.ViewGroupAttrAdapter;
/**
@@ -28,7 +33,11 @@ import com.itsaky.inflater.adapters.android.view.ViewGroupAttrAdapter;
* @author Akash Yadav
*/
public class RelativeLayoutAttrAdapter extends ViewGroupAttrAdapter {
-
+
+ public RelativeLayoutAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
+ super (resourceFinder, displayMetrics);
+ }
+
@Override
public boolean isApplicableTo(View view) {
return view instanceof RelativeLayout;
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/SeekBarAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/SeekBarAttrAdapter.java
index b537d706d..7c57f4a20 100644
--- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/SeekBarAttrAdapter.java
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/SeekBarAttrAdapter.java
@@ -17,12 +17,14 @@
package com.itsaky.inflater.adapters.android.widget;
+import android.util.DisplayMetrics;
import android.view.View;
import android.widget.SeekBar;
import androidx.annotation.NonNull;
import com.itsaky.inflater.IAttribute;
+import com.itsaky.inflater.IResourceFinder;
/**
* Attribute adapter for handling attributes related to
@@ -31,7 +33,11 @@ import com.itsaky.inflater.IAttribute;
* @author Akash Yadav
*/
public class SeekBarAttrAdapter extends AbsSeekBarAttrAdapter {
-
+
+ public SeekBarAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
+ super (resourceFinder, displayMetrics);
+ }
+
@Override
public boolean isApplicableTo(View view) {
return view instanceof SeekBar;
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/SpinnerAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/SpinnerAttrAdapter.java
index 1ff54a229..f868e9bd5 100644
--- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/SpinnerAttrAdapter.java
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/SpinnerAttrAdapter.java
@@ -17,12 +17,14 @@
package com.itsaky.inflater.adapters.android.widget;
+import android.util.DisplayMetrics;
import android.view.View;
import android.widget.Spinner;
import androidx.annotation.NonNull;
import com.itsaky.inflater.IAttribute;
+import com.itsaky.inflater.IResourceFinder;
/**
* Attribute adapter for handling attributes related to
@@ -31,7 +33,11 @@ import com.itsaky.inflater.IAttribute;
* @author Akash Yadav
*/
public class SpinnerAttrAdapter extends AbsSpinnerAttrAdapter {
-
+
+ public SpinnerAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
+ super (resourceFinder, displayMetrics);
+ }
+
@Override
public boolean isApplicableTo(View view) {
return view instanceof Spinner;
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/TextViewAttrAdapter.java b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/TextViewAttrAdapter.java
index a6aa9ef64..960ccb204 100644
--- a/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/TextViewAttrAdapter.java
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/adapters/android/widget/TextViewAttrAdapter.java
@@ -33,6 +33,7 @@ import androidx.annotation.NonNull;
import com.blankj.utilcode.util.SizeUtils;
import com.itsaky.inflater.IAttribute;
+import com.itsaky.inflater.IResourceFinder;
import com.itsaky.inflater.adapters.android.view.ViewAttrAdapter;
import java.util.regex.Pattern;
@@ -42,7 +43,11 @@ import java.util.regex.Pattern;
* @author Akash Yadav
*/
public class TextViewAttrAdapter extends ViewAttrAdapter {
-
+
+ public TextViewAttrAdapter (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
+ super (resourceFinder, displayMetrics);
+ }
+
@Override
public boolean isApplicableTo(View view) {
return view instanceof TextView;
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/drawable/DrawableParserFactory.java b/xml-inflater/src/main/java/com/itsaky/inflater/drawable/DrawableParserFactory.java
new file mode 100644
index 000000000..f6ab721fc
--- /dev/null
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/drawable/DrawableParserFactory.java
@@ -0,0 +1,142 @@
+/*
+ * This file is part of AndroidIDE.
+ *
+ * AndroidIDE is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AndroidIDE is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with AndroidIDE. If not, see .
+ */
+
+package com.itsaky.inflater.drawable;
+
+import android.content.Context;
+import android.graphics.NinePatch;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.NinePatchDrawable;
+import android.util.DisplayMetrics;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.blankj.utilcode.util.FileIOUtils;
+import com.blankj.utilcode.util.ImageUtils;
+import com.itsaky.inflater.IResourceFinder;
+import com.sdsmdg.harjot.vectormaster.VectorMasterDrawable;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+
+/**
+ * Utility methods for creating instances of suitable drawable parsers for a file
+ * or an XML code.
+ *
+ * @author Akash Yadav
+ */
+public abstract class DrawableParserFactory {
+
+ /**
+ * Create a new drawable parser for the given file. If the given file is not an XML Document,
+ * then a no-op parser is returned which simply returns the drawable for that file. If the file
+ * is an XML document, then a suitable parser is determined by looking at the root tag of the
+ * drawable.
+ *
+ * @param context The context. This will be used for obtaining app resources.
+ * @param file The file to parse.
+ * @param resourceFinder The resource finder. Used for finding references to another resources.
+ * @return A new {@link IDrawableParser} instance if the inputs are valid, or {@code null} if there were any
+ * issues creating the parser.
+ * @throws XmlPullParserException Thrown by the {@link XmlPullParser#setInput(Reader)}.
+ * @throws IOException Thrown by {@link XmlPullParser#next()}
+ */
+ @Nullable
+ public static IDrawableParser newParser (@NonNull final Context context, @NonNull final File file, final IResourceFinder resourceFinder) throws XmlPullParserException, IOException {
+ if (file.getName ().endsWith (".xml")) {
+ final var code = FileIOUtils.readFile2String (file);
+ return DrawableParserFactory.newXmlDrawableParser (code, resourceFinder, context.getResources ().getDisplayMetrics ());
+ } else {
+ final var bitmap = ImageUtils.getBitmap (file);
+ if (bitmap == null) {
+ return null;
+ }
+
+ final var chunk = bitmap.getNinePatchChunk ();
+ if (NinePatch.isNinePatchChunk (chunk)) {
+ return new NoParser (new NinePatchDrawable (context.getResources (), new NinePatch (bitmap, chunk)));
+ }
+
+ return new NoParser (new BitmapDrawable (context.getResources (), bitmap));
+ }
+ }
+
+ /**
+ * Create a new drawable parser for the given xml code.
+ *
+ * @param xmlDrawable The XML code. This must be valid.
+ * @param resourceFinder The resource finder. Used for finding references to another resources.
+ * @param displayMetrics This will be used by generated parsers to parse dimension vlaues.
+ * @return A new {@link IDrawableParser} instance if the inputs are valid, or {@code null} if there were any
+ * issues creating the parser.
+ * @throws XmlPullParserException Thrown by the {@link XmlPullParser#setInput(Reader)}.
+ * @throws IOException Thrown by {@link XmlPullParser#next()}
+ */
+ @Nullable
+ public static IDrawableParser newXmlDrawableParser (String xmlDrawable, IResourceFinder resourceFinder, DisplayMetrics displayMetrics) throws XmlPullParserException, IOException {
+ final var factory = XmlPullParserFactory.newInstance ();
+ factory.setNamespaceAware (true);
+
+ final var parser = factory.newPullParser ();
+ parser.setInput (new StringReader (xmlDrawable));
+
+ var event = parser.getEventType ();
+ while (event != XmlPullParser.END_DOCUMENT) {
+ if (event == XmlPullParser.START_TAG) {
+ final var name = parser.getName ();
+ switch (name) {
+ case "shape" :
+ return new ShapeDrawableParser (parser, resourceFinder, displayMetrics);
+ case "vector" :
+ return new NoParser (VectorMasterDrawable.fromXML (xmlDrawable));
+ default:
+ return null;
+ }
+ }
+ event = parser.next ();
+ }
+
+ // We don't support parsing of this type of drawable
+ return null;
+ }
+
+ /**
+ * Instead of parsing anything, this returns the provided drawable.
+ */
+ private static class NoParser extends IDrawableParser {
+
+ private final Drawable parsed;
+
+ protected NoParser (final Drawable parsed) {
+ super (null, null, null);
+ this.parsed = parsed;
+ }
+
+ @Override
+ public Drawable parse () {
+ return parsed;
+ }
+ }
+}
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/drawable/IDrawableParser.java b/xml-inflater/src/main/java/com/itsaky/inflater/drawable/IDrawableParser.java
new file mode 100644
index 000000000..2bf68501e
--- /dev/null
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/drawable/IDrawableParser.java
@@ -0,0 +1,74 @@
+/*
+ * This file is part of AndroidIDE.
+ *
+ * AndroidIDE is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AndroidIDE is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with AndroidIDE. If not, see .
+ */
+
+package com.itsaky.inflater.drawable;
+
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.util.DisplayMetrics;
+
+import com.itsaky.inflater.IResourceFinder;
+import com.itsaky.inflater.util.CommonParseUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Base class for drawable parsers.
+ *
+ * @author Akash Yadav
+ */
+public abstract class IDrawableParser extends CommonParseUtils {
+
+ protected final XmlPullParser parser;
+
+ protected IDrawableParser (XmlPullParser parser, IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
+ super(resourceFinder, displayMetrics);
+ this.parser = parser;
+ }
+
+ public abstract Drawable parse () throws Exception;
+
+ protected int attrIndex (final String name) {
+ for (int i = 0; i < this.parser.getAttributeCount (); i++) {
+ if (this.parser.getAttributeName (i).equals (name)) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ protected String value (int index) {
+ return parser.getAttributeValue (index);
+ }
+
+ protected boolean isApi26() {
+ return Build.VERSION.SDK_INT >= 26;
+ }
+
+ protected boolean isApi28() {
+ return Build.VERSION.SDK_INT >= 28;
+ }
+
+ protected boolean isApi29() {
+ return Build.VERSION.SDK_INT >= 29;
+ }
+
+ protected boolean isApi30() {
+ return Build.VERSION.SDK_INT >= 30;
+ }
+}
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/drawable/ShapeDrawableParser.java b/xml-inflater/src/main/java/com/itsaky/inflater/drawable/ShapeDrawableParser.java
new file mode 100644
index 000000000..1b0fd6d4d
--- /dev/null
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/drawable/ShapeDrawableParser.java
@@ -0,0 +1,349 @@
+/*
+ * This file is part of AndroidIDE.
+ *
+ * AndroidIDE is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AndroidIDE is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with AndroidIDE. If not, see .
+ */
+
+package com.itsaky.inflater.drawable;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.util.DisplayMetrics;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.itsaky.androidide.app.BaseApplication;
+import com.itsaky.inflater.IResourceFinder;
+
+import org.jetbrains.annotations.Contract;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.util.Arrays;
+
+/**
+ * Parses a drawable whose root element is
+ * {@code }. If the parse was successful, returns a {@link GradientDrawable},
+ * otherwise {@code null}.
+ *
+ * @author Akash Yadav
+ */
+public class ShapeDrawableParser extends IDrawableParser {
+
+ protected ShapeDrawableParser (XmlPullParser parser, IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
+ super (parser, resourceFinder, displayMetrics);
+ }
+
+ @Nullable
+ @Override
+ public Drawable parse () throws Exception {
+
+ final var drawable = new GradientDrawable ();
+
+ var index = attrIndex ("shape");
+ drawable.setShape (index == -1 ? GradientDrawable.RECTANGLE : parseShape (this.parser.getAttributeValue (index)));
+
+ if (isApi29 ()) {
+ index = attrIndex ("innerRadius");
+ drawable.setInnerRadius (index == -1 ? 0 : parseDimension (value (index), 0));
+
+ index = attrIndex ("innerRadiusRatio");
+ drawable.setInnerRadiusRatio (index == -1 ? 1f : Float.parseFloat (value (index)));
+
+ index = attrIndex ("thickness");
+ drawable.setThickness (index == -1 ? 0 : parseDimension (value (index), 0));
+
+ index = attrIndex ("thicknessRatio");
+ drawable.setThicknessRatio (index == -1 ? 1f : Float.parseFloat (value (index)));
+ }
+
+ index = attrIndex ("useLevel");
+ drawable.setUseLevel (index != -1 && parseBoolean (value (index)));
+
+ var event = parser.getEventType ();
+ while (event != XmlPullParser.END_DOCUMENT) {
+ final var name = parser.getName ();
+ if (event == XmlPullParser.START_TAG) {
+ switch (name) {
+ case "corners":
+ parseCorners (drawable);
+ break;
+ case "gradient":
+ parseGradient (drawable);
+ break;
+ case "padding" :
+ parsePadding (drawable);
+ break;
+ case "size" :
+ parseSize (drawable);
+ break;
+ case "solid" :
+ parseSolid (drawable);
+ break;
+ case "stroke" :
+ parseStroke (drawable);
+ break;
+ }
+ }
+ event = parser.next ();
+ }
+
+ return drawable;
+ }
+
+ private void parseStroke (@NonNull final GradientDrawable drawable) {
+ var index = attrIndex ("width");
+ var strokeWidth = 0;
+ if (index != - 1) {
+ drawable.setStroke (strokeWidth = parseDimension (value (index), 0), Color.TRANSPARENT);
+ }
+
+ index = attrIndex ("color");
+ var strokeColor = Color.TRANSPARENT;
+ if (index != -1) {
+ drawable.setStroke (strokeWidth, strokeColor = parseColor (value (index), appContext ()));
+ }
+
+ index = attrIndex ("dashWidth");
+ var dashWidth = 0;
+ if (index != -1) {
+ drawable.setStroke (strokeWidth, strokeColor, dashWidth = parseDimension (value (index), 0), 0);
+ }
+
+ index = attrIndex ("dashGap");
+ if (index != -1) {
+ drawable.setStroke (strokeWidth, strokeColor, dashWidth, parseDimension (value (index), 0));
+ }
+ }
+
+ private void parseSolid (@NonNull final GradientDrawable drawable) {
+ var index = attrIndex ("color");
+ if (index != -1) {
+ drawable.setColor (parseColor (value (index), appContext ()));
+ }
+ }
+
+ private void parseSize (@NonNull final GradientDrawable drawable) {
+ var index = attrIndex ("width");
+ if (index != -1) {
+ drawable.setSize (parseDimension (value (index), 0), drawable.getIntrinsicHeight ());
+ }
+
+ index = attrIndex ("height");
+ if (index != -1) {
+ drawable.setSize (drawable.getIntrinsicWidth (), parseDimension (value (index), 0));
+ }
+ }
+
+ private void parsePadding (@NonNull final GradientDrawable drawable) {
+
+ // Padding is available from API 29 only
+ if (!isApi29 ()) {
+ return;
+ }
+
+ final var rect = new Rect ();
+ if (!drawable.getPadding (rect)) {
+ rect.set (0,0,0,0);
+ }
+
+ var changed = false;
+ var index = attrIndex ("left");
+ if (index != -1) {
+ rect.left = parseDimension (value (index), 0);
+ changed = true;
+ }
+
+ index = attrIndex ("top");
+ if (index != -1) {
+ rect.top = parseDimension (value (index), 0);
+ changed = true;
+ }
+
+ index = attrIndex ("right");
+ if (index != -1) {
+ rect.right = parseDimension (value (index), 0);
+ changed = true;
+ }
+
+ index = attrIndex ("bottom");
+ if (index != -1) {
+ rect.bottom = parseDimension (value (index), 0);
+ changed = true;
+ }
+
+ if (changed) {
+ drawable.setPadding (rect.left, rect.top, rect.right, rect.bottom);
+ }
+ }
+
+ private void parseGradient (@NonNull final GradientDrawable drawable) {
+ var index = attrIndex ("angle");
+ drawable.setOrientation (index == -1 ? GradientDrawable.Orientation.LEFT_RIGHT : parseGradientOrientation (value (index)));
+
+ index = attrIndex ("centerX");
+ var center = index == - 1 ? drawable.getGradientCenterX () : Float.parseFloat (value (index));
+ drawable.setGradientCenter (center, drawable.getGradientCenterY ());
+
+ index = attrIndex ("centerY");
+ center = index == - 1 ? drawable.getGradientCenterX () : Float.parseFloat (value (index));
+ drawable.setGradientCenter (drawable.getGradientCenterX (), center);
+
+ final var colors = new int [3];
+ var changed = false;
+ Arrays.fill (colors, -1);
+
+ index = attrIndex ("centerColor");
+ if (index != -1) {
+ colors [1] = parseColor (value (index), appContext ());
+ changed = true;
+ }
+
+ index = attrIndex ("endColor");
+ if (index != -1) {
+ colors [2] = parseColor (value (index), appContext ());
+ changed = true;
+ }
+
+ index = attrIndex ("gradientRadius");
+ drawable.setGradientRadius (index == -1 ? drawable.getGradientRadius () : Float.parseFloat (value (index)));
+
+ index = attrIndex ("startColor");
+ if (index != -1) {
+ colors[0] = parseColor (value (index), appContext ());
+ changed = true;
+ }
+
+ if (changed) {
+ drawable.setColors (colors);
+ }
+
+ index = attrIndex ("type");
+ drawable.setGradientType (index == -1 ? GradientDrawable.LINEAR_GRADIENT : parseGradientType (value (index)));
+
+ index = attrIndex ("useLevel");
+ drawable.setUseLevel (index != -1 && parseBoolean (value (index)));
+ }
+
+ private int parseGradientType (String value) {
+ switch (value) {
+ case "radial" :
+ return GradientDrawable.RADIAL_GRADIENT;
+ case "sweep" :
+ return GradientDrawable.SWEEP_GRADIENT;
+ case "linear" :
+ default:
+ return GradientDrawable.LINEAR_GRADIENT;
+ }
+ }
+
+ private GradientDrawable.Orientation parseGradientOrientation (String value) {
+ final var angle = Integer.parseInt (value);
+
+ // Angle must be between 0-315 and a multiple of 45
+ // Angle moves in anti-clockwise direction
+ if (angle == 45) {
+ return GradientDrawable.Orientation.BL_TR;
+ }
+
+ if (angle == 90) {
+ return GradientDrawable.Orientation.BOTTOM_TOP;
+ }
+
+ if (angle == 135) {
+ return GradientDrawable.Orientation.BR_TL;
+ }
+
+ if (angle == 180) {
+ return GradientDrawable.Orientation.RIGHT_LEFT;
+ }
+
+ if (angle == 225) {
+ return GradientDrawable.Orientation.TR_BL;
+ }
+
+ if (angle == 270) {
+ return GradientDrawable.Orientation.TOP_BOTTOM;
+ }
+
+ if (angle == 315) {
+ return GradientDrawable.Orientation.TL_BR;
+ }
+
+ // Angle 0 or any invalid valid should make the orientation left to right
+ return GradientDrawable.Orientation.LEFT_RIGHT;
+ }
+
+ private void parseCorners (@NonNull GradientDrawable drawable) {
+ int index;
+ index = attrIndex ("radius");
+ drawable.setCornerRadius (index == -1 ? 0f : parseDimension (value (index), 0));
+
+ var changed = false;
+ final var radii = new float[8];
+ Arrays.fill (radii, 0f);
+
+ index = attrIndex ("topLeftRadius");
+ if (index != -1) {
+ radii[0] = radii[1] = parseDimension (value (index), 0);
+ changed = true;
+ }
+
+ index = attrIndex ("topRightRadius");
+ if (index != -1) {
+ radii[2] = radii[3] = parseDimension (value (index), 0);
+ changed = true;
+
+ }
+
+ index = attrIndex ("bottomLeftRadius");
+ if (index != -1) {
+ radii[4] = radii[5] = parseDimension (value (index), 0);
+ changed = true;
+
+ }
+
+ index = attrIndex ("bottomRightRadius");
+ if (index != -1) {
+ radii[6] = radii[7] = parseDimension (value (index), 0);
+ changed = true;
+ }
+
+ if (changed) {
+ drawable.setCornerRadii (radii);
+ }
+ }
+
+ @Contract(pure = true)
+ private int parseShape (@NonNull String value) {
+ switch (value) {
+ case "oval" :
+ return GradientDrawable.OVAL;
+ case "line":
+ return GradientDrawable.LINE;
+ case "ring" :
+ return GradientDrawable.RING;
+ case "rectangle" :
+ default:
+ return GradientDrawable.RECTANGLE;
+ }
+ }
+
+ protected Context appContext () {
+ return BaseApplication.getBaseInstance ();
+ }
+}
diff --git a/xml-inflater/src/main/java/com/itsaky/inflater/util/CommonParseUtils.java b/xml-inflater/src/main/java/com/itsaky/inflater/util/CommonParseUtils.java
new file mode 100644
index 000000000..354f69495
--- /dev/null
+++ b/xml-inflater/src/main/java/com/itsaky/inflater/util/CommonParseUtils.java
@@ -0,0 +1,235 @@
+/*
+ * This file is part of AndroidIDE.
+ *
+ * AndroidIDE is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * AndroidIDE is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with AndroidIDE. If not, see .
+ */
+
+package com.itsaky.inflater.util;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.NinePatch;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.NinePatchDrawable;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.content.ContextCompat;
+
+import com.blankj.utilcode.util.ImageUtils;
+import com.itsaky.androidide.utils.Logger;
+import com.itsaky.inflater.IResourceFinder;
+import com.itsaky.inflater.drawable.DrawableParserFactory;
+import com.sdsmdg.harjot.vectormaster.VectorMasterDrawable;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.regex.Pattern;
+
+/**
+ * Contains methods which are commonly used by layout inflater,
+ * attribute adapters and drawable parsers.
+ *
+ * @author Akash Yadav
+ */
+public class CommonParseUtils {
+
+ private static final Pattern HEX_COLOR = Pattern.compile("#[a-fA-F0-9]{6,8}");
+
+ protected IResourceFinder resourceFinder;
+ protected final DisplayMetrics displayMetrics;
+
+ private static final Logger LOG = Logger.instance ("CommonParseUtils");
+
+ public CommonParseUtils (@NonNull IResourceFinder resourceFinder, DisplayMetrics displayMetrics) {
+ this.resourceFinder = resourceFinder;
+ this.displayMetrics = displayMetrics;
+ }
+
+ protected boolean parseBoolean(@NonNull String value) {
+ if (!value.startsWith("@")) {
+ if ("true".equals(value)) {
+ return true;
+ } else if ("false".equals(value)) {
+ return false;
+ }
+ } else {
+ // TODO Find resource value in booleans.xml
+ }
+ return false;
+ }
+
+ protected int parseDimension(final String value, int defaultValue) {
+ if (value == null) {
+ return defaultValue;
+ }
+
+ char c = value.charAt(0);
+ if (Character.isDigit(c)) {
+ // A dimension value which starts with a digit. E.g.: 1dp, 12sp, 123px, etc.
+ String dimensionVal = "";
+ int index = 0;
+ while (Character.isDigit(c = value.charAt(index))) {
+ dimensionVal += c;
+ index ++;
+ }
+
+ final int dimen = Integer.parseInt(dimensionVal);
+ final String dimensionType = value.substring(index);
+ return (int) TypedValue.applyDimension(getUnitForDimensionType(dimensionType), dimen, displayMetrics);
+ } else if (c == '@') {
+ String name = value.substring("@dimen/".length());
+ return parseDimension(resourceFinder.findDimension(name), defaultValue);
+ } else if (Character.isLetter(c)) {
+ // This could be one of the following :
+ // 1. match_parent
+ // 2. wrap_content
+ // 3. fill_parent
+ switch (value) {
+ case "match_parent" :
+ case "fill_parent" :
+ return ViewGroup.LayoutParams.MATCH_PARENT;
+ case "wrap_content" :
+ default :
+ return ViewGroup.LayoutParams.WRAP_CONTENT;
+ }
+ }
+
+ return defaultValue;
+ }
+
+ protected int getUnitForDimensionType(@NonNull String dimensionType) {
+ switch (dimensionType) {
+ case "dp" :
+ return TypedValue.COMPLEX_UNIT_DIP;
+ case "sp" :
+ return TypedValue.COMPLEX_UNIT_SP;
+ case "px" :
+ return TypedValue.COMPLEX_UNIT_PX;
+ case "pt" :
+ return TypedValue.COMPLEX_UNIT_PT;
+ case "in" :
+ return TypedValue.COMPLEX_UNIT_IN;
+ case "mm" :
+ return TypedValue.COMPLEX_UNIT_MM;
+ }
+ return TypedValue.COMPLEX_UNIT_DIP;
+ }
+
+ protected int parseColor (String color, final Context ctx) {
+ if (HEX_COLOR.matcher(color).matches()) {
+ try {
+ return Color.parseColor(color);
+ } catch (Throwable th) {
+ // Ignored
+ }
+ } else if (color.startsWith("@color/")) {
+ return parseColor(resourceFinder.findColor(color.substring("@color/".length())), ctx);
+ } else if (color.startsWith("@android:color/")) {
+ final int id = findAndroidResId("color", color.substring("@android:color/".length()));
+ return ContextCompat.getColor(ctx, id);
+ }
+
+ return Color.parseColor("#00ffffff");
+ }
+
+ protected Drawable parseDrawable (String value, final Context ctx) {
+ if (HEX_COLOR.matcher(value).matches()) {
+ return drawableForColor(value);
+ } else if (value.startsWith("@")) {
+
+ // First check if this is a reference to an android resource
+ if (value.startsWith("@android:")) {
+ final String typeAndValue = value.substring("@android:".length());
+ final String[] split = typeAndValue.split(Pattern.quote("/")); // For @android:color/white, it will be ["color", "white"]
+ final String type = split[0];
+ final String typeVal = split[1];
+ final int id = findAndroidResId (type, typeVal);
+
+ if (id != -1) {
+ switch (type) {
+ case "color" :
+ return drawableForColor(ContextCompat.getColor(ctx, id));
+ case "drawable" :
+ return ContextCompat.getDrawable(ctx, id);
+ default :
+ return newTransparentDrawable();
+ }
+ }
+
+ } else {
+ // We found a reference to another resource
+ if (value.startsWith("@drawable/")) {
+ final File drawable = resourceFinder.findDrawable (value.substring("@drawable/".length()));
+
+ if (drawable == null) {
+ return null;
+ }
+
+ try {
+ final var parser = DrawableParserFactory.newParser (ctx, drawable, resourceFinder);
+ if (parser == null) {
+ return null;
+ }
+
+ return parser.parse();
+ } catch (Exception e) {
+ LOG.error ("Error parsing drawable", e);
+ return null;
+ }
+ } else if (value.startsWith("@color/")) {
+ final String color = resourceFinder.findColor(value.substring("@color/".length()));
+ // TODO Check if this color resource is a selector
+ return parseDrawable(color, ctx);
+ }
+ }
+ }
+ return newTransparentDrawable();
+ }
+
+ private int findAndroidResId(String type, String name) {
+ try {
+ final Class> typeClass = android.R.class.getClassLoader().loadClass("android.R$" + type);
+ final Field typeField = typeClass.getDeclaredField(name);
+ typeField.setAccessible(true);
+ return (int) typeField.get(null);
+ } catch (Throwable th) {
+ return -1;
+ }
+ }
+
+ protected Drawable drawableForColor (String color) {
+ try {
+ return drawableForColor(Color.parseColor(color));
+ } catch (Throwable th) {
+ return newTransparentDrawable();
+ }
+ }
+
+ protected Drawable drawableForColor (int color) {
+ return new ColorDrawable (color);
+ }
+
+ protected Drawable newTransparentDrawable () {
+ return new ColorDrawable (Color.TRANSPARENT);
+ }
+}