I have a list of arrays of data in my app that I would now like to write to a file (csv) and use a 3rd party app (such as email) to share this csv file. I have had no luck finding any helpful resources for creating, finding the file path for, and appending to a file in Kotlin. Does anyone have experience with this or have examples to point to? Just to get started I'm trying to write the header and close the file so I can see that it is correctly writing.
This is what I have for now:
val HEADER = "ID, time, PSI1, PSI2, PSI3, speed1, speed2, temp1, temp2"
val filename = "export.csv"
var fileOutStream : FileOutputStream = openFileOutput(filename,Context.MODE_PRIVATE)
try {
fileOutStream.write(HEADER.toByteArray())
fileOutStream.close()
}catch(e: Exception){
Log.i("TAG", e.toString())
}
It doesn't throw the exception, but I cannot find the file in the file system. I'm using a physical tablet for testing/debug. I've checked the com.... folder for my app.
I cannot find the file in the file system
Use Android Studio's Device File Explorer and look in /data/data/.../files/, where ... is your application ID.
Also, you can write your code a bit more concisely as:
try {
PrintWriter(openFileOutput(filename,Context.MODE_PRIVATE)).use {
it.println(HEADER)
}
} catch(e: Exception) {
Log.e("TAG", e.toString())
}
use() will automatically close the PrintWriter, and PrintWriter gives you a more natural API for writing out text.
It appears there are many ways to create a file and append to it, depending on the minimum API version you are developing for. I am using minimum Android API 22. The code to create/append a file is below:
val HEADER = "DATE,NAME,AMOUNT_DUE,AMOUNT_PAID"
var filename = "export.csv"
var path = getExternalFilesDir(null) //get file directory for this package
//(Android/data/.../files | ... is your app package)
//create fileOut object
var fileOut = File(path, filename)
//delete any file object with path and filename that already exists
fileOut.delete()
//create a new file
fileOut.createNewFile()
//append the header and a newline
fileOut.appendText(HEADER)
fileOut.appendText("\n")
/*
write other data to file
*/
openFileOutput() creates a private file, likely inside of app storage. These files are not browsable by default. If you want to create a file that can be browsed to, you'll need the WRITE_EXTERNAL_STORAGE permission, and will want to create files into a directory such as is provided by getExternalFilesDir()
Related
This is my code so far, supposedly this helps generate a file but I can't find it anywhere. I believe that's becuase there isn't a file path or directory provided. So what should I add to properly set the path?
fun generateFile(){
val fileName = "dataFile.txt"
try {
val fos = openFileOutput(fileName, MODE_PRIVATE)
val data: String = binding.text.toString()
fos.write(data.toByteArray())
fos.flush()
fos.close()
} catch (e: IOException) {
e.printStackTrace()
}
binding.text.setText("")
Toast.makeText(applicationContext,"writing to file " + fileName.toString() + "completed...",Toast.LENGTH_SHORT).show()
}
I believe that's becuase there isn't a file path or directory provided
There is one implied by openFileOutput(). That puts the file into the directory referred to by getFilesDir() on Context.
but I can't find it anywhere
That location is part of internal storage. That file is accessible by your app, but not by users. On debug builds of your app, you can get it via tools like Android Studio's Device File Explorer.
I am trying to use Android's internal helpers to get a path from the system for my file first and then put my files where the system wants. Because tomorrow they might change their minds.
I made a simple program to explore this subject. Here is my code;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String path = letsMakeAfile(this, "myFile.txt");
}
private static String letsMakeAfile(Context context, String nameOfFile) {
String strOfFinalPath ="";
//ask the system what path to use...
String strOfContextPath = context.getFilesDir() + "/";
//line above doesnt work without ' + "/" ' or something on the end
//line above records this path: /data/user/0/com.example.filesexperimenting/files/
//this appears to be an invalid path unless "user" is a hidden directory
Log.d("IDIOT", "strOfContextPath: "+ strOfContextPath);
try
{
File file = new File(strOfContextPath, nameOfFile);
if (file.exists() == false) {
file.mkdirs();
//after this line "makes dirs" is file automatically still made and dropped in?
letsMakeAfile(context, nameOfFile);
//I assume not so Ive made a recursive call
}
else
;
//escape recursion....
strOfFinalPath = file.getAbsolutePath();
//Here I record the path where I hope the file is located
Log.d("IDIOT", "strOfFinalPath: "+ strOfFinalPath);
}
catch (Exception e) {
e.printStackTrace();
Log.d("IDIOT", "CATCH ERROR: "+ e.getLocalizedMessage());
}
//runs without a catch
return strOfFinalPath;
}
}
Logcat:
2019-04-09 09:59:22.901 16819-16819/? D/IDIOT: strOfContextPath: /data/user/0/com.example.filesexperimenting/files/
2019-04-09 09:59:22.901 16819-16819/? D/IDIOT: strOfFinalPath: /data/user/0/com.example.filesexperimenting/files
Ultimately I am getting a path of /data/user/0/com.example.filesexperimenting/files/ from context.getFilesDir() which appears to be an invalid path unless "user" is a hidden directory (then why can I see root?). In Device File Explorer under data the only other directories are app, data and local
What am I missing? I'll assume its something with file.makedirs()
Full disclosure, I am a student and there is not a lot out there on this so your replies, while obvious to you at your experience level, should help others. I have some experience with Java and more with C++ but Android is new to me. Thanks in advance!
So, in talking outside of StackExchange it appears that using java.io like I am trying to in the example can cause some problems because of the preset file directories that may be locked or restricted that Java io might not know about.
Android has it's own method openFileOutput(String name, int mode) that has the ability to create the app resource file and directory it belongs in.
Description copied from class: android.content.Context
Actions:
~Open a private file associated with this Context's application package for writing.
~Creates the file if it doesn't already exist.
~No additional permissions are required for the calling app to read or write the returned file.
Params:
~name – The name of the file to open; can not contain path separators.
~mode – Operating mode.
Returns: The resulting FileOutputStream.
Throws: java.io.FileNotFoundException
If you want to be able to navigate to the location of your saved files through the file explorer (either in Android Studio or the Files app on the phone) you should use Context.getExternalFilesDir().
Context.getFilesDir() returns a directory not accessible by anyone BUT the creating application. So if you would like to see what is in this file you would need to open it with the same application that wrote it. IE: Print the contents to the screen after you save it in your app.
Context.getExternalFilesDir() returns a directory completely accessible by anyone and any application. So files created and saved in this external directory can be seen by Android Studio's file explorer as the OP has screenshot or by any application installed on the phone.
What is nice about both of these methods is that as long as you are only accessing files you have created you never need to ask the user for storage permissions Read or Write. If you would like to write to someone else's external files dir then you do.
Source
Check if sdcard is mounted or not.
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)){
///mounted
}
Get the path of sd card
File dir= new File(android.os.Environment.getExternalStorageDirectory());
walkdir(dir);
ArrayList<String> filepath= new ArrayList<String>();
//list for storing all file paths
public void walkdir(File dir) {
File listFile[] = dir.listFiles();
if (listFile != null) {
for (int i = 0; i < listFile.length; i++) {
if (listFile[i].isDirectory()) {
// if its a directory need to get the files under that directory
walkdir(listFile[i]);
} else {
// add path of files to your arraylist for later use
//Do what ever u want
filepath.add( listFile[i].getAbsolutePath());
}
}
}
}
Try using this:
context.getFilesDir().getParentFile().getPath()
I'm [still] new on android development and about Java and about Kotlin (also an explanation in Java could be ok, however, I'm studying it also, Kotlin is prefered) and I'm struggling for deleting a simple downloaded file into the ExternalStorage.
Of course I enabled permission for read & write, and, even if this code returns a "True", I still can see the untouched file into my Download folder
here the code:
___UPDATE
// uri of my file in external storage ~/Download dir
var uri = Uri.parse (Environment.getExternalStorageDirectory().getPath() + "/Download/$myFilename$myExtensionVar")
// file object pointing at uri of file in external storage
val downloadedFile = File(uri.toString())
var deletedBool:Boolean = downloadedFile.delete()
println("myTag - deleted Boolean: $deletedBool")
if (deletedBool){
println("myTag - uri of file-to-be-deleted: $uri")
var secondStepToDelete:Int = context.getContentResolver().delete(uri, null, null)
println("myTag - second Step for deletion: $secondStepToDelete")
}
The file i am trying to rid of is a multimedia file (.mp3) and I added the second block of code (the one inside the IF statement) since I found that should work, having to do with the "DocumentProvider" (I'm new and I still don't know how to proper call its methods..) but, of course, It doesn't work at all.
I think I do need the ID (long type i guess) for the file stored into the external storage, however I haven't found yet how to get it
Thanks in advance for the help!
To build a File object, use the File constructor. To build a File object for a location off of a certain root directory, use the two-parameter File(File, String) constructor:
val downloadedFile = File(Environment.getExternalStorageDirectory(), "Download/$myFilename$myExtensionVar")
Unless you are getting a Uri from DownloadManager or something, there is no Uri that you need to delete().
I have more written here on external storage and how to work with it.
In Kotlin
file.deleteRecursively()
I've created a new file using XmlSerializer and StreamWriter to persist data. From the test I did with my app, storing and restoring data using this method is working. Just for curiosity, I've tried to find the file created in the Android File System, without success. I've tried to find it with an Android app (ES File Explorer) and a desktop app (on Mac, Android File Transfer). In both case, I was unable to find the created file.
XmlSerializer xmlSerializer = new XmlSerializer (typeof(GAData));
TextWriter writer = new StreamWriter (FilePath);
xmlSerializer.Serialize (writer, this);
writer.Close ();
Console.WriteLine ("Data saved");
Where FilePath is define here :
// Determining the path
var documentsPath = Environment.GetFolderPath (Environment.SpecialFolder.Personal);
FilePath = Path.Combine (documentsPath, "AppData.txt");
Console.WriteLine (Path.GetFullPath (FilePath));
The last Console.WriteLine is logging : /data/data/com.domain.myapp/files/AppData.txt
The exact same code is working like a charm on iOS, and I can see the file in the File System using an app on my Mac. Why can I find it in the Android File System? Or, where is it saved if it's somewhere else?
/data/data/com.domain.myapp/files/AppData.txt
That is private internal memory. Private as only your app has access.
Your app can reach it using getFilesDir().
Other app like ES File Explorer have no access.
You should be using FilesDir from the ContextWrapper
Path.Combine(FilesDir.Path , "MyFile.text");
Example, copies a file that is in the assets folder to the local file system
if (!File.Exists(Path.Combine(FilesDir.Path , "myFile.Text")))
{
using (var asset = Assets.Open("TextToCopyTo.txt"))
{
using (var dest = File.Create(Path.Combine(FilesDir.Path , "MyFile.text")))
{
asset.CopyTo(dest);
}
}
}
I have following question. I'd like to place a file named data.xml into sdcard/appname folder and use it for read and write application data.
So, when my main activity creates I need to check if this file exist:
public class appname extends ListActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.no_elements_l);
File file = getBaseContext().getFileStreamPath("/sdcard/appname/data.xml");
if(file.exists()) {
return;
} else {
// create a File object for the parent directory
File MTdirectory = new File("/sdcard/appname/");
// have the object build the directory structure, if needed.
MTdirectory.mkdirs();
// create a File object for the output file
File outputFile = new File(MTdirectory, "data.xml");
// now attach the OutputStream to the file object, instead of a String representation
FileOutputStream DataFile = new FileOutputStream(outputFile);
}
But I have Unhandled exception type FileNotFoundException in last line. What's the problem? Uses permission WRITE_EXTERNAL_STORAGE is added to manifest.
Don't hardcode SDCard file path. It can be different for different devices and APIs.
For example it's /mnt/sdcard/ for Froyo while that of my Galaxy Nexus (JellyBean) is /storage/sdcard0/
Android Developer's Guide recommends using Environment.getExternalStorageDirectory()
Try doing it like this:
// Some Code
String path = Environment.getExternalStorageDirectory().getPath() + "/appname/";
File file = getBaseContext().getFileStreamPath(path);
// More Code
Does the path '/sdcard/appname' exist? You check for the file before you check for the sub-directory 'appname'. You need to check if that exists before you try to access a file inside it.
Also if you simply need the file to read-write application data why not just go with internal storage - one less manifest permission :) -> read here for internal storage