/**
   * Starts a new transaction on the database. Note, that transaction can also be nested. So in
   * order to work properly ALWAYS acquire a {@link DatabaseAdapter} instance via the {@link
   * DatabaseAdapter#getInstance(Context)} method.
   *
   * @return Current {@link DatabaseAdapter} instance.
   */
  public DatabaseAdapter beginTransaction() {
    open();

    mDb.beginTransactionWithListener(TransactionListener.getFor(this));

    return this;
  }
  @Override
  public Uri insert(Uri uri, ContentValues values) {
    Uri result = null;
    boolean applyingBatch = applyingBatch();
    if (!applyingBatch) {
      mDb = mOpenHelper.getWritableDatabase();
      mDb.beginTransactionWithListener(this);
      try {
        result = insertInTransaction(uri, values);
        if (result != null) {
          mNotifyChange = true;
        }
        mDb.setTransactionSuccessful();
      } finally {
        mDb.endTransaction();
      }

      onEndTransaction();
    } else {
      result = insertInTransaction(uri, values);
      if (result != null) {
        mNotifyChange = true;
      }
    }
    return result;
  }
  @Override
  public int delete(Uri uri, String selection, String[] selectionArgs) {
    int count = 0;
    boolean applyingBatch = applyingBatch();
    if (!applyingBatch) {
      mDb = mOpenHelper.getWritableDatabase();
      mDb.beginTransactionWithListener(this);
      try {
        count = deleteInTransaction(uri, selection, selectionArgs);
        if (count > 0) {
          mNotifyChange = true;
        }
        mDb.setTransactionSuccessful();
      } finally {
        mDb.endTransaction();
      }

      onEndTransaction();
    } else {
      count = deleteInTransaction(uri, selection, selectionArgs);
      if (count > 0) {
        mNotifyChange = true;
      }
    }
    return count;
  }
  @Override
  public int bulkInsert(Uri uri, ContentValues[] values) {
    int numValues = values.length;
    mDb = mOpenHelper.getWritableDatabase();
    mDb.beginTransactionWithListener(this);
    try {
      for (int i = 0; i < numValues; i++) {
        Uri result = insertInTransaction(uri, values[i]);
        if (result != null) {
          mNotifyChange = true;
        }
        mDb.yieldIfContendedSafely();
      }
      mDb.setTransactionSuccessful();
    } finally {
      mDb.endTransaction();
    }

    onEndTransaction();
    return numValues;
  }
 @Override
 public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
     throws OperationApplicationException {
   int ypCount = 0;
   int opCount = 0;
   mDb = mOpenHelper.getWritableDatabase();
   mDb.beginTransactionWithListener(this);
   try {
     mApplyingBatch.set(true);
     final int numOperations = operations.size();
     final ContentProviderResult[] results = new ContentProviderResult[numOperations];
     for (int i = 0; i < numOperations; i++) {
       if (++opCount >= MAX_OPERATIONS_PER_YIELD_POINT) {
         throw new OperationApplicationException(
             "Too many content provider operations between yield points. "
                 + "The maximum number of operations per yield point is "
                 + MAX_OPERATIONS_PER_YIELD_POINT,
             ypCount);
       }
       final ContentProviderOperation operation = operations.get(i);
       if (i > 0 && operation.isYieldAllowed()) {
         opCount = 0;
         if (mDb.yieldIfContendedSafely(SLEEP_AFTER_YIELD_DELAY)) {
           ypCount++;
         }
       }
       results[i] = operation.apply(this, results, i);
     }
     mDb.setTransactionSuccessful();
     return results;
   } finally {
     mApplyingBatch.set(false);
     mDb.endTransaction();
     onEndTransaction();
   }
 }