I am using the following code to check if camera & storage permissions have been granted when user clicks on input-file field on my website in android webview:
public boolean file_permission(){
if(Build.VERSION.SDK_INT >=23 && (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED)) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA}, 1);
return false;
}else{
return true;
}
}
class MyWebChromeClient extends WebChromeClient {
// Handling onshow file chooser
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
if(file_permission() && Build.VERSION.SDK_INT >= 21) {
file_path = filePathCallback;
Intent takePictureIntent = null;
takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(MainActivity.this.getPackageManager()) != null) {
File photoFile = null;
try {
photoFile = create_image();
takePictureIntent.putExtra("PhotoPath", cam_file_data);
} catch (IOException ex) {
Log.e(TAG, "Image file creation failed", ex);
}
if (photoFile != null) {
cam_file_data = "file:" + photoFile.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
} else {
cam_file_data = null;
takePictureIntent = null;
}
}
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType(file_type);
Intent[] intentArray;
intentArray = new Intent[]{takePictureIntent};
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Select Photo for yourPrint");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
startActivityForResult(chooserIntent, file_req_code);
return true;
} else {
return false;
}
}
}
When the permission is checked via file_permission(), the accept permission dialog appears. But once the user accepts the permission, the input-file field needs to be clicked again by the user to show the file chooser. I want the input-field that originally triggered the onShowFileChooser to be clicked again automatically after user accepts the permissions, so that file chooser opens automatically.
I have tried using onRequestPermissionsResult with javascript to trigger click on input field after permissions acceptance using this code javascript:jQuery('.file-input-field').trigger('click'), However, I do not want to use this way as chrome blocks programmatic clicks on input fields with type file for security purposes.
How to accomplish this?
you must reset this variable like this in java/kotlin :
filePathCallback.onReceiveValue(null)
filePathCallback = null
if filePathCallback not null onShowFileChooser will not triggered
Related
I give all files access by this method :
if (!hasPermissionToManageFiles(myContext)){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){
try {
Intent intentFiles = new Intent();
intentFiles.setAction(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
Uri uriFiles = Uri.fromParts("package", myContext.getPackageName(), null);
intentFiles.setData(uriFiles);
myContext.startActivity(intentFiles);
}
catch (Exception e){
Intent intentFiles = new Intent();
intentFiles.setAction(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
myContext.startActivity(intentFiles);
}
}
}
This line in my manifest :
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
I tried to create method to verify this kind of permission but it's not working. I don't have any error, when my app has the permission to access the intent still appears.
here is my method :
public static boolean hasPermissionToManageFiles(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
return ActivityCompat.checkSelfPermission(context, Manifest.permission.MANAGE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
}
return true;
}
According to the docs you should use Environment.isExternalStorageManager method instead of the checkSelfPermission.
I'm working on uploading file to WebView.
Local files work well. But files from google drive do not work.
URI information of local file is not empty.(not null)
Local file : content://com.android.providers.downloads.documents/document/xxx
Google Drive File : content://com.google.android.apps.docs.storage/document/xxx
This is my code.
...
takeFileResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
int resultCode = result.getResultCode();
if (resultCode == RESULT_OK) {
Intent intent = result.getData();
Fragment fragment = getSupportFragmentManager().findFragmentByTag(FileChooserDialog.class.getSimpleName());
if (fragment instanceof DialogFragment) {
((DialogFragment) fragment).dismiss();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (null == uploadValueCallBack) {
return;
}
if (intent != null) {
Uri uri = intent.getData();
intent.setData(uri);
}
uploadUris = WebChromeClient.FileChooserParams.parseResult(resultCode, intent);
uploadValueCallBack.onReceiveValue(uploadUris);
uploadValueCallBack = null;
} else if (null != uploadMessage) {
Uri resultUri = (intent == null) ? null : intent.getData();
uploadMessage.onReceiveValue(resultUri);
uploadMessage = null;
}
}
});
...
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
String[] fileMimeTypes = {
"application/pdf",
"application/zip",
"image/*",
};
i.setType("*/*");
i.putExtra(EXTRA_MIME_TYPES, fileMimeTypes);
takeFileResultLauncher.launch(Intent.createChooser(i, "File Chooser"));
...
I dont know the reason that happens.
Maybe do I have to check the webpage??
please help me... :(
I saved the google drive file locally and send that local uri to webview.
Intent intent = new Intent();
intent.setData(Uri.fromFile(savedLocalFile(uri));
WebChromeClient.FileChooserParams.parseResult(resultCode, intent);
The savedLocalFile() function returns a File stored in local(or external) storage.
If you have any better ideas, please give leave comments.
I am trying to open zoom app by passing uri to the intent with below and it works fine.
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("<zoom url>"))
val packageManager = packageManager
if (intent.resolveActivity(packageManager) != null) {
startActivity(launchApp)
}
But this shows my browser also as user can select it and open the uri I pass. what I want to do is only open zoom app with the uri.
By using val activities = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY) I can filter the apps that can open my intent but how can I select the exact app(zoom) and pass uri to it and open zoom app only with my meeting url?
May be need a packageName?
I referenced some code from Google in this
public static void openCustomTab(Activity activity,
CustomTabsIntent customTabsIntent,
Uri uri,
CustomTabFallback fallback) {
String packageName = CustomTabsHelper.getPackageNameToUse(activity);
//If we cant find a package name, it means theres no browser that supports
//Chrome Custom Tabs installed. So, we fallback to the webview
if (packageName == null) {
if (fallback != null) {
fallback.openUri(activity, uri);
}
} else {
customTabsIntent.intent.setPackage(packageName);
customTabsIntent.launchUrl(activity, uri);
}
}
it use setPackage(),then the app will open the chrome app without chooser.
This is the method of getPackageNameToUse
static final String STABLE_PACKAGE = "com.android.chrome";
static final String BETA_PACKAGE = "com.chrome.beta";
static final String DEV_PACKAGE = "com.chrome.dev";
static final String LOCAL_PACKAGE = "com.google.android.apps.chrome";
...
...
public static String getPackageNameToUse(Context context) {
if (sPackageNameToUse != null) return sPackageNameToUse;
PackageManager pm = context.getPackageManager();
// Get default VIEW intent handler.
Intent activityIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com"));
ResolveInfo defaultViewHandlerInfo = pm.resolveActivity(activityIntent, 0);
String defaultViewHandlerPackageName = null;
if (defaultViewHandlerInfo != null) {
defaultViewHandlerPackageName = defaultViewHandlerInfo.activityInfo.packageName;
}
// Get all apps that can handle VIEW intents.
List<ResolveInfo> resolvedActivityList = pm.queryIntentActivities(activityIntent, 0);
List<String> packagesSupportingCustomTabs = new ArrayList<>();
for (ResolveInfo info : resolvedActivityList) {
Intent serviceIntent = new Intent();
serviceIntent.setAction(ACTION_CUSTOM_TABS_CONNECTION);
serviceIntent.setPackage(info.activityInfo.packageName);
if (pm.resolveService(serviceIntent, 0) != null) {
packagesSupportingCustomTabs.add(info.activityInfo.packageName);
}
}
// Now packagesSupportingCustomTabs contains all apps that can handle both VIEW intents
// and service calls.
if (packagesSupportingCustomTabs.isEmpty()) {
sPackageNameToUse = null;
} else if (packagesSupportingCustomTabs.size() == 1) {
sPackageNameToUse = packagesSupportingCustomTabs.get(0);
} else if (!TextUtils.isEmpty(defaultViewHandlerPackageName)
&& !hasSpecializedHandlerIntents(context, activityIntent)
&& packagesSupportingCustomTabs.contains(defaultViewHandlerPackageName)) {
sPackageNameToUse = defaultViewHandlerPackageName;
} else if (packagesSupportingCustomTabs.contains(STABLE_PACKAGE)) {
sPackageNameToUse = STABLE_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(BETA_PACKAGE)) {
sPackageNameToUse = BETA_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(DEV_PACKAGE)) {
sPackageNameToUse = DEV_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(LOCAL_PACKAGE)) {
sPackageNameToUse = LOCAL_PACKAGE;
}
return sPackageNameToUse;
}
Maybe it will help you.
I created a button that lets the user choose between "Take picture with camera" and "Select picture from gallery".
When the picture is taken/chosen, I then display it in an ImageView of the next activity which I do by passing the URI of the file created to store the taken/selected picture.
It works as expected when the user takes a picture with his camera but when he selects an image from gallery, no image is shown in the next activity despite both intents (take a picture and select a picture) being coded the same.
My question(s): Why isn't the image displayed in the next activity ONLY when picked from the gallery ? Or how should I proceed to display it ?
Intent to open camera (working fine):
private void openCameraToTakePictureIntent() {
Log.d(TAG, "Method for Intent Camera started");
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
// Error occurred while creating the File
}
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(this,
"com.emergence.pantherapp.fileprovider", photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
}
Intent to access gallery and pick an image:
private void openGalleryIntent() {
Log.d(TAG, "Method for Intent Gallery started");
Intent galleryIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.INTERNAL_CONTENT_URI);
if (galleryIntent.resolveActivity(getPackageManager()) != null) {
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
// Error occurred while creating the File
}
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(this,
"com.emergence.pantherapp.fileprovider", photoFile);
galleryIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(galleryIntent, PICK_IMAGE);
}
}
}
Then here's the onActivityResult: (currentPhotoPath is the absolute path of the file created to store the image)
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK && requestCode == 1) {
Log.d(TAG, currentPhotoPath);
Intent intent = new Intent(this, ModifyPictureActivity.class);
intent.putExtra("USER_IMAGE", currentPhotoPath);
startActivity(intent);
} else if (resultCode == Activity.RESULT_OK && requestCode == 2) {
Log.d(TAG, currentPhotoPath);
Intent intent = new Intent(this, ModifyPictureActivity.class);
intent.putExtra("USER_IMAGE", currentPhotoPath);
startActivity(intent);
}
}
Below is how the image is displayed in the following activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_modify_picture);
Intent intent = getIntent();
String imageUri = intent.getStringExtra("USER_IMAGE");
if (imageUri != null) {
Log.d(TAG, imageUri);
} else {
Log.d(TAG, "imageUri was null");
}
image = findViewById(R.id.picture);
image.setImageURI(Uri.parse(imageUri));
}
I made sure to have the READ_EXTERNAL_STORAGE in the manifest and the xml layout is just set to "match_parent" for height and width but I can add them if it's relevant.
Few Intent actions use EXTRA_OUTPUT. Mostly, that is an ACTION_IMAGE_CAPTURE thing.
More typically, an Intent for getting a piece of content (ACTION_PICK, ACTION_GET_CONTENT, ACTION_OPEN_DOCUMENT, ACTION_CREATE_DOCUMENT, ACTION_OPEN_DOCUMENT_TREE, etc.) return a Uri from the content supplier in the Intentdelivered toonActivityResult(). Given your implementation, that would be data.getData()to get thatUri`.
You can then use a ContentResolver and openInputStream() to get an InputStream on the content identified by the Uri. In your case, for example, you could use that InputStream to copy the bytes to a FileOutputStream to make your own local copy of the content.
Note that you only have short-term access to the content identified by the Uri.
In my app I need to pick images from storage and display them on screen. I'm using an Intent to do this for me. I have posted my code to do this below.
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
try {
startActivityForResult(
Intent.createChooser(intent, "Select a File to Upload"),
FILE_SELECT_CODE);
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(this, "Please install a File Manager.",
Toast.LENGTH_SHORT).show();
}
The issue is that when the intent opens, I see a bunch of empty files of size 0 B. These files are not visible in the File Manager tool on my phone (where I'm testing my app). Is there any way to fix this - either by deleting those empty files or by having the intent ignore them?
Note: I don't know if this is helpful, but the images above are generated by my app, which uses Camera to capture and save images and later load them back through the file picker. The image with the preview is visible in the File Manager and is not 0 B, but the others are invisible.
Screenshot of file picker
Edit: Here's the code I'm using to capture images using camera and save them.
public void captureImageAction()
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_DENIED ||
checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
String[] permission = {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE};
requestPermissions(permission, PERMISSION_CODE);
} else {
openCamera();
}
} else {
openCamera();
}
}
private void openCamera() {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.TITLE, "New Picture");
values.put(MediaStore.Images.Media.DESCRIPTION, "From Camera");
image_uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, image_uri);
int IMAGE_CAPTURE_CODE = 1001;
startActivityForResult(cameraIntent, IMAGE_CAPTURE_CODE);
}