I have a component in angular with the functionality to take picture or select file
The file upload works fine, the problem is that it does not let me take photos, I already tried to adapt the code in various ways but it does not work.
How could I make that adjustment so that both options work, how could I identify which option was selected (take photo, select file)?
Angular code
Html
<input
#inputFile
accept="image/*,application/pdf"
type="file"
(change)="loadFile($event)"
style="display: none;"
onclick="this.value = null"
/>
<input
#cameraFile
accept="image/*"
capture="camera"
type="file"
(change)="loadFile($event)"
style="display: none;"
onclick="this.value = null"
/>
Ts
selectOption(opcion: any) {
if (opcion.tipoArchivo === this.documentType.documento.idDocumento) {
switch (opcion.opcion) {
case MenuBottomSheet.ELEGIR_FOTO:
this.fileChooser.nativeElement.click();
break;
case MenuBottomSheet.TOMAR_FOTO:
this.cameraFile.nativeElement.click();
break;
}
}
}
Código android
web_view?.webChromeClient = object: WebChromeClient() {
override fun onShowFileChooser(webView: WebView?, filePathCallback: ValueCallback<Array<Uri>>?,
fileChooserParams: FileChooserParams): Boolean {
Log.d("onShowFileChooser ======> ", "")
super.onShowFileChooser(webView, filePathCallback, fileChooserParams)
if (uploadMessage != null) {
uploadMessage?.onReceiveValue(null)
uploadMessage = null
}
uploadMessage = filePathCallback
val intent: Intent = fileChooserParams.createIntent()
try {
startActivityForResult(intent, REQUEST_SELECT_FILE)
} catch (e: ActivityNotFoundException) {
uploadMessage = null
return false
}
return true
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK && (requestCode == REQUEST_SELECT_FILE || requestCode == REQUEST_IMAGE_CAPTURE)) {
if (uploadMessage == null) {
return
}
uploadMessage?.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, data))
uploadMessage = null
}
}
I put here the code that worked for me
override fun onShowFileChooser(webView: WebView?, filePathCallback: ValueCallback<Array<Uri>>?, fileChooserParams: FileChooserParams): Boolean {
super.onShowFileChooser(webView, filePathCallback, fileChooserParams)
if (uploadMessage != null) {
uploadMessage?.onReceiveValue(null)
uploadMessage = null
}
uploadMessage = filePathCallback
if (fileChooserParams.isCaptureEnabled()) {
takingPhoto = true
var permissions = ArrayList<String>()
if(!existCameraPermission()) {
permissions.add(Manifest.permission.CAMERA);
}
if(!existMemoryWritePermission()) {
permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
if(!permissions.isEmpty()) {
requestPermissions(permissions.toTypedArray(), 1);
} else {
takePhoto()
}
} else {
takingPhoto = false
val intent: Intent = fileChooserParams.createIntent()
try {
startActivityForResult(intent, REQUEST_SELECT_FILE)
} catch (e: ActivityNotFoundException) {
uploadMessage = null
return false
}
}
return true
}
fun takePhoto() {
var takePictureIntent: Intent? = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (takePictureIntent?.resolveActivity(packageManager) != null) {
var photoFile: File? = null
try {
photoFile = createImageFile()
} catch (ex: IOException) {
Log.d("ocurrio un error ================> ", ex.toString())
}
if (photoFile != null) {
photoFileTmp = photoFile
val uri = FileProvider.getUriForFile(activity, fileProvider, photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri)
}
startActivityForResult(takePictureIntent, REQUEST_SELECT_FILE)
}
}
Related
i'm trying to capture image in android 11 device but request is keep cancelled. i have added also. still unable to capture image. pick image from gallery is working. image is not storing in Android/data folder.
i have added val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) also still unable to create temp file in storage. please provide any solutions
private fun selectImage() {
imageUtils.createImageFile(applicationContext).let {
viewModel.setFileTempPath(it.absolutePath, "doc_name")
startActivityForResult(imageUtils.captureImage(applicationContext, it), 300)
}
override fun onActivityResult(
requestCode: Int,
resultCode: Int,
data: Intent?
) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
300 -> when (resultCode) {
RESULT_OK -> {
Log.d("tmppath", data?.data.toString())
if (data != null && data.data != null) {
val selectedImage: Bitmap =
imageUtils.getBitmap(data.data, this#TaggingActivity)!!
viewModel.dataModel.selectedImagesArrayList.value.let {
if (it == null) {
viewModel.dataModel.selectedImagesArrayList.value = ArrayList()
}
val a = it
a?.add(
ImageDetailsToUpload(
"",
selectedImage,
Constant.CUSTOMER_GEO_TAG,
viewModel.dataModel.documentName.value,
viewModel.commonModel.currentLocation.value!!
)
)
viewModel.dataModel.selectedImagesArrayList.postValue(a)
}
} else {
if (viewModel.dataModel.tmpPath.value != null) {
imageUtils.getBitmapFromPath(viewModel.dataModel.tmpPath.value)
?.let { selectedImage ->
val a = viewModel.dataModel.selectedImagesArrayList.value
a?.add(
ImageDetailsToUpload(
"",
selectedImage,
Constant.CUSTOMER_GEO_TAG,
viewModel.dataModel.documentName.value,
viewModel.commonModel.currentLocation.value!!
)
)
viewModel.dataModel.selectedImagesArrayList.postValue(a)
} ?: run {
viewModel.commonModel.showToastTextView("Sorry Try Again..!")
}
} else {
viewModel.commonModel.showToastTextView("Sorry Try Again..!")
return
}
}
viewModel.dataModel.tmpPath.value = null
}
RESULT_CANCELED -> {
viewModel.setFileTempPath("", "")
Toast.makeText(this,"cancelled",Toast.LENGTH_LONG).show()
}
}
}
//Intent class
fun getCameraIntent(file: File): Intent {
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
takePictureIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file))
if (Build.VERSION.SDK_INT >= 24) {
try {
val m =
StrictMode::class.java.getMethod("disableDeathOnFileUriExposure")
m.invoke(null)
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
} else {
takePictureIntent.putExtra("return-data", true)
takePictureIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
val pickPhoto = Intent(
Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
)
val intents: ArrayList<Intent> = arrayListOf()
intents.add(takePictureIntent)
intents.add(pickPhoto)
val chooserIntent = Intent.createChooser(
intents.removeAt(intents.size - 1),
" Document Upload"
)
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intents.toArray(arrayOf<Parcelable>()))
return chooserIntent
}
I have tried to use the WebView to upload one single file and it works well. However, once I selected more than 1 file and upload, the program crashed. The code is below:
override fun onActivityResult(
requestCode: Int, resultCode: Int,
intent: Intent?
) {
super.onActivityResult(requestCode, resultCode, intent)
if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage || requestCode != FILECHOOSER_RESULTCODE) return
var results: Array<Uri>? = null
if (resultCode === Activity.RESULT_OK) {
if (intent != null) {
val dataString = intent.dataString
val clipData = intent.clipData
if (clipData != null) {
for (i in 0 until clipData.itemCount) {
val item = clipData.getItemAt(i)
results!![i] = item.uri //Here is the crash point
}
}
if (dataString != null) results =
arrayOf(Uri.parse(dataString))
}
}
mUploadMessage!!.onReceiveValue(results)
mUploadMessage = null
return
}
}
And here is the code in WebChromeClient():
override fun onShowFileChooser(
view: WebView,
filePathCallback: ValueCallback<Array<Uri>>,
fileChooserParams: FileChooserParams
): Boolean {
if (mUploadMessage!= null) {
mUploadMessage!!.onReceiveValue(null);
mUploadMessage = null;
}
mUploadMessage = filePathCallback
val intent = fileChooserParams.createIntent()
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
intent.setType("*/*")
startActivityForResult(intent, FILECHOOSER_RESULTCODE)
return true
}
From the logcat I can see the crash point is results!![i] = item.uri when I get the uri from clipData in the For loop in OnActivityResult. I have comment on that code line. The crash message is "kotlin.KotlinNullPointerException".
I found the solution.
Just need to initialize the results:
results= Array(clipData.itemCount, {clipData.getItemAt(0).uri})
I am having problem that I am not able to open camera from webview in that I am having php page so I have to open gallery and camera from there but I am not able to do what I got from this link "https://gist.github.com/jhonsore/8a8378c147ec00ac6f3fa53569c82ef8".
I can open gallery but not camera please give me any solution.
A working example in Kotlin for Android 7+
Add camera permissions
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.CAMERA2" />
Configure your WebView
private fun configureWebViewSettings() {
binding.webView.settings.javaScriptEnabled = true
binding.webView.settings.domStorageEnabled = true
binding.webView.settings.setSupportZoom(false)
binding.webView.settings.allowFileAccess = true
binding.webView.settings.allowContentAccess = true
binding.webView.webChromeClient = getCustomWebChromeClient()
}
Add the lines below to your Activity
private var imagePathCallback: ValueCallback<Array<Uri>>? = null
private var cameraImagePath: String? = null
Implement a WebChromeClient and add it to your WebView
private fun getCustomWebChromeClient() = object : WebChromeClient() {
override fun onShowFileChooser(
view: WebView?,
filePath: ValueCallback<Array<Uri>>?,
fileChooserParams: FileChooserParams?
): Boolean {
if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED)
requestPermissions(arrayOf(Manifest.permission.CAMERA), CAMERA_REQUEST_CODE)
imagePathCallback?.onReceiveValue(null)
imagePathCallback = null
imagePathCallback = filePath
val takePictureIntent = createImageCaptureIntent()
val contentSelectionIntent = Intent(Intent.ACTION_GET_CONTENT)
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE)
contentSelectionIntent.type = INTENT_FILE_TYPE
val intentArray: Array<Intent?>
intentArray = takePictureIntent?.let { arrayOf(it) } ?: arrayOfNulls(0)
val chooserIntent = Intent(Intent.ACTION_CHOOSER)
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent)
chooserIntent.putExtra(Intent.EXTRA_TITLE, getString(R.string.file_chooser_title))
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray)
try {
startActivityForResult(chooserIntent, REQUEST_SELECT_FILE)
} catch (e: ActivityNotFoundException) {
imagePathCallback = null
cameraImagePath = null
Toast.makeText(
this#MainActivity,
getString(R.string.cannot_open_file_chooser_txt),
Toast.LENGTH_LONG
).show()
return false
}
return true
}
private fun createImageCaptureIntent(): Intent? {
var captureImageIntent: Intent? = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (captureImageIntent?.resolveActivity(packageManager) != null) {
var imageFile: File? = null
try {
imageFile = createImageFile()
captureImageIntent.putExtra("CameraImagePath", cameraImagePath)
} catch (ex: IOException) {
ex.printStackTrace()
}
if (imageFile != null) {
cameraImagePath = CAMERA_PHOTO_PATH_POSTFIX + imageFile.absolutePath
captureImageIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(imageFile))
} else {
captureImageIntent = null
}
}
return captureImageIntent
}
private fun createImageFile(): File? {
val timeStamp = SimpleDateFormat.getDateInstance().format(Date())
val imageFileName = PHOTO_NAME_POSTFIX + timeStamp + "_"
val storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
return File.createTempFile(imageFileName, PHOTO_FORMAT, storageDir)
}
Implement onRequestPermissionsResult in your Activity
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == CAMERA_REQUEST_CODE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(
this,
getString(R.string.amera_permission_granted_txt),
Toast.LENGTH_LONG
).show()
} else {
Toast.makeText(
this,
getString(R.string.camera_permission_denied_txt),
Toast.LENGTH_LONG
).show()
}
}
}
Implement onActivityResult in your Activity
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode != REQUEST_SELECT_FILE || imagePathCallback == null) return
var results: Array<Uri>? = null
if (resultCode == RESULT_OK) {
if (data == null) {
if (cameraImagePath != null) results = arrayOf(Uri.parse(cameraImagePath))
} else {
val dataString = data.dataString
if (dataString != null) results = arrayOf(Uri.parse(dataString))
}
}
imagePathCallback?.onReceiveValue(results)
imagePathCallback = null
}
Constants
companion object {
private const val CAMERA_REQUEST_CODE = 113
private const val REQUEST_SELECT_FILE = 13
private const val INTENT_FILE_TYPE = "image/*"
private const val CAMERA_PHOTO_PATH_POSTFIX = "file:"
private const val PHOTO_NAME_POSTFIX = "JPEG_"
private const val PHOTO_FORMAT = ".jpg"
}
Try and see, it should work.
Did you grant Camera permission into your WebView's WebChromeClient?
Please add camera permission into your AndroidManifest.xml.
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.CAMERA2" />
Set WebChromeClient into your WebView.
webView.setWebChromeClient(new ChromeClient())
Define the your ChromeClient class.
public class ChromeClient extends WebChromeClient {
#Override
public boolean onShowFileChooser(WebView view, ValueCallback<Uri[]> filePath, WebChromeClient.FileChooserParams fileChooserParams) {
if (!hasPermissions(MainActivity.this, PERMISSIONS)) {
checkStoragePermission();
return false;
}
// Double check that we don't have any existing callbacks
if (mFilePathCallback != null) {
mFilePathCallback.onReceiveValue(null);
}
mFilePathCallback = filePath;
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = createImageFile();
takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath);
// Continue only if the File was successfully created
if (photoFile != null) {
mCameraPhotoPath = "file:" + photoFile.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(photoFile));
} else {
takePictureIntent = null;
}
}
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("image/*");
Intent[] intentArray;
if (takePictureIntent != null) {
intentArray = new Intent[]{takePictureIntent};
} else {
intentArray = new Intent[0];
}
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Select Photo");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
startActivityForResult(chooserIntent, Constant.INPUT_FILE_REQUEST_CODE);
return true;
}
}
Here are variables.
String[] PERMISSIONS = {
Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
private ValueCallback<Uri[]> mFilePathCallback;
private String mCameraPhotoPath;
Check the storage and camera permission.
private void checkStoragePermission() {
String permission = Manifest.permission.WRITE_EXTERNAL_STORAGE;
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission}, Constant.REQUEST_STORAGE_PERMISSION);
} else {
checkCameraPermission();
}
}
private void checkCameraPermission() {
String permission = Manifest.permission.CAMERA;
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{permission}, Constant.REQUEST_CAMERA_PERMISSION);
} else {
onPermissionGranted();
}
}
After that, I can access to camera and gallery.
Hope it to be helpful.
For Android 10, SDK 30
Add android:requestLegacyExternalStorage="true" in application tag in manifest
https://stackoverflow.com/a/65415381/7775500
this might help you ..
Surely you need the permissions to open the camera on android.
Edit the manifest like this:
<uses-permission android:name="android.permission.CAMERA" />
You might also need a library not included
I also had this problem too. I will share it with others who are likely to have this problem in the future.
You must have the camera permission in the manifest and grant it in the onPermissionRequest methode in your WebChromeClient and be sure to set setMediaPlaybackRequiresUserGesture to false in the webView setting.
Add this permission:
<uses-permission android:name="android.permission.CAMERA" />
Add this in webView setting:
binding.webView.getSettings().setMediaPlaybackRequiresUserGesture(false);
And grant the permission in onPermissionRequest methode:
#Override
public void onPermissionRequest(final PermissionRequest request) {
final String[] requestedResources = request.getResources();
request.grant(requestedResources);
}
for more information see this link:
StackOverFlow solution
I used this method to get file from uri, it is work fine with all devices but only crash with samsung devices!
fun Activity.getFilePath(uri: Uri): File {
var cursor: Cursor? = null
return try {
val dataArray = arrayOf(MediaStore.Images.Media.DATA)
cursor = contentResolver.query(uri, dataArray, null, null, null)
val index = cursor?.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
cursor?.moveToFirst()
File(index?.let { cursor?.getString(it) })
} catch (e: Exception) {
throw KotlinNullPointerException("File not founded")
} finally {
cursor?.close()
}
}
get image from gallery
fun Activity.chooseImageFromGallery(withAskPermission: Boolean) {
if (isPermissionsGranted(Manifest.permission.READ_EXTERNAL_STORAGE)) {
val intent = Intent(Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
startActivityForResult(Intent.createChooser(intent, "Select Picture"), PICK_IMAGE_REQUEST)
} else {
if (withAskPermission && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
PICK_IMAGE_REQUEST)
}
}
}
on Activity result
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == Activity.RESULT_OK && requestCode == PICK_IMAGE_REQUEST) {
uri = data?.data
file = uri?.let { activity?.getFilePath(it) }
//
}
}
Its a simple WebApp with a Webview in it and there is a registration page with upload file option. trying to open file chooser when click on browse button but there is not response. i assume that there is some issue in my gradle files. please help me debug it. Here is my Code.
here is my project gradle
buildscript {
repositories {
google()
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.3'
classpath 'com.google.gms:google-services:3.2.1'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
and here is my app gradle
android {
compileSdkVersion 27
defaultConfig {
applicationId "com.androidapp.myApp"
minSdkVersion 15
targetSdkVersion 27
versionCode 2
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
useLibrary 'org.apache.http.legacy'
multiDexEnabled true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support:support-v4:27.1.1'
implementation 'com.google.firebase:firebase-invites:16.0.1'
implementation 'com.google.firebase:firebase-messaging:17.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.squareup.okhttp3:okhttp:3.8.0'
implementation 'com.android.support:multidex:1.0.3'
}
apply plugin: 'com.google.gms.google-services'
It is not the gradle file error. You just need to provide custom WebChromeClient like below.
class MyWebChromeClient extends WebChromeClient {
// For 3.0+ Devices (Start)
// onActivityResult attached before constructor
protected void openFileChooser(ValueCallback uploadMsg, String acceptType) {
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 Lollipop 5.0+ Devices
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public boolean onShowFileChooser(WebView mWebView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
if (uploadMessage != null) {
uploadMessage.onReceiveValue(null);
uploadMessage = null;
}
uploadMessage = filePathCallback;
Intent intent = fileChooserParams.createIntent();
try {
startActivityForResult(intent, REQUEST_SELECT_FILE);
} catch (ActivityNotFoundException e) {
uploadMessage = null;
Toast.makeText(WebLink.this, "Cannot Open File Chooser", Toast.LENGTH_LONG).show();
return false;
}
return true;
}
//For Android 4.1 only
protected void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
mUploadMessage = uploadMsg;
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(Intent.createChooser(intent, "File Chooser"), FILECHOOSER_RESULTCODE);
}
protected 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);
}
}
and it in your webview like below
webview.setWebChromeClient(new MyWebChromeClient());
some other useful stuff/variables to be declared globally.
public ValueCallback<Uri[]> uploadMessage;
private ValueCallback<Uri> mUploadMessage;
public static final int REQUEST_SELECT_FILE = 100;
private final static int FILECHOOSER_RESULTCODE = 1;
make sure you have all the read/write permissions
UPDATE
Use below lines to provide access to files from storage.
webview.getSettings().setDomStorageEnabled(true);
webview.getSettings().setAllowContentAccess(true);
webview.getSettings().setAllowFileAccess(true);
// EDIT, add this line also
webview.getSettings().setJavaScriptEnabled(true);
UPDATE 2
Get the result in onActivityResult method. You can use the result given like below.
#Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (requestCode == REQUEST_SELECT_FILE) {
if (uploadMessage == null)
return;
uploadMessage.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, intent));
uploadMessage = null;
}
} else if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage)
return;
// Use MainActivity.RESULT_OK if you're implementing WebView inside Fragment
// Use RESULT_OK only if you're implementing WebView inside an Activity
Uri result = intent == null || resultCode != WebLink.RESULT_OK ? null : intent.getData();
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
} else
Toast.makeText(WebLink.this, "Failed to Upload Image", Toast.LENGTH_LONG).show();
}
This Kotlin code worked for all versions:
webView.settings.javaScriptEnabled = true
webView.settings.loadWithOverviewMode = true
webView.settings.useWideViewPort = true
webView.settings.domStorageEnabled = true
webView.settings.allowFileAccess=true
webView.settings.allowContentAccess=true
webView.settings.allowUniversalAccessFromFileURLs=true
webView.settings.allowFileAccessFromFileURLs=true
webView.settings.javaScriptCanOpenWindowsAutomatically=true
webView.loadUrl(Constants.URL)
webView.webChromeClient = object : WebChromeClient() {
override fun onShowFileChooser(
webView: WebView,
filePathCallback: ValueCallback<Array<Uri>>,
fileChooserParams: FileChooserParams
): Boolean {
if (mUMA != null) {
mUMA!!.onReceiveValue(null)
}
mUMA = filePathCallback
var takePictureIntent: Intent? = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (takePictureIntent!!.resolveActivity(this#MainActivity.getPackageManager()) != null) {
var photoFile: File? = null
try {
photoFile = createImageFile()
takePictureIntent.putExtra("PhotoPath", mCM)
} catch (ex: IOException) {
Log.e("Webview", "Image file creation failed", ex)
}
if (photoFile != null) {
mCM = "file:" + photoFile.getAbsolutePath()
takePictureIntent.putExtra(
MediaStore.EXTRA_OUTPUT,
Uri.fromFile(photoFile)
)
} else {
takePictureIntent = null
}
}
val contentSelectionIntent = Intent(Intent.ACTION_GET_CONTENT)
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE)
contentSelectionIntent.type = "*/*"
val intentArray: Array<Intent>
intentArray = takePictureIntent?.let { arrayOf(it) } ?: arrayOf<Intent>()
val chooserIntent = Intent(Intent.ACTION_CHOOSER)
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent)
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser")
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray)
startActivityForResult(chooserIntent, FCR)
return true
}
}
// Create an image file
#Throws(IOException::class)
private fun createImageFile(): File? {
#SuppressLint("SimpleDateFormat") val timeStamp: String =
SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
val imageFileName = "img_" + timeStamp + "_"
val storageDir: File =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
return File.createTempFile(imageFileName, ".jpg", storageDir)
}
fun openFileChooser(uploadMsg: ValueCallback<Uri?>?) {
this.openFileChooser(uploadMsg, "*/*")
}
fun openFileChooser(
uploadMsg: ValueCallback<Uri?>?,
acceptType: String?
) {
this.openFileChooser(uploadMsg, acceptType, null)
}
fun openFileChooser(
uploadMsg: ValueCallback<Uri?>?,
acceptType: String?,
capture: String?
) {
val i = Intent(Intent.ACTION_GET_CONTENT)
i.addCategory(Intent.CATEGORY_OPENABLE)
i.type = "*/*"
this#MainActivity.startActivityForResult(
Intent.createChooser(i, "File Browser"),
FILECHOOSER_RESULTCODE
)
}
override fun onActivityResult(
requestCode: Int,
resultCode: Int,
intent: Intent?
) {
super.onActivityResult(requestCode, resultCode, intent)
if (Build.VERSION.SDK_INT >= 21) {
var results: Array<Uri>? = null
//Check if response is positive
if (resultCode == Activity.RESULT_OK) {
if (requestCode == FCR) {
if (null == mUMA) {
return
}
if (intent == null) { //Capture Photo if no image available
if (mCM != null) {
results = arrayOf(Uri.parse(mCM))
}
} else {
val dataString = intent.dataString
if (dataString != null) {
results = arrayOf(Uri.parse(dataString))
}
}
}
}
mUMA!!.onReceiveValue(results)
mUMA = null
} else {
if (requestCode == FCR) {
if (null == mUM) return
val result =
if (intent == null || resultCode != Activity.RESULT_OK) null else intent.data
mUM!!.onReceiveValue(result)
mUM = null
}
}
}
/*needed fileds
private var mCM: String? = null
private var mUM: ValueCallback<Uri>? = null
private var mUMA: ValueCallback<Array<Uri>>? = null
private const val FCR = 1*/
It got so easy in 2022-23 with Kotlin.
class MainActivity : AppCompatActivity() {
private var fileChooserResultLauncher = createFileChooserResultLauncher()
private var fileChooserValueCallback: ValueCallback<Array<Uri>>? = null
webView.webChromeClient = object : WebChromeClient() {
override fun onShowFileChooser(webView: WebView?, filePathCallback: ValueCallback<Array<Uri>>?, fileChooserParams: FileChooserParams?): Boolean {
try {
fileChooserValueCallback = filePathCallback;
fileChooserResultLauncher.launch(fileChooserParams?.createIntent())
} catch (e: ActivityNotFoundException) {
// You may handle "No activity found to handle intent" error
}
return true
}
}
private fun createFileChooserResultLauncher(): ActivityResultLauncher<Intent> {
return registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == Activity.RESULT_OK) {
fileChooserValueCallback?.onReceiveValue(arrayOf(Uri.parse(it?.data?.dataString)));
} else {
fileChooserValueCallback?.onReceiveValue(null)
}
}
}
}