package org.itsnat.droid.impl.browser;
import android.content.Context;
import android.content.res.Resources;
import android.os.Handler;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import org.itsnat.droid.ClientErrorMode;
import org.itsnat.droid.GenericHttpClient;
import org.itsnat.droid.HttpRequestResult;
import org.itsnat.droid.ItsNatDoc;
import org.itsnat.droid.ItsNatDroidScriptException;
import org.itsnat.droid.ItsNatResources;
import org.itsnat.droid.ItsNatView;
import org.itsnat.droid.OnHttpRequestListener;
import org.itsnat.droid.OnScriptErrorListener;
import org.itsnat.droid.Page;
import org.itsnat.droid.impl.ItsNatResourcesImpl;
import org.itsnat.droid.impl.browser.serveritsnat.ItsNatDocItsNatImpl;
import org.itsnat.droid.impl.browser.serveritsnat.PageItsNatImpl;
import org.itsnat.droid.impl.browser.servernotitsnat.ItsNatDocNotItsNatImpl;
import org.itsnat.droid.impl.browser.servernotitsnat.PageNotItsNatImpl;
import org.itsnat.droid.impl.dom.ResourceDescRemote;
import org.itsnat.droid.impl.domparser.XMLDOMParserContext;
import org.itsnat.droid.impl.util.MimeUtil;
import org.itsnat.droid.impl.util.MiscUtil;
import org.itsnat.droid.impl.util.UINotification;
import org.itsnat.droid.impl.xmlinflater.XMLInflaterRegistry;
import bsh.EvalError;
import bsh.Interpreter;
/**
* Esta clase se accede via script beanshell y representa el "ClientDocument" en el lado Android simétrico a los objetos JavaScript en el modo web
* Created by jmarranz on 9/06/14.
*/
public abstract class ItsNatDocImpl implements ItsNatDoc, ItsNatDocPublic
{
protected final PageImpl page;
protected ItsNatResourcesImpl itsNatResources;
protected final FragmentLayoutInserter fragmentLayoutInserter = new FragmentLayoutInserter(this);
protected final ItsNatViewNullImpl nullView = new ItsNatViewNullImpl(this); // Viene a tener el rol del objeto Window en web, útil para registrar eventos unload etc
protected final DroidEventDispatcher eventDispatcher = DroidEventDispatcher.createDroidEventDispatcher(this); // En el caso de "No ItsNat" hay un limitado soporte de inline event handlers (onclick etc)
protected Handler handler;
public ItsNatDocImpl(PageImpl page)
{
this.page = page;
}
public static ItsNatDocImpl createItsNatDoc(PageImpl page)
{
if (page instanceof PageItsNatImpl)
return new ItsNatDocItsNatImpl((PageItsNatImpl)page,page.getClientErrorMode());
else if (page instanceof PageNotItsNatImpl)
return new ItsNatDocNotItsNatImpl((PageNotItsNatImpl)page);
else
throw MiscUtil.internalError();
}
public XMLDOMParserContext getXMLDOMParserContext()
{
return getPageImpl().getXMLDOMParserContext();
}
@Override
public ItsNatResources getItsNatResources()
{
return getItsNatResourcesImpl();
}
public ItsNatResourcesImpl getItsNatResourcesImpl()
{
if (itsNatResources == null) this.itsNatResources = new ItsNatResourcesRemoteImpl(this);
return itsNatResources;
}
@Override
public ItsNatView getItsNatView(View view)
{
return getItsNatViewImpl(view);
}
public ItsNatViewNullImpl getItsNatViewNull()
{
return nullView;
}
public ItsNatViewImpl getItsNatViewImpl(View view)
{
return ItsNatViewImpl.getItsNatView(this, view);
}
public GenericHttpClientImpl createGenericHttpClientImpl()
{
GenericHttpClientImpl client = new GenericHttpClientImpl(this);
client.setOnHttpRequestErrorListener(getPageImpl().getOnHttpRequestErrorListener());
return client;
}
@Override
public GenericHttpClient createGenericHttpClient()
{
return createGenericHttpClientImpl();
}
public PageImpl getPageImpl()
{
return page;
}
public DroidEventDispatcher getDroidEventDispatcher()
{
return eventDispatcher;
}
@Override
public View getRootView()
{
return page.getInflatedXMLLayoutPageImpl().getRootView();
}
@Override
public View findViewByXMLId(String id)
{
return page.getInflatedXMLLayoutPageImpl().findViewByXMLId(id);
}
@Override
public int getResourceIdentifier(String name)
{
// Formato esperado: package:type/entry ej my.app:id/someId o bien simplemente someId
String packageName;
int posPkg = name.indexOf(':');
if (posPkg != -1)
{
packageName = null; // Tiene el package en el value, ej "android:" delegamos en Resources.getIdentifier() que lo resuelva
name = name.substring(posPkg + 1);
}
else
{
packageName = getContext().getPackageName(); // El package es necesario como parámetro sólo cuando no está en la string (recursos compilados)
}
String type;
int posType = name.indexOf('/');
if (posType != -1)
{
type = null; // Se obtiene del name
name = name.substring(posType + 1); // Extraemos el name tras el /
}
else
{
type = "id";
}
return getResourceIdentifier(name, type, packageName);
}
@Override
public int getResourceIdentifier(String name, String defType, String defPackage)
{
// http://developer.android.com/reference/android/content/res/Resources.html#getIdentifier(java.lang.String, java.lang.String, java.lang.String)
// Formato esperado: package:type/entry ej my.app:id/someId o bien type y package vienen dados como parámetros
Resources res = getContext().getResources();
int id = res.getIdentifier(name, defType, defPackage);
if (id > 0)
return id;
XMLInflaterRegistry layoutService = page.getItsNatDroidBrowserImpl().getItsNatDroidImpl().getXMLInflaterRegistry();
id = layoutService.findViewIdDynamicallyAdded(name);
return id;
}
@Override
public void appendFragment(View parentView, String markup)
{
// Este método NO es llamado directamente por el código interno generado por ItsNat Server, es público para el usuario
insertFragment(parentView,markup,null);
}
@Override
public void insertFragment(View parentView, String markup,View viewRef)
{
// Ver notas en appendFragment
fragmentLayoutInserter.insertPageFragment((ViewGroup) parentView, markup, viewRef, getXMLDOMParserContext());
}
@Override
public void eval(String code)
{
eval(code, null);
}
public void eval(String code,Object context)
{
Interpreter interp = page.getInterpreter();
try
{
//long start = System.currentTimeMillis();
interp.eval(code);
//long end = System.currentTimeMillis();
//System.out.println("LAPSE" + (end - start));
}
catch (EvalError ex)
{
PageImpl page = getPageImpl();
OnScriptErrorListener errorListener = page.getOnScriptErrorListener();
if (errorListener != null)
{
errorListener.onError(page, code, ex, context);
}
else
{
int errorMode = getClientErrorMode();
if (errorMode != ClientErrorMode.NOT_CATCH_ERRORS)
{
showErrorMessage(false, ex.getMessage());
}
else throw new ItsNatDroidScriptException(ex, code);
}
}
catch (Exception ex)
{
PageImpl page = getPageImpl();
OnScriptErrorListener errorListener = page.getOnScriptErrorListener();
if (errorListener != null)
{
errorListener.onError(page, code, ex, context);
}
else
{
int errorMode = getClientErrorMode();
if (errorMode != ClientErrorMode.NOT_CATCH_ERRORS)
{
showErrorMessage(false, ex.getMessage());
}
else throw new ItsNatDroidScriptException(ex, code);
}
}
}
public void showErrorMessage(boolean serverErr, String msg)
{
showErrorMessage(serverErr, msg, getClientErrorMode());
}
public void showErrorMessage(boolean serverErr,HttpRequestResult result,Exception ex,int errorMode)
{
String msg;
if (result != null) msg = result.getResponseText(); // Normalmente el error del servidor, en el caso de SockectTimeoutException será null y usaremos la exception
else msg = ex.getMessage();
showErrorMessage(serverErr,msg,errorMode);
}
public void showErrorMessage(boolean serverErr, String msg,int errorMode)
{
if (errorMode == ClientErrorMode.NOT_SHOW_ERRORS) return;
if (serverErr) // Pagina HTML con la excepcion del servidor
{
if ((errorMode == ClientErrorMode.SHOW_SERVER_ERRORS) ||
(errorMode == ClientErrorMode.SHOW_SERVER_AND_CLIENT_ERRORS)) // 2 = ClientErrorMode.SHOW_SERVER_ERRORS, 4 = ClientErrorMode.SHOW_SERVER_AND_CLIENT_ERRORS
alert("SERVER ERROR: " + msg);
}
else
{
if ((errorMode == ClientErrorMode.SHOW_CLIENT_ERRORS) ||
(errorMode == ClientErrorMode.SHOW_SERVER_AND_CLIENT_ERRORS)) // 3 = ClientErrorMode.SHOW_CLIENT_ERRORS, 4 = ClientErrorMode.SHOW_SERVER_AND_CLIENT_ERRORS
{
// Ha sido un error Beanshell
alert(msg);
}
}
}
protected Context getContext()
{
return page.getContext();
}
@Override
public Page getPage()
{
// Es un método público que puede ser interesante para acceder a info de la página desde beanshell (por ej acceder al Context de la página desde fuera)
return page;
}
public int getClientErrorMode()
{
return page.getClientErrorMode();
}
@Override
public void alert(Object value)
{
alert("Alert", value);
}
@Override
public void alert(String title,Object value)
{
UINotification.alert(title, value, getContext());
}
@Override
public void toast(Object value,int duration)
{
UINotification.toast(value, duration, getContext());
}
@Override
public void toast(Object value)
{
toast(value, Toast.LENGTH_SHORT);
}
@Override
public void postDelayed(Runnable task,long delay)
{
getHandler().postDelayed(task, delay);
}
public Handler getHandler()
{
if (handler == null) this.handler = new Handler(); // Se asociará (debe) al hilo UI
return handler;
}
@Override
public void downloadScript(String src)
{
// Es llamado desde ItsNat Server por ello se define en la interface ItsNatDocPublic
OnHttpRequestListener listener = new OnHttpRequestListener()
{
@Override
public void onRequest(Page page,HttpRequestResult response)
{
eval(response.getResponseText());
}
};
downloadScript(src,listener);
}
public void downloadScript(String src,OnHttpRequestListener listener)
{
downloadFile(src, MimeUtil.MIME_BEANSHELL, listener);
}
private void downloadFile(String src,String mime,OnHttpRequestListener listener)
{
PageImpl page = getPageImpl();
boolean sync = page.getPageRequestClonedImpl().isSynchronous();
String absURL = HttpUtil.composeAbsoluteURL(src, page.getPageURLBase());
GenericHttpClientImpl client = createGenericHttpClientImpl();
client.setURL(absURL)
.setOverrideMimeType(mime)
.setOnHttpRequestListener(listener);
client.request(!sync);
}
public void downloadResources(ResourceDescRemote resDescRemote,final Runnable task)
{
OnHttpRequestListener listener = new OnHttpRequestListener()
{
@Override
public void onRequest(Page page,HttpRequestResult response)
{
task.run();
}
};
PageImpl page = getPageImpl();
boolean sync = page.getPageRequestClonedImpl().isSynchronous();
DownloadResourcesHttpClient client = new DownloadResourcesHttpClient(this);
client.setOnHttpRequestErrorListenerNotFluid(page.getOnHttpRequestErrorListener());
client.setOnHttpRequestListenerNotFluid(listener);
client.request(resDescRemote, !sync);
}
/*
public View getLayout(String resourceLocation,ViewGroup viewParent,int index)
{
PageImpl page = getPageImpl();
XMLInflaterRegistry xmlInflaterRegistry = page.getItsNatDroidBrowserImpl().getItsNatDroidImpl().getXMLInflaterRegistry();
XMLInflaterLayoutPage xmlInflaterParent = page.getXMLInflaterLayoutPage();
DOMAttr attr = DOMAttr.createDOMAttr(null, "_NONE_", resourceLocation);
return xmlInflaterRegistry.getLayout(attr,xmlInflaterParent,viewParent,index,null);
}
*/
}