Nick's .NET Travels

Continually looking for the yellow brick road so I can catch me a wizard....

Windows Phone 8 SDK: Accessing Content from an SD Card

Before you get all excited and worked up about the fact that you can use an SD card with the new Windows Phone SDK there are a couple of limitations that you need to be aware of. Keep in mind that one of the primary scenarios that was being solved was the distribution of apps (errr, did I mention apps…. this is limited to Enterprise apps) and content, rather than being an extension to the device storage.

- You can only access file types that you’ve registered for (this excludes the standard file format – which is a bit nasty if you want your app to say process images or documents supplied on an SD card).

- You can only read from SD card.

Ok, with these limitations in mind, let’s take a shot at reading from an SD card.

First up, we need to get an SD card (oh and a real Windows Phone 8 device to work with) and put some content on it. In this scenario we’re going to use a sqlite database called mydata.sqlite. I simply inserted the Micro SD card, using an SD card adapter, into my laptop and copied across the existing sqlite file. I then inserted the card into my Windows Phone 8 device.

Next, I opened my Windows Phone 8 project and added a couple of nuget packages. These are only required if you’re using sqlite, if you’re using some other file type, you don’t need to worry about these packages.

wp7sqlite – This is a managed implementation of sqlite
sqlite-net – This is a super-easy wrapper that you can use in order to access the content.

image

Unfortunately, whilst these two packages were designed to work together, for some reason the versions that are in nuget are incompatible, requiring some minor modifications to get them to work. Luckily this is mostly a matter of replacing “out” with “ref” in a couple of methods.

The next thing to do is to declare our intent to a) read from the SD card and b) what file types we want to be able to access. Double-click the WMAppManifest.xml to open the designer, switch to the Capabilities tab and check the ID_CAP_REMOVABLE_STORAGE capability.

image

Close the manifest designer, right-click on the WMAppManifest.xml file and select Open With. Select XML (Text) Editor and hit Ok. You need to add a FileTypeAssociation element into the Extensions element. If Extensions doesn’t exist, you may need to create that too – it needs to sit after the Tokens element but before the ScreenResolutions element.

</Tokens>
   <Extensions>
     <FileTypeAssociation Name="sqlite" TaskID="_default" NavUriFragment="fileToken=%s">
       <SupportedFileTypes>
         <FileType ContentType="application/sqlite">.sqlite</FileType>
       </SupportedFileTypes>
     </FileTypeAssociation>
   </Extensions>
   <ScreenResolutions>

Now all that’s left is to write some code in order to access the content from the sd card. If you’re simply going to read from the files you can do so in situ. However, in this case we’re eventually going to want to write to the file, so we’re going to copy it to Isolated Storage first.

// Connect to the current SD card.
var sdCard = (await ExternalStorage.GetExternalStorageDevicesAsync()).FirstOrDefault();

var routeFiles = await sdCard.GetFileAsync("mydata.sqlite");
using (var source = await routeFiles.OpenForReadAsync())
using (
    var target = IsolatedStorageFile.GetUserStoreForApplication()
                                    .OpenFile("writeablecopy.sqlite", FileMode.Create, FileAccess.Write))
{
    await source.CopyToAsync(target);
}

var db = new SQLiteAsyncConnection("writeablecopy.sqlite");
var existing = await db.QueryAsync<MyDataObject>("select * from data");
var data = existing.ToArray();

Don’t forget you’ll want to add appropriate error detection and check that an SD card even exists before you attempt to read from it. For those following the sqlite portion, the MyDataObject class is a relatively simply class, the sqlite-net wrapper handles the conversion between the sqlite data and the .net types:

public class MyDataObject
{
    [PrimaryKey, AutoIncrement]
    public int Id { get; set; }
    public string Name { get; set; }
}

Comments are closed