org.apache.appharness.AppHarnessUI.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.appharness.AppHarnessUI.java

Source

/*
   Licensed to the Apache Software Foundation (ASF) under one
   or more contributor license agreements.  See the NOTICE file
   distributed with this work for additional information
   regarding copyright ownership.  The ASF licenses this file
   to you under the Apache License, Version 2.0 (the
   "License"); you may not use this file except in compliance
   with the License.  You may obtain a copy of the License at
    
     http://www.apache.org/licenses/LICENSE-2.0
    
   Unless required by applicable law or agreed to in writing,
   software distributed under the License is distributed on an
   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   KIND, either express or implied.  See the License for the
   specific language governing permissions and limitations
   under the License.
*/
package org.apache.appharness;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

import org.apache.cordova.CallbackContext;
import org.apache.cordova.ConfigXmlParser;
import org.apache.cordova.CordovaActivity;
import org.apache.cordova.CordovaArgs;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaWebView;
import org.apache.cordova.CordovaWebViewEngine;
import org.apache.cordova.CordovaWebViewImpl;
import org.apache.cordova.PluginEntry;
import org.apache.cordova.PluginResult;
import org.apache.cordova.whitelist.WhitelistPlugin;
import org.json.JSONArray;
import org.json.JSONException;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

import android.annotation.TargetApi;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.XmlResourceParser;
import android.net.Uri;
import android.os.Build;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import android.view.animation.DecelerateInterpolator;

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class AppHarnessUI extends CordovaPlugin {
    private static final String LOG_TAG = "AppHarnessUI";
    ViewGroup contentView;
    CordovaWebView slaveWebView;
    CustomCordovaWebView slaveWebViewEngine;
    boolean slaveVisible;
    CallbackContext eventsCallback;

    @Override
    public void pluginInitialize() {
        contentView = (ViewGroup) cordova.getActivity().findViewById(android.R.id.content);
    }

    @Override
    public void onPause(boolean multitasking) {
        if (slaveWebView != null) {
            slaveWebView.handlePause(multitasking);
        }
    }

    @Override
    public void onResume(boolean multitasking) {
        if (slaveWebView != null) {
            slaveWebView.handleResume(multitasking);
        }
    }

    @Override
    public void onStart() {
        if (slaveWebView != null) {
            slaveWebView.handleStart();
        }
    }

    @Override
    public void onStop() {
        if (slaveWebView != null) {
            slaveWebView.handleStop();
        }
    }

    @Override
    public void onDestroy() {
        if (slaveWebView != null) {
            slaveWebView.handleDestroy();
        }
    }

    @Override
    public void onNewIntent(Intent intent) {
        if (slaveWebView != null) {
            slaveWebView.onNewIntent(intent);
        }
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
        // TODO: implement me by passing a custom CordovaInterface to slaveWebView
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        if (slaveWebView != null) {
            slaveWebView.getPluginManager().onConfigurationChanged(newConfig);
        }
    }

    public boolean isSlaveVisible() {
        return slaveVisible;
    }

    public boolean isSlaveCreated() {
        return slaveWebView != null && slaveWebView.getView().getParent() != null;
    }

    @Override
    public boolean execute(String action, CordovaArgs args, final CallbackContext callbackContext)
            throws JSONException {
        if ("create".equals(action)) {
            final Uri startUri = Uri.parse(args.getString(0));
            final Uri configXmlUri = Uri.parse(args.getString(1));
            final Set<String> pluginIdWhitelistAsSet = jsonArrayToSet(args.getJSONArray(2));
            final String webViewType = args.getString(3);
            this.cordova.getActivity().runOnUiThread(new Runnable() {
                public void run() {
                    create(startUri, configXmlUri, pluginIdWhitelistAsSet, webViewType, callbackContext);
                }
            });
        } else if ("reload".equals(action)) {
            final Uri startUri = Uri.parse(args.getString(0));
            final Uri configXmlUri = Uri.parse(args.getString(1));
            final Set<String> pluginIdWhitelistAsSet = jsonArrayToSet(args.getJSONArray(2));
            this.cordova.getActivity().runOnUiThread(new Runnable() {
                public void run() {
                    reload(startUri, configXmlUri, pluginIdWhitelistAsSet, callbackContext);
                }
            });
        } else if ("destroy".equals(action)) {
            this.cordova.getActivity().runOnUiThread(new Runnable() {
                public void run() {
                    destroy(callbackContext);
                }
            });
        } else if ("setVisible".equals(action)) {
            final boolean value = args.getBoolean(0);
            this.cordova.getActivity().runOnUiThread(new Runnable() {
                public void run() {
                    setSlaveVisible(value, callbackContext);
                }
            });
        } else if ("evalJs".equals(action)) {
            final String code = args.getString(0);
            this.cordova.getActivity().runOnUiThread(new Runnable() {
                public void run() {
                    evalJs(code, callbackContext);
                }
            });
        } else if ("events".equals(action)) {
            eventsCallback = callbackContext;
        } else {
            return false;
        }
        return true;
    }

    private Set<String> jsonArrayToSet(JSONArray jsonArray) throws JSONException {
        final Set<String> set = new HashSet<String>(jsonArray.length());
        for (int i = 0; i < jsonArray.length(); ++i) {
            set.add(jsonArray.getString(i));
        }
        return set;
    }

    public void sendEvent(String eventName) {
        if (eventsCallback != null) {
            PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, eventName);
            pluginResult.setKeepCallback(true);
            eventsCallback.sendPluginResult(pluginResult);
        }
    }

    private void evalJs(String code, CallbackContext callbackContext) {
        if (slaveWebViewEngine == null) {
            Log.w(LOG_TAG, "Not evaluating JS since no app is active");
        } else {
            slaveWebViewEngine.evaluateJavascript(code);
        }
        callbackContext.success();
    }

    private void create(Uri startUri, Uri configXmlUri, Set<String> pluginIdWhitelist, String webViewType,
            CallbackContext callbackContext) {
        CordovaActivity activity = (CordovaActivity) cordova.getActivity();

        if (slaveWebView != null) {
            Log.w(LOG_TAG, "create: already exists");
        } else {
            slaveWebViewEngine = new CustomAndroidWebView(this, activity);
            slaveWebView = new CordovaWebViewImpl((CordovaWebViewEngine) slaveWebViewEngine);
            // A consistent view ID is needed for plugins that utilize the background-activity plugin.
            slaveWebView.getView().setId(200);
            // We'll set the plugin entries in initWebView.
            slaveWebView.init(cordova, new ArrayList<PluginEntry>(), preferences);
        }
        setPluginEntries(pluginIdWhitelist, configXmlUri);

        slaveWebView.clearCache(true);
        slaveWebView.clearHistory();
        slaveWebView.loadUrl(startUri.toString());
        contentView.addView(slaveWebView.getView());
        slaveVisible = true;
        // Back button capturing breaks without these:
        webView.getView().setEnabled(false);
        slaveWebView.getView().requestFocus();
        callbackContext.success();
    }

    private void reload(Uri startUri, Uri configXmlUri, Set<String> pluginIdWhitelist,
            CallbackContext callbackContext) {
        if (slaveWebView == null) {
            Log.w(LOG_TAG, "reload: no webview exists");
        } else {
            // TODO(maxw): If the webview type has changed, create a new webview.
            setPluginEntries(pluginIdWhitelist, configXmlUri);
            slaveWebView.clearCache(true);
            slaveWebView.clearHistory();
            slaveWebView.loadUrl(startUri.toString());
        }
        callbackContext.success();
    }

    private void destroy(CallbackContext callbackContext) {
        if (slaveWebView == null) {
            Log.w(LOG_TAG, "destroy: already destroyed");
        } else {
            slaveWebView.loadUrl("data:text/plain;charset=utf-8,");
            contentView.removeView(slaveWebView.getView());
            webView.getView().setEnabled(true);
            webView.getView().requestFocus();

            slaveWebView.getView().setScaleX(1.0f);
            slaveWebView.getView().setScaleY(1.0f);
            slaveWebViewEngine.setStealTapEvents(false);
            slaveVisible = false;
            sendEvent("destroyed");
        }
        if (eventsCallback != null) {
            eventsCallback.success("");
            eventsCallback = null;
        }
        callbackContext.success();
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    private void setSlaveVisible(boolean value, CallbackContext callbackContext) {
        if (value == slaveVisible) {
            return;
        }
        if (slaveWebView == null) {
            Log.w(LOG_TAG, "setSlaveVisible: slave not created");
        } else {
            slaveVisible = value;
            ViewPropertyAnimator anim = slaveWebView.getView().animate();
            // Note: Pivot is set in onSizeChanged.
            if (value) {
                anim.scaleX(1.0f).scaleY(1.0f);
                webView.getView().setEnabled(false);
                slaveWebView.getView().setEnabled(true);
                slaveWebView.getView().requestFocus();
            } else {
                anim.scaleX(.25f).scaleY(.25f);
                webView.getView().setEnabled(true);
                slaveWebView.getView().setEnabled(false);
                webView.getView().requestFocus();
            }
            slaveWebViewEngine.setStealTapEvents(!value);
            anim.setDuration(300).setInterpolator(new DecelerateInterpolator(2.0f)).start();
        }
        if (callbackContext != null) {
            callbackContext.success();
        }
    }

    private void setPluginEntries(Set<String> pluginIdWhitelist, Uri configXmlUri) {
        CordovaActivity activity = (CordovaActivity) cordova.getActivity();
        // Extract the <feature> from CADT's config.xml, and filter out unwanted plugins.
        ConfigXmlParser parser = new ConfigXmlParser();
        parser.parse(activity);
        ArrayList<PluginEntry> pluginEntries = new ArrayList<PluginEntry>(parser.getPluginEntries());
        for (int i = 0; i < pluginEntries.size();) {
            PluginEntry p = pluginEntries.get(i);
            if (!pluginIdWhitelist.contains(p.service)) {
                pluginEntries.remove(p);
                continue;
            } else if (WhitelistPlugin.class.getCanonicalName().equals(p.pluginClass)) {
                pluginEntries.set(i, new PluginEntry(p.service, createWhitelistPlugin(configXmlUri)));
            }
            ++i;
        }
        slaveWebView.getPluginManager().setPluginEntries(pluginEntries);
        // This is added by cordova-android in code, so we need to re-add it likewise.
        // Note that we re-route navigator.app.exitApp() in JS to close the webview rather than close the Activity.
        slaveWebView.getPluginManager().addService("CoreAndroid", "org.apache.cordova.CoreAndroid");
    }

    private CordovaPlugin createWhitelistPlugin(Uri configXmlUri) {
        InputStream istr = null;
        try {
            istr = new FileInputStream(configXmlUri.getPath());
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            factory.setNamespaceAware(false);
            XmlPullParser parser = factory.newPullParser();
            parser.setInput(istr, "UTF-8");
            return new WhitelistPlugin(parser);
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (istr != null) {
                try {
                    istr.close();
                } catch (IOException e) {
                }
            }
        }
        return null;
    }
}