Android Settings
Please go through session creation instructions given here before proceeding.
-
Allow camera and internet permissions in your
AndroidManifest.xml
<uses-feature android:name="android.hardware.camera" android:required="false" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.INTERNET" />
-
Create
file_paths.xml
in/res/xml/
(this is required for the Android camera app to work)<paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-files-path name="my_images" path="Pictures" /> </paths>
-
Now in your manifest, create a
provider
for this file path:<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <!-- Permissions here --> <application android:icon="@mipmap/ic_launcher" android:label="@string/app_name" > <!-- Add this to your manifest --> <provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.provider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> <!-- Your activities go here --> </application> </manifest>
-
Now you can create an activity that will host your webview with your session URL. A working example of the same is given below...
Full Working Example:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
package com.example.aipriseandroidtest
import android.Manifest
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.os.Environment
import android.provider.MediaStore
import android.webkit.JavascriptInterface
import android.webkit.PermissionRequest
import android.webkit.ValueCallback
import android.webkit.WebChromeClient
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
// JS Interface to listen to events from the WebView
class WebAppInterface(private val activity: MyActivity) {
@JavascriptInterface
fun postMessage(message: String) {
// Here you can listen to the success, complete and error events...
if (message == "AiPriseVerification:Success") {
// User reached the last page of verification
} else if (message == "AiPriseVerification:Complete") {
// User clicked on the 'continue` button on the last page. Time to hide the webview!
} else if (message.startsWith("AiPriseVerification:Error:")) {
// Happens when the session URL is invalid or the session is completed / expired.
// Extract error code from the end of message string and check.
}
// This informs Android if we have to trigger a file picker or the camera app when the user clicks any file upload button.
// Copy this as it is. Don't modify!
else if (message == "AiPriseFilePickerSource:file") {
activity.mode = "file"
} else if (message == "AiPriseFilePickerSource:camera") {
activity.mode = "camera"
}
}
}
// Your activity...
class MyActivity : AppCompatActivity() {
private lateinit var myWebView: WebView
// To handle runtime camera permissions
private var currentPermissionRequest: PermissionRequest? = null
private val CAMERA_PERMISSION_REQUEST_CODE = 1
// To handle file picker + camera app capture
private var cameraImageUri: Uri? = null
private lateinit var cameraLauncher: ActivityResultLauncher<Intent>
private lateinit var fileChooserLauncher: ActivityResultLauncher<Intent>
private var filePathCallback: ValueCallback<Array<Uri>>? = null
var mode: String = "file"; // Flag that decides if we open file picker or the camera app
// Handle activity creation
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my)
// Create launcher to open file picker when user clicks on upload button in the webview
fileChooserLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val resultData = result.data
val resultUris = if (resultData == null || resultData.data == null) null else arrayOf(resultData.data!!)
filePathCallback?.onReceiveValue(resultUris)
} else {
filePathCallback?.onReceiveValue(null)
}
filePathCallback = null
}
// Create launcher to open phone's camera app when user clicks on capture in the webview
cameraLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val resultUris = if (cameraImageUri != null) arrayOf(cameraImageUri!!) else null
filePathCallback?.onReceiveValue(resultUris)
} else {
filePathCallback?.onReceiveValue(null)
}
filePathCallback = null
}
// Find the webview and create clients...
myWebView = findViewById(R.id.webview)
myWebView.webViewClient = WebViewClient()
myWebView.webChromeClient = object : WebChromeClient() {
// Handle camera permission request
override fun onPermissionRequest(request: PermissionRequest) {
if (request.resources.contains(PermissionRequest.RESOURCE_VIDEO_CAPTURE)) {
if (ContextCompat.checkSelfPermission(this@MyActivity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
currentPermissionRequest = request
ActivityCompat.requestPermissions(this@MyActivity, arrayOf(Manifest.permission.CAMERA), CAMERA_PERMISSION_REQUEST_CODE)
} else {
request.grant(request.resources)
}
}
}
// Handle file chooser request (decides if camera should be opened or file picker)
override fun onShowFileChooser(
webView: WebView?,
newFilePathCallback: ValueCallback<Array<Uri>>?,
fileChooserParams: FileChooserParams?
): Boolean {
filePathCallback = newFilePathCallback
// Open camera app
if (mode == "camera") {
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
cameraImageUri = FileProvider.getUriForFile(
this@MyActivity,
"${applicationContext.packageName}.provider",
createImageFile()
)
intent.putExtra(MediaStore.EXTRA_OUTPUT, cameraImageUri)
cameraLauncher.launch(intent)
}
// Open file picker
else {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "*/*"
fileChooserLauncher.launch(intent)
}
return true
}
}
// Update webview settings
myWebView.settings.javaScriptEnabled = true
myWebView.settings.allowFileAccess = true
myWebView.settings.domStorageEnabled = true
myWebView.settings.mediaPlaybackRequiresUserGesture = false
// Attach the interface to your webview to listen to events
myWebView.addJavascriptInterface(WebAppInterface(this), "Android")
// Load the URL returned by our API
myWebView.loadUrl("YOUR_SESSION_URL_HERE")
}
// Handle response from permission request
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) {
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
currentPermissionRequest?.grant(currentPermissionRequest?.resources)
} else {
currentPermissionRequest?.deny()
}
currentPermissionRequest = null
}
}
// Just a helper function that gets us the output of the camera capture as a `File` instance
private fun createImageFile(): File {
val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date())
val storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
return File.createTempFile("JPEG_${timeStamp}_", ".jpg", storageDir)
}
}
Updated 3 months ago