An application is considered to be hybrid if it contains a WebView in the Android application.

If you use a WebView, you can use UICWebView to log request activity in the WebView. UICWebView extends the base WebView from the Android Framework, which inserts the IBM Tealeaf header with the current session ID for sessionization. If you decide not to use UICWebView, then you need to customize according to the framework. See the HybridHtmlEmbeddedManual sample app for more details about setting up the Tealeaf bridge to send Web content to the native SDK.

Important: For each WebView defined in the hybrid application, the following requirements must be done for Replay to properly function:

  1. Instrument the UIC JavaScript library. See UI Capture documentation for more information.
  2. Instrument a JavaScript bridge between the Web and the Android native application. For more information, see Hybrid Application settings.
  3. Upload web content resources such as images, CSS, and static content to the Replay server. For information about uploading images to the Replay server, see Capturing images for application replay.

Google WebView auto instrumentation

IBM Tealeaf Android SDK supports Hybrid applications that use Google WebView for auto instrumentation.

The SDK will set up the necessary hybrid bridge for web contents to flow to the native SDK for posting, such as registering the addJavascriptInterface method in your WebView.

Note: Target applications are hybrid applications built with Google WebView only. UI Capture SDK instrumentation should be completed before native Android SDK integration in order to support a hybrid replay experience.

Ensure the following properties are set:

  • In TeaCutsBasicConfig.properties: GoogleWebViewEnabled=true
  • In TealeafBasicConfig.properties: Whether the application is a single-screen hybrid application or not, use SingleScreenHybridApp=false
  • In TeaCutsAdvancedConfig.json: If the application has a custom WebViewClient, use "ExtendsWebViewClient":false

Note: If you do extend from WebViewClient, you must use the override method for auto instrumentation to work:

public void onPageFinished(final WebView view, final String url) {
   //Hook required by AspectJ to setup Tealeaf hybrid bridge
}

The following sample project demonstrates a single-screen Hybrid application (Google WebView) using auto instrumentation: AndroidRelease/Tealeaf/SampleCode/AndroidStudioProjects/HybridHtmlEmbeddedAuto


Extend android.webkit.WebView

This sample code shows how the base Android WebView is extended with UICWebView.

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (C) Copyright IBM Corp. 2015
 * US Government Users Restricted Rights - Use, duplication or disclosure
 * restricted by GSA ADP Schedule Contract with IBM Corp.
 ******************************************************************************/
package com.tl.uic.webkit;

import java.util.Date;
import java.util.Map;

import org.apache.http.HttpResponse;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.util.AttributeSet;
import android.webkit.WebView;

import com.tl.uic.TLFCache;
import com.tl.uic.Tealeaf;
import com.tl.uic.javascript.JavaScriptInterface;
import com.tl.uic.model.PropertyName;

/**
 * @author ohernandezltmac
 */
public class UICWebView extends WebView {
    private Date endLoad;
    private Date startLoad;
    private String url;
    private HttpResponse httpResponse;
    private Date initTime;
    private long responseTime;
    private long connectionInitFromSession;
    private PropertyName webViewId;

    /**
     * @param context Android context.
     */
    public UICWebView(final Context context) {
        super(context);
        init();
    }

    /**
     * @param context Android context.
     * @param attrs an AttributeSet passed to our parent.
     */
    public UICWebView(final Context context, final AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    /**
     * @param context Android context.
     * @param attrs an AttributeSet passed to our parent.
     * @param defStyle the default style resource ID.
     */
    public UICWebView(final Context context, final AttributeSet attrs, final 
int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    /**
     * When page finished loading.
     * 
     * @return When page finished loading.
     */
    public final Date getEndLoad() {
        return endLoad;
    }

    /**
     * When page finished loading.
     * 
     * @param endLoad When page finished loading.
     */
    public final void setEndLoad(final Date endLoad) {
        this.endLoad = endLoad;
    }

    /**
     * When page starts loading.
     * 
     * @return When page starts loading.
     */
    public final Date getStartLoad() {
        return startLoad;
    }

    /**
     * When page starts loading.
     * 
     * @param startLoad When page starts loading.
     */
    public final void setStartLoad(final Date startLoad) {
        this.startLoad = startLoad;
    }

    /**
     * HttpResponse from the connection.
     * 
     * @return HttpResponse from the connection.
     */
    public final HttpResponse getHttpResponse() {
        return httpResponse;
    }

    /**
     * HttpResponse from the connection.
     * 
     * @param httpResponse HttpResponse from the connection.
     */
    public final void setHttpResponse(final HttpResponse httpResponse) {
        this.httpResponse = httpResponse;
    }

    /**
     * Initial time from the connection.
     * 
     * @return Initial time from the connection.
     */
    public final Date getInitTime() {
        return initTime;
    }

    /**
     * Initial time from the connection.
     * 
     * @param initTime Initial time from the connection.
     */
    public final void setInitTime(final Date initTime) {
        this.initTime = initTime;
        this.connectionInitFromSession = TLFCache.timestampFromSession();
    }

    /**
     * Response time from the connection.
     * 
     * @return Response time from the connection.
     */
    public final long getResponseTime() {
        return responseTime;
    }

    /**
     * Response time from the connection.
     * 
     * @param responseTime Response time from the connection.
     */
    public final void setResponseTime(final long responseTime) {
        this.responseTime = responseTime;
    }
    
    /**
     * Get webview id.
     * 
     * @return Webview id.
     */
    public final PropertyName getWebViewId() {
        return webViewId;
    }

    /**
     * Set webview id.
     * 
     * @param webviewId Webview id.
     */
    public final void setWebViewId(final PropertyName webviewId) {
        this.webViewId = webviewId;
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("PMD.NullAssignment")
    @Override
    public void loadData(final String data, final String mimeType, 
final String encoding) {
        this.url = null;
        this.initTime = null;
        this.connectionInitFromSession = 0;
        this.responseTime = 0;
        this.httpResponse = null;
        this.startLoad = new Date();
        super.loadDataWithBaseURL(this.url, data, mimeType, encoding, "");
    }

    /**
     * {@inheritDoc}
     */
    @SuppressLint("NewApi")
    @Override
    public void loadUrl(final String url) {
        loadUrl(url, null);
    }

    /**
     * {@inheritDoc}
     */
    public final void loadUrl(final String url, final Map<String, String> 
extraHeaders) {
        this.url = url;
        Tealeaf.setTLCookie(this.url);
        super.loadUrl(url, extraHeaders);
    }

    /**
     * Initializes WebView.
     */
    private void init() {
        setStartLoad(new Date());
        //  Need it when in Eclipse editor mode
        if (!this.isInEditMode()) {
            this.setWebViewClient(new UICWebViewClient());
            this.setWebChromeClient(new UICWebChromeClient((Activity) 
this.getContext()));
            this.setWebViewId(Tealeaf.getPropertyName(this));
            this.addJavascriptInterface(new JavaScriptInterface(this.getContext(), 
getWebViewId().getId()), "tlBridge");
        }
    }

    /**
     * Log the load time of the WebView.
     *
     * @return If log connection was added to queue.
     */
    public final Boolean logConnection() {
        final long endTime = this.getEndLoad() != null ? 
this.getEndLoad().getTime() : 0;
        final long startTime = this.getStartLoad() != null ? 
this.getStartLoad().getTime() : 0;
        final long loadTime = endTime - startTime;
        return Tealeaf.logConnection(this.url, this.httpResponse, 
this.connectionInitFromSession, loadTime, this.responseTime);
    }
}

Extend android.webkit.WebViewClient

The sample code that follows extends the base Android WebViewClient with UICWebViewClient.

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (C) Copyright IBM Corp. 2015
 * US Government Users Restricted Rights - Use, duplication or disclosure
 * restricted by GSA ADP Schedule Contract with IBM Corp.
 ******************************************************************************/
package com.tl.uic.webkit;

import android.annotation.SuppressLint;
import android.os.Build;
import android.webkit.WebView;
import android.webkit.WebViewClient;

/**
 * @author ohernandezltmac
 *
 */
public class UICWebViewClient extends WebViewClient {

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean shouldOverrideUrlLoading(final WebView view, final String url) {
        view.loadUrl(url);
        return true;
    }
    
    /**
     * {@inheritDoc}
     */
    @SuppressLint("NewApi")
    @Override
    public void onPageFinished(final WebView view, final String url) {
        // Get the call back mapping string to be evaluated/loaded as 
Javascript code
        final String javascriptString = com.tl.uic.javascript.JavaScriptInterface.
hybridBridgeRegisterCallback();
        
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            view.evaluateJavascript(javascriptString, null);
        } else {
            view.loadUrl("javascript:" + javascriptString);
        }
    }
}


Hybrid Application settings

This setting is for single-activty, hybrid applications. It allows the SDK to queue and post JSON messages when they are collected, instead of waiting for current activity to be in the background before sending payload to the server. It is set in the TealeafBasicConfig.properties.

Item ID Description Value
SingleScreenHybridApp For a single-screen hybrid application with one Activity
For example, the following frameworks need this setting to be true: Cordova, PhoneGap, and Ionic
true
  For a hybrid application with more than one Activity
For example, Google WebView auto-instrumentation
false

Sessionization for PhoneGap based Applications

Since there are no requests and responses being sent back to the server, IBM Tealeaf does not require extending of WebView to add sessionization.


IBM Tealeaf Android SDK APIs available to JavaScript

When you develop hybrid applications, you embed WebView component within a larger Android application. You can access exposed Android native methods from the UI Capture j2 global JavaScript object called "TLT" with methods that you use in your JavaScript code. This table lists and gives examples of the methods that you can include in your JavaScript code:

Method Example
Enable Application Framework
/**
* Public API to enable Tealeaf framework.
* @returns {void}
*/
 enableTealeafFramework();
Disable Application Framework
/**
* Public API to disable Tealeaf framework.
* @returns {void}
*/
disableTealeafFramework();
Log Screen Capture
/**
* Public API to add a screenshot capture.
* @returns {void}
*/
logScreenCapture();
Start New Application Session
/**
* Public API to start a new Tealeaf session.
* @returns {void}
*/
startNewTLFSession();
Current Session ID
/**
* Public API to start get current Tealeaf session Id.
* @returns {String} Current session Id
*/
currentSessionId();
Default Value for Configurable Item
/**
* Public API to get default value of a configurable item in 
* TLFConfigurableItems.properties file. 
* @param {String} configItem This is the name of the configurable item.
* @returns {String} The value for the item.
*/
defaultValueForConfigurableItem(configItem);
Value for Configurable Item
/**
* Public API to get the value of a configurable item either from 					    
* TLFConfigurableItems.properties file
* or in memory data structure.
* @param {String} configItem This is the name of the configurable item.
* @returns {String} The value for the item.
*/
valueForConfigurableItem(configItem);	
Set Configurable Item
/**
 * Public API to set the value of a configurable item in TLFConfigurableItems.properties 	    
* file.
* @param {String} configItem This is the name of the configurable item.
* @param {String} value The value assign to the configItem.
* @returns {boolean} Wether item was set.
*/
setConfigurableItem(configItem, value);
Add Additional HTTP Header
/** 
* Public API to add additional http header.
* @param {String} key This is the key of the configurable item.
* @param {String} value The value assign to the configItem.
* @returns {boolean} Wether item was set.
*/
addAdditionalHttpHeader(key, value);
Log Custom Event Bridge
/** 
* Public API to log custom event.
* @param {String} eventName A custom event name.
* @param {String} jsonData JSON data string.
* @param {int} logLevel Tealeaf library logging level for the event.
* @returns {boolean} Wether item was set.
*/
logCustomEventBridge(eventName, jsonData, logLevel);

Example of how a native Android API is started

This example shows how to start a native method to enable IBM Tealeaf Framework on a UI Capture j2 "TLT" instance using JavaScript:

<script type="text/javascript">
            // Example of calling the native API to enable Tealeaf Framework using Javascript
	TLT.enableTealeafFramework(); 
</script>

Sample test HTML file that starts supported native methods in JavaScript

This example is an HTML file that starts supported native methods in JavaScript:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head> 
	<script type="text/javascript" src="js/tealeaf.js"></script>
    <script type="text/javascript" src="js/defaultconfiguration.js" ></script>
    
    <title>test APIs</title>
    <body>
        <h2>Test page for Android bridge APIs in hybrid app</h2>
        <h3>Click on below buttons to run tests:</h3>
        <input type="button" style="width: 150px; height: 30px; font-size: 20px"  value="Screen Capture" onclick="TLT.logScreenCapture();return false;"/>
        <input type="button" style="width: 150px; height: 30px; font-size: 20px" value="Native APIs" onclick="runBridgeNativeTealeafAPIs();return false;"/>
        <p/>
        <p>Test native APIs output here:
        <div id="queueData"></div>
    </body>
	<script type="text/javascript">
	

function htmlConsoleLog(textData, apiRetVal){
    var para = document.createElement("p");
    var node;
    if( apiRetVal !== undefined && apiRetVal !== null )
    {
        node = document.createTextNode(textData + " returned: " + apiRetVal);
    }
    else
    {
        node = document.createTextNode(textData );
    }
    para.appendChild(node);
    var element = document.getElementById("queueData");
    element.appendChild(para);
}

function runBridgeNativeTealeafAPIs() {
    htmlConsoleLog( '----- -------------------------------- -----' );
    htmlConsoleLog( '----- Calling Tealeaf native APIs -----' );

    var apiRetVal = null;
    htmlConsoleLog( '----- Calling enableTealeaf -----' );
    TLT.enableTealeafFramework();
    
	apiRetVal = null;
    htmlConsoleLog( '----- Calling currentSessionId -----' );
    apiRetVal = TLT.currentSessionId();
    htmlConsoleLog( '----- currentSessionId -----', apiRetVal );

    apiRetVal = null;
    htmlConsoleLog( '----- Calling disableTealeaf -----' );
    TLT.disableTealeafFramework();
    
    var apiRetVal = null;
    htmlConsoleLog( '----- Calling enableTealeaf -----' );
    TLT.enableTealeafFramework();
    
	apiRetVal = null;
    htmlConsoleLog( '----- Calling currentSessionId -----' );
    apiRetVal = TLT.currentSessionId();
    htmlConsoleLog( '----- currentSessionId -----', apiRetVal );
    
	apiRetVal = null;
    htmlConsoleLog( '----- Calling defaultValueForConfigurableItem(PostMessageUrl) -----' );
    var PostMessageUrlVal = TLT.defaultValueForConfigurableItem('PostMessageUrl');
    htmlConsoleLog( '----- defaultValueForConfigurableItem -----', PostMessageUrlVal );
    
    apiRetVal = null;
    htmlConsoleLog( '----- Calling setConfigurableItem("PostMessageUrl", "aValidPostUrl") -----' );
    apiRetVal = TLT.setConfigurableItem('PostMessageUrl', 'aValidPostUrl');
    htmlConsoleLog( '----- setConfigurableItem -----', apiRetVal );
    
    apiRetVal = null;
    htmlConsoleLog( '----- Calling valueForConfigurableItem("PostMessageUrl") -----' );
    apiRetVal = TLT.valueForConfigurableItem('PostMessageUrl');
    htmlConsoleLog( '----- valueForConfigurableItem -----', apiRetVal );
    
    apiRetVal = null;
    htmlConsoleLog( '----- Calling setConfigurableItem(PostMessageUrl, ' + PostMessageUrlVal + ') -----' );
    apiRetVal = TLT.setConfigurableItem('PostMessageUrl', PostMessageUrlVal );
    htmlConsoleLog( '----- setConfigurableItem -----', apiRetVal );

    apiRetVal = null;
    htmlConsoleLog( '----- Calling valueForConfigurableItem("PostMessageUrl") -----' );
    apiRetVal = TLT.valueForConfigurableItem('PostMessageUrl');
    htmlConsoleLog( '----- valueForConfigurableItem -----', apiRetVal );

    apiRetVal = null;
    htmlConsoleLog( '----- Calling startNewTLFSession -----' );
    apiRetVal = TLT.startNewTLFSession();
    htmlConsoleLog( '----- startNewTLFSession -----', apiRetVal );

    apiRetVal = null;
    htmlConsoleLog( '----- Calling addAdditionalHttpHeader -----' );
    TLT.addAdditionalHttpHeader('HeaderFromJavaScript','HeaderValueFromJavaScript');
    htmlConsoleLog( '----- addAdditionalHttpHeader -----' );

    apiRetVal = null;
    htmlConsoleLog( '----- Calling enableTealeaf again -----' );
    TLT.enableTealeafFramework();

    apiRetVal = null;
    htmlConsoleLog( '----- Calling currentSessionId -----' );
    apiRetVal = TLT.currentSessionId();
    htmlConsoleLog( '----- currentSessionId -----', apiRetVal );

    apiRetVal = null;
    var str = '{\'key1AndroidBridge\': \'value1AndroidBridge\', \'key2AndroidBridge\': \'value2AndroidBridge\'}';
    htmlConsoleLog( '----- Calling logCustomEvent("Test Android Bridge Custom Event",' + str +') -----' );
    apiRetVal = TLT.logCustomEventBridge('Test Android Bridge Custom Event', str, 0);
    htmlConsoleLog( '----- logCustomEvent(Test Android Bridge Event, ' + str +' ) -----', apiRetVal );

    htmlConsoleLog( '----- Done Calling Tealeaf native APIs -----' );
    htmlConsoleLog( '----- -------------------------------- -----' );
    htmlConsoleLog( '----- -------------------------------- -----' );
    htmlConsoleLog( '----- -------------------------------- -----' );
}

</script>
</head>
</html>

Related Tutorials


Join The Discussion

Your email address will not be published. Required fields are marked *