Accessing the External SD Card

We're using Xamarin and C# to convert our application from a 9590 to the TC75 and we're having issues accessing the external SD card.

When I run: Android.App.Application.Context.GetExternalFilesDirs( null );

Samsung S5 returns

files[0] == /storage/emulated/0/files/

files[1] == /storage/extSdCard/files/

TC70 returns

files[0] == /storage/sdcard0/files/

files[1] == /storage/sdcard1/files/

TC75 returns

files[0] == /storage/emulated/0/files

files[1] == /storage/sdcard0/files

When we run: Android.OS.Environment.ExternalStorageDirectory.AbsolutePath and there is an SD card in the device:

Path TC70 returns

/storage/sdcard0/ --> and the application writes to the SD card

Path TC75 returns

/storage/emulated/0/ --> and the application writes to the internal memory

Path Samsung returns

/storage/emulated/0/ --> and the application writes to the internal memory

We want to store our database on the SD card, as we currently do on the 9590.

We can't use the first method, as this adds too much risk to supporting the application in the field, because this forces us to send out a new database every time the app is updated and if someone from the support team tries to re-install the app without making a copy of the database first, data will be lost.

The second process only works on the TC70. When there is an SD card in the device, the call returns the actual SD card, not the internal storage. The TC75 responds the same way the Samsung device does, in that there is no reliable way to determine if there is an SD installed and then no reliable way to access the card. If we just try and manually put the path in, then the files are marked as read only and cannot be updated.

Pietro Francesc...
Hi Tom,you can usually use

Hi Tom,

you can usually use environment variable to get the External SDCard path.

Here you can find a description of the details:

http://pietromaggi.com/blog/finding-the-sdcard-path-on-android-devices/

and here the linked sample, is in Java but should be straightforward to convert it to C# in Xamarin:

https://github.com/pfmaggi/GetDeviceInfo

This was written before the final release of TC70, however the informations should be still valid.

Regarding TC75, we're working to provide a consistent behaviour.

Best regards

~Pietro

Vote: 
Vote up!
Vote down!

Points: 0

You voted ‘up’


Tom Flynn
Pietro,Thanks for the quick

Pietro,

Thanks for the quick update.

I tested this and while it works to get us the path to the SD card, we're still unable to use this as the main storage, (on the TC75), because when the database is placed in a folder on the drive and then accessed we're unable to update the database as it is marked as read only.

This is not the case on the TC70, we're able to update the database when we have it on the SD card.

-Tom

Vote: 
Vote up!
Vote down!

Points: 0

You voted ‘up’


Pietro Francesc...
Hi Tom,TC75 and other KitKat

Hi Tom,

TC75 and other KitKat devices will adopt the Android habit to have the internal storage as Primary External Storage and the SDCard as Secondary External Storage.

From this choice (common with all the major OEM) came the behaviour that an app cannot write on the SDCard outside of its package-specific folder.

These are Android rules:

Secondary external storage must not be writable by apps, except in package-specific directories as allowed by synthesized permissions.

https://source.android.com/devices/storage/

This means that, if you want to a DB on an SDCard, install it in the TC75 and then have R/W access to it, you need to put the files in the package-specific directory /Android/data/<package.name>.

Android KitKat introduced some API to get the path of these "package-specific" directory: GetExternalFilesDirs() this is a new API introduced with KitKat (API Level 19) that support having multiple External Storages.

You can use below code to retrieve the correct "package-specific" path name on the SDCard calling it with your application context. You need API Level 21 to compile this code, but it work even on older OSes.

getExternalFilesDir(getApplicationContext());

This is the function implementation coming from this stackoverflow thread.

    @TargetApi(21)

    public static File getExternalFilesDir(Context context) {

        File extFiles[] = ContextCompat.getExternalFilesDirs(context, null);

        File sd = null;

        int currentapiVersion = android.os.Build.VERSION.SDK_INT;

        if (currentapiVersion >= android.os.Build.VERSION_CODES.LOLLIPOP) {

            // Use the isExternalStorageRemovable function added in Lollipop

            for (File file : extFiles) {

                if (Environment.isExternalStorageRemovable(file)) {

                    sd = file;

                }

            }

        } else if (currentapiVersion >= android.os.Build.VERSION_CODES.KITKAT) {

            // getExternalFilesDirs will return all storages on KitKat. Assume

            // the second storage is the sd card

            if (Environment.isExternalStorageRemovable()) {

                sd = extFiles[0];

            } else if (extFiles.length >= 2) {

                sd = extFiles[1];

            }

        } else if (currentapiVersion >= android.os.Build.VERSION_CODES.GINGERBREAD) {

            // Use the secondary storage if the primary one is not removable

            if (Environment.isExternalStorageRemovable()) {

                sd = extFiles[0];

            } else {

                String secStore = System.getenv("SECONDARY_STORAGE");

                String packageName = context.getPackageName();

                if (secStore != null) {

                    sd = new File(secStore + "/Android/data/" + packageName + "/files");

                }

            }

        } else {

            // Froyo and Eclair do not have emulated storages

            sd = extFiles[0];

        }

        if (sd != null && !sd.exists()) {

            sd.mkdirs();

        }

        if (sd != null) {

            Log.v("getExternalFilesDir", sd.getAbsolutePath());

        }

        return sd;

    }

~Pietro

Vote: 
Vote up!
Vote down!

Points: 0

You voted ‘up’


Tom Flynn
Thanks Pietro,I've already

Thanks Pietro,

I've already tried this approach, and it doesn't work for us for a couple of reasons, mainly due to the fact that it causes a lot of extra risk for us whenever we send out an update to the application.

-Tom

Vote: 
Vote up!
Vote down!

Points: 0

You voted ‘up’


Pietro Francesc...
Can you elaborate on your

Can you elaborate on your latest statement?

you can reach out to me privately if you prefer. I'm pietro.maggi at  zebra

~Pietro

Vote: 
Vote up!
Vote down!

Points: 0

You voted ‘up’


Log in to post comments