Merge branch 'upgrade-schema-2020-03' of https://github.com/AlphaWallet/alpha-wallet-android into upgrade-schema-2020-03

pull/1263/head
James Brown 5 years ago
commit 9f6db5c5a5
  1. 30
      app/src/main/java/com/alphawallet/app/entity/tokens/Token.java
  2. 4
      app/src/main/java/com/alphawallet/app/ui/FunctionActivity.java
  3. 5
      app/src/main/java/com/alphawallet/app/ui/widget/holder/TokenFunctionViewHolder.java
  4. 4
      app/src/main/java/com/alphawallet/app/ui/widget/holder/TokenscriptViewHolder.java
  5. 35
      app/src/main/java/com/alphawallet/app/web3/JsInjectorClient.java
  6. 51
      app/src/main/java/com/alphawallet/app/web3/Web3TokenView.java
  7. 2
      app/src/main/res/raw/init_token.js
  8. 50
      lib/src/main/java/com/alphawallet/token/tools/TokenDefinition.java

@ -45,6 +45,8 @@ import java.util.concurrent.ConcurrentHashMap;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import static com.alphawallet.token.tools.TokenDefinition.TOKENSCRIPT_ERROR;
public class Token implements Parcelable, Comparable<Token>
{
public final TokenInfo tokenInfo;
@ -1090,29 +1092,39 @@ public class Token implements Parcelable, Comparable<Token>
assetService.resolveAttrs(this, tokenId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(attr -> onAttr(attr, attrs), throwable -> onError(throwable, ctx, assetService, attrs, waitSpinner, tokenView, iconified),
() -> displayTicket(ctx, assetService, attrs, waitSpinner, tokenView, iconified))
.subscribe(attr -> onAttr(attr, attrs), throwable -> onError(throwable, ctx, assetService, attrs,
waitSpinner, tokenView, iconified, tokenId),
() -> displayTicket(ctx, assetService, attrs, waitSpinner, tokenView, iconified, tokenId))
.isDisposed();
}
private void displayTicket(Context ctx, AssetDefinitionService assetService, StringBuilder attrs, ProgressBar waitSpinner, Web3TokenView tokenView, boolean iconified)
private void displayTicket(Context ctx, AssetDefinitionService assetService, StringBuilder attrs, ProgressBar waitSpinner,
Web3TokenView tokenView, boolean iconified, BigInteger tokenId)
{
if (waitSpinner != null) waitSpinner.setVisibility(View.GONE);
tokenView.setVisibility(View.VISIBLE);
String view = assetService.getTokenView(tokenInfo.chainId, getAddress(), iconified ? "item-view" : "view");
String style = assetService.getTokenView(tokenInfo.chainId, getAddress(), "style");
String viewData = tokenView.injectWeb3TokenInit(ctx, view, attrs.toString());
viewData = tokenView.injectStyleData(viewData, style); //style injected last so it comes first
String viewData = tokenView.injectWeb3TokenInit(ctx, view, attrs.toString(), tokenId);
viewData = tokenView.injectStyleAndWrapper(viewData, style); //style injected last so it comes first
String base64 = android.util.Base64.encodeToString(viewData.getBytes(StandardCharsets.UTF_8), Base64.DEFAULT);
tokenView.loadData(base64, "text/html; charset=utf-8", "base64");
if (viewData.contains(TOKENSCRIPT_ERROR))
{
tokenView.showError(viewData);
}
else
{
String base64 = android.util.Base64.encodeToString(viewData.getBytes(StandardCharsets.UTF_8), Base64.DEFAULT);
tokenView.loadData(base64, "text/html; charset=utf-8", "base64");
}
}
private void onError(Throwable throwable, Context ctx, AssetDefinitionService assetService, StringBuilder attrs, ProgressBar waitSpinner, Web3TokenView tokenView, boolean iconified)
private void onError(Throwable throwable, Context ctx, AssetDefinitionService assetService, StringBuilder attrs,
ProgressBar waitSpinner, Web3TokenView tokenView, boolean iconified, BigInteger tokenId)
{
throwable.printStackTrace();
displayTicket(ctx, assetService, attrs, waitSpinner, tokenView, iconified);
displayTicket(ctx, assetService, attrs, waitSpinner, tokenView, iconified, tokenId);
}
private void onAttr(TokenScriptResult.Attribute attribute, StringBuilder attrs)

@ -134,9 +134,9 @@ public class FunctionActivity extends BaseActivity implements FunctionCallback,
Map<String, TSAction> functions = viewModel.getAssetDefinitionService().getTokenFunctionMap(token.tokenInfo.chainId, token.getAddress());
TSAction action = functions.get(actionMethod);
String magicValues = viewModel.getAssetDefinitionService().getMagicValuesForInjection(token.tokenInfo.chainId);
String injectedView = tokenView.injectWeb3TokenInit(this, action.view, tokenAttrs);
String injectedView = tokenView.injectWeb3TokenInit(this, action.view, tokenAttrs, tokenId);
injectedView = tokenView.injectJSAtEnd(injectedView, magicValues);
if (action.style != null) injectedView = tokenView.injectStyleData(injectedView, action.style);
injectedView = tokenView.injectStyleAndWrapper(injectedView, action.style);
String base64 = Base64.encodeToString(injectedView.getBytes(StandardCharsets.UTF_8), Base64.DEFAULT);
tokenView.loadData(base64, "text/html; charset=utf-8", "base64");

@ -17,6 +17,7 @@ import com.alphawallet.app.web3.entity.Message;
import com.alphawallet.app.web3.entity.PageReadyCallback;
import com.alphawallet.app.web3.entity.ScriptFunction;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import com.alphawallet.token.tools.Numeric;
@ -66,9 +67,9 @@ public class TokenFunctionViewHolder extends BinderViewHolder<String> implements
try
{
reloaded = false;
String injectedView = tokenView.injectWeb3TokenInit(getContext(), view, "");
String injectedView = tokenView.injectWeb3TokenInit(getContext(), view, "", BigInteger.ONE);
String style = assetDefinitionService.getTokenView(token.tokenInfo.chainId, token.getAddress(), "style");
injectedView = tokenView.injectStyleData(injectedView, style);
injectedView = tokenView.injectStyleAndWrapper(injectedView, style);
String base64 = Base64.encodeToString(injectedView.getBytes(StandardCharsets.UTF_8), Base64.DEFAULT);
tokenView.loadData(base64, "text/html; charset=utf-8", "base64");

@ -99,8 +99,8 @@ public class TokenscriptViewHolder extends BinderViewHolder<TicketRange> impleme
String viewType = iconified ? "item-view" : "view";
String view = assetDefinitionService.getTokenView(token.tokenInfo.chainId, token.getAddress(), viewType);
String style = assetDefinitionService.getTokenView(token.tokenInfo.chainId, token.getAddress(), "style");
String viewData = tokenView.injectWeb3TokenInit(getContext(), view, tokenAttrs);
viewData = tokenView.injectStyleData(viewData, style); //style injected last so it comes first
String viewData = tokenView.injectWeb3TokenInit(getContext(), view, tokenAttrs, data.tokenIds.get(0));
viewData = tokenView.injectStyleAndWrapper(viewData, style); //style injected last so it comes first
String base64 = Base64.encodeToString(viewData.getBytes(StandardCharsets.UTF_8), Base64.DEFAULT);
tokenView.loadData(base64, "text/html; charset=utf-8", "base64");

@ -11,6 +11,7 @@ import com.alphawallet.app.web3.entity.Address;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
@ -117,12 +118,17 @@ public class JsInjectorClient {
return injectJS(html, js);
}
String injectWeb3TokenInit(Context ctx, String view, String tokenContent)
String injectWeb3TokenInit(Context ctx, String view, String tokenContent, BigInteger tokenId)
{
String initSrc = loadFile(ctx, R.raw.init_token);
initSrc = String.format(initSrc, tokenContent, walletAddress, EthereumNetworkRepository.getNodeURLByNetworkId(chainId), chainId);
//put the view in here
String tokenIdWrapperName = "token-card-" + tokenId.toString(10);
initSrc = String.format(initSrc, tokenContent, walletAddress, EthereumNetworkRepository.getNodeURLByNetworkId(chainId), chainId, tokenIdWrapperName);
//now insert this source into the view
return injectJSembed(view, initSrc);
// note that the <div> is not closed because it is closed in njectStyleAndWrap().
String wrapper = "<div id=\"token-card-" + tokenId.toString(10) + "\" class=\"token-card\">";
initSrc = "<script>\n" + initSrc + "</script>\n" + wrapper;
return injectJS(view, initSrc);
}
String injectJSAtEnd(String view, String newCode)
@ -136,12 +142,6 @@ public class JsInjectorClient {
return view;
}
String injectJSembed(String view, String initSrc)
{
initSrc = "<script>\n" + initSrc + "</script>\n";
return injectJS(view, initSrc);
}
String injectJS(String html, String js) {
if (TextUtils.isEmpty(html)) {
return html;
@ -169,6 +169,9 @@ public class JsInjectorClient {
if (index < 0) {
index = body.indexOf("</head");
}
if (index < 0) {
index = 0; //just wrap whole view
}
return index;
}
@ -201,11 +204,17 @@ public class JsInjectorClient {
return String.format(initSrc, address, rpcUrl, chainId);
}
String injectStyle(String view, String style)
String injectStyleAndWrap(String view, String style)
{
String injectHeader = "<head><meta name=\"viewport\" content=\"width=device-width, user-scalable=false\" /></head>";
style = "<style type=\"text/css\">\n" + style + "</style><body>\n";
return injectHeader + style + view + "</body>";
if (style == null) style = "";
//String injectHeader = "<head><meta name=\"viewport\" content=\"width=device-width, user-scalable=false\" /></head>";
String injectHeader = "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, shrink-to-fit=no\" /></head>"; //iOS uses these header settings
style = "<style type=\"text/css\">\n" + style + ".token-card {\n" +
"padding: 0pt;\n" +
"margin: 0pt;\n" +
"}</style><body>\n";
// the opening of the following </div> is in injectWeb3TokenInit();
return injectHeader + style + view + "</div></body>";
}
private static String loadFile(Context context, @RawRes int rawRes) {

@ -8,11 +8,11 @@ import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.webkit.ConsoleMessage;
import android.webkit.JavascriptInterface;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
@ -31,12 +31,18 @@ import com.alphawallet.app.web3.entity.PageReadyCallback;
import com.alphawallet.app.web3.entity.TypedData;
import com.alphawallet.app.web3.entity.Web3Transaction;
import java.math.BigInteger;
import static com.alphawallet.token.tools.TokenDefinition.TOKENSCRIPT_ERROR;
/**
* Created by James on 3/04/2019.
* Stormbird in Singapore
*/
public class Web3TokenView extends WebView
{
public static final String RENDERING_ERROR = "<html>" + TOKENSCRIPT_ERROR + "${ERR1}</html>";
private static final String JS_PROTOCOL_CANCELLED = "cancelled";
private static final String JS_PROTOCOL_ON_SUCCESSFUL = "executeCallback(%1$s, null, \"%2$s\")";
private static final String JS_PROTOCOL_ON_FAILURE = "executeCallback(%1$s, \"%2$s\", null)";
@ -44,6 +50,7 @@ public class Web3TokenView extends WebView
private JsInjectorClient jsInjectorClient;
private TokenScriptClient tokenScriptClient;
private PageReadyCallback assetHolder;
private boolean showingError = false;
protected WebCompletionCallback keyPressCallback;
@ -89,6 +96,7 @@ public class Web3TokenView extends WebView
setInitialScale(0);
clearCache(true);
showingError = false;
addJavascriptInterface(new SignCallbackJSInterface(
this,
@ -98,10 +106,31 @@ public class Web3TokenView extends WebView
innerOnSignTypedMessageListener), "alpha");
super.setWebViewClient(tokenScriptClient);
setWebChromeClient(new WebChromeClient()
{
@Override
public boolean onConsoleMessage(ConsoleMessage msg)
{
if (!showingError && msg.messageLevel() == ConsoleMessage.MessageLevel.ERROR)
{
String errorMessage = RENDERING_ERROR.replace("${ERR1}", msg.message());
showError(errorMessage);
}
return true;
}
});
}
public void showError(String error)
{
showingError = true;
loadData(error, "text/html", "utf-8");
}
@Override
public void setWebChromeClient(WebChromeClient client) {
public void setWebChromeClient(WebChromeClient client)
{
super.setWebChromeClient(client);
}
@ -216,9 +245,9 @@ public class Web3TokenView extends WebView
assetHolder = holder;
}
public String injectWeb3TokenInit(Context ctx, String view, String tokenContent)
public String injectWeb3TokenInit(Context ctx, String view, String tokenContent, BigInteger tokenId)
{
return jsInjectorClient.injectWeb3TokenInit(ctx, view, tokenContent);
return jsInjectorClient.injectWeb3TokenInit(ctx, view, tokenContent, tokenId);
}
public String injectJS(String view, String buildToken)
@ -231,9 +260,9 @@ public class Web3TokenView extends WebView
return jsInjectorClient.injectJSAtEnd(view, JSCode);
}
public String injectStyleData(String viewData, String style)
public String injectStyleAndWrapper(String viewData, String style)
{
return jsInjectorClient.injectStyle(viewData, style);
return jsInjectorClient.injectStyleAndWrap(viewData, style);
}
public void setLayout(Token token, boolean iconified)
@ -286,5 +315,11 @@ public class Web3TokenView extends WebView
}
super.onUnhandledKeyEvent(view, event);
}
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error)
{
showError(RENDERING_ERROR.replace("${ERR1}", error.getDescription()));
}
}
}

@ -37,7 +37,7 @@ web3 = {
web3.tokens.data.currentInstance = _currentTokenInstance
function refresh() {
web3.tokens.dataChanged('test', 'test')
web3.tokens.dataChanged('test', web3.tokens.data, '%5$s') //TODO: Cache previous value of token to feed into first arg
}
window.onload = refresh;

@ -38,6 +38,9 @@ public class TokenDefinition {
private static final String TOKENSCRIPT_NAMESPACE = "http://tokenscript.org/" + TOKENSCRIPT_CURRENT_SCHEMA + "/tokenscript";
private static final String TOKENSCRIPT_BASE_URL = "http://tokenscript.org/";
public static final String TOKENSCRIPT_ERROR = "<h2 style=\"color:rgba(207, 0, 15, 1);\">TokenScript Error</h2>";
private static final String LEGACY_WARNING_TEMPLATE = "<html>" + TOKENSCRIPT_ERROR + "<h3>ts:${ERR1} is deprecated.<br/>Use ts:${ERR2}</h3>";
/* the following are incorrect, waiting to be further improved
with suitable XML, because none of these String typed class variables
are going to be one-per-XML-file:
@ -893,24 +896,6 @@ public class TokenDefinition {
}
}
/**
* Check for 'appearance' attribute set
* @param tag
* @return
*/
public String getAppearance(String tag)
{
Map<String, String> appearanceSet = attributeSets.get("appearance");
if (appearanceSet != null)
{
return appearanceSet.get(tag);
}
else
{
return "";
}
}
/**
* Check for 'cards' attribute set
* @param tag
@ -918,15 +903,38 @@ public class TokenDefinition {
*/
public String getCardData(String tag)
{
String view = null;
Map<String, String> appearanceSet = attributeSets.get("cards");
if (appearanceSet != null)
{
return appearanceSet.get(tag);
view = appearanceSet.get(tag);
if (view == null) view = checkLegacyCards(tag, appearanceSet);
}
else
return view != null ? view : "";
}
private String checkLegacyCards(String tag, Map<String, String> appearanceSet)
{
String legacyWarningMessage = null;
switch (tag)
{
return "";
case "item-view":
if (appearanceSet.containsKey("view-iconified"))
{
legacyWarningMessage = LEGACY_WARNING_TEMPLATE.replace("${ERR1}", "view-iconified")
.replace("${ERR2}", tag) + "<br/>See <a href=\"http://tokenscript.org/2020/03/tokenscript\">http://tokenscript.org/2020/03/tokenscript</a>";
}
break;
//add instances here when any view name is deprecated
default:
break;
}
if (legacyWarningMessage != null) legacyWarningMessage += "</html>"; //close error message
return legacyWarningMessage;
}
public Map<String, TSAction> getActions()

Loading…
Cancel
Save