Good day everyone,
I would like to ask a few questions, if I may.
I have an app that works on pre-Android 10 phones, but I see that various code changes have been made when accessing external directories. I don't have access to an Android 10 phone, so I have come up with the following code after perusal of many pages on Stackoverflow. I don't know of a way to test it myself, so could someone please kindly let me know what they think of this code:
String imageFileName;
String timestamp; // creation of the value omitted here
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
{
imageFileName = getApplicationContext().getExternalFilesDir(null).getAbsolutePath() + "/" + timeStamp + ".jpg";
}
else
{
imageFileName = Environment.getExternalStorageDirectory()+ "/" + timeStamp + ".jpg";
}
OutputStream fout = null; // Now write out the image to external directory
File imageFile = new File(imageFileName);
try {
fout = new FileOutputStream(imageFile);
bitmap.compress(Bitmap.CompressFormat.JPEG, 95, fout);
fout.flush();
fout.close();
} catch (Exception e) {
// do stuff
}
// Now, "update" the directory using MediaScannerConnection.scanFile to refresh
// its contents when viewed on the device
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
{
MediaScannerConnection.scanFile(activityReference.get(), new String[]{getApplicationContext().getExternalFilesDir(null).getAbsolutePath().toString()}, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri)
{
}
});
}
else // Not Android 10
{
MediaScannerConnection.scanFile(activityReference.get(), new String[]{Environment.getExternalStorageDirectory().toString()}, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri)
{
}
});
}
The parts above that don't explicitly reference Android 10 work fine
The other questions are about permissions to access the filestore, which I would like to read/write to. I have these declared in the Manifest and currently my code, for pre-Android 10 is
if( (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
|| (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED))
{
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, PERMISSION_ALL);
}
if(ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED)
{
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, PERMISSION_ALL);
}
I am loading an image file and, again pre-Android 10, I have
findbutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(intent, 7);
}
});
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
switch(requestCode){
case 7:
if(resultCode==RESULT_OK){
Uri PathHolder = data.getData();
try
{
img = BitmapFactory.decodeStream(getContentResolver().openInputStream(PathHolder));
}
catch(Exception e)
{
//
}
// Do more image related stuff here
}
break;
}
}
Is there anything here that needs to have an "if..then" statement to account for file access in Android 10?
Thanks in advance for any help given.
Related
In devices below android 10 we can access any file like this::
File f = new File("storage/emulated/0/filename.txt");
But I want to do to same on android 10+ devices which are using something like scoped storage or mediastore class that I didn't understood in android studio I don't know exactly how to do it I want to access files any directories not only public directories like "Pictures" please help me
File f = new File("storage/emulated/0/file.jpg");
To get all the files of folder xyz (storage/emulated/0/xyz) below android 10:
File xyzFolder = new File(Environment.getExternalStorageDirectory() + File.separator + "xyz");
File[] allFiles = xyzFolder.listFiles();
But for android 10 and above either use declare MANAGE_EXTERNAL_STORAGE (not recommended if it's not some kind of file manager app) or you can use below method:
1). First take folder permission
uriMain = Uri.parse("content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fmedia/document/primary%3Axyz");
private final int REQUEST_CODE = 100;
List<Object> filesList = new ArrayList<>();
private void aboveQFolderPermission() {
try {
Intent createOpenDocumentTreeIntent = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
createOpenDocumentTreeIntent = ((StorageManager) getSystemService(STORAGE_SERVICE)).getPrimaryStorageVolume().createOpenDocumentTreeIntent();
}
assert createOpenDocumentTreeIntent != null;
String replace = createOpenDocumentTreeIntent.getParcelableExtra("android.provider.extra.INITIAL_URI").toString().replace("/root/", "/document/");
createOpenDocumentTreeIntent.putExtra("android.provider.extra.INITIAL_URI", Uri.parse(replace + "%3A" + "xyz"));
startActivityForResult(createOpenDocumentTreeIntent, REQUEST_CODE);
} catch (Exception e) {
e.printStackTrace()
}
}
Now in onActivityResult(),
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE) {
if (data != null) {
getContentResolver().takePersistableUriPermission(data.getData(), Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
//save shared preference to check whether app specific folder permission granted or not
sharedPreferences = getSharedPreferences("tree", MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("treeUriString", String.valueOf(uriMain));
editor.apply();
new Thread(() -> {
getListAboveQ(uriMain);
handler. Post(() -> /*Some code here as per your need*/);
}).start();
}
}
}
2). Now get the List of files:
private void getListAboveQ(uriMain) {
ContentResolver contentResolver = getContext().getContentResolver();
Uri buildChildDocumentsUriUsingTree = DocumentsContract.buildChildDocumentsUriUsingTree(uriMain, DocumentsContract.getDocumentId(uriMain));
try (Cursor cursor = contentResolver.query(buildChildDocumentsUriUsingTree, new String[]{"document_id"}, null, null, null)) {
while (cursor.moveToNext()) {
filesList.add(DocumentsContract.buildDocumentUriUsingTree(uriMain, cursor.getString(0)));
}
} catch (Exception e) {
e.printStackTrace();
}
}
Edit:- You can use saved preferences like:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
sharedPreferences = getSharedPreferences("tree", Context.MODE_PRIVATE);
String uriString = sharedPreferences.getString("treeUriString", "");
if (uriString.matches("")) {
//ask for folder permission
aboveQFolderPermission()
} else {
//Permission is already granted
//TODO: Whatever you want
}
}
You'll need to request MANAGE_EXTERNAL_STORAGE permission. With that permission, you can access shared storage files with the File API.
Reference:
https://developer.android.com/training/data-storage/manage-all-files#all-files-access
my app is working fine below the 10 version but android 11 and higher versions do not support external storage. my picker is not picking any document file. but after giving manage_external_storage permission in manifest google play store did not approve my app. please help if you know any alternate solution for all file access permission.
Try like below. Create global variable as needed or refactor as per your need. This is tested upto Android 11.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
private void OpenCamera() {
try {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File photoFile = null;
try {
photoFile = createFile();
} catch (Exception ex) {
// Error occurred while creating the File
}
// Continue only if the File was successfully created
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(getActivity().getApplicationContext(), getActivity().getApplicationContext().getPackageName() + ".fileprovider", photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, 3);
}
// }
} catch (Exception e) {
}
}
private File createFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = getActivity().getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(imageFileName, /* prefix */".jpg", /* suffix */storageDir /* directory */);
// Save a file: path for use with ACTION_VIEW intents
ImageLoc = image.getAbsolutePath();
return image;
}
// Open Gallery
private void OpenGallery() {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Select Image"), 1);
}
// onActivityResult handles gallery pics, camera pics and pdf.
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 1) {
if (resultCode == Activity.RESULT_OK) {
if (data.getData() != null) {
try {
Uri imageUri = data.getData();
File file = new File(Path_from_Uri.getPath(getActivity(), imageUri));
} catch (Exception e) {
Toast.makeText(getActivity(), "Please Select Image from Gallery", Toast.LENGTH_LONG).show();
}
}
}
} else if (requestCode == 3) {
try {
File file = new File(ImageLoc);
} catch (Exception e) {
Log.d("TAG", e.toString());
}
}
}
I tried to delete the image file from the gallery, but it won't. Image files are output normally, and sharing functions are done. It can't write and delete in my App. Files are deleted from the default app.
I tried to delete it use File class and ContentResolver. but file has not been deleted.
Android targetSdkVersion is 26 and compileSdkVersion is 28.
Manifest
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
I get SDcard path from getSDcardPath()
public String getSDcardPath(Context context) {
File[] storage = ContextCompat.getExternalFileDirs(context, null);
if(storage.length > 1 && storage[0] != null && storage[1] != null)
return storage[1].toString();
else
return "";
}
File Class code Used
public void useFileClass() {
File mFile = new File("file Parent + file NAME");
if (mFile.exists()) {
mFile.delete();
}
}
ContentResolver code Used
public void useContentResolver(Context context, File mFile) {
ContentResolver contentResolver = context.getContentResolver();
Uri mUri = getUri(context, mFile);
contentResolver.delete(mUri, null, null);
}
public Uri getUri(Context context, File mFile) {
Uri mUri;
mUri = FileProvider.getUriForFile(context, "MyApplication", mFile);
return mUri;
}
share code
public void shareImage() {
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.setType("image/*");
shareIntent.putExtra(Intent.EXTRA_STREAM, getUri(this, mFile));
startActivity(Intent.createChooser(shareIntent, "Share image too..."));
}
You need request Permission before access into storage. Try this:
private static final int MY_WRITE_STORAGE_PERMISSION_CODE = 200;
private void checkPermission() {
if (ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
MY_WRITE_STORAGE_PERMISSION_CODE);
} else {
// Todo (Add, Delete, Edit, ...)
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions,
#NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode == MY_WRITE_STORAGE_PERMISSION_CODE)
{
if (ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
// Todo (Add, Delete, Edit, ...)
} else {
// Permission Deny
}
}
}
Hope this help you.
I have 8 edittext fields in my android app, I enter the data manually into the same and at the end of the field there is so called "Save" button
What I want is when I click on the save button, a pdf file should be generated with all the values that I have filled in the edittext. Any help is appreciated.. Thanks
You can use itext7 library to do just that. But do look at their licensing as they mention you may need to buy license as soon as you are using it for commercial purpose.
I actually used itext5 which seems deprecated now. But the code should not be much different.
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mContentEditText.getText().toString().isEmpty()){
mContentEditText.setError("Body is empty");
mContentEditText.requestFocus();
return;
}
try {
createPdfWrapper();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
}
}
});
The createPdfWrapper() method definition is as follows:
private void createPdfWrapper() throws FileNotFoundException,DocumentException{
int hasWriteStoragePermission = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (hasWriteStoragePermission != PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!shouldShowRequestPermissionRationale(Manifest.permission.WRITE_CONTACTS)) {
showMessageOKCancel("You need to allow access to Storage",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_CODE_ASK_PERMISSIONS);
}
}
});
return;
}
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_CODE_ASK_PERMISSIONS);
}
return;
}else {
createPdf();
}
As you can see you will also need WRITE_EXTERNAL_STORAGE permission which should be declared in AndroidManifest.xml .
The permission result can be handled in onRequestPermissionsResult as:
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case REQUEST_CODE_ASK_PERMISSIONS:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission Granted
try {
createPdfWrapper();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
}
} else {
// Permission Denied
Toast.makeText(this, "WRITE_EXTERNAL Permission Denied", Toast.LENGTH_SHORT)
.show();
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
The other functions used here have the following definitions:
private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
new AlertDialog.Builder(this)
.setMessage(message)
.setPositiveButton("OK", okListener)
.setNegativeButton("Cancel", null)
.create()
.show();
}
private void createPdf() throws FileNotFoundException, DocumentException {
File docsFolder = new File(Environment.getExternalStorageDirectory() + "/Documents");
if (!docsFolder.exists()) {
docsFolder.mkdir();
Log.i(TAG, "Created a new directory for PDF");
}
pdfFile = new File(docsFolder.getAbsolutePath(),"HelloWorld.pdf");
OutputStream output = new FileOutputStream(pdfFile);
Document document = new Document();
PdfWriter.getInstance(document, output);
document.open();
document.add(new Paragraph(mContentEditText.getText().toString()));
document.close();
previewPdf();
}
private void previewPdf() {
PackageManager packageManager = getPackageManager();
Intent testIntent = new Intent(Intent.ACTION_VIEW);
testIntent.setType("application/pdf");
List list = packageManager.queryIntentActivities(testIntent, PackageManager.MATCH_DEFAULT_ONLY);
if (list.size() > 0) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
//Uri uri = Uri.fromFile(pdfFile); //This did not work, SO created a GenericFileProvider, register in xml, created provider_paths.xml
Uri uri = FileProvider.getUriForFile(getApplicationContext(),
getApplicationContext().getPackageName()
+ ".my.package.name.provider", pdfFile);
intent.setDataAndType(uri, "application/pdf");
startActivity(intent);
}else{
Toast.makeText(this,"Download a PDF Viewer to see the generated PDF",Toast.LENGTH_SHORT).show();
}
}
The main thing to look for here is the createPdf() method This is where I have created a PDF file with text from my EditText.
If you wish to have a look at the full code that I did you can visit my github respository here.
Good Luck!!!
I wanted to save a picture from drawable. The application works, but only for android versions below API 22. In newer versions, the image is not saved in gallery.
My code:
public class MainActivity extends AppCompatActivity {
Bitmap anImage;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Drawable myDrawable = getResources().getDrawable(R.drawable.tapeta1, null);
anImage = ((BitmapDrawable) myDrawable).getBitmap();
}
public void addImageToGallery(View view) {
int e = view.getId();
switch (e) {
case R.id.button:
saveImageToExternalStorage(anImage);
Toast.makeText(getApplicationContext(), "Saved successfully, Check gallery", Toast.LENGTH_SHORT).show();
break;
}
}
private void saveImageToExternalStorage(Bitmap finalBitmap) {
String root = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString();
File myDir = new File(root + "/saved_images_1");
myDir.mkdirs();
Random generator = new Random();
int n = 10000;
n = generator.nextInt(n);
String fname = "Image-" + n + ".jpg";
File file = new File(myDir, fname);
if (file.exists())
file.delete();
try {
FileOutputStream out = new FileOutputStream(file);
finalBitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
MediaScannerConnection.scanFile(this, new String[]{file.toString()}, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.i("ExternalStorage", "Scanned " + path + ":");
Log.i("ExternalStorage", "-> uri=" + uri);
}
});
}
}
Check if you have granted the storage permission to your app from
Settings -> Apps -> Your App Name ->Permissions
If this solution fixes your issue then please read - https://developer.android.com/training/permissions/requesting.html
Before you anthing else on oncreate method call requestpermissions by passing write external storage permission . This will show user to grant permission . You can override onpermissionsresult in the activity to see the result of permissions if granted you can go ahead with saving the file . If you dont want all that then save in internal storage . getfilesdir method will get you the path to app's internal directory and will not need runtime permissions .
You need to request RunTime permission from app.
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}