In a library, I want to return a file that represents App own directory in External Storage, the directory that is returned by this method:
context.getExternalFilesDir(null);
But using this method before API Level 19, needs WRITE_EXTERNAL_STORAGE permission and I do not want to force user to use this permission, specially my method only want to return abstract File and does not want to create directory.
I can use this code:
Environment.getExternalStorageDirectory().getCanonicalPath() + "/Android/data/" + packageName + "/files";
But I think hard coding is not safe.Is there a way to return that directory without forcing user to use WRITE permission?
If you want to avoid the hard coded string, you could try to use reflect.
Class<?> environmentClass = Environment.class;
try {
Field androidDirectoryField = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
androidDirectoryField = environmentClass.getDeclaredField("DIR_ANDROID");
} else {
androidDirectoryField = environmentClass.getDeclaredField("DIRECTORY_ANDROID");
}
androidDirectoryField.setAccessible(true);
final String externalDir = Environment.getExternalStorageDirectory().getAbsolutePath()
+ getFilesDir().getAbsolutePath().replaceFirst("data", androidDirectoryField.get(null).toString());
Log.d(TAG, "external directory " + externalDir);
} catch (NoSuchFieldException | IllegalAccessException | IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Related
We are integrating scoped storage in our app, we are reading and writing video files in sahred storage e.g Environment.DIRECTORY_MOVIES folder (shared storage movies directory) it is working as expected but we need to write custom metadata in video to do that we are using org.mp4parser:isoparser. To read and write metadata, this library needs file object before scoped storage we can get absolute path using Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES) now it's deprecated is there any other way to get the file path in scoped storage?
public static String readVideoMetadata(File videoFile) throws IOException {
if (!videoFile.canRead()) {
throw new IllegalStateException("No read permissions to file " + videoFile.getAbsolutePath());
}
if(!isImageFile(videoFile)) {
try {
IsoFile isoFile = new IsoFile(videoFile);
if (null != Path.getPath(isoFile, "moov[0]/udta[0]/meta[0]/ilst/©cmt")) {
AppleCommentBox nam = Path.getPath(isoFile, "moov[0]/udta[0]/meta[0]/ilst/©cmt");
String xml = nam.getValue();
isoFile.close();
return xml;
}
} catch (OutOfMemoryError | Exception e) {
e.printStackTrace();
}
}else{
ExifInterface exifInterface=new ExifInterface(videoFile);
String metaData= exifInterface.getAttribute(ExifInterface.TAG_USER_COMMENT);
if(metaData!=null){
return metaData;
}
}
return "";
}
Use FFMPEG Android library
You can get file path from media URI using the below code
Uri safUri = intent.getData();
String inputVideoPath = FFmpegKitConfig.getSafParameterForRead(requireContext(), safUri);
You can read media information using this below function
MediaInformationSession mediaInformation = FFprobeKit.getMediaInformation("<file path or uri>");
mediaInformation.getMediaInformation();
Stackoverflow
And thank you so much for reading this question.
I am currently trying to fetch an certain entire directory from my Linux server to
My android phone via sftp.
So far, My code worked fine.
However as soon as I built my app as Signed APK and installed it, Code below does not work as expected.
It doesn't download file from my Linux server but create 0B size files.
private void recursiveFolderDownload(String src, DocumentFile pickedDir) throws SftpException, InterruptedException {
Vector<ChannelSftp.LsEntry> fileAndFolderList = channelSftp.ls(src);
for (ChannelSftp.LsEntry item : fileAndFolderList) {
if (!item.getAttrs().isDir()) {
DocumentFile newFile = pickedDir.findFile(item.getFilename());
if(newFile==null){
MainActivity.changeLoadingMessage("Fetching "+item.getFilename());
restoreStatus = "Fetching "+item.getFilename();
newFile = pickedDir.createFile("",item.getFilename());
restoringFiles.add(getPathFromUri(newFile.getUri()));
write(src + "/" + item.getFilename(),newFile.getUri());
}
else{
MainActivity.changeLoadingMessage("Skipping "+item.getFilename());
restoreStatus = "Skipping "+item.getFilename();
}
} else if (!(".".equals(item.getFilename()) || "..".equals(item.getFilename()))) {
DocumentFile newDir = pickedDir.findFile(item.getFilename());
if(newDir==null){
newDir = pickedDir.createDirectory(item.getFilename());
}
recursiveFolderDownload(src + "/" + item.getFilename(), newDir);
}
}
}
private void write(String src, Uri uri) throws InterruptedException {
try {
ParcelFileDescriptor descriptor = context.getContentResolver().openFileDescriptor(uri,"w");
if(descriptor!=null) {
FileOutputStream fos=new FileOutputStream(descriptor.getFileDescriptor());
channelSftp.get(src,fos);
fos.close();
}
} catch (IOException | SftpException e) {
e.printStackTrace();
}
}
Of course I implemented Storage Access Framework by using activity with Intent.ACTION_OPEN_DOCUMENT_TREE
It's strange that this code works as non-release APK but not as release APK.
Anyway. Thank you so much for reading this question.
Hope I could find answer.
I have a strange problem writing on my external sd card running Android Oreo :
First the usual commands
getExternalFilesDir(null).getAbsolutePath()
or
Environment.getExternalStorageDirectory().getAbsolutePath()
return the path to the emulated storage not the real sd card sometimes called secondary external storage.
But well that's not the issue as I manage to get the path to the SD card using the method getExternalStoragePath() described at the end of my post.
Now the strange thing is that I cannot get my app (let's say the package is com.example.myapp) creating the directory
SDCARDPATH/Android/data/com.example.myapp/files
despite all necessary authorizations have been properly granted (but it is possible that the WRITE_EXTERNAL_STORAGE and READ_EXTERNAL_STORAGE apply only to the emulated storage, I have a feeling that the issue is there!) Here is what I'm doing :
String SDCARDPATH=getExternalStoragePath(this, true);
File md = new File(new File(new File(new File(SDCARDPATH,"Android"),"data"),PACKAGE_NAME),"files");
if(!md.exists()) {
try {
md.mkdirs();
} catch (Exception e) {
e.printStackTrace();
}
}
The strange thing is that if I create the directory manually through the phone file explorer the app can then write and read within that directory without any trouble.
So my question is how to properly create that directory on my external sdcard
Side note I see some app installed on my phone have managed to create their app directory on the sd card.
Finally here is the getExternalStoragePath method I use and which was grabbed on the net :
Thanks to all in advance for your help,
Lea
public static String getExternalStoragePath(Context mContext, boolean is_removable) {
StorageManager mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
Class<?> storageVolumeClazz = null;
try {
storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList");
Method getPath = storageVolumeClazz.getMethod("getPath");
Method isRemovable = storageVolumeClazz.getMethod("isRemovable");
Object result = getVolumeList.invoke(mStorageManager);
final int length = Array.getLength(result);
for (int i = 0; i < length; i++) {
Object storageVolumeElement = Array.get(result, i);
String path = (String) getPath.invoke(storageVolumeElement);
boolean removable = (Boolean) isRemovable.invoke(storageVolumeElement);
if (is_removable == removable) {
return path;
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
I manage to get the path to the SD card using the method getExternalStoragePath() described at the end of my post
That code is not going to work on all versions of Android. In particular, it will not work on Android 10+. I would not be the least bit surprised if it fails on Android 9.0 as well, given that it relies upon reflection into system classes. It will also fail on older devices where those classes and methods do not exist.
First the usual commands getExternalFilesDir(null).getAbsolutePath() or Environment.getExternalStorageDirectory().getAbsolutePath() return the path to the emulated storage not the real sd card sometimes called secondary external storage
Use getExternalFilesDirs() (note the s at the end). That returns a File[]. If that array has 2+ elements, all but the first element point to directories on removable storage that you can use from your app, without any permissions. The directory should already be created, but you can call mkdirs() on the File to ensure that it exists.
I want to rename my png file. Image current path like this:
/storage/emulated/0/Android/data/sample.png
I want to save this image under app's file directory. I give write external storage permission on runtime.
File toFileDir = new File(getFilesDir() + "images");
if(toFileDir.exists()) {
File file = new File("/storage/emulated/0/Android/data/sample.png");
File toFile = new File(getFilesDir() + "images/sample-1.png");
file.renameTo(toFile);
}
renameTo returns false. But I couldn't understand the reason.
Internal and external memory is two different file systems. Therefore renameTo() fails.
You will have to copy the file and delete the original
Original answer
You can try the following method:
private void moveFile(File src, File targetDirectory) throws IOException {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (!src.renameTo(new File(targetDirectory, src.getName()))) {
// If rename fails we must do a true deep copy instead.
Path sourcePath = src.toPath();
Path targetDirPath = targetDirectory.toPath();
try {
Files.move(sourcePath, targetDirPath.resolve(sourcePath.getFileName()), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException ex) {
throw new IOException("Failed to move " + src + " to " + targetDirectory + " - " + ex.getMessage());
}
}
} else {
if (src.exists()) {
boolean renamed = src.renameTo(targetDirectory);
Log.d("TAG", "renamed: " + renamed);
}
}
}
I wrote a logfile to my sd (internal storage of the Samsung Galaxy Nexus). Actually the file is invisible in windows. How can I let users access them?
this.state = Environment.getExternalStorageState();
Returns "mounted"
This is my Code:
/**
* Creates a new instance of Logger
*
* */
public Logger() {
String path = Environment.getExternalStorageDirectory()
.getAbsolutePath();
this.file = new File(path, "log.txt");
}
/**
* Writes results to log file
*
* **/
public void log(boolean isConnected, int signalStrength) {
try {
this.buf = new BufferedWriter(new FileWriter(this.file, true));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
Calendar time = Calendar.getInstance();
String result = time.getTime().toString() + " "
+ String.valueOf(isConnected) + " "
+ String.valueOf(signalStrength);
this.buf.write(result);
this.buf.flush();
this.buf.close();
} catch (IOException e) { // TODO Auto-generated catch block
e.printStackTrace();
}
}
It won't write anything (even though I receive no exception).
Manifest contains:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Edit:
File path = Environment.getExternalStorageDirectory()
.getAbsoluteFile();
Didn't help.
The file is there, but it has not been picked up by the MediaScanner yet. You can verify by opening a shell like so:
adb shell ls -l /mnt/sdcard/clown.txt
You need to either wait for it to be picked up, reboot, or (preferably), call MediaScannerConnection.scanFile() when you are done writing to the file.