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
Related
So, I am creating an app, with the goal of turning text to pdf, and saving it to the external storage. After running the app with no errors, I encountered a weird bug, the "Manage External Storage Permission" button was not clickable. After reading the stack overflow posts, it seems like I have wrote the proper functions, but all the examples were in activities, so the problem may be there
Any help?
class CreatePdfFragment : Fragment(R.layout.fragment_create_pdf) {
private lateinit var binding: FragmentCreatePdfBinding
private val STORAGE_PERMISSION_CODE: Int = 100
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentCreatePdfBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.btnToPdf.setOnClickListener {
if (checkPermission()) {
Log.d(TAG, "onViewCreated: Permission already granted, create folder")
savePdf()
} else {
Log.d(TAG, "onViewCreated: Permission was not granted, request")
requestPermission()
}
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == STORAGE_PERMISSION_CODE) {
if (grantResults.isNotEmpty()) {
val write = grantResults[0] == PackageManager.PERMISSION_GRANTED
val read = grantResults[1] == PackageManager.PERMISSION_GRANTED
if (write && read) {
Log.d(TAG, "onRequestPermissionsResult: External Storage Permission granted")
savePdf()
} else {
Log.d(TAG, "onRequestPermissionsResult: External Storage Permission denied...")
toast("External Storage Permission denied...")
}
}
}
}
private fun savePdf() {
//create object of Document class
val mDoc = Document()
//pdf file name
val mFileName = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(System.currentTimeMillis())
//pdf file path
val mFilePath = Environment.getExternalStorageDirectory().toString() + "/" + mFileName +".pdf"
try {
//create instance of PdfWriter class
PdfWriter.getInstance(mDoc, FileOutputStream(mFilePath))
//open the document for writing
mDoc.open()
//get text from EditText i.e. textEt
val mText = binding.etPdfText.text.toString()
//add author of the document (metadata)
mDoc.addAuthor("Atif Pervaiz")
//add paragraph to the document
mDoc.add(Paragraph(mText))
//close document
mDoc.close()
//show file saved message with file name and path
toast("$mFileName.pdf\nis saved to\n$mFilePath")
}
catch (e: Exception){
//if anything goes wrong causing exception, get and show exception message
toast(e.message.toString())
}
}
private val storageActivityResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
Log.d(TAG, "storageActivityResultLauncher: ")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (Environment.isExternalStorageManager()) {
Log.d(TAG, "storageActivityResultLauncher: ")
savePdf()
} else {
Log.d(TAG, "storageActivityResultLauncher: Manage External Storage Permission is denied...")
toast("Manage External Storage Permission is denied...")
}
} else {
}
}
private fun requestPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
try {
Log.d(TAG, "requestPermission: try")
val intent = Intent()
intent.action = Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
val uri = Uri.fromParts("package", this#CreatePdfFragment.requireActivity().packageName, null)
intent.data = uri
storageActivityResultLauncher.launch(intent)
} catch (e: Exception) {
Log.d(TAG, "requestPermission: ", e)
val intent = Intent()
intent.action = Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
storageActivityResultLauncher.launch(intent)
}
} else {
ActivityCompat.requestPermissions(this#CreatePdfFragment.requireActivity(),
arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE),
STORAGE_PERMISSION_CODE)
}
}
private fun checkPermission(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Environment.isExternalStorageManager()
} else {
val write = ContextCompat.checkSelfPermission(this#CreatePdfFragment.requireActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)
val read = ContextCompat.checkSelfPermission(this#CreatePdfFragment.requireActivity(), Manifest.permission.READ_EXTERNAL_STORAGE)
write == PackageManager.PERMISSION_GRANTED && read == PackageManager.PERMISSION_GRANTED
}
}
private fun toast(message: String) {
Toast.makeText(this#CreatePdfFragment.requireActivity(), message, Toast.LENGTH_SHORT).show()
}
}
Please ignore, the permission below was missing
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
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 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)
}
}
I have kept an html file on a website.
https://pinapakait.com/cam/testcam.html
It has following simple input type file code
input type="file" accept="image/*;capture=camera"
It is opening files, camera options when opened in mobile through web browser. But now opening in web view of android.
Your help is very much appreciated.
Gave following permissions
uses-permission android:name="android.permission.INTERNET"
uses-permission android:name="android.permission.CAMERA"
uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
uses-feature android:name="android.hardware.camera" android:required="true"
uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"
uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE"
uses-permission android:name="android.permission.READ_INTERNAL_STORAGE"
set following settings for web view.
setLoadsImagesAutomatically(true)
setJavaScriptEnabled(true)
setDomStorageEnabled(true)
setLoadWithOverviewMode(true)
setPluginState(WebSettings.PluginState.ON)
setMediaPlaybackRequiresUserGesture(false)
setPluginState(WebSettings.PluginState.ON)
setAppCacheEnabled(true)
setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY)
setAllowFileAccessFromFileURLs(true)
setAllowUniversalAccessFromFileURLs(true)
setJavaScriptCanOpenWindowsAutomatically(true)
setBuiltInZoomControls(true)
setPluginsEnabled(true)
setAllowFileAccess(true)
setAllowContentAccess(true)
setSupportZoom(true)
I had same problem with webview and after googling it I found this solution by extending WebChromeClient .
public class MyWebChromeClient extends WebChromeClient {
DetailsFragmentWebView context;
public ValueCallback<Uri> mUploadMessage;
public ValueCallback<Uri[]> mUploadMessageAboveL;
public Uri mCapturedImageURI;
public MyWebChromeClient(DetailsFragmentWebView context){
this.context = context;
//this.mCapturedImageURI = mCapturedImageURI;
//this.mUploadMessage = mUploadMessage;
//this.mUploadMessageAboveL = mUploadMessageAboveL;
}
// openFileChooser for Android 3.0+
public void openFileChooser(ValueCallback<Uri> uploadMsg, ValueCallback<Uri[]> uploadMsgAboveL, String acceptType){
// Update message
mUploadMessage = uploadMsg;
mUploadMessageAboveL = uploadMsgAboveL;
try{
// Create AndroidExampleFolder at sdcard
File imageStorageDir = new File(
Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES)
, "AndroidExampleFolder");
if (!imageStorageDir.exists()) {
// Create AndroidExampleFolder at sdcard
imageStorageDir.mkdirs();
}
// Create camera captured image file path and name
File file = new File(
imageStorageDir + File.separator + "IMG_"
+ String.valueOf(System.currentTimeMillis())
+ ".jpg");
mCapturedImageURI = Uri.fromFile(file);
// Camera capture image intent
final Intent captureIntent = new Intent(
android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mCapturedImageURI);
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
// Create file chooser intent
Intent chooserIntent = Intent.createChooser(i, "Image Chooser");
// Set camera intent to file chooser
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS
, new Parcelable[] { captureIntent });
// On select image call onActivityResult method of activity
context.startActivityForResult(chooserIntent, FILECHOOSER_RESULTCODE);
}
catch(Exception e){
Toast.makeText(context.getActivity(), "Chrome Exception:"+e,
Toast.LENGTH_LONG).show();
}
}
//openFileChooser for other Android versions
public void openFileChooser(ValueCallback<Uri> uploadMsg,
String acceptType,
String capture) {
openFileChooser(uploadMsg , mUploadMessageAboveL, acceptType);
}
#Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
openFileChooser(null ,filePathCallback , "");
return true;
}
}
And set your webview to use this class :
MyWebChromeClient myChromeClient = new myChromeClient(this);
webView.webChromeClient = myChromeClient;
Also you need to add this code to your Activity :
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
//super.onActivityResult(requestCode, resultCode, data)
if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == myChromeClient.mUploadMessage && null == myChromeClient.mUploadMessageAboveL) return
val result = if (resultCode != RESULT_OK) null
else if (data!=null) data.data
else myChromeClient.mCapturedImageURI
if (myChromeClient.mUploadMessageAboveL != null) {
onActivityResultAboveL(requestCode, resultCode, data)
} else if (myChromeClient.mUploadMessage != null) {
myChromeClient.mUploadMessage?.onReceiveValue(result)
myChromeClient.mUploadMessage = null
}
}
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private fun onActivityResultAboveL(requestCode: Int, resultCode: Int, intent: Intent?) {
if (requestCode != FILECHOOSER_RESULTCODE || myChromeClient.mUploadMessageAboveL == null)
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) {
results = Array<Uri>(clipData.itemCount) { _->return Unit}
for (i in 0 until clipData.itemCount) {
val item = clipData.getItemAt(i)
results[i] = item.uri
}
}
if (dataString != null)
results = arrayOf(Uri.parse(dataString))
}else if (myChromeClient.mCapturedImageURI!=null)
results = arrayOf(myChromeClient.mCapturedImageURI)
}
myChromeClient.mUploadMessageAboveL?.onReceiveValue(results)
myChromeClient.mUploadMessageAboveL = null
}
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)
}
}
}
}