• 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调用时候

  • 相关阅读:
    sql 生成javabean实体
    git 安装 使用过程遇到的问题
    CentOS7 ab压力测试安装
    Lvs+keepalived+mysql(主从复制)
    liunx下tomcat启动 Cannot find ./catalina.sh
    ftp和ssh登录缓慢的解决办法
    Contos7 FTP 安装步骤
    python生成100以内格式化的数
    Windows中更新python模块的命令
    scrapy的User-Agent中间件、代理IP中间件、cookies设置、多个爬虫自定义settings设置
  • 原文地址:https://www.cnblogs.com/xtxtx/p/13968267.html
Copyright © 2020-2023  润新知