Skip to main content
Version: Next

Android

Mobile integrations are quite similar to Web's, it should use WebView instead of the iframe element. You can almost use all the functions that is mentioned in the Web section. Main difference of the integration is that you need to tell the app explicitly to allow taking photo or using gallery to choose a photo. There might be slight differences by the Android API version your app is using, but the core concept is the same.

Note: Do not create different WebViews for Makeup and Nailpolish solutions. They should be using the same WebView.

1. Importing WebView#

To add a WebView to your app in the layout, add the following code to your activity's layout XML file:

<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="496dp"
android:layout_marginBottom="0dp"
/>

2. Structure#

On Android, you need to add some additional configurations. First create a file_paths.xml folder inside your app/src/main/res/xml then Add the following codes

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="." />
</paths>

Secondly add the following code inside the tag of your android/app/src/main/AndroidManifest.xml: and change the android:authorities with your projects name with .provider extension

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.demopulpoarjava.provider"
android:exported="false"
android:grantUriPermissions="true"
>
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"
/>
</provider>

After configuration is done. We can start creating our WebView. Initialize WebView instance by id and set configuration to allow the customization for JavaScript.

val webView: WebView = findViewById(R.id.webview)
// setJavaScriptEnabled for execute JavaScript commands
webView.settings.javaScriptEnabled = true
// setDomStorageEnabled for display model images
webView.settings.domStorageEnabled = true
// You are supposed to be given an URL that contains the unique identifier for your integration.
webView.loadUrl(url)
webView.webViewClient = object : WebViewClient() {
// JavaScript codes that should be executed after page load finish.
override fun onPageFinished(view: WebView?, url: String?) {
// You are free to customize or communicate with Plugin from now on.
}
}

After this step we can see our WebView with with Choose Model and Take a Photo options. Choose Model should be working but Take a photo is not. We will use onShowFileChooser function for open camera createImageFile for generate image path and onActivityResult function will be executed after image selected

webView.webChromeClient = object :WebChromeClient(){
// Method to take photo from camera or to choose image from the gallery.
override fun onShowFileChooser(
webView: WebView?,filePathCallback: ValueCallback<Array<Uri>>?, fileChooserParams: FileChooserParams?
): Boolean {
if (mFilePathCallback != null) {
mFilePathCallback!!.onReceiveValue(null)
}
mFilePathCallback = filePathCallback
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if(takePictureIntent.resolveActivity(packageManager)!= null){
// create the file where the photo should go
var photoFile: File? = null
try {
val destination =
Environment.getExternalStorageDirectory()
.path + "/image.jpg"
takePictureIntent.putExtra(
MediaStore.EXTRA_OUTPUT,
Uri.fromFile(File(destination))
)
// creates the File
photoFile = createImageFile()
takePictureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
}catch (ex:IOException){
// Error occurred while creating the File
Log.e(tag,"Unable to create Image File",ex)
}
// continue only if the file was successfully created
if(photoFile != null){
mCameraPhotoPath = "file:"+photoFile.absolutePath
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,Uri.fromFile(photoFile))
}
}
val contentSelectionIntent = Intent(Intent.ACTION_GET_CONTENT)
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE)
contentSelectionIntent.type = "image/*"
val intentArray: Array<Intent?>
intentArray = arrayOf(takePictureIntent)
val chooserIntent = Intent(Intent.ACTION_CHOOSER)
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent)
chooserIntent.putExtra(Intent.EXTRA_TITLE,"İşlem Seçin")
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS,intentArray)
startActivityForResult(chooserIntent, fileChooserResultCode)
return true
}
}
private fun createImageFile(): File? {
@SuppressLint("SimpleDateFormat") val timeStamp: String =
java.text.SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
val imageFileName = "JPEG_" + timeStamp + "_"
val storageDir =
getExternalFilesDir(Environment.DIRECTORY_PICTURES)
val image: File = File.createTempFile(
imageFileName, // prefix
".jpg", // suffix
storageDir // directory
)
currentPhotoPath = image.absolutePath
return image
}
// Parse image from camera or from gallery to pass into the callback.
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
var results: Array<Uri>? = null
// Check that the response is a good one
if(resultCode == Activity.RESULT_OK){
// If there is not data, then we may have taken a photo
if(data == null) {
results = arrayOf(Uri.parse("file:$currentPhotoPath"))
} else {
// if image selected from file this part will be executed
val dataString:String? = data.dataString
results = arrayOf(Uri.parse(mCameraPhotoPath))
if(dataString != null){
results = arrayOf(Uri.parse(dataString))
}
}
}
mFilePathCallback?.onReceiveValue(results)
mFilePathCallback = null
}

3. Communication with the Plugin/Remote Control#

The function calls to manipulate/customize the Plugin is the same that of Web version. There is only slight tweaks in invoking them due to the platform differences as you can can see the below.

Please remember that you can call the functions only after the initial load of WebView, just like the way it was in Web.

note

If you are already using our Template Engine on Landing Page Integration, you might not need them at all. They are only needed to control the Plugin remotely.

// Function that executes JavaScript code.
fun injectJS(command:String) {
webView.evaluateJavascript(command, null)
}
// Apply the product with unique product_code.
fun applyProductWithCode(v: View?) {
// Get the unique product code when button is clicked, it's up to your own preference.
// E.g., it can be retrieved from a "tag" that's set on button.
val productCode = "P_001";
injectJS("javascript:postMessage(JSON.stringify({applyProductWithCode:{product_code:'${productCode}'}}),'*')")
}
// Apply the product by its category and key values.
fun applyProductWithNameAndCategory(v: View?) {
// Values are just examples, you need to retrieve them by selected product/button.
val categoryName = "Lipstick"
val fullProductName = "${brandName} ${productName} ${productColor}"
injectJS("javascript:postMessage(JSON.stringify({applyProductWithNameAndCategory:{category: '${categoryName}', key: '${fullProductName}'}}),'*')")
}
// Remove/clear any Makeup that is currently available.
fun removeProduct() {
injectJS("javascript:postMessage(JSON.stringify({removeProduct:true}),'*')")
}

4. Extra features#

How to start the Plugin by specific product applied?

You need to have related product details registered in for your product by our digitalization team.

  • Set by product code
// Add custom product code to the webview url as query param. Query value should be `product code`.
// It needs to be set for the initial load as it's described in the first section above.
// url = <url>?activeProduct=<productCode>
webView.loadUrl(url);
  • Set by special product id
// Add custom product code to the webview url as query param. Query value should be `product code`.
// It needs to be set for the initial load as it's described in the first section above.
// url = <url>?specialProduct=<specialProductId>
webView.loadUrl(url);

Demo applications#

You can request to see the basic demo applications that work on Android, as well.