In my app, I store the user's app data using MySQLite databases, and I allow the user to backup the app data to the SD card within a folder that I create on the SD card (let's call it MyAppFolder). On Android devices that have only a single SD card slot, everything works fine (e.g. my Droid).
However, on devices such as the Galaxy S that have more than 1 SD card, things don't work. Unfortunately, I don't actually have one of these devices, so I can't debug anything, I can only go by user reports. I also did some searching and found this is a known issue. However, I did not find any solutions that didn't involve just hardcoding the other paths that get used, so I'm looking for some help with that.
In my app, I check and see if MyAppFolder already exists. If not, I create that folder. The folder is always created successfully, although it is created on the "internal" memory slot returned by getExternalStorageDirectory() when there are 2 slots present. However, the files do not get created and copied there. I don't understand why the folder is created, but the files are not created.
Can someone tell me how I can modify this code to work for devices with 2 card slots as well as 1 card slot? I'd prefer not to hard-code locations to check, but if that's the only way, I'll do it just to get things working.
Here is the code I use(slightly modified to make more readable here):
File root = Environment.getExternalStorageDirectory();
String state = Environment.getExternalStorageState();
if( Environment.MEDIA_MOUNTED.equals(state))
{
File dataDirectory = Environment.getDataDirectory();
if (root.canWrite())
{
String savePath = root + "/MyAppFolder/";
File directory = new File(savePath);
if( !directory.exists() )
{
directory.mkdirs(); //folder created successfully
}
String currentDBPath = "\\data\\my_app\\databases\\database.db";
File currentDB = new File(datDirectory, currentDBPath);
File backupDB = new File(savePath, "database.db");
if (currentDB.exists())
{
FileChannel src = new FileInputStream(currentDB[i]).getChannel();
FileChannel dst = new FileOutputStream(backupDB[i]).getChannel();
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
}
}
Can someone tell me how I can modify this code to work for devices with 2 card slots as well as 1 card slot?
"External storage" does not mean "removable storage". "External storage" means "mountable storage" -- IOW, the user has access to that storage when they plug their device into a host machine via a USB cable.
Android, at present, is designed to allow developers to write things to one external storage point, and it is up to the device manufacturer whether that is fixed flash or something removable. Hence, you should be backing things up to external storage, not thinking that you are backing things up to an SD card.
Can someone tell me how I can modify this code to work for devices with 2 card slots as well as 1 card slot?
Use getDatabasePath() to get a database path, rather than the gyrations you are presently going through. Never use concatenation to create paths, the way you are with root + "/MyAppFolder/" -- use a File constructor, as you are elsewhere. Make sure you hold the WRITE_EXTERNAL_STORAGE permission. Beyond that, this should work fine for any device with sufficient external storage to hold your database, regardless of how many "card slots" the device may have.
Related
I have a requirement for my Android app to be able to receive files from a server and save them in an arbitrary location on my SD card. When I say "SD card", I don't mean the /sdcard/ directory - AKA /storage/emulated/0/, I mean the physical removable SD card.
My initial attempt:
FileOutputStream(File("storage/ABCD-1234", "test_file.txt")).use {
it.write("Hello world!".toByteArray())
}
where "ABCD-1234" is replaced with the actual storage ID string. This gives me an IOException saying permission denied, even though I have the WRITE_EXTERNAL_STORAGE permission granted. Fine, there must just be some restrictions on SD card writing access. So I spent most of today researching around the internet for apps which can already do this. And I come across this one called SSH/SFTP Server, using which I was able to push a file to the device's SD card via WinSCP from a laptop over WiFi. The thing is, I can't figure out how to go about requesting this ability in my app. Can anyone point me in the right direction?
This is targeting Android Q (10), in case it matters at all.
Using Android 6.0.1 (API23) on a Nexus 5X,
The different Android versions and how to access an external storage (i.e. USB-Stick in my case) can be very confusing. From what I understood, you need to give permissions in your manifest, as follows:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
After that you should be able to access external storage..
Here is my approach on how to get the external-storage path, using "getExternalFilesDirs()" method. But it turns out, only the internal SD card is recognized (i.e. first array-element returned by the method).
From what I read, there should be more array-elements returned with the "getExternalFilesDirs()"-method if more external memory devices are connected to the phone. But in my case, none of them is available.
Here is my code :
String strInfo = "";
File folders[] = getExternalFilesDirs(null);
// File folders[] = ContextCompat.getExternalFilesDirs(this,null);
strInfo += "\ngetExternalFilesDirs(null):\n";
for(File f : folders){
strInfo += f.getAbsolutePath() + "\n";
}
if (folders.length > 1) {
Toast.makeText(this, "nr of folders = " + folders.length + "/ info = " + strInfo, Toast.LENGTH_SHORT).show();
File myFile = new File(folders[1], "testfile.txt");
} else {
// !!!!!!!!!!!!! Keep ending up in this case, even tough a USB-stick is
// connected to the phone (...also tried with a second SD-card
// connected...but same thing, keeeping up ending here...
// any idea WHY ???????????????
Toast.makeText(this, "No external storage device found / info = " + strInfo, Toast.LENGTH_SHORT).show();
}
Eventually, I would like to create files and folders on an externally connected USB-stick. Can anybody tell me a reliable method on how to create new files and folders on an USB-stick connected to an Android-6.0.1 phone ??
I appreciate it.
To close this off: USB-sticks do not seem to work as SECONDARY STORAGE (= external storage) for Android-mobile phones, unfortunately.
I just found this link, saying:
External storage devices returned here are considered a permanent part
of the device, including both emulated external storage and physical
media slots, such as SD cards in a battery compartment. The returned
paths do not include transient devices, such as USB flash drives.
And similar, the official Android documentation about getExternalFilesDirs() sais:
The returned paths do not include transient devices, such as USB flash drives
connected to handheld devices.
If anybody knows a C-Type USB-stick that does fulfill the requirements of a SECONDARY STORAGE, please let me know ! The same, if anybody knows USB-C-type SDcard-Adapters that work as SECONDARY STORAGE, please let me know...
Too bad, I am not able to write files & folders from my java android app to an external USB-stick :(
So the app I'm working on is making use of the external files dir to store some downloaded information. The issue I'm running into is, very ocassionally the getExternalFilesDir() method will return null. My understanding of this method might be flawed, but I was under the impression that it would return null under TWO distinct scenarios:
1) The user has an SD card slot but does NOT have an sd card mounted (or the SD card is mounted but plugged into a computer as mass storage option)
2) The app does not actually have read/write permissions
The app has the read/write permissions (it must since it works in most scenarios), and I I'm checking for the first case with the following code:
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()))
{
editor.putBoolean(AUDIO_LOCATION_KEY, true);
subPath = context.getExternalFilesDir(null).getPath();
}
The app is crashing with a null pointer exception from the getExternalFilesDir line. I can't reproduce the crash, Is there something I'm missing? I would've that that if that check passed that we'd be guaranteed to get something back. Any insight would be much appreciated!
Thanks.
I'm not sure what the source of the problem is - older android version with bug or if I'm doing something wrong, but my problem is that no matter what I do, android reports the SD card as mounted. Even if it's not physically in the tablet (archos 7o)..
public boolean saveToDisk(String filename, String header) {
/* first check to see if the SD card is mounted */
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
//throw some exception so we can display an error message
// XXX
return false;
}
try {
File root = Environment.getExternalStorageDirectory();
File dir = new File(root.getAbsolutePath() + "/bioz");
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(dir, filename);
....
The first test is always true, getExternalStorageDirectory() responds with /mnt/storage and the test to see if /mnt/storage/bioz exists indicates that the directory does exist.
Any idea what's going on? Am I doing something wrong, is the API broken, or something else?
Thanks,
Reza
External storage is not the same as SD card, at least not on all devices. Devices that have internal flash memory (for example my Nexus S does) threat this as " external storage".
Now, devices that have both internal flash and SD card, threat internal flash as external memory and SD card is then added as directory under this external memory.
From programmers view it's a pain, but not much we can do about it.
I almost feel embarrased asking this but I really can't find my app on my phone. I read that the app should be installed in /data/data/ folder but my /data folder appears to be empty when viewed in Astro. My app is most definitely installed on the phone, should I transfer it to the SD for it to become visible? I have an unrooted HTC Desire HD on Orange UK. I just need to have a peek at the SQLite database managed by my App.
I almost feel embarrased asking this but I really can't find my app on my phone.
Look for it in Settings > Applications to see if it is installed. If it is, any activities you declared in the LAUNCHER category (action MAIN) will appear in the home screen launcher.
I read that the app should be installed in /data/data/ folder but my /data folder appears to be empty when viewed in Astro.
You do not have permissions to view that directory on an un-rooted phone.
My app is most definitely installed on the phone, should I transfer it to the SD for it to become visible?
Your app will not be any more "visible".
I just need to have a peek at the SQLite database managed by my App.
Add a backup/restore feature to your app that copies your SQLite file to/from external storage. Be sure all your SQLiteDatabase objects are closed first, though.
Simple answer would be,
If you need to browse the "Data" directory in actual phone, it should be a rooted device.
You can simply browse "Data" directory in simulators, because, Simulator act as a rooted device and it has all the super user access.
Happy coding!!!
Update
There is another way by copying database to your SD card from below function,
public void exportDatabse(String databaseName) {
try {
File sd = Environment.getExternalStorageDirectory();
File data = Environment.getDataDirectory();
if (sd.canWrite()) {
String currentDBPath = "//data//"+getPackageName()+"//databases//"+databaseName+"";
String backupDBPath = "backupname.db";
File currentDB = new File(data, currentDBPath);
File backupDB = new File(sd, backupDBPath);
if (currentDB.exists()) {
FileChannel src = new FileInputStream(currentDB).getChannel();
FileChannel dst = new FileOutputStream(backupDB).getChannel();
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
}
}
} catch (Exception e) {
}
}
root is not necessary if your application is debuggable (android:debuggable="true") see How can I see the contents of a sqlite db of my app in a real device?