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)
}
Related
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'm trying to add a directory of files to a zip. The directory is around 150 files large. A few, 5-75 files in, I keep getting a crash with the error message "The process cannot access the file because it is being used by another process."
I tried a delay which may be helping but is certainly not solving the bug.
Using code from:
Is it possible to create a NEW zip file using the java FileSystem?
final File folder = new File("C:/myDir/img");
for (final File fileEntry : folder.listFiles()) {
if (fileEntry.isDirectory()) {
continue;
}
else {
String filename = fileEntry.getName();
String toBeAddedName = "C:/myDir/img/" + filename;
Path toBeAdded = FileSystems.getDefault().getPath(toBeAddedName).toAbsolutePath();
createZip(zipLocation, toBeAdded, "./" + filename);
System.out.println("Added file " + ++count);
//Delay because 'file in use' bug
try { Thread.sleep(1000); } //1secs
catch (InterruptedException e) {}
}
}
public static void createZip(Path zipLocation, Path toBeAdded, String internalPath) throws Throwable {
Map<String, String> env = new HashMap<String, String>();
//Check if file exists.
env.put("create", String.valueOf(Files.notExists(zipLocation)));
//Use a zip filesystem URI
URI fileUri = zipLocation.toUri(); //Here
URI zipUri = new URI("jar:" + fileUri.getScheme(), fileUri.getPath(), null);
System.out.println(zipUri);
//URI uri = URI.create("jar:file:"+zipLocation); //Here creates the zip
//Try with resource
try (FileSystem zipfs = FileSystems.newFileSystem(zipUri, env)) {
//Create internal path in the zipfs
Path internalTargetPath = zipfs.getPath(internalPath);
//Create parent dir
Files.createDirectories(internalTargetPath.getParent());
//Copy a file into the zip file
Files.copy(toBeAdded, internalTargetPath, StandardCopyOption.REPLACE_EXISTING);
}
}
I can't promise this is the cause of your problem, but your code compresses files into a ZIP file in a strange, or at least inefficient, manner. Specifically, you're opening up a new FileSystem for each individual file you want to compress. I'm assuming you're doing it this way because that's what the Q&A you linked to does. However, that answer is only compressing one file whereas you want to compress multiple files at the same time. You should keep the FileSystem open for the entire duration of compressing your directory.
public static void compress(Path directory, int depth, Path zipArchiveFile) throws IOException {
var uri = URI.create("jar:" + zipArchiveFile.toUri());
var env = Map.of("create", Boolean.toString(Files.notExists(zipArchiveFile, NOFOLLOW_LINKS)));
try (var fs = FileSystems.newFileSystem(uri, env)) {
Files.walkFileTree(directory, Set.of(), depth, new SimpleFileVisitor<>() {
private final Path archiveRoot = fs.getRootDirectories().iterator().next();
#Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
// Don't include the directory itself
if (!directory.equals(dir)) {
Files.createDirectory(resolveDestination(dir));
}
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Files.copy(file, resolveDestination(file), REPLACE_EXISTING);
return FileVisitResult.CONTINUE;
}
private Path resolveDestination(Path path) {
/*
* Use Path#resolve(String) instead of Path#resolve(Path). I couldn't find where the
* documentation mentions this, but at least three implementations will throw a
* ProviderMismatchException if #resolve(Path) is invoked with a Path argument that
* belongs to a different provider (i.e. if the implementation types don't match).
*
* Note: Those three implementations, at least in OpenJDK 12.0.1, are the JRT, ZIP/JAR,
* and Windows file system providers (I don't have access to Linux's or Mac's provider
* source currently).
*/
return archiveRoot.resolve(directory.relativize(path).toString());
}
});
}
}
Note: The depth parameter is used in exactly the same way as maxDepth is in Files#walkFileTree.
Note: If you only ever care about the files in the directory itself (i.e. don't want to recursively traverse the file tree), then you can use Files#list(Path). Don't forget to close the Stream when finished with it.
It's possible that you opening and closing the FileSystem over and over is causing your problems, in which case the above should solve the issue.
I am trying to write a text file to internal storage of my android application. But it is not possible for me to see if the file is generated or not.
My text file is stored in the following path:
data/data/"MyApplcationPackageName"/files/MyFile.txt
Permission : drwxrwx-x
I have tried the following things -
1) Using device file explorer:
Device file explorer does not open my application package. it gives following error if I try to open it.
Device File Explorer
2) Terminal:
I have also tried opening it using adb in the terminal. But when I try to open files inside my application package it says permission denied.
adb terminal
Please let me know how I can open my text file for debugging. Thanks in advance.
public static void StoreDB() {
if(isExternalStorageWritable() && checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
File file = getFinalDir();
try {
FileOutputStream fos = new FileOutputStream(file);
fos.write("something".getBytes());
fos.close();
ToastUtil.showToast(Resource.getAppContext(),"File Saved");
} catch (IOException e) {
Log.e("StoreDB", "Exception");
e.printStackTrace();
}
}
}
public static boolean isExternalStorageWritable() {
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
Log.d("External storage", "Writable");
return true;
}
Log.d("External storage", "Not Writable");
return false;
}
public static boolean checkPermission(String Permission){
int check = ContextCompat.checkSelfPermission(Resource.getAppContext(),Permission);
boolean Perm = (check == PackageManager.PERMISSION_GRANTED);
Log.d("Check Permission", "Result: " + Perm);
return Perm;
}
private static File getFinalDir() {
return createDirIfNotExist(Environment.getExternalStorageDirectory().getPath() + "/Co_Ordinate.txt/");
}
public static File createDirIfNotExist(String path) {
File dir = new File(path);
if (!dir.exists()) {
dir.mkdirs();
}
return dir;
}
Now I am trying to put the file in external storage. Above code always gives IO exception.
You need to root your phone to view those files. Or you can do it on an emulator by using the Device File Explorer.
EDIT: Or just use an unprotected file path. This will create the directory. After that you just need to save the .txt file to that directory.
private static File getFinalDir() {
return createDirIfNotExist(Environment.getExternalStorageDirectory().getPath() + "/MyAppName/");
}
public static File createDirIfNotExist(String path) {
File dir = new File(path);
if (!dir.exists()) {
dir.mkdirs();
}
return dir;
}
You can do this with Emulator. Run your app in emulator and go to Android Device monitor
Choose device, go to File Explorer menu and search your text file in data folder
Sorry for my English, but I want to write in this file because in my opinion is the best.
Now my problem:
I want to create a folder in Internal storage to share with 2 application.
In my app, I downloaded an Apk from my server and I run it.
Before I used external storage and everything worked.
Now I want to use the internal storage for users that don't have an external storage.
I use this:
String folderPath = getFilesDir() + "Dir"
but when i try to run the Apk, it doesn't work, and I can't find this folder on my phone.
Thank you..
From this post :
Correct way:
Create a File for your desired directory (e.g., File path=new
File(getFilesDir(),"myfolder");)
Call mkdirs() on that File to create the directory if it does not exist
Create a File for the output file (e.g., File mypath=new File(path,"myfile.txt");)
Use standard Java I/O to write to that File (e.g., using new BufferedWriter(new FileWriter(mypath)))
Enjoy.
Also to create public file I use :
/**
* Context.MODE_PRIVATE will create the file (or replace a file of the same name) and make it private to your application.
* Other modes available are: MODE_APPEND, MODE_WORLD_READABLE, and MODE_WORLD_WRITEABLE.
*/
public static void createInternalFile(Context theContext, String theFileName, byte[] theData, int theMode)
{
FileOutputStream fos = null;
try {
fos = theContext.openFileOutput(theFileName, theMode);
fos.write(theData);
fos.close();
} catch (FileNotFoundException e) {
Log.e(TAG, "[createInternalFile]" + e.getMessage());
} catch (IOException e) {
Log.e(TAG, "[createInternalFile]" + e.getMessage());
}
}
Just set theMode to MODE_WORLD_WRITEABLE or MODE_WORLD_READABLE (note they are deprecated from api lvl 17).
You can also use theContext.getDir(); but note what doc says :
Retrieve, creating if needed, a new directory in which the application can place its own custom data files. You can use the returned File object to create and access files in this directory. Note that files created through a File object will only be accessible by your own application; you can only set the mode of the entire directory, not of individual files.
Best wishes.
You can create a public into a existing system public folder, there is some public folder accessible from internal storage :
public static String DIRECTORY_MUSIC = "Music";
public static String DIRECTORY_PODCASTS = "Podcasts";
public static String DIRECTORY_RINGTONES = "Ringtones";
public static String DIRECTORY_ALARMS = "Alarms";
public static String DIRECTORY_NOTIFICATIONS = "Notifications";
public static String DIRECTORY_PICTURES = "Pictures";
public static String DIRECTORY_MOVIES = "Movies";
public static String DIRECTORY_DOWNLOADS = "Download";
public static String DIRECTORY_DCIM = "DCIM";
public static String DIRECTORY_DOCUMENTS = "Documents";
To create your folder, use this code :
File myDirectory = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "MyPublicFolder");
myDirectory.mkdir();
With this example, a public will be created in Documents and can be visible in any file's explorer app for Android.
try the below
File mydir = context.getDir("Newfolder", Context.MODE_PRIVATE); //Creating an internal dir;
if(!mydir.exists)
{
mydir.mkdirs();
}
This is what i have used and is working fine for me:
String extStorageDirectory = Environment.getExternalStorageDirectory().toString();
File file = new File(extStorageDirectory, fileName);
File parent=file.getParentFile();
if(!parent.exists()){
parent.mkdirs();
}
This will create a new directory if not already present or use the existing if already present.
We use a lot of legacy package.html files in our project and we want to convert them to package-info.java files. Doing that manually isn't an option (way too many files). Is there a good way to automate that?
We want to convert them for a couple of reasons:
From the javadoc specs: This file is new in JDK 5.0, and is preferred over package.html.
To not mix both types of files in the same codebase
To avoid that Intellij/Eclipse builds put those *.html files in our classes dirs (and possibly in a release binary jars) so they behave like our other normal html resources.
You may need to change the directory separator if you're not running windows. Also, the conversion is a bit of a hack, but it should work. Out of curiosity, how many packages do you have that manual isn't an option?
public class Converter {
public static void main(String[] args) {
File rootDir = new File(".");
renamePackageToPackageInfo(rootDir);
}
private static void renamePackageToPackageInfo(File dir) {
File[] files = dir.listFiles(new FilenameFilter() {
#Override
public boolean accept(File dir, String name) {
return "package.html".equals(name);
}
});
for (File file : files) {
convertFile(file);
}
// now recursively rename all the child directories.
File[] dirs = dir.listFiles(new FileFilter() {
#Override
public boolean accept(File pathname) {
return pathname.isDirectory();
}
});
for (File subdir : dirs) {
renamePackageToPackageInfo(subdir);
}
}
private static void convertFile(File html) {
// determine the FQN package name
String fqpn = getPackageName(html);
// check if package-info.java already exists
File packageInfo = new File(html.getParent(), "package-info.java");
if (packageInfo.exists()) {
System.out.println("package-info.java already exists for package: "+fqpn);
return;
}
// create the i/o streams, and start pumping the data
try {
PrintWriter out = new PrintWriter(packageInfo);
BufferedReader in = new BufferedReader(new FileReader(html));
out.println("/**");
// skip over the headers
while (true) {
String line = in.readLine();
if (line.equalsIgnoreCase("<BODY>"))
break;
}
// now pump the file into the package-info.java file
while (true) {
String line = in.readLine();
if (line.equalsIgnoreCase("</BODY>"))
break;
out.println(" * " + line);
}
out.println("*/");
out.println("package "+fqpn+";");
out.close();
in.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// queue the package.html file for deletion
//html.deleteOnExit();
}
private static String getPackageName(File file) {
StringBuilder path = new StringBuilder(file.getParent());
// trim the first two characters (./ or .\)
path.delete(0, 2);
// then convert all separators into . (HACK: should use directory separator property)
return path.toString().replaceAll("\\\\", ".");
}
}
The IntelliJ guys have made an intention to do this for all files. It's been resolved and will probably be released in the next IntelliJ release.
To do this in batch mode in IDEA:
In settings, activate the inspection gadget "'package.html' may be converted to 'package-info.java' inspection"
Open a package.html file
You see a banner fix the inspection on top the file
Click on the settings icon at the right on the banner
Select "Run inspection on" >> "Whole project"
Click on "Convert to package-info.java" >> OK
Optionally remove the inappropriate lines (sed -i "/Put #see and #since/d" `find . -name "package-info.java"`)