Sample Code for IBM Tealeaf Android SDK
Instrumenting TextView based controls
Instrumenting ExpandableListView based controls
Instrumenting SlidingDrawer based controls
Privacy masking controls
Privacy masking specific rows of a RecycleView
Privacy masking specific rows of a ListView that is reused by two activities
Implementing AdvertisingId in your application
Instrumenting Form Completion
Extracting and capturing application images for replay

How to instrument TextView based controls

Because TextView based controls are used for text fields, to get dwell time and other data you instrument the OnFocusChangeListener to know when a user starts and completes typing.

// Get TextView based control
final EditText nameEditText = (EditText) findViewById(R.id.nameEditText);
// Create a OnFocusChangeListener
OnFocusChangeListener focusListen = new OnFocusChangeListener () {
   public void onFocusChange(View view, boolean hasFocus){                    
      if(hasFocus == false){
         Tealeaf.logEvent(view, Tealeaf.TLF_ON_FOCUS_CHANGE_OUT);
      }
      else{
         Tealeaf.logEvent(view, Tealeaf.TLF_ON_FOCUS_CHANGE_IN);
      }
   }
});
// Set OnFocusChangeListener on TextView based control
nameEditText.setOnFocusChangeListener(focusListen);
// Register TextView based control
Tealeaf.registerFormField(nameEditText, this);

How to instrument ExpandableListView based controls

For ExpandableListView controls, in order to know when a user expands or collapses a control you instrument the OnGroupCollapseListener and OnGroupExpandListener.

// Get ExpandableListView based control
final ExpandableListView elv = (ExpandableListView) findViewById(R.id.elv);

elv.setOnChildClickListener(new OnChildClickListener() {
   public boolean onChildClick(ExpandableListView parent, View view, 
      int groupPosition, int childPosition, long id) {
         Tealeaf.logEvent(view);
         return true;
         }    
});

elv.setOnGroupCollapseListener(new OnGroupCollapseListener() {
   public void onGroupCollapse(int groupPosition) {
      Tealeaf.logEvent(elv, Tealeaf.TLF_ON_GROUP_COLLAPSE);
   } 
});
        
elv.setOnGroupExpandListener(new OnGroupExpandListener(){
   public void onGroupExpand(int groupPosition) {
      Tealeaf.logEvent(elv, Tealeaf.TLF_ON_GROUP_EXPAND);
   } 
});

How to instrument SlidingDrawer based controls

For SlidingDrawer controls, to know when a user opens or closes a control you instrument the OnDrawerOpenListener and OnDrawerCloseListener.

// Get SlidingDrawer based control
<span class="keyword">final</span> SlidingDrawer sd = (SlidingDrawer) findViewById(R.id.sd);

sd.setOnDrawerOpenListener(<span class="keyword">new</span> OnDrawerOpenListener() {
   <span class="keyword">public</span> void onDrawerOpened(){
      Tealeaf.logEvent(slidingDrawer_c5, Tealeaf.TLF_ON_DRAWER_OPENED);
   }
});
        
sd.setOnDrawerCloseListener(<span class="keyword">new</span> OnDrawerCloseListener(){
   <span class="keyword">public</span> void onDrawerClosed(){                    
      Tealeaf.logEvent(slidingDrawer_c5, Tealeaf.TLF_ON_DRAWER_CLOSED);
   }
});

How to privacy mask controls

Custom privacy masking is a feature that matches specified IDs and regular expressions and then does character substitutions. In the example that follows, custom privacy masking converts actual values to the letters that are supplied as replacements. If custom privacy masking is set to false, it returns an empty string. You specify privacy masking in the TLFConfigurableItem.properties file that is in the assets folder of the Android application.

#Masking settings
HasMasking=true
#It can be a series of ids and regular expressions comma delimited
MaskIdList=com.tealeaf.sp:id\/EditText*,com.tealeaf.sp:id\/login.password
#If set to false it will return an empty string
HasCustomMask=true
#It will turn small letters to value given
SensitiveSmallCaseAlphabet=x
#It will turn capital letters to value given
SensitiveCapitalCaseAlphabet=X
#It will turn symbols to value given
SensitiveSymbol=#
#It will turn digits to value given
SensitiveNumber=9

How to privacy mask specific rows of a RecycleView

You can privacy mask specific rows of a RecycleView by adjusting your privacy masking settings.

Procedure

  1. In your application, go to the RecyclerAdapter class attached to the RecycleView.

    RecyclerAdapter adapter = new RecyclerAdapter(persons);
    rv.setAdapter(adapter);
    
  2. Inside the RecyclerAdapter class, go to the function where you set the content of the TextView you want to mask. This function is called onBindViewHolder.

    @Override
    public void onBindViewHolder(final ProductViewHolder productViewHolder, int i) {
    
         productViewHolder.productName.setText(products.get(i).name);
         productViewHolder.productDescription.setText(products.get(i).description);
         productViewHolder.productPhoto.setImageResource(products.get(i).photoId);tAdapter(adapter);
    
  3. Set the tag of the TextView that you would like to mask. In this example, we used the product name on rows 0 and 6, meaning the product name from the first and seventh rows.

    if(productViewHolder.getLayoutPosition() == 0 || productViewHolder.getLayoutPosition() == 6){
         productViewHolder.productName.setTag("customTag");
    } else {
         productViewHolder.productName.setTag(null);
    }
    
  4. In your application folder, go to Assets and TealeafBasicConfig.properties to find the Masking Settings. Make sure that the name of your tag (in this example, customTag) is added to the MaskIdList. This indicates that every UI component that has customTag as a tag will be masked.

    #Masking settings
    HasMasking=true
    MaskIdList=com.tealeaf.sp:id\/EditText*,com.tealeaf.sp:id\/login.password,customTag
    HasCustomMask=true
    SensitiveSmallCaseAlphabet=x
    SensitiveCapitalCaseAlphabet=x
    SensitiveSymbol=#
    SensitiveNumber=9
    

How to privacy mask specific rows of a ListView that is reused by two activities

You can privacy mask specific rows of a ListView that is reused by two activities, similar to masking specific rows of RecycleView.

Procedure

  1. From the first activity that is using a list view in your application, go to the adapter attached to the ListView.

    mListView = (ListView) findViewById(R.id.list_view);
    adapter = new CustomBaseAdapter(this, items, false);
    mListView.setAdapter(adapter);
    
  2. In the CustomBaseAdapter class, go to the function where you set the content of the TextView you want to mask (this function is called onView).

    public View getView(int position, View convertView, ViewGroup parent) {
         holder = null;
    
         LayoutInflater mInflater = (LayoutInflater)
                 context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
         if (convertView == null) {
             convertView = mInflater.inflate(R.layout.custom_main_list_row, parent, false);
             holder = =new ViewHolder();
             holder.txtDesc = (TextView) convertView.findViewById(R.id.desc);
             holder.imageView = (ImageView) convertView.findViewById(R.id.icon);
             convertView.setTag(holder);
         } else {
             holder = (ViewHolder) convertView.getTag();
         }
    
         RowItem rowItem = (RowItem) getItem(position);
    
         final int indexRow = position;
    
         holder.txtDesc.setText(rowItem.getDesc());
    
         holder.imageView.setImageResource(rowItem.getImageId());
    
         if(fromSearch){
             holder.imageView.setVisibility(View.GONE);
         }
    
  3. Set the tag of the TextView you want to privacy mask. In this example, the description of the third row from the MainActivity class.

    if(indexRow == 2 && context.getClass().equals(MainActivity.class)){
         holder.txtDesc.setTag("third-row");
    }
    
  4. In your application folder, go to Assets and TealeafBasicConfig.properties to find the Masking Settings. Make sure that the name of your tag (in this example, third-row) is added to the MaskIdList. This indicates that every UI component that has third-row as a tag will be masked.

    #Masking settings
    HasMasking=true
    MaskIdList=third-row,clicked
    HasCustomMask=true
    SensitiveSmallCaseAlphabet=x
    SensitiveCapitalCaseAlphabet=x
    SensitiveSymbol=#
    SensitiveNumber=9
    

How to implement AdvertisingId in your application to capture Google Advertising ID (GAID) data

IBM Tealeaf can capture Google Advertising ID (GAID) data using AdvertisingId. The Google Play services advertising ID is a unique, user-resettable ID for advertising. The advertising ID gives users better controls and provides developers with a standard system to continue to monetize their apps. The advertising ID also gives users the ability to reset their identifier or opt out of personalized ads within Google Play applications.

Procedure

  1. Open AndroidManifest.xml, add the following snippet to the application section.

    <meta-data
        android:name="com.google.android.gms.version"   
        android:value="@integer/google_play_services_version" />
    
  2. Open the app.gradle file for your application and add the following snippet to add the Google Play Service ads library.

    dependencies {
      // Requires for Google Play ads library compile
      'com.google.android.gms:play-services-ads:9.4.0'
      
    }
    

Example

The following example shows the JSON data with the implemented advertisingId.

"clientEnvironment": {               
      "osVersion": "5.0",                
      "height": 1920,                
      "width": 1080,                
      "deviceWidth": 360,                
      "deviceHeight": 640,                
      "pixelDensity": 3,                
      "mobileEnvironment": {                   
      "totalStorage": 6619593,                    
      "totalMemory": 369287168,                    
      "locale": "English (United States)",
      "language": "English",                    
      "manufacturer": "samsung",                    
      "deviceModel": "SM-N900V",                    
      "appName": "AuroraAuto",                    
      "appVersion": "1.0",                    
      "deviceId": "",                    
      "orientationType": "PORTRAIT",                    
      "android": {},                    
      "advertisingId": "567eca34-2ea3-4f80-9436-ea63a7abc5b0"                },                
      "osType": "Android",                
      "orientation": 0
                }

If the end user has chosen to opt out of ads, advertisingId returns the following value:

advertisingId: N/A

How to instrument Form Completion in your application

If you use OverStat in an Activity or Fragment, you must implement logFormCompletion to generate reports based on user activity within a form.
Note: This step is completely manual and will not be automated because of the different architectures that an application can have, it is difficult to instrument. The form page can also have additional custom validation that would indicate if completion was correct or not.

Example when a form is complete and ready to submit:

Button addToCart = (Button) productView.findViewById(R.id.buttonAddCart);
addToCart.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Tealeaf.logFormCompletion(true);

How to extract and capture application images for replay

IBM Tealeaf provides capabilities to capture and replay an application's local and external images.

Local images

Images that are bundled within an application can be extracted by using the IBM Tealeaf Android Image Extraction tool as an initial step for Tealeaf SDK integration and better replay experience. To get started, find the README.txt file under the Android Release folder: AndroidRelease/Tealeaf/AndroidImageCaptureTool/README.txt.

The tool extracts all the packaged image resources in the APK file and set an extra attribute called "tag" on the application's layout XML file for Views that contain images.

For example, android:tag="@drawable/home_background" tag is automatically added by the tool as shown. You can also manually update it to select a different image resource ID if needed:

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/home_background"
android:tag="@drawable/home_background" />

External images

These images are usually hosted on external servers, which are loaded dynamically during an application's runtime environment via network requests.

Due to Android framework limitations on capturing runtime images and performance considerations, you would need to pass external image URL paths to Tealeaf SDK by using one of the following mechanisms:

  • Use the ImageView.setTag((String) url) method
    Note: This method assumes that you are not using the ImageView's tag object in your code.
  • Use the ImageView.setTag(resourceId,(String) url) method
    Note: This method assumes that you are using the View's tag object in your code, then you can follow the following code snippet to redirect the Tealeaf tag to ImageView.setTag(ImageView.getId(), tag).

    • The following sample ImageView definition shows an "android:tag" attribute, which would be automatically generated by the image extraction tool, or you can manually update support image replay:

      <ImageView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:src="@drawable/home_background"
      android:tag="@drawable/home_background" />
      
    • For image replay, use a View's tag object. The following example shows how to redirect the tag object to the View's setTag:

      ImageView myView = (ImageView) findViewById(R.i.d.image1);
      //Potential XML tag found
      Object tag = myView.getTag();
      
      //Assuming myTag is the object you want to set for the view
      if ((tag == null) || (tag != null && !(tag instance of myTag))) {
      
      //Redirect the tag to the other setTag(View.setTag(resourceId, object) method which operated on a HashMap data structure.
      if ( View.NO_ID !=view.getID() && view.getId() != 0 && tag != null) {
      myView.setTag(myView.getId(), tag);
      }
      

Android auto instrumentation capability configuration files

teacuts.jar is a module that provides Android auto instrumentation capabilities. The two configuration files are shown:

  • TeaCutsAdvancedConfig.json – Advanced configuration file for enabling or disabling auto instrumentation features.

    {
    "TeaCutsLibraryVersion":"2.0.0.1",
    "DefaultAlertDialogLayoutDelay":500
    }
    

    DefaultAlertDialogLayoutDelay property is default at 500 milliseconds. You can change this value if the replay shows a timing issue when the AlertDialog is displayed.

  • TeaCutsBasicConfig.properties – Basic configuration file for enabling or disabling auto instrumentation features.
    • Starting the 2.0.0.1 ability to auto instrument AlertDialogs
      • It defaults to true. If you used manual instrumentation for AlertDialog, then you can set it to false.

        AlertDialogEnabled=true
        
    • Starting 2.0.0.1 ability to auto instrument TextView/Button/EditText without explicit OnClick listener
      Note: This feature supports only API level 15 or above

      • It defaults to true. If you used manual instrumentation, then you can set it to false.

        TextViewEnabled=true