getFilesDir() vs getDatabasePath() - android

I am in the process of implementing access to a SQLite database via SQLCipher in my hybrid Cordova app for Android which uses one custom plugin (i.e. written by me). The SQLCipher documentation - as well as other tutorials on using SQLite in Android - keep referring to Context.getDatabasePath. In my plugin code I store other app files and make extensive use of Context.getFilesDir. In what way is getDatabasePath different from getFilesDir. For instance, does it promise a better chance that the database will persist and not somehow get dumped because the OS decides to create "some more room" by deleting some files stored in Context.getFilesDir?

Both are resolved to the same directory. getDatabasePath calls getDatabasesDir.
getDatabasesDir:
private File getDatabasesDir() {
synchronized (mSync) {
if (mDatabasesDir == null) {
if ("android".equals(getPackageName())) {
mDatabasesDir = new File("/data/system");
} else {
mDatabasesDir = new File(getDataDir(), "databases");
}
}
return ensurePrivateDirExists(mDatabasesDir);
}
}
getFilesDir:
#Override
public File getFilesDir() {
synchronized (mSync) {
if (mFilesDir == null) {
mFilesDir = new File(getDataDir(), "files");
}
return ensurePrivateDirExists(mFilesDir);
}
}
Notice the returned File is resolved by ensurePrivateDirExists in both method, which has the same input directory resolved by getDataDir.
getDataDir
Returns the absolute path to the directory on the filesystem where all
private files belonging to this app are stored.
So, there is NO difference in your case.
Do not forget the returned path can change, as the doc says:
The returned path may change over time if the calling app is moved to
an adopted storage device, so only relative paths should be persisted.

Related

Android Studio: context.getFilesDir() returns a path [/data/user/0/com.example.filesexperimenting/files/] that I can not find. What am I missing?

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()

How to access Steaming Assets folder?

So in my project I have an image object that loads various different sprites throughout the game. In order to implement this, I have a folder with a bunch of .pngs that the game accesses. In order for this to work in a standalone build I put these images in the following path:
StreamingAssets/Question Images
However, in Android I am getting an error because, as the manual says:
"On Android, the files are contained within a compressed .jar file (which is essentially the same format as standard zip-compressed files). This means that if you do not use Unity’s WWW class to retrieve the file, you need to use additional software to see inside the .jar archive and obtain the file.
The thing is I have no idea how to go about implementing this, any ideas?
From Application.streamingAssetsPath
It is not possible to access the StreamingAssets folder on WebGL and Android platforms. No file access is available on WebGL. Android uses a compressed .apk file. These platforms return a URL. Use the UnityWebRequest class to access the Assets.
Example using UnityWebRequestTexture
private void Start()
{
StartCoroutine(GetTexture());
}
private IEnumerator GetTexture()
{
// in general I would always avoid to have spaces in file-paths
var path = Path.Combine(Application.streamingAssetsPath, "QuestionImages", "exampleImage.png");
using(var www = UnityWebRequestTexture.GetTexture(path))
{
yield return www.SendWebRequest();
if(www.isNetworkError || www.isHttpError)
{
Debug.LogErrorFormat(this, "Unable to load texture due to {0} - {1}", www.responseCode, www.error);
}
else
{
Texture myTexture = ((DownloadHandlerTexture)www.downloadHandler).texture;
}
}
}
Note: Typed on smartphone so no warrenty but I hope the idea gets clear.

Can't create an SQLite table on Android

I have been following this tutorial in its entirety, on how to store data in a local database. On iOS everything works fine, unfortunately on android i get an exception upon creating a table in my database file. The method for creating the table is:
public CalcDatabase(string dbPath)
{
database = new SQLiteAsyncConnection(dbPath);
database.CreateTableAsync<Calculation>().Wait();
}
And my custom class for getting the database path on Android is as follows:
public class LocalFileHelper : IFileHelper
{
public string GetLocalFilePath(string filename)
{
var path = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
path = Path.Combine(path, filename);
if(!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
return path;
}
}
This is the error that im getting from SQLite:
Could not open database file: /data/user/0/com.companyname.XX/files/Calculation.db3 (CannotOpen)
I have tried targeting several different APIs, I have tried to clean and rebuild my solution, and I have tried to reinstall the SQLite-net-pcl NuGet packages across the whole solution. Nothing works.
I have read, that it has to do with permissions, still, I do however find it strange that the official Microsoft tutorial doesn't cover this if it was a thing.
You need to check out the Android Permissions declaration (and grants) for Write and Read to external storage on your device or emulator.
It must be enough.
Make sure to delete bin and obj folders, clean and rebuild solution to get rid of any cached data on app compilation.

Creating SQLite database on Internal storage ANDROID

I am trying to create a SQLIte database on Internal Storage in my Xamarin application. I am creating a system for an offline environment where at least 3 applications are inter-connected with shared data. So if one application creates some data the other application should be able to read that.
The Database project is a Portable Class Library which I plan to include in all three applications.
My DbHelper is as follows:
public Database()
{
string folder = "/data/data/Anchor.Main";
_dbPath = System.IO.Path.Combine(folder, "Anchor.db");
try
{
if (!System.IO.Directory.Exists(folder))
{
System.IO.Directory.CreateDirectory(folder); //Getting error here
}
_connection = new SQLiteConnection(_dbPath);
_connection.CreateTable<Orders>();
_connection.CreateTable<Items>();
}
catch(Exception ex)
{ }
}
I get error which says
Access to the path "/data/data/Anchor.Main" is denied.
Following is the stack trace
at System.IO.Directory.CreateDirectoriesInternal (System.String path)
[0x0004b] in <3fd174ff54b146228c505f23cf75ce71>:0 at
System.IO.Directory.CreateDirectory (System.String path) [0x00075] in
<3fd174ff54b146228c505f23cf75ce71>:0 at
anchorapp.db.Helper.Database..ctor () [0x0002e]
I understand this is because of the permissions, but what permissions do I need to set in order to write to the Internal storage from a PCL?
You are accessing Android system folder of "/data/data/Anchor.Main".
Your app internal storage will be in
/data/data/#PACKAGE_NAME#/files
You can use the following code to get the folder to store the database:
// Equal to /data/data/#PACKAGE_NAME#/files
var homePath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
_dbPath = System.IO.Path.Combine(homePath, "Anchor.db");
_connection = new SQLiteConnection(_dbPath);
Android and iOS does not work that way. Each platform keeps apps in a sandboxed environment. If you want to create and store data in your app you need to create the path like the following sample:
var docFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
_dbPath = System.IO.Path.Combine(docFolder, "Anchor.db");
Also set the permissions ReadExternalStorage and WriteExternalStorage in your Android project
Instead of writing to a private folder, you could create the database in a public one. To do so the docFolder would change to:
var docFolder = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, "YourDirectoryName");
Please be advised, that if you go that way everyone can see and open the database.
I did this by creating a sharedId within my projects and using the SharedContext to access the databases created by other apps

Something is deleting image files from getExternalFilesDir()?

I'm having a hard time figuring out what is going on with my app. Various users have reported that the app generated image files are gone. However, the database data isn't gone and it is stored at the common location Context.getDatabasePath(). Also, all folders are kept intact just images missing.
So I'm thinking there is some routine in Android causing this? Or some other app cleaning up *.png files? I know my app isn't removing them since I don't have any routine to recursively remove all image files.
Also, the parent folder has the .nomedia file so all child folders shouldn't be touched by the gallery right?
I'm storing these files inside the following path structure where %d is a unique number:
getExternalFilesDir()/projects/p_%d/l_%d/%d.png
This is how I get the projects path creates:
public static File getProjectsDir(Context context)
{
// External app directory handled by the OS. Meaning that when the app is uninstalled all
// the data inside this folder will be also removed.
File appRoot = context.getExternalFilesDir(null);
if (null == appRoot) {
Log.e(TAG,"getProjectsDir() -> External storage not accessible!");
return null;
}
File projectsDir = new File(appRoot, "projects");
// create projects directory
if (!projectsDir.exists()) {
if (!projectsDir.mkdir()) {
Log.e(TAG,"getProjectsDir() -> Unable to create projects folder!");
return null;
} else {
File noMediaFile = new File(projectsDir, ".nomedia");
if (!noMediaFile.exists()) {
try {
if (!noMediaFile.createNewFile()) {
Log.e(TAG,"getProjectsDir() -> no media file failed to be created!");
}
} catch (IOException e) {
Log.e(TAG,"getProjectsDir() -> no media file failed to be created!",e);
}
}
}
}
return projectsDir;
}
According to this, it appears that the DownloadManager deletes any files from third-party apps that haven't been accessed (i.e. "UI-Visible") in over 7 days.
The workaround would be to rename the file after it's downloaded so the DownloadManager can no longer track it.

Categories

Resources