I have a directory structure of files in external storage. They don't show up in the Android File Transfer app, so I think it's a media scanner problem.
I'm creating them with a FileOutputStream in a directory based on Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).
I have the following method, called from an activity, so context is an activity (forget that this blocks the main thread for now!):
public void scan(Context context, File base) {
File[] files = base.listFiles();
if (files == null) {
return;
} else {
for (File file : files) {
if (file.isFile()) {
String path = file.getAbsolutePath();
MediaScannerConnection.scanFile(context, new String[]{path}, null, null);
Log.e("Langstroth", path);
} else if (file.isDirectory()) {
this.scan(context, file);
}
}
}
}
public void scan(Context context) {
this.scan(context, this.baseDir);
}
}
The output of the log is as expected:
E/MyApp﹕ /storage/emulated/0/Documents/Langstroth/sample/5000/1430576404874.wav
E/MyApp﹕ /storage/emulated/0/Documents/Langstroth/sample/5000/1430577209491.wav
And then lots of:
E/MyApp﹕ Scan completed path /storage/emulated/0/Documents/Langstroth/sample/5000/1430576404874.wav uri content://media/external/audio/media/7836
E/MyApp﹕ Scan completed path /storage/emulated/0/Documents/Langstroth/sample/5000/1430577209491.wav uri content://media/external/audio/media/7838
This proves that the files exist. They don't show up in the Android File Transfer though.
Here's the strange thing. Another method:
public void otherDemo(Context context, File baseDir) {
String newPath = baseDir.getAbsolutePath() + "/some/random/dirs";
Log.e("Langstroth", "New path " + newPath);
File dir = new File(newPath);
dir.mkdirs();
Log.e("Langstroth", dir.exists() ? "Dir exists": "Dir does not exist");
File f = new File(dir, "myfile.txt");
try {
new BufferedOutputStream(new FileOutputStream(f)).close();
} catch (IOException e) {
e.printStackTrace();
}
Log.e("Langstroth", f.exists() ? "File exists": "File does not exist");
MediaScannerConnection.scanFile(context, new String[]{f.getAbsolutePath()}, null, null);
}
and the log output:
E/MyApp﹕ New path /storage/emulated/0/Documents/Langstroth/some/random/other/dirs
E/MyApp﹕ Dir exists
E/MyApp﹕ File exists
E/MyApp﹕ File: /storage/emulated/0/Documents/Langstroth/some/random/other/dirs/myfile.txt
E/MyApp﹕ Other scan completed path /storage/emulated/0/Documents/Langstroth/some/random/other/dirs/myfile.txt uri content://media/external/file/7842
One test file shows up, the others don't
Proof:
Where are the other files?
Generally speaking, before you let another process work with a file, you want to ensure all bytes are flushed to disk, via getFD().sync(). In particular, this seems to help with the whole media scanning thing.
the files shows up in a .listFiles(), and .exist(), and the callback for the MediaScanner says that it completed correctly. Surely an extant (if empty) file should show up?
The ways of the media scanner are mysterious. :-) IOW, beats me.
Bear in mind that there are multiple moving parts here: your app, the media scanner, the MTP daemon on Android, and your MTP client. The breakdown could be at any stage. If you unplug and re-plug in the device, and now the files show up in your MTP client, my guess would be that the MTP client is working off of a slightly stale cache.
Related
My android-application writes a bunch of csv-files and jpg-files to the internal storage of the device. I am using MediaScannerConnection.scanFile() to make the files accessable from my windows-system without rebooting the android-device.
private void scanFiles() {
File targetDirectory = new File(Environment.getExternalStorageDirectory(), "DIR_OF_MY_APP");
if (targetDirectory.exists()) {
List<File> filesToScan = getFiles(targetDirectory);
List<String> filePathsToScan = new ArrayList<>();
for(File file : filesToScan) {
filePathsToScan.add(file.getPath());
}
MediaScannerConnection.scanFile(this, filePathsToScan.toArray(new String[0]), null, new MediaScannerConnection.OnScanCompletedListener() {
#Override
public void onScanCompleted(String path, Uri uri) {
Log.d("OK", "Path: " + path);
Log.d("OK", "Uri : " + uri);
}
});
}
}
In my Logcat i can see every file is getting scanned. The new ones and the old ones.
My problem is when my app is adding new lines to an existing csv-file and the file is getting scanned, The new lines do not appear in the csv-file when its opend from my pc. How can i fix this problem?
I already tried to rename all the files from filename to tmp_filename, rescann all the files and rename them back from tmp_filename to filename and rescann them again. After this, i have can see the oldfilename-file and the tmp_oldfilename-file on my windows-computer. The tmp_oldfilename-file can not be opend (Unknown error on [memory-adress]). The oldfilename-file shows the not updated csv-file.
I also tried to use a intent to scan the files, since some questions on so say its going to update them:
for(File file : filesToScan) {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE)
Uri contentUri = Uri.fromFile(file);
mediaScanIntent.setData(contentUri);
sendBroadcast(mediaScanIntent);
Log.d("OK", "File: " + file.getName() + " scanned...");
}
here i can see the files getting scanned too, but they do not show up updated on my windows-computer.
Okay the only solution i came up with, is to set the usb-mode to load-only (this must be done by hand from the user) before performing the MediaScannerConnection.scanFile();. After this is done, the user can set the usb-mode back to mtp and than the csv-files will show up with the new added lines.
This is a really bad workarround, but still better than rebooting the device. If someone has an better solution, pls share.
My app should save files to a place where, when you connect your phone/tablet to a computer, you can see them through the system file explorer.
This is the way I implemented file writing:
protected String mDir = Environment.DIRECTORY_DOCUMENTS;
protected File mPath = Environment.getExternalStoragePublicDirectory(mDir);
protected void writeLogFile(String filename) {
File f = new File(mPath, filename + ".txt");
f.getParentFile().mkdirs();
try (BufferedWriter bw = new BufferedWriter(new FileWriter(f, false))) {
// Details omitted.
} catch (Exception e) {
e.printStackTrace();
return;
}
makeText("Wrote " + f.getAbsolutePath());
}
This is what I see when I connect my Sony Xperia Z4 tablet to Windows (notice missing documents folder):
This is the directory to which the file is written (using above implementation):
What is wrong with my implementation?
What is wrong with my implementation?
MediaStore has not discovered your newly-created files yet. What you see in Windows — and in many on-device "gallery" apps — is based on what MediaStore has indexed.
Use MediaScannerConnection and its scanFile() method to tell MediaStore about your file, once you have written out your data to disk:
public void scanFile(Context ctxt, File f, String mimeType) {
MediaScannerConnection
.scanFile(ctxt, new String[] {f.getAbsolutePath()},
new String[] {mimeType}, null);
}
or, in Kotlin:
fun scanFile(ctxt: Context, f: File, mimeType: String) {
MediaScannerConnection.scanFile(ctxt, arrayOf(f.getAbsolutePath()), arrayOf(mimeType), null)
}
I have an android 4.3 device and often connect it to my PC via USB so that I can view files created by one of my apps. Strangely though, it seems that not all files on the android file system are visible. After much searching I came across this page which (correct me if I'm wrong) suggests that if I call MediaScannerConnection.scanFile, with the path I'm interested in, then it would make all files in those directories visible. I have implemented the code as follows:
void make_files_in_directory_visible_on_pc(String directory)
{
Log.i("xx","make_files_in_directory_visible_on_pc: dir=["+directory+"]");
MediaScannerConnection.scanFile(
getApplicationContext(),
new String[]{ directory }, // array of paths
null, // mime types
new OnScanCompletedListener() {
#Override
public void onScanCompleted(String path, Uri uri)
{
Log.i("xx","file " + path + " was scanned successfully: " + uri);
}
});
}
My app saves its files in Environment.getExternalStorageDirectory().getAbsolutePath() which, on my device, is "/storage/emulated/0".
When I execute this from my app I see the following in the logs:
file /storage/emulated/0 was scanned successfully: content://media/external/file/2552
After the code has run there is no change to the list of files visible from my PC, many files are still missing.
Any ideas?
EDIT: FYI, I do not have a file called "2552" on that directory, or anywhere else on my device.
EDIT: If I execute the following:
MediaScannerConnection.scanFile(this, new String[] {Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+name_of_individual_file}, null, null);
Then that single file does indeed become visible, but I want to achieve this for all files in the directory.
I've worked it out...
void make_files_in_directory_visible_on_pc(String path)
{
File f = new File(path);
File file[] = f.listFiles();
for (int i=0; i < file.length; i++)
{
MediaScannerConnection.scanFile(this, new String[] {file[i].getAbsolutePath()}, null, null);
}
}
I created a file by file.createNewFile() command in "data/data/com.android.bonvoyage" folder to test file creation in the internal storage of my android tablet.
I found that the file should be visible when I have root account, but I want to find a way
to see the file created without root permission.
I don't care where the file is created, just want to see and test it on actual tablet.
Can I do that?
The process was successful by
File file = new File("data/data/com.android.bonvoyage/myfile.txt");
boolean tf = false;
if (!file.exists()) {
try {
tf = file.createNewFile();
} catch (IOException ioe) {
Toast.makeText(this, ioe.toString(), 5000).show();
//ioe.printStackTrace();
} finally {
Toast.makeText(this, "File Created? " + Boolean.toString(tf), Toast.LENGTH_SHORT).show();
}
}
With root explorer or any file explorer that supports root you could browse to: data/data/com.android.bonvoyage/myfile.txt and look whether its there or not?
Write the file to the sdcard and then you can see it with the file browser without root permissions.
Use the following code (taking from the android guide) to open a file in the pictures folder for example:
public File getAlbumStorageDir(String albumName) {
// Get the directory for the user's public pictures directory.
File file = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), albumName);
if (!file.mkdirs()) {
Log.e(LOG_TAG, "Directory not created");
}
return file;
}
The full guide can be found here:
http://developer.android.com/training/basics/data-storage/files.html#WriteExternalStorage
I have a problem with creating a folder and a file on the sdcard.
Here's the code:
File folder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString() + "/folder");
boolean success;
if (!folder.exists()) {
success = folder.mkdirs();
}
File obdt = new File(folder, "file.txt");
try {
success = obdt.createNewFile();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
With this code I expect to create the folderfolder in the Download folder of the sdcard and in this the file file. I want that the user can access the file. So I want to put it in a shared folder.
The success variable is true and when I run the code again the folder already exists and doesnt come in the if-block.
But I can't see the created folder and file on the sdcard in file explorer.
Info:getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString() returns storage/sdcard/Download
I work with a Galaxy Nexus.
Damn! :)
Now I solved my problem...I was misunderstanding the operation of creating files in the file system.
When I spoke of file explorer I meant the file explorer of the operating system and NOT the file explorer in the DDMS :).
I thought when I create a file I will see it in the file explorer of the operating system but when the device is connected to the PC the files can only be seen in the DDMS file explorer.
Sorry I'm new to Android ;)
When the App is running standalone without PC connection and afterwards I connect with the PC I see the created files and folders of course :)
Thanks for help
Any errors from logcat?
Else: try something like Log.I("PATHNAME",folder.absolutePath()); and then look in your logcat to make sure where you are creating the folder where you think it is.
If you haven't done so already, you will need to give your app the correct permission to write to the SD Card by adding the line below to your Manifest:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
If you have already done that see if :
File obdt = new File(/sdcard/folder/file.txt)
try {
success = obdt.createNewFile();
} catch (IOException e1) {
e1.printStackTrace();
}
works.
You cannot see the folder/file in explorer? Maybe it is because the MediaScanner is active, but not adding your files. You can do this in your program or switch the Media Scanner of somewhere in your phone settings.
MediaScanner
Trigger MediaScanner
Try this out.
File dir = new File(Environment.getExternalStorageDirectory()
+ "/XXX/Wallpapers/");
File[] files = dir.listFiles();
if (files == null)
{
int numberOfImages = 0;
BitmapDrawable drawable = (BitmapDrawable) imageView
.getDrawable();
Bitmap bitmap = drawable.getBitmap();
File sdCardDirectory = Environment
.getExternalStorageDirectory();
new File(sdCardDirectory + "/XXX/Wallpapers/").mkdirs();
File image = new File(sdCardDirectory
+ "/XXX/Wallpapers/Sample" + numberOfImages + ".JPG");
boolean success = false;
FileOutputStream outStream;
try {
outStream = new FileOutputStream(image);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
outStream.flush();
outStream.close();
success = true;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if (success) {
Toast.makeText(
getApplicationContext(),
"Image saved successfully in Sdcard/XXX/Wallpapers",
Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getApplicationContext(),
"Error during image saving", Toast.LENGTH_LONG)
.show();
}
Dont forget to add permission in manifest
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Apparently there is a known bug in MTP.
Issue 195362
All phones using MTP instead of USB Mass storage do not properly show the list of files when that phone is connected to a computer using a USB cable. Android apps running on the device also cannot see these files.
It is actually as old as 2012
I've encountered the same problem: created files and folders don't show immediately after being written to sdcard, despite the file being flushed and closed !!
They don't show on your computer over USB or a file explorer on the phone.
I observed three things:
if the absolute path of the file starts with /storage/emulated/0/ it doesn't mean it'll be on your sdcard - it could be on your main storage instead.
if you wait around 5 minutes, the files do begin to show over USB (i.e. Windows explorer and built-in file explorer)
if you use adb shell ls /sdcard from terminal, then the file does show! you could use adb pull ... to get the file immediately. You could probably use DDMS too.
Code I used was:
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = gson.toJson(myArrayList);
try {
File externalDir = getExternalStorageDirectory();
File newFile = new File(externalDir, "myfile.txt");
FileOutputStream os = new FileOutputStream(newFile);
os.write(json.getBytes());
os.flush();
os.close();
Timber.i("saved file to %s",newFile.getAbsoluteFile().toString());
}catch (Exception ex)
{
Toast.makeText(getApplicationContext(), "Save to private external storage failed. Error message is " + ex.getMessage(), Toast.LENGTH_LONG).show();
}
and
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = gson.toJson(myArrayList);
try {
File externalDir = getExternalStorageDirectory();
File newFile = new File(externalDir, "myfile.txt");
FileWriter fw = new FileWriter(newFile);
fw.write(json);
fw.flush();
fw.close();
Timber.i("saved file to %s",newFile.getAbsoluteFile().toString());
}catch (Exception ex)
{
Toast.makeText(getApplicationContext(), "Save to private external storage failed. Error message is " + ex.getMessage(), Toast.LENGTH_LONG).show();
}
why is it like this? Seems like another one of those "Android-isms" that you have to suffer through the first time you experience it.