Android

Updated 

Step 1 - Getting Started

1.) Creating Webview and adding live chat URL


Before you begin,
Important -

http://prod-live-chat.sprinklr.com/page?appId=65afb12b62317d2d4a58bfad_app_1866548&device=MOBILE&enableClose=true&zoom

In the above URL, 

appId=60c1d169c96beb5bf5a326f3_app_950954 It indicates the live chat application ID; please ask Sprinlr services team to get you the correct app ID.
device=MOBILE It indicates that the page is Mobile Responsive.
enableClose = true  It indicates a close button has been added to live chat, on pressing that close button onClose sdk will be called. You will need to set enableClose to true before you try to access it.
zoom = false  It indicates zoom has been disabled inside the webview. 


Please check with success team on the environment you are part of, it can be prod, prod2, prod4 etc. and use the URL accordingly.


In Your Webview Activity, add the following code snippet.

Important → Example Projects can be viewed here


- Define a private Webview Instance

private WebView myWebView;
private String liveChatUrl = "http://prod-live-chat.sprinklr.com/page?appId=60c1d169c96beb5bf5a326f3_app_950954&device=MOBILE&enableClose=true&zoom=false"; // example url, use one provided by sprinklr


  • Initialize it in activity’s “onCreate” method

@Override

protected void onCreate(Bundle savedInstanceState) {

 super.onCreate(savedInstanceState);


 setContentView(R.layout.activity_webview); // Webview content view name


 myWebView = (WebView) findViewById(R.id.webview); // Webview id inside content view


 WebSettings settings = myWebView.getSettings();

 settings.setDomStorageEnabled(true);

 settings.setJavaScriptEnabled(true);

 myWebView.loadUrl(liveChatUrl);

}


2.) Adding Back button handling to control hardware back button inside the webview


Clicking the back button will take you back to the last page within the Live Chat web view.

private String backHandler = "(function() {"+
"window.sprChat('goBack', (canGoBack) => {"+
    "if(!canGoBack) {"+
        "AndroidInterface.GoBack && AndroidInterface.GoBack('');"+
    "}"+
"});"+
"})();";

@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void onBackPressed() {
myWebView.evaluateJavascript(backHandler, new ValueCallback<String>() {
  @Override
  public void onReceiveValue(String s) { }
});
}


To achieve the best user experience, it is recommended to hide and show webview instead of completely destroying it. Also the webview should be loaded in the background to avoid showing loading state to the user on first launch.


3.) Adding support to allow users to add media inside webview


Define variables and in your “onCreate” method add webChrome client and add file choose support.

<Code snippet>

private ValueCallback<Uri> mUploadMessage;
private ValueCallback<Uri[]> mUploadMessageArray;
private Uri fileUri;
private Uri videoUri;

@Override

protected void onCreate(Bundle savedInstanceState) {

 // Rest of the code added above


 myWebView.setWebChromeClient(new WebChromeClient() {

   //The undocumented magic method override

   //Eclipse will swear at you if you try to put @Override here

   // For Android 3.0+

   public void openFileChooser(ValueCallback<Uri> uploadMsg) {


     mUploadMessage = uploadMsg;

     Intent i = new Intent(Intent.ACTION_GET_CONTENT);

     i.addCategory(Intent.CATEGORY_OPENABLE);

     i.setType("image/*");

     startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);


   }


   // For Android 3.0+

   public void openFileChooser(ValueCallback uploadMsg, String acceptType) {

     mUploadMessage = uploadMsg;

     Intent i = new Intent(Intent.ACTION_GET_CONTENT);

     i.addCategory(Intent.CATEGORY_OPENABLE);

     i.setType("*/*");

     startActivityForResult(

       Intent.createChooser(i, "File Browser"),

       FILECHOOSER_RESULTCODE);

   }


   //For Android 4.1

   public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {

     mUploadMessage = uploadMsg;

     Intent i = new Intent(Intent.ACTION_GET_CONTENT);

     i.addCategory(Intent.CATEGORY_OPENABLE);

     i.setType("image/*");

     startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);


   }


   //For Android 5.0+

   public boolean onShowFileChooser(

     WebView webView, ValueCallback<Uri[]> filePathCallback,

     FileChooserParams fileChooserParams) {

     if (mUploadMessageArray != null) {

       mUploadMessageArray.onReceiveValue(null);

     }

     mUploadMessageArray = filePathCallback;


     List<Intent> intentList = new ArrayList<Intent>();

     fileUri = null;

     videoUri = null;


     // For photo

     Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

     fileUri = getOutputFilename(MediaStore.ACTION_IMAGE_CAPTURE);

     takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);

     intentList.add(takePhotoIntent);


     // For video

     Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);

     videoUri = getOutputFilename(MediaStore.ACTION_VIDEO_CAPTURE);

     takeVideoIntent.putExtra(MediaStore.EXTRA_OUTPUT, videoUri);

     intentList.add(takeVideoIntent);


     Intent contentSelectionIntent;

     if (Build.VERSION.SDK_INT >= 21) {

       final boolean allowMultiple = fileChooserParams.getMode() == FileChooserParams.MODE_OPEN_MULTIPLE;

       contentSelectionIntent = fileChooserParams.createIntent();

       contentSelectionIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, allowMultiple);

     } else {

       contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);

       contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);

       contentSelectionIntent.setType("*/*");

     }

     Intent[] intentArray = intentList.toArray(new Intent[intentList.size()]);


     Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);

     chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);

     chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);

     startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE);

     return true;

   }

 });

}



private Uri getOutputFilename(String intentType) {

 String prefix = "";

 String suffix = "";


 if (intentType == MediaStore.ACTION_IMAGE_CAPTURE) {

   prefix = "image-";

   suffix = ".jpg";

 } else if (intentType == MediaStore.ACTION_VIDEO_CAPTURE) {

   prefix = "video-";

   suffix = ".mp4";

 }


 String packageName = this.getPackageName();

 File capturedFile = null;

 try {

   capturedFile = createCapturedFile(prefix, suffix);

 } catch (IOException e) {

   e.printStackTrace();

 }

 return FileProvider.getUriForFile(this, packageName + ".fileprovider", capturedFile);

}


private File createCapturedFile(String prefix, String suffix) throws IOException {

 String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());

 String imageFileName = prefix + "_" + timeStamp;

 File storageDir = this.getExternalFilesDir(null);

 return File.createTempFile(imageFileName, suffix, storageDir);

}



@Override

public void onActivityResult(int i, int i1, Intent intent) {

 super.onActivityResult(i, i1, intent);

 handleResult(i, i1, intent);

}


public boolean handleResult(int requestCode, int resultCode, Intent intent) {

 boolean handled = false;

 if (Build.VERSION.SDK_INT >= 21) {

   if (requestCode == FILECHOOSER_RESULTCODE) {

     Uri[] results = null;

     if (resultCode == Activity.RESULT_OK) {

       if (fileUri != null && getFileSize(fileUri) > 0) {

         results = new Uri[]{fileUri};

       } else if (videoUri != null && getFileSize(videoUri) > 0) {

         results = new Uri[]{videoUri};

       } else if (intent != null) {

         results = getSelectedFiles(intent);

       }

     }

     if (mUploadMessageArray != null) {

       mUploadMessageArray.onReceiveValue(results);

       mUploadMessageArray = null;

     }

     handled = true;

   }

 } else {

   if (requestCode == FILECHOOSER_RESULTCODE) {

     Uri result = null;

     if (resultCode == RESULT_OK && intent != null) {

       result = intent.getData();

     }

     if (mUploadMessage != null) {

       mUploadMessage.onReceiveValue(result);

       mUploadMessage = null;

     }

     handled = true;

   }

 }

 return handled;

}


private long getFileSize(Uri fileUri) {

 Cursor returnCursor = getContentResolver().query(fileUri, null, null, null, null);

 returnCursor.moveToFirst();

 int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);

 return returnCursor.getLong(sizeIndex);

}


private Uri[] getSelectedFiles(Intent data) {

 // we have one files selected

 if (data.getData() != null) {

   String dataString = data.getDataString();

   if (dataString != null) {

     return new Uri[]{Uri.parse(dataString)};

   }

 }

 // we have multiple files selected

 if (data.getClipData() != null) {

   final int numSelectedFiles = data.getClipData().getItemCount();

   Uri[] result = new Uri[numSelectedFiles];

   for (int i = 0; i < numSelectedFiles; i++) {

     result[i] = data.getClipData().getItemAt(i).getUri();

   }

   return result;

 }

 return null;

}


4.) Initialize Messenger

Sprinklr Messenger can be initialized for both unauthenticated and authenticated users:

  • For Anonymous Users:

If a user is not logged into your mobile app, you can open live chat without passing user information. The profile created into sprinklr will be an anonymous user.

URL -

http://prod-live-chat.sprinklr.com/page?appId=65afb12b62317d2d4a58bfad_app_1866548&device=MOBILE&enableClose=true&zoom

  • For Authenticated Users: 

If a user is already logged into your mobile app, you can pass the authenticated details securely to the chat to authenticate the user on the chat itself. This process is called pre-authentication. In pre-authentication you can pass following information securely from the mobile app to the chat - 

URL -

http://prod-live-chat.sprinklr.com/page?appId=65afb12b62317d2d4a58bfad_app_1866548&device=MOBILE&enableClose=true&zoom&user_id=12345&user_firstName=John&user_lastName=Doe&user_profileImageUrl=https://example.com/profilePic.jpg&user_email=John.Doe@example.com&user_phoneNo=9876543210&user_hash=29862e418f58273af0f7fcd0f24652e11ec1ff6c2213b2db7f6e754e59778fc3

In the above URL,

  • user_id = “pass required value”  It indicates the ID which is passed to profile of user

  • user_firstName = “pass required value”  It indicates the first name which is passed to profile of user

  • user_lastName = “pass required value”  It indicates the last name which is passed to profile of user

  • user_profileImageUrl = “pass required value”  It indicates the profile image which is passed to profile of user

  • user_email = “pass required value”  It indicates the email ID which is passed to profile of user

  • user_phoneNo = “pass required value”  It indicates the phone number which is passed to profile of user

  • user_hash = “pass required value”  It indicates the hash generated for profile of user

Note: If you want to pass more values, please use user context i.e. scenario 3

How to generate userHash?

The userHash is a generated HMAC or Hash-based Message Authentication Code. For HMAC, Sprinklr uses the sha256 hash function. You need to generate HMAC for the following "string" of user details. User details are concatenated to form a string, separated by underscore as shown below -

userId_firstName_lastName_profileImageUrl_phoneNo_email

So for the above example, the string for which you need to generate the hash would be

12345_John_Doe_https://example.com/profilePic.jpg_9876543210_John.Doe@example.com

Sample code to generate HMAC is given below for following languages - PHP, Python 2.7, Python 3.6.1, Ruby (Rails), Java, Node.js

PHP

$key = "acf32e61-14a6-291b-3a1b-cc8854134ea1";

$userId = "12345";
$firstName = "John";
$lastName = "Doe";
$profileImageUrl = "https://example.com/profilePic.jpg";
$phoneNo = "9876543210";
$email = "John.Doe@example.com";

$userDetails = $userId."_".$firstName."_".$lastName."_".$profileImageUrl."_".$phoneNo."_".$email;

$userHash = hash_hmac('sha256',$userDetails,$key);

Run the above PHP code online - https://repl.it/@AbhishekPriyam/HMACPHPExample

Python 2.7/3.6.1

import hmac
import hashlib

userId = "12345"
firstName = "John"
lastName = "Doe"
profileImageUrl = "https://example.com/profilePic.jpg"
phoneNo = "9876543210"
email = "John.Doe@example.com"

userDetails = userId+"_"+firstName+"_"+lastName+"_"+profileImageUrl+"_"+phoneNo+"_"+email

def create_sha256_signature(key, data):
  byte_key = key.encode()
  data= data.encode()
  return hmac.new(byte_key, data, hashlib.sha256).hexdigest()

userHash=create_sha256_signature("acf32e61-14a6-291b-3a1b-cc8854134ea1", userDetails)

Run the above Python code online - https://repl.it/@AbhishekPriyam/HMACPythonExample

Ruby (Rails)

require 'openssl'

key = 'acf32e61-14a6-291b-3a1b-cc8854134ea1'
userId = "12345"
firstName = "John"
lastName = "Doe"
profileImageUrl = "https://example.com/profilePic.jpg"
phoneNo = "9876543210"
email = "John.Doe@example.com"

userDetails = userId+"_"+firstName+"_"+lastName+"_"+profileImageUrl+"_"+phoneNo+"_"+email

digest = OpenSSL::Digest.new('sha256')

userHash = OpenSSL::HMAC.hexdigest(digest, key, userDetails)

Run the above Ruby code online - https://repl.it/@AbhishekPriyam/HMACRubyExample

Java

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
import javax.xml.bind.DatatypeConverter;

class Main {
public static void main(String[] args) {
try {
      String key = "acf32e61-14a6-291b-3a1b-cc8854134ea1";
      String message = "12345_John_Doe_https://example.com/profilePic.jpg_9876543210_John.Doe@example.com";

      Mac hasher = Mac.getInstance("HmacSHA256");
      hasher.init(new SecretKeySpec(key.getBytes(), "HmacSHA256"));
      byte[] hash = hasher.doFinal(message.getBytes());
 
      System.out.println((DatatypeConverter.printHexBinary(hash)).toLowerCase());
  }
  catch (NoSuchAlgorithmException e) {}
  catch (InvalidKeyException e) {}
}
}

Run the above Java code online - https://repl.it/@AbhishekPriyam/HMACJavaExample

C#

using System;

using System.Security.Cryptography;

using System.Text;

class Program

{

static void Main(string[] args)

{

string key = "acf32e61-14a6-291b-3a1b-cc8854134ea1";

string userId = "12345";

string firstName = "John";

string lastName = "Doe";

string profileImageUrl = "https://example.com/profilePic.jpg";

string phoneNo = "9876543210";

string email = "John.Doe@example.com";

string userDetails = $"{userId}_{firstName}_{lastName}_{profileImageUrl}_{phoneNo}_{email}";

using (HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key)))

{

byte[] hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(userDetails));

string userHash = BitConverter.ToString(hashBytes).Replace("-", "").ToLower();

Console.WriteLine(userHash);

}

}

}

Run the above Java code online - https://replit.com/@RoshanDash1/HmacCExample

Node.js

var crypto = require('crypto');

var key = 'acf32e61-14a6-291b-3a1b-cc8854134ea1';
var userDetails = '12345_John_Doe_https://example.com/profilePic.jpg_9876543210_John.Doe@example.com';

var hash = crypto.createHmac('sha256', key).update(userDetails);

hash.digest('hex');

Run the above Node.js code online - https://repl.it/@AbhishekPriyam/HMACNodeExample


Note:

  • firstName, hash and one of email or phoneNumber are mandatory

  • User details are supposed to be concatenated to form a string, separated by underscore as shown below:

userId_firstName_lastName_profileImageUrl_phoneNo_email

  •  If you don’t have a few values, you need not send anything but keep the underscores as is. For example, let’s say you don’t have profileimageUrl, there will be 2 underscores after lastName. The string will be as shown below: 

userId_firstName_lastName__phoneNo_email


Step 2 - Configurations

1.) Localization support  

To achieve localization in live chat via webview approach, you can pass certain “locale” string in query parameters of URL; this will be in addition to ones discussed in Step 1

URL - 

http://prod-live-chat.sprinklr.com/page?appId=65afb12b62317d2d4a58bfad_app_1866548&device=MOBILE&enableClose=true&zoom&locale=en

In the above URL,
locale = en It indicates the locale which you want to send for this app ID 


2.) Opening live chat with different landing screen  

To open live chat in different states at different locations,  you can use pass the relevant string in the query parameters of URL; this will be in addition to ones discussed in Step 1

Scenario 1: Directly landing on the home screen

URL - 

http://prod-live-chat.sprinklr.com/page?appId=65afb12b62317d2d4a58bfad_app_1866548&device=MOBILE&enableClose=true&zoom&locale=en

Scenario 2: Directly landing on the new conversation screen

URL - 

http://prod-live-chat.sprinklr.com/page?appId=65afb12b62317d2d4a58bfad_app_1866548&device=MOBILE&enableClose=true&zoom&landingScreen=NEW_CONVERSATION&scope=CONVERSATION

In the above URL,
scope = CONVERSATION  It indicates the the scope of live chat is limited to conversation screen
landingScreen = NEW_CONVERSATION  It indicates the landing screen i.e. new conversation is opened

Scenario 3: Directly landing on the last conversation screen

URL - 

http://prod-live-chat.sprinklr.com/page?appId=65afb12b62317d2d4a58bfad_app_1866548&device=MOBILE&enableClose=true&zoom&landingScreen=LAST_CONVERSATION&scope=CONVERSATION

In the above URL,
scope = CONVERSATION  It indicates the the scope of live chat is limited to conversation screen
landingScreen = LAST_CONVERSATION  It indicates the landing screen i.e. last conversation is opened

Note: landingScreen: "LAST_CONVERSATION allows brands to get users to land the customer on the last interacted open conversation. If there are no open conversations, the customer lands on a new conversation window


3.) Passing contextual information of users to live chat from mobile  

Scenario 1: Capture customer context from the mobile app on all cases of the user

Sometimes you might want to pass some contextual information in case custom fields for all conversations started by the user. (clientContext)

URL -

http://prod-live-chat.sprinklr.com/page?appId=65afb12b62317d2d4a58bfad_app_1866548&device=MOBILE&enableClose=true&zoom&locale=en&context_fieldId=value

In the above URL,
context_fieldId = “pass required value”  It indicates the context which is passed to all cases of user

Scenario 2: Update the profile context from mobile app on profile of user 

Sometimes you might want to capture some context on the profile/user during the conversation or after the conversation. (userContext)

URL - 

http://prod-live-chat.sprinklr.com/page?appId=65afb12b62317d2d4a58bfad_app_1866548&device=MOBILE&enableClose=true&zoom&locale=en&userContext_fieldId=value


In the above URL,
userContext_fieldId = “pass required value”  It indicates the context which is passed to profile of user

4.) Adding javascript interface to listen to webview events


WebAppInterface.java

import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.webkit.JavascriptInterface;

public class WebAppInterface {
Context mContext;

// Instantiate the interface and set the context
WebAppInterface(Context c) {
  mContext = c;
}

// Show a toast from the web page
@JavascriptInterface
public void Close(String close) {
  ((Activity) mContext).finish(); //Instead of closing the webview, it is recommended to hide webview for best user experience
}

@JavascriptInterface
public void GoBack(String goBack) {
  ((Activity) mContext).finish(); //Instead of closing the webview, it is recommended to hide webview for best user experience
}

@JavascriptInterface
public void OnUnreadCountChange(String count) {
  // You can show the notification count using this
}

@JavascriptInterface
public void OnLoadError(String error) {
  // You can show some ui error
}

@JavascriptInterface
public void OnExternalEventReceived(String event) {
  // Handle the event here
}
}


Adding javascript channels to your webview

This should also be done inside onCreate

myWebView.addJavascriptInterface(new WebAppInterface(this), "AndroidInterface");


Injecting Javascript

private String jsChannels = "(function() {" +
"window.sprChat('onLoad', (error) => {"+
    "if(!error) {"+
      "AndroidInterface.OnLoadError && AndroidInterface.OnLoadError('');"+
    "}"+
"});"+
"window.sprChat('onClose', () => {"+
      "AndroidInterface.Close && AndroidInterface.Close('');"+
  "});"+
"window.sprChat('onUnreadCountChange', (count) => {" +
      "AndroidInterface.OnUnreadCountChange && AndroidInterface.OnUnreadCountChange(count.toString());"+
"});"+
"window.sprChat('onExternalEvent', (event) => {" +
      "AndroidInterface.OnExternalEventReceived && AndroidInterface.OnExternalEventReceived(JSON.stringify(event));"+
"});"+
"})();";


5.) Blocking URLs which does not contain live chat URL domain to open it in the external browser

Add Webview client to your webview and override shouldOverrideUrlLoading to block all URL which do not contain live chat url domain, inside onCreate method.

<Code snippet>

private String[] domainsToWhiteList = {"prod-live-chat.sprinklr.com", "live-chat-static.sprinklr.com"}; // urls to whitelist, to ensure these are opened in app webview

myWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
  boolean isWhitelistUrl = false;
  for (String domain : domainsToWhiteList) {
    if(url.contains(domain)) {
      isWhitelistUrl = true;
    }
  }

  if(isWhitelistUrl) {
    view.loadUrl(url);
  } else {
    Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
    startActivity(i);
  }
  return true;
}
});


6.) Add support to download attachment

Whenever user clicks on the download button, we will redirect to a URL which contains fileUrl and fileName which looks like this ->  

  1. “livechat-app:url=${fileUrl}?fileName=${fileName}” [when there are no query parameters present in the fileUrl, fileName is added as a new query parameter] 
    OR 

  2. “livechat-app:url=${fileUrl}&fileName=${fileName}” [when there are query parameters present in the fileUrl already, fileName is added as an extra query parameter] 

 

You need to intercept the requests that have URLs starting with the “livechat-app:” prefix and parse these URLs to properly download the attachment. The steps to parse are: 

  1. Strip the “livechat-app:url=” prefix. 

  2. From the remaining URL, extract and remove the “fileName” query parameter. Use its value to name your file. 

  3. Using the remaining URL to fetch and download content. 
    Please note: While extracting the file URL we need to strip the fileName query Param from the original query because if we do not remove the fileName query parameter then it will lead to signature mismatch and will throw SignatureDoesNotMatch error or BlobNotFound error. 

 

For Example, when receiving a URL like “livechat-app:url= https://prod-sprcdn-assets.sprinklr.com/1234/sample.pdf?signature=XYZ&fileName=sample_name.pdf”, follow the above steps i.e.  

  1. Strip the prefix to get “https://prod-sprcdn-assets.sprinklr.com/1234/sample.pdf?signature=XYZ& fileName=sample_name.pdf”  

  2. Extract fileName query param and name the file sample_name.pdf. 

  3. Use the remaining URL “https://prod-sprcdn-assets.sprinklr.com/1234/sample.pdf?signature=XYZ” to download file. 

@Override 

public boolean shouldOverrideUrlLoading(WebView view,  WebResourceRequest request) {  

    boolean isWhitelistUrl = false; 

    String url = request.getUrl().toString(); 

    if (url.contains("livechat-app:")) { 

 

String strippedUrl = url.replace("livechat-app:url=", ""); 

  

        URI uri = new URI(strippedUrl); 

  

        String query = uri.getQuery(); 

        String fileName = null; 

        String[] pairs = query.split("&"); 

        ArrayList < String > newQueryPairs = new ArrayList < > (); 

  

        for (String pair: pairs) { 

        int idx = pair.indexOf("="); 

        String key = idx > 0 ? pair.substring(0, idx) : pair; 

        String value = idx > 0 && pair.length() > idx + 1 ? pair.substring(idx + 1) : null; 

  

        if ("fileName".equals(key)) { 

            fileName = value; 

        } else { 

            newQueryPairs.add(pair); 

        } 

        } 

  

        String newQuery = String.join("&", newQueryPairs); 

  

        URI newUri = new URI( 

        uri.getScheme(), 

        uri.getAuthority(), 

        uri.getPath(), 

        newQuery, 

        uri.getFragment() 

        ); 

  

        String cdnUrl = newUri.toString(); 

  

        //put your download logic here 

    } 

    else { 

       Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); 

       startActivity(i); 

    } 

    return true; 

Step 3 - Push Notifications

For more details on mobile push notifications, please refer here

Prerequisite

  1. Android: FCM Servey Key

  2. iOS: APNS certificate (P12) along with its credentials

Note:

  • FCM Server key can be different for staging/prod env

  • APNS certificate (P12) and its credentials must be different for staging/prod env

  • If you are testing the push notification setup on prod mobile application(iOS), plesae ensure to use test flight build

  • If the FCM server key is different sandbox/prod env and you are testing the push notification setup on prod mobile application(Android), plesae ensure to use Android release build

Configuration

To enable push notifications, please raise a support ticket to tickets@sprinklr.com with the following information:

  1. FCM Server Key (copy it from your Firebase Console)

  2. APNS certificate (P12) along with its credentials

  3. Live Chat AppID

  4. Partner ID

  5. Env


1.) Register for push notifications 

You can register the messenger for sending push notifications by providing push token received from fcm as below:


Create a function in the following way and pass token, deviceId and deviceName as arguments to it to register your device for push notification.

private String getTokenRegistrationJSChannel(String token, String deviceId, String deviceName) {
JSONObject obj=new JSONObject();
try {
  obj.put("deviceId", deviceId);
  obj.put("deviceToken", token);
  obj.put("appVersion", 1);
  obj.put("deviceName", deviceName);
  obj.put("mobileClientType", "ANDROID");

  String stringifiedPayload = obj.toString();
  return
          "(function() {"+
                  "window.sprChat('registerDevice'," + stringifiedPayload + ");"+
                  "})();";

} catch (JSONException e) {
  e.printStackTrace();
  return "";
}
}


myWebView.evaluateJavascript(getTokenRegistrationJSChannel("1234", "123", "android_device"), new ValueCallback<String>() {
@Override
public void onReceiveValue(String s) { }
});


2.) Unregister for push notifications- 

If you ever want to stop receiving notifications, then you can deregister your device in the following way:


private String getTokenUnregistrationJSChannel(String deviceId, String deviceName) {
JSONObject obj=new JSONObject();
try {
  obj.put("deviceId", deviceId);
  obj.put("appVersion", 1);
  obj.put("deviceName", deviceName);
  obj.put("mobileClientType", "ANDROID");

  String stringifiedPayload = obj.toString();
  return
          "(function() {"+
                  "window.sprChat('unregisterDevice'," + stringifiedPayload + ");"+
                  "})();";

} catch (JSONException e) {
  e.printStackTrace();
  return "";
}
}


myWebView.evaluateJavascript(getTokenUnregistrationJSChannel("123", "android_device"), new ValueCallback<String>() {
@Override
public void onReceiveValue(String s) { }
});


3.) Handle Messenger Push Notifications


Once you have registered for messenger push notifications then you might receive notifications from your platform as well as messenger. To check if notification is messenger notification you can check as below:

Note: When the user is on the Live Chat app, they will always receive the Live Chat messenger push notifications. However, you can define whether users should receive the messenger notifications when they are on the brand app and not the Live Chat app. That is, the brand app is in the foreground and the Live Chat app is running in the background.

To do that, you can check whether the notification is a Live Chat notification and handle it according to brand requirements. For example, you can choose not to display Live Chat notifications when the user is on the brand app.

String et = notification.getString("et", "");
Boolean isMessengerNotification = et.equals("LIVE_CHAT_MOBILE_NOTIFICATION");

if (isMessengerNotification) {
handleNotification(notification);
}


Once you have identified if the notification is messenger notification you need open the messenger and call handle notification method as described below:


private void handleNotification(Bundle notification) {
JSONObject json = new JSONObject();
Set<String> keys = notification.keySet();
for (String key : keys) {
  try {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
      json.put(key, JSONObject.wrap(notification.get(key)));
    }
  } catch(JSONException e) {
    //Handle exception here
  }
}

String stringifiedPayload = json.toString();
String sdkFunction = "(function() {"+
                "window.sprChat('onNotification'," + stringifiedPayload + ");"+
                "})();";

myWebView.evaluateJavascript(sdkFunction, new ValueCallback<String>() {
  @Override
  public void onReceiveValue(String s) { }
});
}


Troubleshooting

Issue 1: Sprinklr webview URL is not working

  1. Check network connections establishing or not; to verify this you can simply try to open google.com in your webview

  2. Check whitelisting is done properly in live chat builder in Sprinklr platform

    Steps: Sprinklr Service → Live Chat Care → “ three dots” next to your Live chat Application → Edit → Application Configuration Screen

  3. Check example URL are loading like https://prod0-live-chat.sprinklr.com/page?appId=app_1000073145

Issue 2: LiveChat not showing up while passing authenticated user details

Check the expected hash for any authenticated user passed inside chat settings.

  • Hover over the Options icon alongside your Live Chat Application and select Validate User Hash.

  • On the Validate User Hash window, enter User ID, Profile Image URL, First Name, Last Name, Phone Number and Email. Click Copy Generated Hash and verify it with the hash generated by you.

Issue 3: Attachment icon not working

  1. Please check if user is having permission like storage and camera permissions

  2. Please check step 4

Issue 4: Reply box is not showing with keypad

  1. Check and add one configuration in AndroidManifest.xml android:windowSoftInputMode="adjustResize"

Refer here for more details:

https://developer.android.com/develop/ui/views/touch-and-input/keyboard-input/visibility#Respond