AndroidStudio getExternalStoragePublicDirectory in API 29 - java

In API 29, getExternalStoragePublicDirectory was deprecated so that I have to find way to convert the following code to API 29
String pathSave = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
+ new StringBuilder("/GroupProjectRecord_")
.append(new SimpleDateFormat("dd-MM-yyyy-hh_mm_ss")
.format(new Date())).append(".3gp").toString();
Thanks for your help!

As mentioned in android docs
Apps can continue to access content stored on shared/external storage
by migrating to alternatives such as
Context#getExternalFilesDir(String)
Try with this method.
public void getFilePath(Context context){
String path = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
+ new StringBuilder("/GroupProjectRecord_")
.append(new SimpleDateFormat("dd-MM-yyyy-hh_mm_ss")
.format(new Date())).append(".3gp").toString();
Log.d(TAG, "getFilePath: "+path);
}

In API 29 and above doing anything with Paths outside of the apps private storage won't work.
See https://developer.android.com/training/data-storage/files/external-scoped for details
So to save you need to do something like:-
// OnClick Save Button
public void Save(View view){
// Ask for a new filename
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
// Restrict to openable items
intent.addCategory(Intent.CATEGORY_OPENABLE);
// Set mimeType
intent.setType("text/plain");
// Suggest a filename
intent.putExtra(Intent.EXTRA_TITLE, "text.txt");
// Start SAF file chooser
startActivityForResult(intent, 1);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent resultData) {
super.onActivityResult(requestCode, resultCode, resultData);
if (requestCode == 1 && resultCode == RESULT_OK) {
Log.d("SAF", "Result code 1");
if (resultData != null) {
Uri uri = resultData.getData();
Log.d("SAF", uri.toString());
// Now write the file
try {
ParcelFileDescriptor pfd =
this.getContentResolver().
openFileDescriptor(uri, "w");
// Get a Java FileDescriptor to pass to Java IO operations
FileDescriptor fileDescriptor = pfd.getFileDescriptor();
// Read Input stream
FileOutputStream fileOutputStream =
new FileOutputStream(fileDescriptor);
// .....
} catch (Exception e){
// Do something with Exceptions
}
} else {
Log.d("SAF", "No Result");
}
}
}

Related

java.io.FileNotFoundException: /sdcard/...: open failed: EISDIR (Is a directory)

hello I'm trying to upload a file to my application and on some devices for uploading from sdcard it gives me an error:java.io.FileNotFoundException: (path in sdcard): open failed: EISDIR (Is a directory). anybody have any idea why?? My code:
browsing file and opening file manager:
private void doBrowseFile() {
Intent chooseFileIntent = new Intent(Intent.ACTION_GET_CONTENT);
chooseFileIntent.setType("application/pdf");
// Only return URIs that can be opened with ContentResolver
chooseFileIntent.addCategory(Intent.CATEGORY_OPENABLE);
chooseFileIntent = Intent.createChooser(chooseFileIntent, "Choose a file");
startActivityForResult(chooseFileIntent, UNIQUE_REQUEST_CODE);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == UNIQUE_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
if (data != null) {
Uri fileUri = data.getData();
Log.i(LOG_TAG, "Uri: " + fileUri);
String filePath = null;
try {
filePath = FileUtils.getPath(this, fileUri);
} catch (Exception e) {
Log.e(LOG_TAG, "Error: " + e);
Toast.makeText(this, "Error: " + e, Toast.LENGTH_SHORT).show();
}
getBase64FromPath(filePath);
}
}
}
super.onActivityResult(requestCode, resultCode, data);
}
Encoding file from file path:
#RequiresApi(api = Build.VERSION_CODES.O)
public void getBase64FromPath(String path) {
String base64 = "";
try {
File file = new File(path);
byte[] buffer = new byte[(int) file.length() + 100];
file.getParentFile().mkdirs();
FileInputStream fileInputStream = new FileInputStream(file); //THIS LINE GIVES ME ERROR
#SuppressWarnings("resource")
int length = fileInputStream.read(buffer);
base64 = Base64.encodeToString(buffer, 0, length,
Base64.DEFAULT);
uploadFile(base64);
} catch (IOException e) {
Toast.makeText(this, "error:" + e.getMessage() , Toast.LENGTH_SHORT).show();
}
}
If anybody know any idea why please tell. It gives this error on some devices only. on the others it works perfectly fine. thanks.
Get rid of FileUtils.getPath(), as it will never work reliably.
You can get an InputStream on your content by calling getContentResolver().openInputStream(fileUri). You can then use that InputStream instead of the FileInputStream to read in your content.
Note that your app will crash with an OutOfMemoryError with large PDFs. I strongly recommend that you find some way to upload your content without reading it all into memory at once. For example, you might consider not converting it to base-64.

How to write multiples files to user accessible directories on an Android device?

In my Android Studio project I want to implement a function, where 3 files should get exported. So I want the user to choose a directory and enter the name for a new directory, in which the files are going to be stored.
Right now, I already have an intent which lets the user choose, where to place the new folder and how to name the new folder:
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType(DocumentsContract.Document.MIME_TYPE_DIR);
intent.putExtra(Intent.EXTRA_TITLE, getString(R.string.folder_backup));
startActivityForResult(intent, REQUEST_CODE_SAVE_BACKUP);
In the onActivityResult method, I tried to save the 3 files. Here is the code:
#Override
public void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_SAVE_BACKUP
&& resultCode == Activity.RESULT_OK) {
Uri uri = null;
if (data != null) {
uri = data.getData();
String dbFileName = ExampleDatabase.DB_NAME;
List<String> dbComponentsNames = new ArrayList<>();
dbComponentsNames.add(dbFileName);
dbComponentsNames.add(dbFileName + "-shm");
dbComponentsNames.add(dbFileName + "-wal");
try {
for (int i = 0; i < dbComponentsNames.size(); i++) {
uri = Uri.parse(uri.toString() + "%2F" + dbComponentsNames.get(i));
File dbComponent = getActivity().getDatabasePath(dbComponentsNames.get(i));
byte[] byteArray = new byte[(int) dbComponent.length()];
FileInputStream fileInputStream = new FileInputStream(dbComponent);
fileInputStream.read(byteArray);
ParcelFileDescriptor pfd = getActivity().getContentResolver().openFileDescriptor(uri, "w");
FileOutputStream fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());
fileOutputStream.write(byteArray);
pfd.close();
}
} catch (Exception e) {
}
}
}
}
My idea was to get the URI from the created folder and just append the file names to that URI, so these files get stored in the newly created folder. I also found out, that a \ in the URI is replaced by %2F but this doesn't matter. Does anyone know, how to achieve saving multiple files without using MediaStore?

Convert an internal Uri to a File in Android?

I have an app that allows the user to select a file from the file chooser. The problem lies when I try to turn that Uri to a File, it creates something that I can't use (/document/raw:/storage/emulated/0/Download/CBTJourney-Backup/EntriesBackup1570487830108) I would like to get rid of everything before raw: but the right way. Where ever I try to copy from that file using InputStream, it doesn't copy anything. It's like the file doesn't exist. Any ideas?
public void chooseDatabaseFile() {
Intent intent = new Intent();
intent.addCategory(Intent.CATEGORY_OPENABLE);
// Set your required file type
intent.setType("*/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Choose Database to Import"),GET_FILE_PATH);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == GET_FILE_PATH && data != null) {
if(resultCode == RESULT_OK){
Uri currFileURI = data.getData();
if(currFileURI == null) {
return;
}
else{
String databasePath = currFileURI.getPath();
// TODO: Determine if actual database
importDatabase(new File(databasePath));
// File produces "/document/raw:/storage/emulated/0/Download/CBTJourney-Backup/EntriesBackup1570487830108"
}
}
}
}
Have you tried changing your mimetype?
Otherwise take a look at:
Convert file: Uri to File in Android

Sending images between 2 users using parse and sinch

I want to be able to send a picture from one user to another in a messaging app like on whatsApp, but I am not sure how to do that. I am using android and parse as my DB. I tried googling and nothing seems to help, I am new on Android development. I would prefer to use it as I do with my texts , since when sending messages between users I am using parse as my database. Can someone please assist, I am able to select the image from galery and load it in an image view but I am not sure how to send it as I would with text. The code that should be under when the button "send" is clicked.
Below is the code that I have. Please have a look at it. I have been trying everything that I can think of but I am not getting anywhere.
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
//Everything Okay
if (requestCode == LOAD_IMAGE_RESULTS) {
Uri pickedImage = data.getData();
InputStream inputStream;
try {
inputStream = getContentResolver().openInputStream(pickedImage);
Bitmap selectImage = BitmapFactory.decodeStream(inputStream);
sendPicture.setImageBitmap(selectImage);
selectImage = ((BitmapDrawable) sendPicture.getDrawable()).getBitmap();
ByteArrayOutputStream stream = new ByteArrayOutputStream();
selectImage.compress(Bitmap.CompressFormat.PNG, 5, stream);
byte[] imageRec = stream.toByteArray();
final ParseObject imgMsgToBeSent = new ParseObject("SentImages");
final ParseFile fileRenamed;
//create parse file
fileRenamed = new ParseFile("SentImage.png", imageRec);
imgMsgToBeSent.put("receipientId", MessagingActivity.recipientId.toString());
imgMsgToBeSent.put("senderId", MessagingActivity.currentUserId.toString());
imgMsgToBeSent.put("imageReceived", fileRenamed);
sendImgBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (v.getId() == R.id.sendImageBtn) {
messageService.sendMessage(MessagingActivity.recipientId.toString(), fileRenamed.toString());
finish();
}
}
});
} catch (FileNotFoundException e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(),
"Unable to load image",
Toast.LENGTH_LONG).show();
}
}
}
}
For Sharing Image after selected from Gallery ##
First get the image path and than send it via intent like this:
Intent shareIntent = new Intent(Intent.ACTION_SEND);
Uri Imageuri = Uri.fromFile(new File(image));
shareIntent.setType("image/");
shareIntent.putExtra(Intent.EXTRA_STREAM, Imageuri);
startActivity(Intent.createChooser(shareIntent, "ShareWia"));
for sharing image and text both just add one more settype and use put extra with text like this:
Intent shareIntent = new Intent(Intent.ACTION_SEND);
Uri Imageuri = Uri.fromFile(new File(image));
shareIntent.setType("image/");
shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_TEXT, text);
shareIntent.putExtra(Intent.EXTRA_STREAM, Imageuri);
startActivity(Intent.createChooser(shareIntent, "ShareWia"));
Sinch does not support attachments in IM

Take picture from Camera Intent and overlay another small image on it

I have an app that right now opens up the camera at the click of a button. Then the user can take the picture with the standard camera app which leads to a Cancel/Save option. If cancel if chosen, the picture can be taken again. If the Save option is chosen the image is saved to the Gallery. I want to add some stuff to the image in this Cancel/Save mode before either are clicked.
I think the best way to do this would be to bring the photo into my app and do my modifications there and save with a button. I have no idea how to do this. I know I have to use the onActivityResult function, but thats about it.
Any advice is appreciated.
When you launch the IMAGE_CAPTURE intent to let the user take a photo, you should pass as parameter the path where you store the image.
First you should save the path of the taken picture, then, when the user come back to your activity, manage the bitmap and combine with other elements.
camera.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
String storageState = Environment.getExternalStorageState();
if (storageState.equals(Environment.MEDIA_MOUNTED)) {
long time = System.currentTimeMillis();
File root = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
File dir = new File(root.getAbsolutePath() + "/Camera");
if (dir.exists() == false) {
dir.mkdirs();
}
String path = dir.getAbsolutePath() + File.separatorChar
+ time + ".jpg";
filesaved = new File(path);
try {
if (filesaved.exists() == false) {
filesaved.getParentFile().mkdirs();
filesaved.createNewFile();
}
} catch (IOException e) {
Toast.makeText(
context,
"Unable to create external file"
+ storageState, Toast.LENGTH_LONG).show();
return;
}
uritopass = Uri.fromFile(filesaved);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uritopass);
startActivityForResult(intent, TAKE_PICTURE);
} else {
Toast.makeText(
context,
"External Storeage (SD Card) is required.\n\nCurrent state: "
+ storageState, Toast.LENGTH_LONG).show();
}
}
});
...
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
if (requestCode == TAKE_PICTURE) {
//user took a photo
File imageFile = new File(filesaved.toString());
Bitmap bm = decodeFile(imageFile);
if (bm != null) {
bm = combineImages(bm);
img.setImageBitmap(bm);
}
}
}
}
...
decodefile method to load the Bitmap from the original file Here.
combineImages method to combine 2 or more Bitmap Here .

Categories

Resources