Search code examples
androiddatabasesqlitexamarinandroid-contentprovider

Why is my DB empty after I insert an item using the ContentProvider in Xamarin?


I made a custom ContentProvider, and implemented the Insert method, like this:

ServiceDB _s_DB; // implements SQLiteOpenHelper, I'll add the code bellow
public const string AUTHORITY = "com.***.***.CustomProvider";
static string BASE_PATH = "accesstokens";
static string DATABASE_TABLE = "accesstokens";
public static Android.Net.Uri CONTENT_URI = Android.Net.Uri.Parse("content://" + AUTHORITY + "/" + BASE_PATH);
// MIME types used for getting a list, or a single access token
public const string MIME_TYPES = ContentResolver.CursorDirBaseType + "/vnd.com.***.***.AccessTokens";
public const string MIME_TYPE = ContentResolver.CursorItemBaseType + "/vnd.com.***.***.AccessTokens";
// Column names
public static class InterfaceConsts
{
    public const string Id = "_id";
    public const string Token = "token";
    //ubaci exparation date
}


public override Android.Net.Uri Insert(Android.Net.Uri uri, ContentValues values)
{
    Android.Util.Log.Debug("Test", "Insert");
    //---add a new token---  
    var _database = _s_DB.WritableDatabase;
    _database.BeginTransaction();
    long rowID = _database.Insert(DATABASE_TABLE, "",  values);

    //---if added successfully---  
    if (rowID > 0)
    {
        var _uri = ContentUris.WithAppendedId(CONTENT_URI, rowID);
        Context.ContentResolver.NotifyChange(_uri, null);
        _database.SetTransactionSuccessful();
        _database.EndTransaction();
        return _uri;
    }
    throw new SQLException("Failed to insert row into " + uri);
}

My object ServiceDB _s_DB has this method implemented:

const string DatabaseName = "accesstokens.db";
const string DatabaseTable = "accesstokens";
const string create_table_sql = "CREATE TABLE " + DatabaseTable + " (_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE, token TEXT NOT NULL UNIQUE)";
const int DatabaseVersion = 1;
public ServiceDB(Context context) : base(context, DatabaseName, null, DatabaseVersion) { }


public override void OnCreate(SQLiteDatabase db)
{
    Android.Util.Log.Debug("Test", "Service Database Created");
    db.ExecSQL(create_table_sql);
    // seed with data
    db.ExecSQL("INSERT INTO accesstokens (token) VALUES ('token1')");
    db.ExecSQL("INSERT INTO accesstokens (token) VALUES ('token2')");
}

And finaly, my MainActivity class, where I first read the 2 automatically created tokens, then add a third token, then read all three again...

MainActivity onCreate has this:

string[] projection = new string[] { CustomProvider.InterfaceConsts.Id, CustomProvider.InterfaceConsts.Token};
string[] fromColumns = new string[] { CustomProvider.InterfaceConsts.Token };

    // CursorLoader introduced in Honeycomb (3.0, API_11)
    var loader = new CursorLoader(this, CustomProvider.CONTENT_URI, projection, null, null, null);
    var cursor = (ICursor)loader.LoadInBackground();
    if (cursor != null)
    {
        while (cursor.MoveToNext())
        {
            String s = cursor.GetString(cursor.GetColumnIndexOrThrow("token"));
            Android.Util.Log.Debug("Test","aaa " + s);
        }
        cursor.Close();
    }

    Android.Util.Log.Debug("Test", "Create new item");
    ContentValues content = new ContentValues();
    content.Put(CustomProvider.InterfaceConsts.Id, "3"); 
    content.Put(CustomProvider.InterfaceConsts.Token, "token3");
    var ddd = ApplicationContext.ContentResolver.Insert(CustomProvider.CONTENT_URI, content);

    Android.Util.Log.Debug("Test", "ddd: " + ddd);
    ICursor c = ApplicationContext.ContentResolver.Query(CustomProvider.CONTENT_URI, null, null, null, null);

    if (c != null)
    {
        while (c.MoveToNext())
        {
            String s = c.GetString(c.GetColumnIndexOrThrow("token"));
            Android.Util.Log.Debug("Test","ccc " + s);
        }
        c.Close();
    }

Then, I take my created DB from the box (I work on an AndroidTV) and open it with a DB Browser, and the DB is empty, even the table is not created!!

The output from the console shows:

aaa token1
aaa token2

Insert

ddd: content://com.***.***.CustomProvider/accesstokens/3

ccc token1
ccc token2
ccc token3

Again, I close the app, then extract the accesstokens.db from the tv, then open it with a DB Browser, and not even the table is created, and ofc no rows as well.

What am I missing?

Thank you for your time.

Edit 1:

It may have something to do with the permissions, so here are the providers attributes:

[ContentProvider(new string[] { CustomProvider.AUTHORITY }, Exported = true, GrantUriPermissions = true, Label ="CustomProvider")]

Edit 2: I have managed to capture this error in the console:

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.***.***/com.***.***.MainActivity}: android.database.SQLException: Failed to insert row into content://com.***.***.CustomProvider/accesstokens

Solution

  • Turns out, I asked for permission to the content provider in my main activity via this line:

    ApplicationContext.ContentResolver.TakePersistableUriPermission(CustomProvider.CONTENT_URI, Android.Content.ActivityFlags.GrantWriteUriPermission);
    

    But since, this is the app that originally has the content provider component that I am using, it does not need that permission, since it already has it by default.

    So, removing it solved my issue.