I am creating a sharedpreferences file in my android code. I then want to email that file in my code. For that I need to access the path of the sharedpreferences file. The code I am using is below. But it does not seem to work. I am able to open the email but there is no attachment there since I guess it could not get the file. Can someone suggest me any solution here.
File f = getDatabasePath("userPrefsFile.xml");
String filelocation=f.getAbsolutePath();
Intent email = new Intent(Intent.ACTION_SEND);
email.setType("application/xml");
String[] to = {"test#test.com"};
email.putExtra(Intent.EXTRA_EMAIL, to);
email.putExtra(Intent.EXTRA_STREAM,filelocation);
email.putExtra(Intent.EXTRA_SUBJECT,"test file send");
startActivity(Intent.createChooser(email, "Send email"));
So SharedPreferences files are located at directory
/data/data/your.package/shared_prefs
So you need to use path above.
Pseudo-code:
File root = new File("/data/data/your.package/shared_prefs");
if (root.isDirectory()) {
for (File child: root.listFiles()) {
Toast.makeText(this, child.getPath(), Toast.LENGTH_SHORT).show();
}
}
Reason why you cannot use getDatabasePath() is that returns databases folder 1
/data/data/your.package/databases/
1 Same issue is also related to getFileStreamPath() method that returns
/data/data/your.package/files
Related
I've been trying to figure out a way to open the Android File Manager directly in my app's Documents directory so the user can select a JSON file among several without requiring the user to go search for the file path /storage/emulated/0/Android/data/com.company.app/files/Documents/. So far, I can make the "go find it yourself" tactic work, but not the "take the user to the directory for them" approach. Here's what I've tried:
// this is the "go find it yourself" approach that I've used:
String filename = this.getResources().getString(R.string.ExportImportFileNameString);
File directory = this.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);
Uri dirPathUri = Uri.fromFile(directory);
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
Intent.createChooser(intent, "Open in...");
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, directory);
startActivityForResult(intent, IMPORT_REQUEST);
When my onActivityComplete handler is called for IMPORT_REQUEST I see the returned data looks like dat=content://com.lge.filemanager.FileProvider/storage/emulated/0/Android/data/com.company.app/files/Documents/SelectedFile.json flg=0x1 }
I've tried to invoke two different combinations of intent.setDataAndType instead of intent.setTypefollowing and that fails to let me select anything:
// This setDataAndType setup does not allow the user to open File Manager, nor navigate to the app Documents:
intent.setDataAndType(dirPath2, "application/json");
// This allows opening of File Manager but returns immediately without allowing the user to select a file, and returns a null data pointer:
intent.setDataAndType(dirPath2, "*/*");
Note that I've tried creating the intent object with ACTION_OPEN_DOCUMENT, ACTION_GET_CONTENT, and ACTION_VIEW with the similar result.
If I only have one file, I know I can have the app simply open a stream reader for a known file name as such:
File directory = this.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);
File importFile = new File(directory, filename);
try
{
fis = new FileInputStream (importFile);
InputStreamReader inputStreamReader =
new InputStreamReader(fis, StandardCharsets.UTF_8);
...
However, that doesn't allow me the flexibility that I desire to allow a user to select from multiple files. Can anyone illuminate what's going on here and how to correct the situation.
Based on Commonsware feedback, here's what I have for the solution to this question:
//--------------
// Get the documents location for this app
File directory = this.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);
File desiredFilePath = new File(directory.toString());
try{
// read all pathnames for files and directory
File[] allFilesInDirectory = desiredFilePath.listFiles();
// prepare array to place all path strings into
ArrayList<String> filesInDirectoryArray = new ArrayList<String>();
// retrieve each pathname file array
// but someone could simply use the "path" object instead
for(File path:allFilesInDirectory){
if (path != null){
// put all file paths into string array
filesInDirectoryArray.add(path.getPath());
// could discriminate based on file extension, if so desired
}
}
// send file path array for processing
ProcessDocuments(filesInDirectoryArray);
}catch(SecurityException e){
e.printStackTrace();
}
I really need your help because I'm stuck <.<
I have already tryed all solutions I've found here in "stackoverflow" for this problem, but any of them worked for me..
I have updated my application to follow the new Google Play Store policies, so actually my app is using only "Scoped Storage" without "Shared Storage" behaviour, so I've removed from the manifest the "requestLegacyExternalStorage".
In my app I need to send to the server some file selected by the user, these file can be stored in the "internal storage" or in the "external storage". They are always stored outside of the app-specific directory, so the directory in:
"/storage/emulated/0/Android/data/[APP_PACKAGE]/files"
My biggest problem is that I can't open any file stored outside of the app-specific directory!
I really need to open the files that the user has selected and convert its content to "base64" to send it to the server.
But when I use this method to get the file content encoded in base64:
public static String fileToBase64(#NonNull String filePath) {
String ret = null;
byte[] buffer = new byte[8192];
int bytesRead;
try(ByteArrayOutputStream bAOS = new ByteArrayOutputStream();
Base64OutputStream b64OS = new Base64OutputStream(bAOS, Base64.DEFAULT);
InputStream iS = new FileInputStream(filePath)){
while ((bytesRead = iS.read(buffer)) != -0x1) {
b64OS.write(buffer, 0x0, bytesRead);
}
b64OS.flush();
ret = bAOS.toString();
} catch (IOException ioE) {
Logger.onException(TAG, ioE);
}
return ret;
}
I always get an ACCESS PERMISSION DENIED.
Is there any way to solve this problem without using "requestLegacyStorage" in the manifest?
I know that Google will remove all applications that don't use only "SCOPED STORAGE" from the store starting from 5 of july, so I can't use the "reqestLegacyStorage" to solve this problem...
I'm already requesting and giving the "READ_EXTERNAL_STORAGE" and "WRITE_EXTERNAL_STORAGE" permissions, but I still can't open the file content if it is stored outside of the dedicated application directory...
I can only open and encode file content if it is in the app directory (so: "/storage/emulated/0/Android/data/[APP_PACKAGE]/files"), but I need to upload to my server files choosed by the user so these files were never stored inside the app directory, they are always stored inside the internal storage or the external storage in directories like the "Download" dir or the "Pictures" dir (I need to upload every type of files, so pictures, documents, pdfs ecc.. ecc.)
Is there any solution to this problem?
I have already tryed all the solutions I found online, but I always get an ACCESS EXCEPTION if I don't add "requestLegacyStorage" to the manifest (and I can't add it to notg go against Google's policies...)
Please I really need to solve this problem because this is one of the most important feature of my app..
Thank you so much!
I hope anybody can help me solve this problem T_T
Have a nice day and nice coding!
(Ask if you need more informations and I will add them!)
##########################################################################
If anyone needs it I found a "solution" but works only by using "ACTION_GET_CONTENT" (and probably by using "ACTION_OPEN_DOCUMENT", but I didn't try it yet).
When you select a file (stored outside the app-specific directory) using "ACTION_GET_CONTENT" this file is copied inside the app-specific directory ("/storage/emulated/0/Android/data/[APP_PACKAGE]/files") so you can open it because it agrees with the "SCOPED STORAGE" policy.
# "ACTION_GET_CONTENT" code:
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
String[] mimes = {
"application/*",
"audio/*",
"font/*",
"image/*",
"message/*",
"model/*",
"multipart/*",
"text/*",
"video/*"
};
intent.setType("*/*");
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimes);
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
startActivityForResult(Intent.createChooser(intent, getString(R.string.msg_select_file_to_upload)), REQ_CODE_OPEN_DOCUMENT);
Theoretically it also works without passing the "mimes" array to the intent extra "EXTRA_MIME_TYPES".
To get the path inside the "onActivityResult":
String path = FilesUtils.onActivityResultOpenDocument(this, data);
public static String onActivityResultOpenDocument(Context context, Intent data){
String selectedPath, fileName;
Uri uri = data.getData();
String mimeType = context.getContentResolver().getType(uri);
if (mimeType == null) {
String path = getPathFromOpenDocumentUri(context, uri);
if (path == null) {
fileName = FilenameUtils.getName(uri.toString());
} else {
File file = new File(path);
fileName = file.getName();
}
} else {
Uri returnUri = data.getData();
Cursor returnCursor = context.getContentResolver().query(returnUri, null, null, null, null);
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
fileName = returnCursor.getString(nameIndex);
}
String sourcePath = context.getExternalFilesDir(null).toString();
selectedPath = formatFilepath(sourcePath, fileName);
File fileSave = new File(selectedPath);
try {
copyUriStreamToFile(context, uri, fileSave);
} catch (Exception e) {
Logger.onException(TAG, e);
Toast.makeText(context, R.string.error_impossibile_recuperare_file, Toast.LENGTH_SHORT).show();
selectedPath = null;
}
return selectedPath;
}
So summarizing by using "ACTION_GET_CONTENT" (and maybe "ACTION_OPEN_DOCUMENT" too, but I didn't tryed this) the selected file is copied inside the app-specific directory (so: "/storage/emulated/0/Android/data/[APP_PACKAGE]/files") in this way the file can be opened because it agrees with the "Scoped Storage" policy.
Thank you all for your answers and your time! (: <3
I still don't know how to read a file if it is stored outside the app-specific directory, so if anybody know it please share your solution (:
I'm building an app that allows the user to save the bitmap or share it without saving it. The 2nd functionality doesn't quite work. I understand that the app needs to save the file to the device before sharing it on a social media app so my idea was, immediately after the file was successfully shared, to automatically delete the file from the device. I've build a delete method trying 2 different approaches and neither have worked:
First approach:
public void deleteFile(String path){
File file = new File(path);
try {
file.getCanonicalFile().delete();
} catch (IOException e) {
e.printStackTrace();
}
}
Second approach:
public void deleteFile(String path){
File file = new File(path);
boolean deleted = file.delete();
}
And I'm calling deleteFile(String) from the sharing method:
public void shareMeme(Bitmap bitmap) {
String path = MediaStore.Images.Media.insertImage(Objects.requireNonNull(getContext()).getContentResolver(), bitmap, "Meme", null);
Uri uri = Uri.parse(path);
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("image/*");
share.putExtra(Intent.EXTRA_STREAM, uri);
share.putExtra(Intent.EXTRA_TEXT, "This is my Meme");
getContext().startActivity(Intent.createChooser(share, "Share Your Meme!"));
deleteFile(path);
}
With respect to your stated problem, insertImage() returns a string representation of a Uri. That Uri is not a file. Calling getPath() on it is pointless, and you cannot delete anything based on that path.
More broadly, if your intention is to delete the content right away:
Do not put it in the MediaStore
Do not share it, as you will be deleting it before the other app has a chance to do anything with it
If you want to share it, but then delete it:
Do not put it in the MediaStore
Delete it the next day, or in a few hours, or something, as you have no good way of knowing when the other app is done with the content
To share an image with another app without using the MediaStore:
Save the image to a file in getCacheDir() (call that on a Context, such as an Activity or Service)
Use FileProvider to make that file available to other apps
Beyond that:
Do not use wildcard MIME types in ACTION_SEND. You are the one who is supplying the content to send. You know the actual MIME type. Use it.
Note that there is no requirement for an ACTION_SEND activity to honor both EXTRA_TEXT and EXTRA_STREAM. Most seem to do so, but that behavior is outside of the ACTION_SEND specification.
Note that insertImage() is deprecated on Android Q.
First, you need to check if your file exists, (maybe you set the wrong path?). Then delete the file
File file = new File(path);
if (file.exists()){
if (file.delete()) {
Toast.makeText(this, "file Deleted :" + path, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "file not Deleted :" + path, Toast.LENGTH_SHORT).show();
}
}
Please help me about this question.
I know how to open a file by default app like this :
Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW);
File file = ExternalStorageHelper.getFileFromName(fileName);
MimeTypeMap mime = MimeTypeMap.getSingleton();
String ext =fileName.substring(fileName.lastIndexOf(".")+1);
String type = mime.getMimeTypeFromExtension(ext);
if(type != null){
if(mime.hasMimeType(type)){
intent.setDataAndType(Uri.fromFile(file),type);
containActivity.startActivityForResult(intent,1);
}else{
Toast t = Toast.makeText(containActivity, R.string.download_open_file_error_message, Toast.LENGTH_SHORT);
t.show();
}
}else{
Toast t = Toast.makeText(containActivity, R.string.download_open_file_error_message, Toast.LENGTH_SHORT);
t.show();
}
but I can't find out how to open a folder by default app.
From this code how can I determine to MimeTypeMap fileName is a Directory.
I am really need it now, please help me :)
Unfortunately, there is no built in way to do that. You can try using
the OI FileManager - it is quite nice but the user is required to
install the app to use it.
There's also a good example of how to make your own here.
How do I open the file browser? ( Android SDK)
I am trying to send file attached email from my App.
The file saved into external storage(SDCard) can successfully attached, but the same file that is saved into temporary directory where I can get getCacheDir() method cannot be attached.
The only difference is to where the file I want to attatch is saved,
Is this because of an Android spec or limitation, or am I missing something?
I'm using ACTION_SEND intent to send attachment file via email
//// getting file path to save
//[fail] -- no attatchment in email
//path = new StorageUtil().getCacheFilePath(this, "attatchment.html");
//[success] -- attatchment.html is on email
path = new StorageUtil().getExternalAppStoragePath(this, "attatchment.html");
/// start intent if file saving is successful
if (this.export(path)==true) {
Intent i = new Intent();
i.setAction(Intent.ACTION_SEND);
i.setType("text/html");
i.putExtra(Intent.EXTRA_SUBJECT, "a subject");
i.putExtra(Intent.EXTRA_TEXT, "");
File f = new File(path);
i.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(f));
startActivity(Intent.createChooser(i, "Send Email"));
}
getCacheFilePath() creates path from following code where fileName is the second argument of the method:
File cacheDir = ctx.getCacheDir();
File cacheFile = new File(cacheDir, fileName);
return cacheFile.getPath(); //path
each path is as follows
//cache dir (attachcment failed):
/data/data/{PACKAGE_NAME}/cache/attachment.html
//external dir (attachment successed):
/mnt/sdcard/Android/data/{PACKAGE_NAME}/files/attachment.html
File object from the cache dir canRead() and could obtain file length.
thanks!
== SOLVED ==
I found that the following error is on Logcat when sending Gmail:
file:// attachment paths must point to file:///mnt/sdcard. Ignoring attachment file:///data/data/{PACKAGE_NAME}/cache/attachment.html"
So this should be a Android limitation. Adding Intent.FLAG_GRANT_READ_URI_PERMISSION has no effect and other Activity like Dropbox results similar.
I suggest you to use getExternalCacheDir() instead of getCacheDir(), which point to "/storage/emulated/0/Android/data/". File under this folder seems can be attached to gmail.