Quickstart: Android WebView

WebView is not supported

We recommend using our native mobile SDKs (Android, iOS) instead of WebView, and discourage new WebView implementations. WebView integrations experience lower conversion due to reduced performance.

For more information, see Native mobile integration vs. WebView.

Below are some code snippets to get you integrating with the WebView API as soon as possible.

webview
Example WebView on Android in Sandbox mode

Demo App

If you just want to kick the tires on the WebView, we have an open source example app available at persona-id/persona-android-webview that you can clone and open in Android Studio.

shell
$# If you want to build and install the demo app from the command line:
>git clone https://github.com/persona-id/persona-android-webview
>cd persona-android-webview
>./gradlew installDebug

Example Code

To launch the WebView, create a WebView and pass in query parameters specified in WebView Flow.

java
1// inside Activity#onCreate
2// assuming some WebView called "webView"
3WebSettings settings = webView.getSettings();
4settings.setDomStorageEnabled(true);
5settings.setJavaScriptEnabled(true);
6settings.setMediaPlaybackRequiresUserGesture(false);
7
8final Uri personaUrl = new Uri.Builder()
9 .scheme("https")
10 .encodedAuthority("withpersona.com")
11 .path("verify")
12 .appendQueryParameter("is-webview", "true")
13 .appendQueryParameter("template-id", "<your template ID starting with itmpl_>")
14 .appendQueryParameter("environment-id", "<your environment ID starting with env_>")
15 // optional - specify theme for new inquiry
16 // .appendQueryParameter("theme-id", "the_Z7S2Ltor9fp2oEGXMLXBPsHa")
17 .build();

Handling Permissions

Be sure to add the required permissions for your use case to your AndroidManifest file. For example:

text
<uses-permission android:name="android.permission.CAMERA" />

See https://github.com/persona-id/persona-android-webview/blob/main/app/src/main/AndroidManifest.xml in our sample app.

In addition to adding to the Manifest file, you’ll need to override onPermissionRequest and onShowFileChooser depending on your use case. See an example below.

java
1// set fields on your Activity
2public static final int INPUT_FILE_REQUEST_CODE = 1;
3private static final int CAMERA_PERMISSION_REQUEST = 1111;
4private PermissionRequest cameraPermission;
5private ValueCallback<Uri[]> filePathCallback;
6private String cameraPhotoPath;
7
8@Override
9public void onActivityResult(int requestCode, int resultCode, Intent data) {
10 if (requestCode != INPUT_FILE_REQUEST_CODE || filePathCallback == null) {
11 super.onActivityResult(requestCode, resultCode, data);
12 return;
13 }
14
15 Uri[] results = null;
16
17 // Check that the response is a good one
18 if (resultCode == Activity.RESULT_OK) {
19 if (data == null) {
20 // If there is not data, then we may have taken a photo
21 if (cameraPhotoPath != null) {
22 results = new Uri[] { Uri.parse(cameraPhotoPath) };
23 }
24 } else {
25 String dataString = data.getDataString();
26 if (dataString != null) {
27 results = new Uri[] { Uri.parse(dataString) };
28 }
29 }
30 }
31
32 filePathCallback.onReceiveValue(results);
33 filePathCallback = null;
34}
35
36// in the Activity#onCreate method
37webView.setWebChromeClient(new WebChromeClient() {
38 @Override
39 public void onPermissionRequest(final PermissionRequest request) {
40 if (request.getOrigin().toString().equals("https://withpersona.com/")) {
41 ActivityCompat.requestPermissions(MainActivity.this,
42 new String[] { Manifest.permission.CAMERA }, CAMERA_PERMISSION_REQUEST);
43 cameraPermission = request;
44 } else {
45 request.deny();
46 }
47 }
48
49 @Override
50 public boolean onShowFileChooser(
51 WebView webView, ValueCallback<Uri[]> newFilePathCallback,
52 FileChooserParams fileChooserParams) {
53
54 if (filePathCallback != null) {
55 filePathCallback.onReceiveValue(null);
56 }
57 filePathCallback = newFilePathCallback;
58
59 Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
60 if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
61 // Create the File where the photo should go
62 File photoFile = null;
63 // Create an image file name
64 String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
65 String imageFileName = "JPEG_" + timeStamp + "_";
66 File storageDir =
67 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
68 try {
69 photoFile = File.createTempFile(imageFileName, ".jpg", storageDir);
70 } catch (IOException ex) {
71 // Error occurred while creating the File
72 }
73
74 // Continue only if the File was successfully created
75 if (photoFile != null) {
76 cameraPhotoPath = "file:" + photoFile.getAbsolutePath();
77 takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
78 Uri.fromFile(photoFile));
79 } else {
80 takePictureIntent = null;
81 }
82 }
83
84 Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
85 contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
86 contentSelectionIntent.setType("image/*");
87
88 Intent[] intentArray;
89 if (takePictureIntent != null) {
90 intentArray = new Intent[] { takePictureIntent };
91 } else {
92 intentArray = new Intent[0];
93 }
94
95 Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
96 chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
97 chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
98 chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
99
100 startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE);
101
102 return true;
103 }
104});
105
106// overwriting your AppCompatActivity's #onRequestPermissionsResult
107@Override
108public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
109 @NonNull int[] grantResults) {
110 super.onRequestPermissionsResult(requestCode, permissions, grantResults);
111 if (requestCode == CAMERA_PERMISSION_REQUEST) {
112 if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
113 cameraPermission.grant(cameraPermission.getResources());
114 } else {
115 cameraPermission.deny();
116 }
117 }
118}

Handling the callback redirect

On a successful or canceled inquiry flow, Persona will redirect to the redirectUri specified on when launching the flow, or “personacallback” if no custom redirectUri is specified. You can retrieve the query parameters specified in Redirecting On Complete or Cancel on the redirectUri.

java
1@Override
2public boolean shouldOverrideUrlLoading(WebView view, String url) {
3 Uri parsedUri = Uri.parse(url);
4 if (Objects.equals(parsedUri.getAuthority(), "personacallback")) {
5 // User succeeded verification or `inquiryID` is `null`
6 String inquiryId = parsedUri.getQueryParameter("inquiry-id");
7 // ... do something with the inquiryId
8 return true;
9 } else {
10 return false;
11 }
12}