package com.mozz.htmlnative.script.lua; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import com.mozz.htmlnative.HNRenderer; import com.mozz.htmlnative.HNSandBoxContext; import com.mozz.htmlnative.HtmlTag; import com.mozz.htmlnative.InheritStyleStack; import com.mozz.htmlnative.css.Styles; import com.mozz.htmlnative.css.stylehandler.LayoutStyleHandler; import com.mozz.htmlnative.css.stylehandler.StyleHandler; import com.mozz.htmlnative.css.stylehandler.StyleHandlerFactory; import com.mozz.htmlnative.dom.AttachedElement; import com.mozz.htmlnative.dom.DomElement; import com.mozz.htmlnative.exception.AttrApplyException; import com.mozz.htmlnative.parser.CssParser; import com.mozz.htmlnative.utils.MainHandlerUtils; import com.mozz.htmlnative.view.LayoutParamsLazyCreator; import org.luaj.vm2.LuaBoolean; import org.luaj.vm2.LuaString; import org.luaj.vm2.LuaTable; import org.luaj.vm2.LuaValue; import org.luaj.vm2.lib.OneArgFunction; import org.luaj.vm2.lib.ZeroArgFunction; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; /** * @author Yang Tao, 17/3/23. */ class LView extends LuaTable { private static final int INSERT_LAST = -1; private static final int INSERT_FIRST = 0; private View mView; volatile boolean mAdded; private volatile boolean mCreated; private HNSandBoxContext mContext; private DomElement mDomElement; // for cache insert information private Map<String, Object> mInlineStyleRaw; private List<LView> mToBeAdded; private int mInsertIndex = -1; private static StringBuilder sParserBuffer = new StringBuilder(); private final Object mLock = new Object(); LView(final DomElement domElement, Map<String, Object> inlineStyle, final HNSandBoxContext context) { mDomElement = domElement; mInlineStyleRaw = inlineStyle; mContext = context; mCreated = false; mAdded = false; initLuaFunction(); } /** * Used only by {@link LFindViewById}, which will look up view in existing view tree. */ LView(final View v, final HNSandBoxContext context) { this((DomElement) v.getTag(), null, context); mView = v; mCreated = true; mAdded = true; } private void initLuaFunction() { set("toString", new ZeroArgFunction() { @Override public LuaValue call() { if (mCreated) { return LView.valueOf(mView.toString()); } else { return LuaValue.NIL; } } }); set("setAttribute", new OneArgFunction() { @Override public LuaValue call(final LuaValue arg) { if (mCreated) { String style = arg.tojstring(); final Map<String, Object> styleMaps = new HashMap<>(); CssParser.parseInlineStyle(style, sParserBuffer, styleMaps); MainHandlerUtils.instance().post(new Runnable() { @Override public void run() { LayoutParamsLazyCreator tempCreator = new LayoutParamsLazyCreator (mView.getLayoutParams()); ViewGroup parent = (mView.getParent() != null && mView.getParent() instanceof ViewGroup) ? (ViewGroup) mView.getParent() : null; try { HNRenderer.renderStyle(mView.getContext(), mContext, mView, mDomElement, tempCreator, parent, styleMaps, false, null); LayoutParamsLazyCreator.createLayoutParams(tempCreator, mView .getLayoutParams()); mView.requestLayout(); } catch (AttrApplyException e) { e.printStackTrace(); } } }); } else { final Map<String, Object> newStyle = new HashMap<>(); CssParser.parseInlineStyle(arg.tojstring(), sParserBuffer, newStyle); MainHandlerUtils.instance().post(new Runnable() { @Override public void run() { synchronized (mLock) { if (mInlineStyleRaw != null) { mInlineStyleRaw.putAll(newStyle); } } } }); } return NIL; } }); set("id", new ZeroArgFunction() { @Override public LuaValue call() { if (mCreated) { Object obj = mView.getTag(); if (obj != null && obj instanceof DomElement) { return LuaString.valueOf(((DomElement) obj).getId()); } } return LuaString.valueOf(""); } } ); set("className", new ZeroArgFunction() { @Override public LuaValue call() { String[] classes = ((AttachedElement) mView.getTag()).getClazz(); LuaTable classesLua = new LuaTable(); if (classes != null && classes.length > 0) { for (String clazz : classes) { if (clazz != null) { classesLua.add(LuaValue.valueOf(clazz)); } } return classesLua; } return LuaTable.NIL; } } ); set("appendChild", new OneArgFunction() { @Override public LuaValue call(LuaValue arg) { if (arg instanceof LView) { final LView child = (LView) arg; appendTo(LView.this, child); } return LuaValue.NIL; } }); set("insertBefore", new OneArgFunction() { @Override public LuaValue call(LuaValue arg) { if (arg instanceof LView) { final LView child = (LView) arg; child.mInsertIndex = INSERT_FIRST; appendTo(LView.this, child); } return LuaValue.NIL; } } ); set("removeChild", new OneArgFunction() { @Override public LuaValue call(LuaValue arg) { if (mAdded && arg instanceof LView && mView instanceof ViewGroup) { final LView toRemoved = (LView) arg; MainHandlerUtils.instance().post(new Runnable() { @Override public void run() { if (toRemoved.mAdded) { ((ViewGroup) mView).removeView(toRemoved.mView); toRemoved.mAdded = false; } } }); } return LuaValue.NIL; } }); set("childNodes", new ZeroArgFunction() { @Override public LuaValue call() { if (mView instanceof ViewGroup && mAdded) { LuaTable children = new LuaTable(); int childCount = ((ViewGroup) mView).getChildCount(); for (int i = 0; i < childCount; i++) { LView lView = new LView(((ViewGroup) mView).getChildAt(i), mContext); children.add(lView); } return children; } return LuaValue.NIL; } } ); set("getAttribute", new OneArgFunction() { @Override public LuaValue call(LuaValue arg) { if (mAdded && mCreated) { StyleHandler styleHandler = StyleHandlerFactory.get(mView); StyleHandler extraHandler = StyleHandlerFactory.extraGet(mView); LayoutStyleHandler parentHandler = StyleHandlerFactory.parentGet(mView); Object object = Styles.getStyle(mView, arg.tojstring(), styleHandler, extraHandler, parentHandler); if (object != null) { return LuaString.valueOf(object.toString()); } else { return LuaValue.NIL; } } else { return LuaValue.NIL; } } } ); set("tagName", new ZeroArgFunction() { @Override public LuaValue call() { if (mCreated) { AttachedElement attachedElement = (AttachedElement) mView.getTag(); return LuaString.valueOf(attachedElement.getType()); } else { return LuaString.valueOf(""); } } } ); set("parentNode", new ZeroArgFunction() { @Override public LuaValue call() { if (mCreated && mAdded) { ViewParent parent = mView.getParent(); if (parent instanceof ViewGroup) { return new LView((View) parent, mContext); } } return LuaValue.NIL; } } ); set("hasChildNode", new ZeroArgFunction() { @Override public LuaValue call() { if (mCreated && mAdded) { if (mView instanceof ViewGroup) { boolean hasChild = ((ViewGroup) mView).getChildCount() > 0; return LuaBoolean.valueOf(hasChild); } } return LuaBoolean.FALSE; } } ); } private static void appendTo(LView parent, LView child) { if (!HtmlTag.isGroupingElement(parent.mDomElement.getType())) { return; } if (parent.mAdded) { generateAndroidViewAndAppend(parent, child); } else { synchronized (parent.mLock) { if (parent.mToBeAdded == null) { parent.mToBeAdded = new LinkedList<>(); } parent.mToBeAdded.add(child); } } } private static void generateAndroidViewAndAppend(final LView parent, final LView child) { if (!parent.mAdded) { return; } MainHandlerUtils.instance().post(new Runnable() { @Override public void run() { if (child.mAdded) { return; } LayoutParamsLazyCreator creator = new LayoutParamsLazyCreator(); try { if (!child.mCreated) { // This must be invoked before HNRenderer.createView child.mDomElement.setParent(parent.mDomElement); // Compute the inherit style of parent InheritStyleStack inheritStyleStack = HNRenderer.computeInheritStyle (parent.mView); child.mView = HNRenderer.createView(null, child.mDomElement, child .mContext, (ViewGroup) parent.mView, parent.mView.getContext(), null, creator, child.mContext.getSegment().getStyleSheet(), inheritStyleStack); Map<String, Object> inlineStyles; synchronized (child.mLock) { inlineStyles = child.mInlineStyleRaw; } try { HNRenderer.renderStyle(child.mView.getContext(), parent.mContext, child.mView, child.mDomElement, creator, (ViewGroup) parent .mView, inlineStyles, false, inheritStyleStack); } catch (AttrApplyException e) { e.printStackTrace(); } child.mCreated = true; } if (child.mInsertIndex == INSERT_LAST) { ((ViewGroup) parent.mView).addView(child.mView, LayoutParamsLazyCreator .createLayoutParams(parent.mView, creator)); } else { ((ViewGroup) parent.mView).addView(child.mView, child.mInsertIndex, LayoutParamsLazyCreator.createLayoutParams(parent.mView, creator)); } child.mAdded = true; if (child.mToBeAdded != null) { for (LView grandChild : child.mToBeAdded) { generateAndroidViewAndAppend(child, grandChild); } } synchronized (child.mLock) { // consume the inline style child.mInlineStyleRaw = null; child.mToBeAdded = null; } } catch (HNRenderer.HNRenderException e) { e.printStackTrace(); } } }); } @Override public int type() { return TUSERDATA; } @Override public String typename() { return TYPE_NAMES[3]; } }