Java tutorial
/* This file was modified from or inspired by Apache Cordova. Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package com.polyvi.xface.view; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.security.KeyStore; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.Enumeration; import org.json.JSONException; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.ActivityNotFoundException; import android.content.Intent; import android.graphics.Bitmap; import android.net.Uri; import android.net.http.SslError; import android.os.Build; import android.webkit.ClientCertRequestHandler; import android.webkit.SslErrorHandler; import android.webkit.WebResourceResponse; import android.webkit.WebView; import android.webkit.WebViewClientClassicExt; import com.polyvi.xface.app.XWhiteList; import com.polyvi.xface.core.XConfiguration; import com.polyvi.xface.core.XISystemContext; import com.polyvi.xface.core.XNativeToJsMessageQueue; import com.polyvi.xface.event.XIWebAppEventListener; import com.polyvi.xface.ssl.XSSLManager; import com.polyvi.xface.util.XConstant; import com.polyvi.xface.util.XLog; /** * ?WebView?? */ public class XWebViewClient extends WebViewClientClassicExt { private static final String CLASS_NAME = XWebViewClient.class.getSimpleName(); private static final String SMS_BODY = "body="; private XAppWebView mWebAppView; private String ABOUT_BLANK = "about:blank"; private XInputScaleHandler mInputScaleHandler; /** < ??input? */ private XISystemContext mSystemContext; private String[] URL_PARAMS_TAG = { "#", "?" }; private static final String XFACE_EXEC_URL_PREFIX = "http://xface_exec/"; public XWebViewClient(XISystemContext systemContext, XAppWebView appWebView) { mWebAppView = appWebView; mInputScaleHandler = new XInputScaleHandler(); mSystemContext = systemContext; } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); XIWebAppEventListener evtListener = mWebAppView.getAppEventListener(); if (null == evtListener) { XLog.w(CLASS_NAME, "onPageFinished: XIWebAppEventListener is null!"); return; } XAppWebView appView = (XAppWebView) view; evtListener.onPageLoadingFinished(appView); } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { XIWebAppEventListener evtListener = mWebAppView.getAppEventListener(); if (null == evtListener) { XLog.w(CLASS_NAME, "onPageStarted: XIWebAppEventListener is null!"); return; } // ???WideViewPorttrue??? view.getSettings().setUseWideViewPort(true); // ???? view.getSettings().setSupportZoom(true); // ?about_blank????pageStart if (!ABOUT_BLANK.equals(url)) { evtListener.onPageStarted((XAppWebView) view); } XAppWebView appView = (XAppWebView) view; // TODO:4.0 ??? ???,?????? ?? // 4.02.2 2.3[3.1] ????,??? if (Build.VERSION.SDK_INT < XInputScaleHandler.APILEVEL_11) { appView.setAdapated(false); } } @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { // TODO ? super.onReceivedError(view, errorCode, description, failingUrl); } @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { try { // ?exec()?. if (XNativeToJsMessageQueue.ENABLE_LOCATION_CHANGE_EXEC_MODE && url.startsWith(XFACE_EXEC_URL_PREFIX)) { handleExecUrl(url); } XWhiteList whiteList = mWebAppView.getOwnerApp().getAppInfo().getWhiteList(); if (url.startsWith("tel") && !XConfiguration.getInstance().isTelLinkEnabled()) { return true; } // TODO:??phonegap?url???? else if (url.startsWith(XConstant.FILE_SCHEME) || url.startsWith("data:") || ((url.startsWith(XConstant.HTTP_SCHEME) || url.startsWith(XConstant.HTTPS_SCHEME)) && (null != whiteList && whiteList.isUrlWhiteListed(url)))) { /** * I9003??url? url??? */ return handleUrl(url); } else if (url.startsWith(XConstant.SCHEME_SMS)) { Intent intent = new Intent(Intent.ACTION_VIEW); // ?? String address = null; int parmIndex = url.indexOf('?'); if (-1 == parmIndex) { address = url.substring(XConstant.SCHEME_SMS.length()); } else { address = url.substring(XConstant.SCHEME_SMS.length(), parmIndex); Uri uri = Uri.parse(url); String query = uri.getQuery(); if (null != query) { if (query.startsWith(SMS_BODY)) { intent.putExtra("sms_body", query.substring(SMS_BODY.length())); } } } intent.setData(Uri.parse(XConstant.SCHEME_SMS + address)); intent.putExtra("address", address); intent.setType("vnd.android-dir/mms-sms"); mSystemContext.getContext().startActivity(intent); return true; } else { // ????? return startSysApplication(url); } } catch (ActivityNotFoundException e) { XLog.e(CLASS_NAME, e.toString()); } return false; } /** * ?webViewurl? * * @param url */ private void handleExecUrl(String url) { try { int idx1 = XFACE_EXEC_URL_PREFIX.length(); int idx2 = url.indexOf('#', idx1 + 1); int idx3 = url.indexOf('#', idx2 + 1); int idx4 = url.indexOf('#', idx3 + 1); if (idx1 == -1 || idx2 == -1 || idx3 == -1 || idx4 == -1) { XLog.e(CLASS_NAME, "Could not decode URL command: " + url); return; } String service = url.substring(idx1, idx2); String action = url.substring(idx2 + 1, idx3); String callbackId = url.substring(idx3 + 1, idx4); String jsonArgs = url.substring(idx4 + 1); mWebAppView.getOwnerApp().getJSNativeBridge().exec(service, action, callbackId, jsonArgs); } catch (JSONException e) { XLog.e(CLASS_NAME, "handleExecUrl: JSONException"); e.printStackTrace(); return; } } @Override public void onScaleChanged(WebView view, float oldScale, float newScale) { XLog.d("xface", "old " + oldScale + " new " + newScale); mInputScaleHandler.changeDefaultScale(view, oldScale, newScale); } /** * ?url?? url ??url */ private boolean startSysApplication(String url) { Intent intent = null; if (url.contains("content://media/external")) { intent = new Intent(Intent.ACTION_PICK); } else { intent = new Intent(Intent.ACTION_VIEW); } intent.setData(Uri.parse(url)); try { mSystemContext.getContext().startActivity(intent); } catch (ActivityNotFoundException e) { XLog.e(CLASS_NAME, e.toString()); } return true; } /** * url?? * * @param url * ??url * @param return true:url??false? */ private boolean handleUrl(String url) { // TODO: I9003? ?? ?? return false; } @SuppressLint("Override") @TargetApi(11) public WebResourceResponse shouldInterceptRequest(WebView view, String url) { /** url????? */ XWhiteList whiteList = mWebAppView.getOwnerApp().getAppInfo().getWhiteList(); if ((url.startsWith("http://") || url.startsWith("https://")) && (null != whiteList && !whiteList.isUrlWhiteListed(url))) { return getWhitelistResponse(); } if (url.indexOf(XConstant.ASSERT_PROTACAL) == 0) { for (int i = 0; i < URL_PARAMS_TAG.length; i++) { if (url.contains(URL_PARAMS_TAG[i])) { String filePath = url.substring(XConstant.ASSERT_PROTACAL.length(), url.length()); filePath = filePath.substring(0, filePath.indexOf(URL_PARAMS_TAG[i])); try { InputStream is = mSystemContext.getContext().getAssets().open(filePath); WebResourceResponse wr = new WebResourceResponse("text/javascript", "utf-8", is); return wr; } catch (IOException e) { return null; } } } } return null; } /** * ????CA?SslError proceed()?SSL */ @TargetApi(8) @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { handler.proceed(); } /** * android4.0???SSLContext Android 4.x * WebView???WebKit?ClientCertRequestHandler * ?jar/cer.jar? */ @TargetApi(14) public void onReceivedClientCertRequest(WebView view, ClientCertRequestHandler handler, String host_and_port) { try { KeyStore store = XSSLManager.getInstace().getKeyStore(); // ? if (store == null) { return; } PrivateKey privateKey = null; X509Certificate[] certificates = null; Enumeration<String> e = store.aliases(); while (e.hasMoreElements()) { String alias = e.nextElement(); if (store.isKeyEntry(alias)) { KeyStore.PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry) store.getEntry(alias, null); privateKey = entry.getPrivateKey(); certificates = (X509Certificate[]) entry.getCertificateChain(); break; } } handler.proceed(privateKey, certificates); } catch (Exception e) { e.printStackTrace(); XLog.e(CLASS_NAME, e.getMessage()); } } /** ????? */ @SuppressLint("NewApi") private WebResourceResponse getWhitelistResponse() { String empty = ""; ByteArrayInputStream data = new ByteArrayInputStream(empty.getBytes()); return new WebResourceResponse("text/plain", "UTF-8", data); } }