cordova混合App开发:Cordova+Vue实现Android APP开发 (app内打开浏览器及横竖屏) (七)

app内打开浏览器目前主要2个cordova插件

cordova-plugin-inappbrowser 和  cordova-plugin-themeablebrowser
themeablebrowser是在cordova-plugin-inappbrowser基础上二次开发的, 支持webview中有按钮,及相关按钮事件 是我们想要的
 

第一步

在已有项目目录下添加插件 cordova plugin add cordova-plugin-themeablebrowser

第二步 准备图片

 vue项目下新建static/browserIcons文件夹, 放入准备好的 back.png 和 close.png

 

第三步  修改webpack打包规则

根据插件要求, wwwImage从Cordova的www目录加载图像所以我们需要把插件需要的第二步图片 放到cordova www目录下

因为www目录 我们之前修改过vue项目的build目录 每次vue build 会把www目录删掉从新生成, 所以手工拷贝不靠谱

安装 copy-webpack-plugin

npm install --save copy-webpack-plugin

修改 vue项目  vue.config.js 

 

 from 定义要拷贝的源文件 from:__dirname+'/src/components'

to 定义要拷贝到的目标文件夹 to: __dirname+'/dist'

toType file 或者 dir 可选,默认是文件

force 强制覆盖前面的插件 可选,默认是文件

context 可选,默认base context可用specific context

flatten 只拷贝指定的文件 可以用模糊匹配

ignore 忽略拷贝指定的文件 可以模糊匹配

 

 

第四步 修改相应vue页面  我这里是修改的 helloword.vue

 相关按钮及事件 参考 插件github  https://github.com/initialxy/cordova-plugin-themeablebrowser

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <button @click="handleAxiosRequest">axios调用</button>
    <h1>{{ bjtime }}</h1>
    <button @click="openAppBrowser('https://www.google.com/')">跳转页面1</button>
    <button @click="openAppBrowser('https://www.nginx.com/')">跳转页面2</button>

  </div>
</template>

<script>
export default {
  name: "HelloWorld",
  props: {
    msg: String,
  },
  data() {
    return {
      bjtime: null,
    };
  },
  methods: {
    openAppBrowser(url) {
      // Keep in mind that you must add your own images to native resource.
      // Images below are for sample only. They are not imported by this plugin.
      window.cordova.ThemeableBrowser.open(url, "_blank", {
        statusbar: {
          color: "#EBCE9C",
        },
        title: {
          color: "#000000",
          showPageTitle: true,
          staticText:"换成自己的标题,或者不显示"
        },
        backButton: {
          wwwImage: "browserIcons/back.png",
          wwwImagePressed: "browserIcons/back.png",
          align: "left",
          wwwImageDensity: 2,
          event: "backPressed",
        },
        closeButton: {
          wwwImage: "browserIcons/close.png",
          wwwImagePressed: "browserIcons/close.png",
          align: "right",
          wwwImageDensity: 2,
          event: "closePressed",
        },
        backButtonCanClose: true,
      })
        .addEventListener("backPressed", function (e) {
          alert("back pressed"+e);
        })
        .addEventListener("helloPressed", function (e) {
          alert("hello pressed"+e);
        })
        .addEventListener("sharePressed", function (e) {
          alert(e.url);
        })
        // .addEventListener(window.cordova.ThemeableBrowser.EVT_ERR, function (e) {
        //   console.error(e.message);
        // })
        // .addEventListener(window.cordova.ThemeableBrowser.EVT_WRN, function (e) {
        //   console.log(e.message);
        // });
    },
    handleAxiosRequest() {
      this.$axios
        .get("https://quan.suning.com/getSysTime.do")
        .then(({ data }) => {
          this.bjtime = data;
        })
        .catch((err) => {
          console.log(err);
        });
    },
  },
};
</script>

 

就可以看到页面了 点击会返回和关闭 会触发设定好的事件

 

第五步 修改源码支持横竖屏打开

 cordova-plugin-themeablebrowser这个插件 并不支持横竖屏打开webview页面 如何让它支持呢

用 Android studio打开 platforms/android/build.gradle 文件 找到下面 ThemeableBrowser 类

 

 

 

把代码贴出来

   1 /*
   2        Licensed to the Apache Software Foundation (ASF) under one
   3        or more contributor license agreements.  See the NOTICE file
   4        distributed with this work for additional information
   5        regarding copyright ownership.  The ASF licenses this file
   6        to you under the Apache License, Version 2.0 (the
   7        "License"); you may not use this file except in compliance
   8        with the License.  You may obtain a copy of the License at
   9 
  10          http://www.apache.org/licenses/LICENSE-2.0
  11 
  12        Unless required by applicable law or agreed to in writing,
  13        software distributed under the License is distributed on an
  14        "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15        KIND, either express or implied.  See the License for the
  16        specific language governing permissions and limitations
  17        under the License.
  18 */
  19 package com.initialxy.cordova.themeablebrowser;
  20 
  21 import android.annotation.SuppressLint;
  22 import android.content.Context;
  23 import android.content.Intent;
  24 import android.content.res.Resources;
  25 import android.graphics.Bitmap;
  26 import android.graphics.BitmapFactory;
  27 import android.graphics.Canvas;
  28 import android.graphics.Paint;
  29 import android.graphics.drawable.BitmapDrawable;
  30 import android.graphics.drawable.Drawable;
  31 import android.graphics.drawable.StateListDrawable;
  32 import android.net.Uri;
  33 import android.os.Build;
  34 import android.os.Bundle;
  35 import android.provider.Browser;
  36 import android.text.InputType;
  37 import android.text.TextUtils;
  38 import android.util.DisplayMetrics;
  39 import android.util.Log;
  40 import android.util.TypedValue;
  41 import android.view.Gravity;
  42 import android.view.KeyEvent;
  43 import android.view.MotionEvent;
  44 import android.view.View;
  45 import android.view.ViewGroup;
  46 import android.view.ViewGroup.LayoutParams;
  47 import android.view.Window;
  48 import android.view.WindowManager;
  49 import android.view.inputmethod.EditorInfo;
  50 import android.view.inputmethod.InputMethodManager;
  51 import android.webkit.CookieManager;
  52 import android.webkit.WebSettings;
  53 import android.webkit.WebView;
  54 import android.webkit.WebViewClient;
  55 import android.widget.AdapterView;
  56 import android.widget.ArrayAdapter;
  57 import android.widget.Button;
  58 import android.widget.EditText;
  59 import android.widget.FrameLayout;
  60 import android.widget.LinearLayout;
  61 import android.widget.RelativeLayout;
  62 import android.widget.Spinner;
  63 import android.widget.TextView;
  64 
  65 import org.apache.cordova.CallbackContext;
  66 import org.apache.cordova.CordovaArgs;
  67 import org.apache.cordova.CordovaPlugin;
  68 import org.apache.cordova.CordovaWebView;
  69 import org.apache.cordova.PluginManager;
  70 import org.apache.cordova.PluginResult;
  71 import org.apache.cordova.Whitelist;
  72 import org.json.JSONException;
  73 import org.json.JSONObject;
  74 
  75 import java.io.File;
  76 import java.io.IOException;
  77 import java.io.InputStream;
  78 import java.lang.reflect.InvocationTargetException;
  79 import java.lang.reflect.Method;
  80 
  81 @SuppressLint("SetJavaScriptEnabled")
  82 public class ThemeableBrowser extends CordovaPlugin {
  83 
  84     private static final String NULL = "null";
  85     protected static final String LOG_TAG = "ThemeableBrowser";
  86     private static final String SELF = "_self";
  87     private static final String SYSTEM = "_system";
  88     // private static final String BLANK = "_blank";
  89     private static final String EXIT_EVENT = "exit";
  90     private static final String LOAD_START_EVENT = "loadstart";
  91     private static final String LOAD_STOP_EVENT = "loadstop";
  92     private static final String LOAD_ERROR_EVENT = "loaderror";
  93 
  94     private static final String ALIGN_LEFT = "left";
  95     private static final String ALIGN_RIGHT = "right";
  96 
  97     private static final int TOOLBAR_DEF_HEIGHT = 44;
  98     private static final int DISABLED_ALPHA = 127;  // 50% AKA 127/255.
  99 
 100     private static final String EVT_ERR = "ThemeableBrowserError";
 101     private static final String EVT_WRN = "ThemeableBrowserWarning";
 102     private static final String ERR_CRITICAL = "critical";
 103     private static final String ERR_LOADFAIL = "loadfail";
 104     private static final String WRN_UNEXPECTED = "unexpected";
 105     private static final String WRN_UNDEFINED = "undefined";
 106 
 107     private ThemeableBrowserDialog dialog;
 108     private WebView inAppWebView;
 109     private EditText edittext;
 110     private CallbackContext callbackContext;
 111 
 112     /**
 113      * Executes the request and returns PluginResult.
 114      *
 115      * @param action          The action to execute.
 116      * @param args            The exec() arguments, wrapped with some Cordova helpers.
 117      * @param callbackContext The callback context used when calling back into JavaScript.
 118      * @return
 119      * @throws JSONException
 120      */
 121     public boolean execute(String action, CordovaArgs args, final CallbackContext callbackContext) throws JSONException {
 122         if (action.equals("open")) {
 123             this.callbackContext = callbackContext;
 124             final String url = args.getString(0);
 125             String t = args.optString(1);
 126             if (t == null || t.equals("") || t.equals(NULL)) {
 127                 t = SELF;
 128             }
 129             final String target = t;
 130             final Options features = parseFeature(args.optString(2));
 131 
 132             this.cordova.getActivity().runOnUiThread(new Runnable() {
 133                 @Override
 134                 public void run() {
 135                     String result = "";
 136                     // SELF
 137                     if (SELF.equals(target)) {
 138                         /* This code exists for compatibility between 3.x and 4.x versions of Cordova.
 139                          * Previously the Config class had a static method, isUrlWhitelisted(). That
 140                          * responsibility has been moved to the plugins, with an aggregating method in
 141                          * PluginManager.
 142                          */
 143                         Boolean shouldAllowNavigation = null;
 144                         if (url.startsWith("javascript:")) {
 145                             shouldAllowNavigation = true;
 146                         }
 147                         if (shouldAllowNavigation == null) {
 148                             shouldAllowNavigation = new Whitelist().isUrlWhiteListed(url);
 149                         }
 150                         if (shouldAllowNavigation == null) {
 151                             try {
 152                                 Method gpm = webView.getClass().getMethod("getPluginManager");
 153                                 PluginManager pm = (PluginManager)gpm.invoke(webView);
 154                                 Method san = pm.getClass().getMethod("shouldAllowNavigation", String.class);
 155                                 shouldAllowNavigation = (Boolean)san.invoke(pm, url);
 156                             } catch (NoSuchMethodException e) {
 157                             } catch (IllegalAccessException e) {
 158                             } catch (InvocationTargetException e) {
 159                             }
 160                         }
 161                         // load in webview
 162                         if (Boolean.TRUE.equals(shouldAllowNavigation)) {
 163                             webView.loadUrl(url);
 164                         }
 165                         //Load the dialer
 166                         else if (url.startsWith(WebView.SCHEME_TEL))
 167                         {
 168                             try {
 169                                 Intent intent = new Intent(Intent.ACTION_DIAL);
 170                                 intent.setData(Uri.parse(url));
 171                                 cordova.getActivity().startActivity(intent);
 172                             } catch (android.content.ActivityNotFoundException e) {
 173                                 emitError(ERR_CRITICAL,
 174                                         String.format("Error dialing %s: %s", url, e.toString()));
 175                             }
 176                         }
 177                         // load in ThemeableBrowser
 178                         else {
 179                             result = showWebPage(url, features);
 180                         }
 181                     }
 182                     // SYSTEM
 183                     else if (SYSTEM.equals(target)) {
 184                         result = openExternal(url);
 185                     }
 186                     // BLANK - or anything else
 187                     else {
 188                         result = showWebPage(url, features);
 189                     }
 190 
 191                     PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, result);
 192                     pluginResult.setKeepCallback(true);
 193                     callbackContext.sendPluginResult(pluginResult);
 194                 }
 195             });
 196         }
 197         else if (action.equals("close")) {
 198             closeDialog();
 199         }
 200         else if (action.equals("injectScriptCode")) {
 201             String jsWrapper = null;
 202             if (args.getBoolean(1)) {
 203                 jsWrapper = String.format("prompt(JSON.stringify([eval(%%s)]), 'gap-iab://%s')", callbackContext.getCallbackId());
 204             }
 205             injectDeferredObject(args.getString(0), jsWrapper);
 206         }
 207         else if (action.equals("injectScriptFile")) {
 208             String jsWrapper;
 209             if (args.getBoolean(1)) {
 210                 jsWrapper = String.format("(function(d) { var c = d.createElement('script'); c.src = %%s; c.onload = function() { prompt('', 'gap-iab://%s'); }; d.body.appendChild(c); })(document)", callbackContext.getCallbackId());
 211             } else {
 212                 jsWrapper = "(function(d) { var c = d.createElement('script'); c.src = %s; d.body.appendChild(c); })(document)";
 213             }
 214             injectDeferredObject(args.getString(0), jsWrapper);
 215         }
 216         else if (action.equals("injectStyleCode")) {
 217             String jsWrapper;
 218             if (args.getBoolean(1)) {
 219                 jsWrapper = String.format("(function(d) { var c = d.createElement('style'); c.innerHTML = %%s; d.body.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId());
 220             } else {
 221                 jsWrapper = "(function(d) { var c = d.createElement('style'); c.innerHTML = %s; d.body.appendChild(c); })(document)";
 222             }
 223             injectDeferredObject(args.getString(0), jsWrapper);
 224         }
 225         else if (action.equals("injectStyleFile")) {
 226             String jsWrapper;
 227             if (args.getBoolean(1)) {
 228                 jsWrapper = String.format("(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %%s; d.head.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId());
 229             } else {
 230                 jsWrapper = "(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %s; d.head.appendChild(c); })(document)";
 231             }
 232             injectDeferredObject(args.getString(0), jsWrapper);
 233         }
 234         else if (action.equals("show")) {
 235             this.cordova.getActivity().runOnUiThread(new Runnable() {
 236                 @Override
 237                 public void run() {
 238                     dialog.show();
 239                 }
 240             });
 241             PluginResult pluginResult = new PluginResult(PluginResult.Status.OK);
 242             pluginResult.setKeepCallback(true);
 243             this.callbackContext.sendPluginResult(pluginResult);
 244         }
 245         else if (action.equals("reload")) {
 246             if (inAppWebView != null) {
 247                 this.cordova.getActivity().runOnUiThread(new Runnable() {
 248                     @Override
 249                     public void run() {
 250                         inAppWebView.reload();
 251                     }
 252                 });
 253             }
 254         }
 255         else {
 256             return false;
 257         }
 258         return true;
 259     }
 260 
 261     /**
 262      * Called when the view navigates.
 263      */
 264     @Override
 265     public void onReset() {
 266         closeDialog();
 267     }
 268 
 269     /**
 270      * Called by AccelBroker when listener is to be shut down.
 271      * Stop listener.
 272      */
 273     public void onDestroy() {
 274         closeDialog();
 275     }
 276 
 277     /**
 278      * Inject an object (script or style) into the ThemeableBrowser WebView.
 279      *
 280      * This is a helper method for the inject{Script|Style}{Code|File} API calls, which
 281      * provides a consistent method for injecting JavaScript code into the document.
 282      *
 283      * If a wrapper string is supplied, then the source string will be JSON-encoded (adding
 284      * quotes) and wrapped using string formatting. (The wrapper string should have a single
 285      * '%s' marker)
 286      *
 287      * @param source      The source object (filename or script/style text) to inject into
 288      *                    the document.
 289      * @param jsWrapper   A JavaScript string to wrap the source string in, so that the object
 290      *                    is properly injected, or null if the source string is JavaScript text
 291      *                    which should be executed directly.
 292      */
 293     private void injectDeferredObject(String source, String jsWrapper) {
 294         String scriptToInject;
 295         if (jsWrapper != null) {
 296             org.json.JSONArray jsonEsc = new org.json.JSONArray();
 297             jsonEsc.put(source);
 298             String jsonRepr = jsonEsc.toString();
 299             String jsonSourceString = jsonRepr.substring(1, jsonRepr.length()-1);
 300             scriptToInject = String.format(jsWrapper, jsonSourceString);
 301         } else {
 302             scriptToInject = source;
 303         }
 304         final String finalScriptToInject = scriptToInject;
 305         this.cordova.getActivity().runOnUiThread(new Runnable() {
 306             @SuppressLint("NewApi")
 307             @Override
 308             public void run() {
 309                 if (inAppWebView != null) {
 310                     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
 311                         // This action will have the side-effect of blurring the currently focused
 312                         // element
 313                         inAppWebView.loadUrl("javascript:" + finalScriptToInject);
 314                     } else {
 315                         inAppWebView.evaluateJavascript(finalScriptToInject, null);
 316                     }
 317                 }
 318             }
 319         });
 320     }
 321 
 322     /**
 323      * Put the list of features into a hash map
 324      *
 325      * @param optString
 326      * @return
 327      */
 328     private Options parseFeature(String optString) {
 329         Options result = null;
 330         if (optString != null && !optString.isEmpty()) {
 331             try {
 332                 result = ThemeableBrowserUnmarshaller.JSONToObj(
 333                         optString, Options.class);
 334             } catch (Exception e) {
 335                 emitError(ERR_CRITICAL,
 336                         String.format("Invalid JSON @s", e.toString()));
 337             }
 338         } else {
 339             emitWarning(WRN_UNDEFINED,
 340                     "No config was given, defaults will be used, "
 341                     + "which is quite boring.");
 342         }
 343 
 344         if (result == null) {
 345             result = new Options();
 346         }
 347 
 348         // Always show location, this property is overwritten.
 349         result.location = true;
 350 
 351         return result;
 352     }
 353 
 354     /**
 355      * Display a new browser with the specified URL.
 356      *
 357      * @param url
 358      * @return
 359      */
 360     public String openExternal(String url) {
 361         try {
 362             Intent intent = null;
 363             intent = new Intent(Intent.ACTION_VIEW);
 364             // Omitting the MIME type for file: URLs causes "No Activity found to handle Intent".
 365             // Adding the MIME type to http: URLs causes them to not be handled by the downloader.
 366             Uri uri = Uri.parse(url);
 367             if ("file".equals(uri.getScheme())) {
 368                 intent.setDataAndType(uri, webView.getResourceApi().getMimeType(uri));
 369             } else {
 370                 intent.setData(uri);
 371             }
 372             intent.putExtra(Browser.EXTRA_APPLICATION_ID, cordova.getActivity().getPackageName());
 373             this.cordova.getActivity().startActivity(intent);
 374             return "";
 375         } catch (android.content.ActivityNotFoundException e) {
 376             Log.d(LOG_TAG, "ThemeableBrowser: Error loading url "+url+":"+ e.toString());
 377             return e.toString();
 378         }
 379     }
 380 
 381     /**
 382      * Closes the dialog
 383      */
 384     public void closeDialog() {
 385         this.cordova.getActivity().runOnUiThread(new Runnable() {
 386             @Override
 387             public void run() {
 388                 // The JS protects against multiple calls, so this should happen only when
 389                 // closeDialog() is called by other native code.
 390                 if (inAppWebView == null) {
 391                     emitWarning(WRN_UNEXPECTED, "Close called but already closed.");
 392                     return;
 393                 }
 394 
 395                 inAppWebView.setWebViewClient(new WebViewClient() {
 396                     // NB: wait for about:blank before dismissing
 397                     public void onPageFinished(WebView view, String url) {
 398                         if (dialog != null) {
 399                             dialog.dismiss();
 400                         }
 401 
 402                         // Clean up.
 403                         dialog = null;
 404                         inAppWebView = null;
 405                         edittext = null;
 406                         callbackContext = null;
 407                     }
 408                 });
 409 
 410                 // NB: From SDK 19: "If you call methods on WebView from any
 411                 // thread other than your app's UI thread, it can cause
 412                 // unexpected results."
 413                 // http://developer.android.com/guide/webapps/migrating.html#Threads
 414                 inAppWebView.loadUrl("about:blank");
 415 
 416                 try {
 417                     JSONObject obj = new JSONObject();
 418                     obj.put("type", EXIT_EVENT);
 419                     sendUpdate(obj, false);
 420                 } catch (JSONException ex) {
 421                 }
 422             }
 423         });
 424     }
 425 
 426     private void emitButtonEvent(Event event, String url) {
 427         emitButtonEvent(event, url, null);
 428     }
 429 
 430     private void emitButtonEvent(Event event, String url, Integer index) {
 431         if (event != null && event.event != null) {
 432             try {
 433                 JSONObject obj = new JSONObject();
 434                 obj.put("type", event.event);
 435                 obj.put("url", url);
 436                 if (index != null) {
 437                     obj.put("index", index.intValue());
 438                 }
 439                 sendUpdate(obj, true);
 440             } catch (JSONException e) {
 441                 // Ignore, should never happen.
 442             }
 443         } else {
 444             emitWarning(WRN_UNDEFINED,
 445                     "Button clicked, but event property undefined. "
 446                     + "No event will be raised.");
 447         }
 448     }
 449 
 450     private void emitError(String code, String message) {
 451         emitLog(EVT_ERR, code, message);
 452     }
 453 
 454     private void emitWarning(String code, String message) {
 455         emitLog(EVT_WRN, code, message);
 456     }
 457 
 458     private void emitLog(String type, String code, String message) {
 459         if (type != null) {
 460             try {
 461                 JSONObject obj = new JSONObject();
 462                 obj.put("type", type);
 463                 obj.put("code", code);
 464                 obj.put("message", message);
 465                 sendUpdate(obj, true);
 466             } catch (JSONException e) {
 467                 // Ignore, should never happen.
 468             }
 469         }
 470     }
 471 
 472     /**
 473      * Checks to see if it is possible to go back one page in history, then does so.
 474      */
 475     public void goBack() {
 476         if (this.inAppWebView != null && this.inAppWebView.canGoBack()) {
 477             this.inAppWebView.goBack();
 478         }
 479     }
 480 
 481     /**
 482      * Can the web browser go back?
 483      * @return boolean
 484      */
 485     public boolean canGoBack() {
 486         return this.inAppWebView != null && this.inAppWebView.canGoBack();
 487     }
 488 
 489     /**
 490      * Checks to see if it is possible to go forward one page in history, then does so.
 491      */
 492     private void goForward() {
 493         if (this.inAppWebView != null && this.inAppWebView.canGoForward()) {
 494             this.inAppWebView.goForward();
 495         }
 496     }
 497 
 498     /**
 499      * Navigate to the new page
 500      *
 501      * @param url to load
 502      */
 503     private void navigate(String url) {
 504         InputMethodManager imm = (InputMethodManager)this.cordova.getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
 505         imm.hideSoftInputFromWindow(edittext.getWindowToken(), 0);
 506 
 507         if (!url.startsWith("http") && !url.startsWith("file:")) {
 508             this.inAppWebView.loadUrl("http://" + url);
 509         } else {
 510             this.inAppWebView.loadUrl(url);
 511         }
 512         this.inAppWebView.requestFocus();
 513     }
 514 
 515     private ThemeableBrowser getThemeableBrowser() {
 516         return this;
 517     }
 518 
 519     /**
 520      * Display a new browser with the specified URL.
 521      *
 522      * @param url
 523      * @param features
 524      * @return
 525      */
 526     public String showWebPage(final String url, final Options features) {
 527         final CordovaWebView thatWebView = this.webView;
 528 
 529         // Create dialog in new thread
 530         Runnable runnable = new Runnable() {
 531             @SuppressLint("NewApi")
 532             public void run() {
 533                 // Let's create the main dialog
 534                 dialog = new ThemeableBrowserDialog(cordova.getActivity(),
 535                         android.R.style.Theme_Black_NoTitleBar,
 536                         features.hardwareback);
 537                 if (!features.disableAnimation) {
 538                     dialog.getWindow().getAttributes().windowAnimations
 539                             = android.R.style.Animation_Dialog;
 540                 }
 541                 dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
 542                 dialog.setCancelable(true);
 543                 dialog.setThemeableBrowser(getThemeableBrowser());
 544 
 545                 // Main container layout
 546                 ViewGroup main = null;
 547 
 548                 if (features.fullscreen) {
 549                     main = new FrameLayout(cordova.getActivity());
 550                 } else {
 551                     main = new LinearLayout(cordova.getActivity());
 552                     ((LinearLayout) main).setOrientation(LinearLayout.VERTICAL);
 553                 }
 554 
 555                 // Toolbar layout
 556                 Toolbar toolbarDef = features.toolbar;
 557                 FrameLayout toolbar = new FrameLayout(cordova.getActivity());
 558                 toolbar.setBackgroundColor(hexStringToColor(
 559                         toolbarDef != null && toolbarDef.color != null
 560                                 ? toolbarDef.color : "#ffffffff"));
 561                 toolbar.setLayoutParams(new ViewGroup.LayoutParams(
 562                         LayoutParams.MATCH_PARENT,
 563                         dpToPixels(toolbarDef != null
 564                                 ? toolbarDef.height : TOOLBAR_DEF_HEIGHT)));
 565 
 566                 if (toolbarDef != null
 567                         && (toolbarDef.image != null || toolbarDef.wwwImage != null)) {
 568                     try {
 569                         Drawable background = getImage(toolbarDef.image
 570                                 , toolbarDef.wwwImage, toolbarDef.wwwImageDensity);
 571                         setBackground(toolbar, background);
 572                     } catch (Resources.NotFoundException e) {
 573                         emitError(ERR_LOADFAIL,
 574                                 String.format("Image for toolbar, %s, failed to load",
 575                                         toolbarDef.image));
 576                     } catch (IOException ioe) {
 577                         emitError(ERR_LOADFAIL,
 578                                 String.format("Image for toolbar, %s, failed to load",
 579                                         toolbarDef.wwwImage));
 580                     }
 581                 }
 582 
 583                 // Left Button Container layout
 584                 LinearLayout leftButtonContainer = new LinearLayout(cordova.getActivity());
 585                 FrameLayout.LayoutParams leftButtonContainerParams = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
 586                 leftButtonContainerParams.gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL;
 587                 leftButtonContainer.setLayoutParams(leftButtonContainerParams);
 588                 leftButtonContainer.setVerticalGravity(Gravity.CENTER_VERTICAL);
 589 
 590                 // Right Button Container layout
 591                 LinearLayout rightButtonContainer = new LinearLayout(cordova.getActivity());
 592                 FrameLayout.LayoutParams rightButtonContainerParams = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
 593                 rightButtonContainerParams.gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
 594                 rightButtonContainer.setLayoutParams(rightButtonContainerParams);
 595                 rightButtonContainer.setVerticalGravity(Gravity.CENTER_VERTICAL);
 596 
 597                 // Edit Text Box
 598                 edittext = new EditText(cordova.getActivity());
 599                 RelativeLayout.LayoutParams textLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
 600                 textLayoutParams.addRule(RelativeLayout.RIGHT_OF, 1);
 601                 textLayoutParams.addRule(RelativeLayout.LEFT_OF, 5);
 602                 edittext.setLayoutParams(textLayoutParams);
 603                 edittext.setSingleLine(true);
 604                 edittext.setText(url);
 605                 edittext.setInputType(InputType.TYPE_TEXT_VARIATION_URI);
 606                 edittext.setImeOptions(EditorInfo.IME_ACTION_GO);
 607                 edittext.setInputType(InputType.TYPE_NULL); // Will not except input... Makes the text NON-EDITABLE
 608                 edittext.setOnKeyListener(new View.OnKeyListener() {
 609                     public boolean onKey(View v, int keyCode, KeyEvent event) {
 610                         // If the event is a key-down event on the "enter" button
 611                         if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
 612                             navigate(edittext.getText().toString());
 613                             return true;
 614                         }
 615                         return false;
 616                     }
 617                 });
 618 
 619                 // Back button
 620                 final Button back = createButton(
 621                     features.backButton,
 622                     "back button",
 623                     new View.OnClickListener() {
 624                         public void onClick(View v) {
 625                             emitButtonEvent(
 626                                     features.backButton,
 627                                     inAppWebView.getUrl());
 628 
 629                             if (features.backButtonCanClose && !canGoBack()) {
 630                                 closeDialog();
 631                             } else {
 632                                 goBack();
 633                             }
 634                         }
 635                     }
 636                 );
 637 
 638                 if (back != null) {
 639                     back.setEnabled(features.backButtonCanClose);
 640                 }
 641 
 642                 // Forward button
 643                 final Button forward = createButton(
 644                     features.forwardButton,
 645                     "forward button",
 646                     new View.OnClickListener() {
 647                         public void onClick(View v) {
 648                             emitButtonEvent(
 649                                     features.forwardButton,
 650                                     inAppWebView.getUrl());
 651 
 652                             goForward();
 653                         }
 654                     }
 655                 );
 656 
 657                 if (back != null) {
 658                     back.setEnabled(false);
 659                 }
 660 
 661 
 662                 // Close/Done button
 663                 Button close = createButton(
 664                     features.closeButton,
 665                     "close button",
 666                     new View.OnClickListener() {
 667                         public void onClick(View v) {
 668                             emitButtonEvent(
 669                                     features.closeButton,
 670                                     inAppWebView.getUrl());
 671                             closeDialog();
 672                         }
 673                     }
 674                 );
 675 
 676                 // Menu button
 677                 Spinner menu = features.menu != null
 678                         ? new MenuSpinner(cordova.getActivity()) : null;
 679                 if (menu != null) {
 680                     menu.setLayoutParams(new LinearLayout.LayoutParams(
 681                             LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
 682                     menu.setContentDescription("menu button");
 683                     setButtonImages(menu, features.menu, DISABLED_ALPHA);
 684 
 685                     // We are not allowed to use onClickListener for Spinner, so we will use
 686                     // onTouchListener as a fallback.
 687                     menu.setOnTouchListener(new View.OnTouchListener() {
 688                         @Override
 689                         public boolean onTouch(View v, MotionEvent event) {
 690                             if (event.getAction() == MotionEvent.ACTION_UP) {
 691                                 emitButtonEvent(
 692                                         features.menu,
 693                                         inAppWebView.getUrl());
 694                             }
 695                             return false;
 696                         }
 697                     });
 698 
 699                     if (features.menu.items != null) {
 700                         HideSelectedAdapter<EventLabel> adapter
 701                                 = new HideSelectedAdapter<EventLabel>(
 702                                 cordova.getActivity(),
 703                                 android.R.layout.simple_spinner_item,
 704                                 features.menu.items);
 705                         adapter.setDropDownViewResource(
 706                                 android.R.layout.simple_spinner_dropdown_item);
 707                         menu.setAdapter(adapter);
 708                         menu.setOnItemSelectedListener(
 709                                 new AdapterView.OnItemSelectedListener() {
 710                                     @Override
 711                                     public void onItemSelected(
 712                                             AdapterView<?> adapterView,
 713                                             View view, int i, long l) {
 714                                         if (inAppWebView != null
 715                                                 && i < features.menu.items.length) {
 716                                             emitButtonEvent(
 717                                                     features.menu.items[i],
 718                                                     inAppWebView.getUrl(), i);
 719                                         }
 720                                     }
 721 
 722                                     @Override
 723                                     public void onNothingSelected(
 724                                             AdapterView<?> adapterView) {
 725                                     }
 726                                 }
 727                         );
 728                     }
 729                 }
 730 
 731                 // Title
 732                 final TextView title = features.title != null
 733                         ? new TextView(cordova.getActivity()) : null;
 734                 if (title != null) {
 735                     FrameLayout.LayoutParams titleParams
 736                             = new FrameLayout.LayoutParams(
 737                             LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
 738                     titleParams.gravity = Gravity.CENTER;
 739                     title.setLayoutParams(titleParams);
 740                     title.setSingleLine();
 741                     title.setEllipsize(TextUtils.TruncateAt.END);
 742                     title.setGravity(Gravity.CENTER);
 743                     title.setTextColor(hexStringToColor(
 744                             features.title.color != null
 745                                     ? features.title.color : "#000000ff"));
 746                     if (features.title.staticText != null) {
 747                         title.setText(features.title.staticText);
 748                     }
 749                 }
 750 
 751                 // WebView
 752                 inAppWebView = new WebView(cordova.getActivity());
 753                 final ViewGroup.LayoutParams inAppWebViewParams = features.fullscreen
 754                         ? new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
 755                         : new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, 0);
 756                 if (!features.fullscreen) {
 757                     ((LinearLayout.LayoutParams) inAppWebViewParams).weight = 1;
 758                 }
 759                 inAppWebView.setLayoutParams(inAppWebViewParams);
 760                 inAppWebView.setWebChromeClient(new InAppChromeClient(thatWebView));
 761                 WebViewClient client = new ThemeableBrowserClient(thatWebView, new PageLoadListener() {
 762                     @Override
 763                     public void onPageFinished(String url, boolean canGoBack, boolean canGoForward) {
 764                         if (inAppWebView != null
 765                                 && title != null && features.title != null
 766                                 && features.title.staticText == null
 767                                 && features.title.showPageTitle) {
 768                             title.setText(inAppWebView.getTitle());
 769                         }
 770 
 771                         if (back != null) {
 772                             back.setEnabled(canGoBack || features.backButtonCanClose);
 773                         }
 774 
 775                         if (forward != null) {
 776                             forward.setEnabled(canGoForward);
 777                         }
 778                     }
 779                 });
 780                 inAppWebView.setWebViewClient(client);
 781                 WebSettings settings = inAppWebView.getSettings();
 782                 settings.setJavaScriptEnabled(true);
 783                 settings.setJavaScriptCanOpenWindowsAutomatically(true);
 784                 settings.setBuiltInZoomControls(features.zoom);
 785                 settings.setDisplayZoomControls(false);
 786                 settings.setPluginState(android.webkit.WebSettings.PluginState.ON);
 787 
 788                 //Toggle whether this is enabled or not!
 789                 Bundle appSettings = cordova.getActivity().getIntent().getExtras();
 790                 boolean enableDatabase = appSettings == null || appSettings.getBoolean("ThemeableBrowserStorageEnabled", true);
 791                 if (enableDatabase) {
 792                     String databasePath = cordova.getActivity().getApplicationContext().getDir("themeableBrowserDB", Context.MODE_PRIVATE).getPath();
 793                     settings.setDatabasePath(databasePath);
 794                     settings.setDatabaseEnabled(true);
 795                 }
 796                 settings.setDomStorageEnabled(true);
 797 
 798                 if (features.clearcache) {
 799                     CookieManager.getInstance().removeAllCookie();
 800                 } else if (features.clearsessioncache) {
 801                     CookieManager.getInstance().removeSessionCookie();
 802                 }
 803 
 804                 inAppWebView.loadUrl(url);
 805                 inAppWebView.getSettings().setLoadWithOverviewMode(true);
 806                 inAppWebView.getSettings().setUseWideViewPort(true);
 807                 inAppWebView.requestFocus();
 808                 inAppWebView.requestFocusFromTouch();
 809 
 810                 // Add buttons to either leftButtonsContainer or
 811                 // rightButtonsContainer according to user's alignment
 812                 // configuration.
 813                 int leftContainerWidth = 0;
 814                 int rightContainerWidth = 0;
 815 
 816                 if (features.customButtons != null) {
 817                     for (int i = 0; i < features.customButtons.length; i++) {
 818                         final BrowserButton buttonProps = features.customButtons[i];
 819                         final int index = i;
 820                         Button button = createButton(
 821                             buttonProps,
 822                             String.format("custom button at %d", i),
 823                             new View.OnClickListener() {
 824                                 @Override
 825                                 public void onClick(View view) {
 826                                     if (inAppWebView != null) {
 827                                         emitButtonEvent(buttonProps,
 828                                                 inAppWebView.getUrl(), index);
 829                                     }
 830                                 }
 831                             }
 832                         );
 833 
 834                         if (ALIGN_RIGHT.equals(buttonProps.align)) {
 835                             rightButtonContainer.addView(button);
 836                             rightContainerWidth
 837                                     += button.getLayoutParams().width;
 838                         } else {
 839                             leftButtonContainer.addView(button, 0);
 840                             leftContainerWidth
 841                                     += button.getLayoutParams().width;
 842                         }
 843                     }
 844                 }
 845 
 846                 // Back and forward buttons must be added with special ordering logic such
 847                 // that back button is always on the left of forward button if both buttons
 848                 // are on the same side.
 849                 if (forward != null && features.forwardButton != null
 850                         && !ALIGN_RIGHT.equals(features.forwardButton.align)) {
 851                     leftButtonContainer.addView(forward, 0);
 852                     leftContainerWidth
 853                             += forward.getLayoutParams().width;
 854                 }
 855 
 856                 if (back != null && features.backButton != null
 857                         && ALIGN_RIGHT.equals(features.backButton.align)) {
 858                     rightButtonContainer.addView(back);
 859                     rightContainerWidth
 860                             += back.getLayoutParams().width;
 861                 }
 862 
 863                 if (back != null && features.backButton != null
 864                         && !ALIGN_RIGHT.equals(features.backButton.align)) {
 865                     leftButtonContainer.addView(back, 0);
 866                     leftContainerWidth
 867                             += back.getLayoutParams().width;
 868                 }
 869 
 870                 if (forward != null && features.forwardButton != null
 871                         && ALIGN_RIGHT.equals(features.forwardButton.align)) {
 872                     rightButtonContainer.addView(forward);
 873                     rightContainerWidth
 874                             += forward.getLayoutParams().width;
 875                 }
 876 
 877                 if (menu != null) {
 878                     if (features.menu != null
 879                             && ALIGN_RIGHT.equals(features.menu.align)) {
 880                         rightButtonContainer.addView(menu);
 881                         rightContainerWidth
 882                                 += menu.getLayoutParams().width;
 883                     } else {
 884                         leftButtonContainer.addView(menu, 0);
 885                         leftContainerWidth
 886                                 += menu.getLayoutParams().width;
 887                     }
 888                 }
 889 
 890                 if (close != null) {
 891                     if (features.closeButton != null
 892                             && ALIGN_RIGHT.equals(features.closeButton.align)) {
 893                         rightButtonContainer.addView(close);
 894                         rightContainerWidth
 895                                 += close.getLayoutParams().width;
 896                     } else {
 897                         leftButtonContainer.addView(close, 0);
 898                         leftContainerWidth
 899                                 += close.getLayoutParams().width;
 900                     }
 901                 }
 902 
 903                 // Add the views to our toolbar
 904                 toolbar.addView(leftButtonContainer);
 905                 // Don't show address bar.
 906                 // toolbar.addView(edittext);
 907                 toolbar.addView(rightButtonContainer);
 908 
 909                 if (title != null) {
 910                     int titleMargin = Math.max(
 911                             leftContainerWidth, rightContainerWidth);
 912 
 913                     FrameLayout.LayoutParams titleParams
 914                             = (FrameLayout.LayoutParams) title.getLayoutParams();
 915                     titleParams.setMargins(titleMargin, 0, titleMargin, 0);
 916                     toolbar.addView(title);
 917                 }
 918 
 919                 if (features.fullscreen) {
 920                     // If full screen mode, we have to add inAppWebView before adding toolbar.
 921                     main.addView(inAppWebView);
 922                 }
 923 
 924                 // Don't add the toolbar if its been disabled
 925                 if (features.location) {
 926                     // Add our toolbar to our main view/layout
 927                     main.addView(toolbar);
 928                 }
 929 
 930                 if (!features.fullscreen) {
 931                     // If not full screen, we add inAppWebView after adding toolbar.
 932                     main.addView(inAppWebView);
 933                 }
 934 
 935                 WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
 936                 lp.copyFrom(dialog.getWindow().getAttributes());
 937                 lp.width = WindowManager.LayoutParams.MATCH_PARENT;
 938                 lp.height = WindowManager.LayoutParams.MATCH_PARENT;
 939 
 940                 dialog.setContentView(main);
 941                 dialog.show();
 942                 dialog.getWindow().setAttributes(lp);
 943                 // the goal of openhidden is to load the url and not display it
 944                 // Show() needs to be called to cause the URL to be loaded
 945                 if(features.hidden) {
 946                     dialog.hide();
 947                 }
 948             }
 949         };
 950         this.cordova.getActivity().runOnUiThread(runnable);
 951         return "";
 952     }
 953 
 954     /**
 955      * Convert our DIP units to Pixels
 956      *
 957      * @return int
 958      */
 959     private int dpToPixels(int dipValue) {
 960         int value = (int) TypedValue.applyDimension(
 961                 TypedValue.COMPLEX_UNIT_DIP,
 962                 (float) dipValue,
 963                 cordova.getActivity().getResources().getDisplayMetrics()
 964         );
 965 
 966         return value;
 967     }
 968 
 969     private int hexStringToColor(String hex) {
 970         int result = 0;
 971 
 972         if (hex != null && !hex.isEmpty()) {
 973             if (hex.charAt(0) == '#') {
 974                 hex = hex.substring(1);
 975             }
 976 
 977             // No alpha, that's fine, we will just attach ff.
 978             if (hex.length() < 8) {
 979                 hex += "ff";
 980             }
 981 
 982             result = (int) Long.parseLong(hex, 16);
 983 
 984             // Almost done, but Android color code is in form of ARGB instead of
 985             // RGBA, so we gotta shift it a bit.
 986             int alpha = (result & 0xff) << 24;
 987             result = result >> 8 & 0xffffff | alpha;
 988         }
 989 
 990         return result;
 991     }
 992 
 993     /**
 994     * This is a rather unintuitive helper method to load images. The reason why this method exists
 995     * is because due to some service limitations, one may not be able to add images to native
 996     * resource bundle. So this method offers a way to load image from www contents instead.
 997     * However loading from native resource bundle is already preferred over loading from www. So
 998     * if name is given, then it simply loads from resource bundle and the other two parameters are
 999     * ignored. If name is not given, then altPath is assumed to be a file path _under_ www and
1000     * altDensity is the desired density of the given image file, because without native resource
1001     * bundle, we can't tell what density the image is supposed to be so it needs to be given
1002     * explicitly.
1003     */
1004     private Drawable getImage(String name, String altPath, double altDensity) throws IOException {
1005         Drawable result = null;
1006         Resources activityRes = cordova.getActivity().getResources();
1007 
1008         if (name != null) {
1009             int id = activityRes.getIdentifier(name, "drawable",
1010                     cordova.getActivity().getPackageName());
1011             if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
1012                 result = activityRes.getDrawable(id);
1013             } else {
1014                 result = activityRes.getDrawable(id, cordova.getActivity().getTheme());
1015             }
1016         } else if (altPath != null) {
1017             File file = new File("www", altPath);
1018             InputStream is = null;
1019             try {
1020                 is = cordova.getActivity().getAssets().open(file.getPath());
1021                 Bitmap bitmap = BitmapFactory.decodeStream(is);
1022                 bitmap.setDensity((int) (DisplayMetrics.DENSITY_MEDIUM * altDensity));
1023                 result = new BitmapDrawable(activityRes, bitmap);
1024             } finally {
1025                 // Make sure we close this input stream to prevent resource leak.
1026                 try {
1027                     is.close();
1028                 } catch (Exception e) {}
1029             }
1030         }
1031         return result;
1032     }
1033 
1034     private void setButtonImages(View view, BrowserButton buttonProps, int disabledAlpha) {
1035         Drawable normalDrawable = null;
1036         Drawable disabledDrawable = null;
1037         Drawable pressedDrawable = null;
1038 
1039         CharSequence description = view.getContentDescription();
1040 
1041         if (buttonProps.image != null || buttonProps.wwwImage != null) {
1042             try {
1043                 normalDrawable = getImage(buttonProps.image, buttonProps.wwwImage,
1044                         buttonProps.wwwImageDensity);
1045                 ViewGroup.LayoutParams params = view.getLayoutParams();
1046                 params.width = normalDrawable.getIntrinsicWidth();
1047                 params.height = normalDrawable.getIntrinsicHeight();
1048             } catch (Resources.NotFoundException e) {
1049                 emitError(ERR_LOADFAIL,
1050                         String.format("Image for %s, %s, failed to load",
1051                                 description, buttonProps.image));
1052             } catch (IOException ioe) {
1053                 emitError(ERR_LOADFAIL,
1054                         String.format("Image for %s, %s, failed to load",
1055                                 description, buttonProps.wwwImage));
1056             }
1057         } else {
1058             emitWarning(WRN_UNDEFINED,
1059                     String.format("Image for %s is not defined. Button will not be shown",
1060                             description));
1061         }
1062 
1063         if (buttonProps.imagePressed != null || buttonProps.wwwImagePressed != null) {
1064             try {
1065                 pressedDrawable = getImage(buttonProps.imagePressed, buttonProps.wwwImagePressed,
1066                         buttonProps.wwwImageDensity);
1067             } catch (Resources.NotFoundException e) {
1068                 emitError(ERR_LOADFAIL,
1069                         String.format("Pressed image for %s, %s, failed to load",
1070                                 description, buttonProps.imagePressed));
1071             } catch (IOException e) {
1072                 emitError(ERR_LOADFAIL,
1073                         String.format("Pressed image for %s, %s, failed to load",
1074                                 description, buttonProps.wwwImagePressed));
1075             }
1076         } else {
1077             emitWarning(WRN_UNDEFINED,
1078                     String.format("Pressed image for %s is not defined.",
1079                             description));
1080         }
1081 
1082         if (normalDrawable != null) {
1083             // Create the disabled state drawable by fading the normal state
1084             // drawable. Drawable.setAlpha() stopped working above Android 4.4
1085             // so we gotta bring out some bitmap magic. Credit goes to:
1086             // http://stackoverflow.com/a/7477572
1087             Bitmap enabledBitmap = ((BitmapDrawable) normalDrawable).getBitmap();
1088             Bitmap disabledBitmap = Bitmap.createBitmap(
1089                     normalDrawable.getIntrinsicWidth(),
1090                     normalDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
1091             Canvas canvas = new Canvas(disabledBitmap);
1092 
1093             Paint paint = new Paint();
1094             paint.setAlpha(disabledAlpha);
1095             canvas.drawBitmap(enabledBitmap, 0, 0, paint);
1096 
1097             Resources activityRes = cordova.getActivity().getResources();
1098             disabledDrawable = new BitmapDrawable(activityRes, disabledBitmap);
1099         }
1100 
1101         StateListDrawable states = new StateListDrawable();
1102         if (pressedDrawable != null) {
1103             states.addState(
1104                 new int[] {
1105                     android.R.attr.state_pressed
1106                 },
1107                 pressedDrawable
1108             );
1109         }
1110         if (normalDrawable != null) {
1111             states.addState(
1112                 new int[] {
1113                     android.R.attr.state_enabled
1114                 },
1115                 normalDrawable
1116             );
1117         }
1118         if (disabledDrawable != null) {
1119             states.addState(
1120                 new int[] {},
1121                 disabledDrawable
1122             );
1123         }
1124 
1125         setBackground(view, states);
1126     }
1127 
1128     private void setBackground(View view, Drawable drawable) {
1129         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
1130             view.setBackgroundDrawable(drawable);
1131         } else {
1132             view.setBackground(drawable);
1133         }
1134     }
1135 
1136     private Button createButton(BrowserButton buttonProps, String description,
1137             View.OnClickListener listener) {
1138         Button result = null;
1139         if (buttonProps != null) {
1140             result = new Button(cordova.getActivity());
1141             result.setContentDescription(description);
1142             result.setLayoutParams(new LinearLayout.LayoutParams(
1143                     LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
1144             setButtonImages(result, buttonProps, DISABLED_ALPHA);
1145             if (listener != null) {
1146                 result.setOnClickListener(listener);
1147             }
1148         } else {
1149             emitWarning(WRN_UNDEFINED,
1150                     String.format("%s is not defined. Button will not be shown.",
1151                             description));
1152         }
1153         return result;
1154     }
1155 
1156     /**
1157      * Create a new plugin success result and send it back to JavaScript
1158      *
1159      * @param obj a JSONObject contain event payload information
1160      */
1161     private void sendUpdate(JSONObject obj, boolean keepCallback) {
1162         sendUpdate(obj, keepCallback, PluginResult.Status.OK);
1163     }
1164 
1165     /**
1166      * Create a new plugin result and send it back to JavaScript
1167      *
1168      * @param obj a JSONObject contain event payload information
1169      * @param status the status code to return to the JavaScript environment
1170      */
1171     private void sendUpdate(JSONObject obj, boolean keepCallback, PluginResult.Status status) {
1172         if (callbackContext != null) {
1173             PluginResult result = new PluginResult(status, obj);
1174             result.setKeepCallback(keepCallback);
1175             callbackContext.sendPluginResult(result);
1176             if (!keepCallback) {
1177                 callbackContext = null;
1178             }
1179         }
1180     }
1181 
1182     public static interface PageLoadListener {
1183         public void onPageFinished(String url, boolean canGoBack,
1184                 boolean canGoForward);
1185     }
1186 
1187     /**
1188      * The webview client receives notifications about appView
1189      */
1190     public class ThemeableBrowserClient extends WebViewClient {
1191         PageLoadListener callback;
1192         CordovaWebView webView;
1193 
1194         /**
1195          * Constructor.
1196          *
1197          * @param webView
1198          * @param callback
1199          */
1200         public ThemeableBrowserClient(CordovaWebView webView,
1201                 PageLoadListener callback) {
1202             this.webView = webView;
1203             this.callback = callback;
1204         }
1205 
1206         /**
1207          * Override the URL that should be loaded
1208          *
1209          * This handles a small subset of all the URIs that would be encountered.
1210          *
1211          * @param webView
1212          * @param url
1213          */
1214         @Override
1215         public boolean shouldOverrideUrlLoading(WebView webView, String url) {
1216             if (url.startsWith(WebView.SCHEME_TEL)) {
1217                 try {
1218                     Intent intent = new Intent(Intent.ACTION_DIAL);
1219                     intent.setData(Uri.parse(url));
1220                     cordova.getActivity().startActivity(intent);
1221                     return true;
1222                 } catch (android.content.ActivityNotFoundException e) {
1223                     Log.e(LOG_TAG, "Error dialing " + url + ": " + e.toString());
1224                 }
1225             } else if (url.startsWith("geo:") || url.startsWith(WebView.SCHEME_MAILTO) || url.startsWith("market:")) {
1226                 try {
1227                     Intent intent = new Intent(Intent.ACTION_VIEW);
1228                     intent.setData(Uri.parse(url));
1229                     cordova.getActivity().startActivity(intent);
1230                     return true;
1231                 } catch (android.content.ActivityNotFoundException e) {
1232                     Log.e(LOG_TAG, "Error with " + url + ": " + e.toString());
1233                 }
1234             }
1235             // If sms:5551212?body=This is the message
1236             else if (url.startsWith("sms:")) {
1237                 try {
1238                     Intent intent = new Intent(Intent.ACTION_VIEW);
1239 
1240                     // Get address
1241                     String address = null;
1242                     int parmIndex = url.indexOf('?');
1243                     if (parmIndex == -1) {
1244                         address = url.substring(4);
1245                     } else {
1246                         address = url.substring(4, parmIndex);
1247 
1248                         // If body, then set sms body
1249                         Uri uri = Uri.parse(url);
1250                         String query = uri.getQuery();
1251                         if (query != null) {
1252                             if (query.startsWith("body=")) {
1253                                 intent.putExtra("sms_body", query.substring(5));
1254                             }
1255                         }
1256                     }
1257                     intent.setData(Uri.parse("sms:" + address));
1258                     intent.putExtra("address", address);
1259                     intent.setType("vnd.android-dir/mms-sms");
1260                     cordova.getActivity().startActivity(intent);
1261                     return true;
1262                 } catch (android.content.ActivityNotFoundException e) {
1263                     Log.e(LOG_TAG, "Error sending sms " + url + ":" + e.toString());
1264                 }
1265             }
1266             return false;
1267         }
1268 
1269 
1270         /*
1271          * onPageStarted fires the LOAD_START_EVENT
1272          *
1273          * @param view
1274          * @param url
1275          * @param favicon
1276          */
1277         @Override
1278         public void onPageStarted(WebView view, String url, Bitmap favicon) {
1279             super.onPageStarted(view, url, favicon);
1280             String newloc = "";
1281             if (url.startsWith("http:") || url.startsWith("https:") || url.startsWith("file:")) {
1282                 newloc = url;
1283             }
1284             else
1285             {
1286                 // Assume that everything is HTTP at this point, because if we don't specify,
1287                 // it really should be.  Complain loudly about this!!!
1288                 Log.e(LOG_TAG, "Possible Uncaught/Unknown URI");
1289                 newloc = "http://" + url;
1290             }
1291 
1292             // Update the UI if we haven't already
1293             if (!newloc.equals(edittext.getText().toString())) {
1294                 edittext.setText(newloc);
1295             }
1296 
1297             try {
1298                 JSONObject obj = new JSONObject();
1299                 obj.put("type", LOAD_START_EVENT);
1300                 obj.put("url", newloc);
1301                 sendUpdate(obj, true);
1302             } catch (JSONException ex) {
1303                 Log.e(LOG_TAG, "URI passed in has caused a JSON error.");
1304             }
1305         }
1306 
1307         public void onPageFinished(WebView view, String url) {
1308             super.onPageFinished(view, url);
1309 
1310             try {
1311                 JSONObject obj = new JSONObject();
1312                 obj.put("type", LOAD_STOP_EVENT);
1313                 obj.put("url", url);
1314 
1315                 sendUpdate(obj, true);
1316 
1317                 if (this.callback != null) {
1318                     this.callback.onPageFinished(url, view.canGoBack(),
1319                             view.canGoForward());
1320                 }
1321             } catch (JSONException ex) {
1322             }
1323         }
1324 
1325         public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
1326             super.onReceivedError(view, errorCode, description, failingUrl);
1327 
1328             try {
1329                 JSONObject obj = new JSONObject();
1330                 obj.put("type", LOAD_ERROR_EVENT);
1331                 obj.put("url", failingUrl);
1332                 obj.put("code", errorCode);
1333                 obj.put("message", description);
1334 
1335                 sendUpdate(obj, true, PluginResult.Status.ERROR);
1336             } catch (JSONException ex) {
1337             }
1338         }
1339     }
1340 
1341     /**
1342      * Like Spinner but will always trigger onItemSelected even if a selected
1343      * item is selected, and always ignore default selection.
1344      */
1345     public class MenuSpinner extends Spinner {
1346         private OnItemSelectedListener listener;
1347 
1348         public MenuSpinner(Context context) {
1349             super(context);
1350         }
1351 
1352         @Override
1353         public void setSelection(int position) {
1354             super.setSelection(position);
1355 
1356             if (listener != null) {
1357                 listener.onItemSelected(null, this, position, 0);
1358             }
1359         }
1360 
1361         @Override
1362         public void setOnItemSelectedListener(OnItemSelectedListener listener) {
1363             this.listener = listener;
1364         }
1365     }
1366 
1367     /**
1368      * Extension of ArrayAdapter. The only difference is that it hides the
1369      * selected text that's shown inside spinner.
1370      * @param <T>
1371      */
1372     private static class HideSelectedAdapter<T> extends ArrayAdapter {
1373 
1374         public HideSelectedAdapter(Context context, int resource, T[] objects) {
1375             super(context, resource, objects);
1376         }
1377 
1378         public View getView (int position, View convertView, ViewGroup parent) {
1379             View v = super.getView(position, convertView, parent);
1380             v.setVisibility(View.GONE);
1381             return v;
1382         }
1383     }
1384 
1385 
1386     /**
1387      * A class to hold parsed option properties.
1388      */
1389     private static class Options {
1390         public boolean location = true;
1391         public boolean hidden = false;
1392         public boolean clearcache = false;
1393         public boolean clearsessioncache = false;
1394         public boolean zoom = true;
1395         public boolean hardwareback = true;
1396 
1397         public Toolbar toolbar;
1398         public Title title;
1399         public BrowserButton backButton;
1400         public BrowserButton forwardButton;
1401         public BrowserButton closeButton;
1402         public BrowserMenu menu;
1403         public BrowserButton[] customButtons;
1404         public boolean backButtonCanClose;
1405         public boolean disableAnimation;
1406         public boolean fullscreen;
1407     }
1408 
1409     private static class Event {
1410         public String event;
1411     }
1412 
1413     private static class EventLabel extends Event {
1414         public String label;
1415 
1416         public String toString() {
1417             return label;
1418         }
1419     }
1420 
1421     private static class BrowserButton extends Event {
1422         public String image;
1423         public String wwwImage;
1424         public String imagePressed;
1425         public String wwwImagePressed;
1426         public double wwwImageDensity = 1;
1427         public String align = ALIGN_LEFT;
1428     }
1429 
1430     private static class BrowserMenu extends BrowserButton {
1431         public EventLabel[] items;
1432     }
1433 
1434     private static class Toolbar {
1435         public int height = TOOLBAR_DEF_HEIGHT;
1436         public String color;
1437         public String image;
1438         public String wwwImage;
1439         public double wwwImageDensity = 1;
1440     }
1441 
1442     private static class Title {
1443         public String color;
1444         public String staticText;
1445         public boolean showPageTitle;
1446     }
1447 }

 

在搞之前 首先要有常识   在 Android 手机中内置了一款高性能 webkit 内核浏览器,在 SDK 中封装为一个叫做 WebView 组件.

其次 在打开页面时选择横竖屏 , 然后去谷歌搜下 android webview 横竖屏 比如下面这篇文章截图  知道 Activity类可以通过setRequestedOrientation 方法设置 横竖屏显示

 

 

 

  我们调用js 是用的 ThemeableBrowser 类的open  看上面的代码copy

发现它重写了父类 CordovaPlugin 的execute方法去执行open操作 我们调用的方法参数是

js调用第二个参数是  _black 所以直接看188行 然后跳转526行 showWebPage
看752行
inAppWebView = new WebView(cordova.getActivity());
804行
inAppWebView.loadUrl(url);

loadUrl就是把webview展示出来, 这个webview实例是通过 752行 cordova.getActivity() 来实例化的

cordova.getActivity() 返回的就是  Activity 类型  上面搜索知道  Activity.setRequestedOrientation 可以设置横竖屏展示 

然后搜索 cordova.getActivity()   发现 cordova是个接口  然后搜一下这个接口的实现类 发现是 CordovaInterfaceImpl这个类 发现他的getActivity方法是返回的 构造函数注入的 activity

然后再找 CordovaInterfaceImpl 实在 CordovaActivity.java的 makeCordovaInterface 方法中实例化的

 makeCordovaInterface 是在 CordovaActivity的 oncreate方法实例化的

CordovaActivity 继承 Activity  MainActivity又继承CordovaActivity

 

Activity是Android的四大组件之一。是用户操作的可视化界面  MainActivity一般作为主页面展示

Android手机APP启动的第一个Activity是可以自己设置的,不是必须的MainActivity,可以是任何的Activity。

设置Android手机APP启动的第一个Activity得看Android项目里的mainfest.xml文件:

 

简单理一下就是

app启动 => MainActivity初始化 onCreate方法 => makeCordovaInterface(this) 把MainActivity本身最为参数实例化CordovaInterfaceImpl类 => CordovaInterface作为接口参数cordova 被组合到 ThemeableBrowser类

js调用 ThemeableBrowser.open 方法实例化一个inAppWebView = new WebView(cordova.getActivity());    cordova.getActivity()即是 MainActivity实例本身

我们通过给MainActivity设置横竖屏的显示方式( setRequestedOrientation) 然后展示 url页面  inAppWebView.loadUrl(url);

 

 

再看关闭按钮事件 671行 调用 closeDialog

我们在这里用 setRequestedOrientation把 Activity改为竖屏

 

 

整个解决方式是

 

 

 

 

 

 js调用时候

 

posted @ 2020-11-13 11:00  小谭行天下  阅读(1924)  评论(0编辑  收藏  举报