/* * Copyright (c) 2015 Ngewi Fet <ngewif@gmail.com> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gnucash.android.db.adapter; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteStatement; import android.net.Uri; import android.support.annotation.NonNull; import org.gnucash.android.R; import org.gnucash.android.app.GnuCashApplication; import org.gnucash.android.db.DatabaseSchema.BookEntry; import org.gnucash.android.model.Book; import org.gnucash.android.ui.settings.PreferenceActivity; import org.gnucash.android.util.TimestampHelper; import java.util.ArrayList; import java.util.List; /** * Database adapter for creating/modifying book entries */ public class BooksDbAdapter extends DatabaseAdapter<Book> { /** * Opens the database adapter with an existing database * @param db SQLiteDatabase object */ public BooksDbAdapter(SQLiteDatabase db) { super(db, BookEntry.TABLE_NAME, new String[] { BookEntry.COLUMN_DISPLAY_NAME, BookEntry.COLUMN_ROOT_GUID, BookEntry.COLUMN_TEMPLATE_GUID, BookEntry.COLUMN_SOURCE_URI, BookEntry.COLUMN_ACTIVE, BookEntry.COLUMN_UID, BookEntry.COLUMN_LAST_SYNC }); } /** * Return the application instance of the books database adapter * @return Books database adapter */ public static BooksDbAdapter getInstance(){ return GnuCashApplication.getBooksDbAdapter(); } @Override public Book buildModelInstance(@NonNull Cursor cursor) { String rootAccountGUID = cursor.getString(cursor.getColumnIndexOrThrow(BookEntry.COLUMN_ROOT_GUID)); String rootTemplateGUID = cursor.getString(cursor.getColumnIndexOrThrow(BookEntry.COLUMN_TEMPLATE_GUID)); String uriString = cursor.getString(cursor.getColumnIndexOrThrow(BookEntry.COLUMN_SOURCE_URI)); String displayName = cursor.getString(cursor.getColumnIndexOrThrow(BookEntry.COLUMN_DISPLAY_NAME)); int active = cursor.getInt(cursor.getColumnIndexOrThrow(BookEntry.COLUMN_ACTIVE)); String lastSync = cursor.getString(cursor.getColumnIndexOrThrow(BookEntry.COLUMN_LAST_SYNC)); Book book = new Book(rootAccountGUID); book.setDisplayName(displayName); book.setRootTemplateUID(rootTemplateGUID); book.setSourceUri(uriString == null ? null : Uri.parse(uriString)); book.setActive(active > 0); book.setLastSync(TimestampHelper.getTimestampFromUtcString(lastSync)); populateBaseModelAttributes(cursor, book); return book; } @Override protected @NonNull SQLiteStatement setBindings(@NonNull SQLiteStatement stmt, @NonNull final Book book) { stmt.clearBindings(); String displayName = book.getDisplayName() == null ? generateDefaultBookName() : book.getDisplayName(); stmt.bindString(1, displayName); stmt.bindString(2, book.getRootAccountUID()); stmt.bindString(3, book.getRootTemplateUID()); if (book.getSourceUri() != null) stmt.bindString(4, book.getSourceUri().toString()); stmt.bindLong(5, book.isActive() ? 1L : 0L); stmt.bindString(6, book.getUID()); stmt.bindString(7, TimestampHelper.getUtcStringFromTimestamp(book.getLastSync())); return stmt; } /** * Deletes a book - removes the book record from the database and deletes the database file from the disk * @param bookUID GUID of the book * @return <code>true</code> if deletion was successful, <code>false</code> otherwise * @see #deleteRecord(String) */ public boolean deleteBook(@NonNull String bookUID){ Context context = GnuCashApplication.getAppContext(); boolean result = context.deleteDatabase(bookUID); if (result) //delete the db entry only if the file deletion was successful result &= deleteRecord(bookUID); PreferenceActivity.getBookSharedPreferences(bookUID).edit().clear().apply(); return result; } /** * Sets the book with unique identifier {@code uid} as active and all others as inactive * <p>If the parameter is null, then the currently active book is not changed</p> * @param bookUID Unique identifier of the book * @return GUID of the currently active book */ public String setActive(@NonNull String bookUID){ if (bookUID == null) return getActiveBookUID(); ContentValues contentValues = new ContentValues(); contentValues.put(BookEntry.COLUMN_ACTIVE, 0); mDb.update(mTableName, contentValues, null, null); //disable all contentValues.clear(); contentValues.put(BookEntry.COLUMN_ACTIVE, 1); mDb.update(mTableName, contentValues, BookEntry.COLUMN_UID + " = ?", new String[]{bookUID}); return bookUID; } /** * Checks if the book is active or not * @param bookUID GUID of the book * @return {@code true} if the book is active, {@code false} otherwise */ public boolean isActive(String bookUID){ String isActive = getAttribute(bookUID, BookEntry.COLUMN_ACTIVE); return Integer.parseInt(isActive) > 0; } /** * Returns the GUID of the current active book * @return GUID of the active book */ public @NonNull String getActiveBookUID(){ Cursor cursor = mDb.query(mTableName, new String[]{BookEntry.COLUMN_UID}, BookEntry.COLUMN_ACTIVE + "= 1", null, null, null, null, "1"); try{ if (cursor.getCount() == 0) throw new RuntimeException("There is no active book in the app. This should NEVER happen, fix your bugs!"); cursor.moveToFirst(); return cursor.getString(cursor.getColumnIndexOrThrow(BookEntry.COLUMN_UID)); } finally { cursor.close(); } } public @NonNull List<String> getAllBookUIDs(){ List<String> bookUIDs = new ArrayList<>(); try (Cursor cursor = mDb.query(true, mTableName, new String[]{BookEntry.COLUMN_UID}, null, null, null, null, null, null)) { while (cursor.moveToNext()) { bookUIDs.add(cursor.getString(cursor.getColumnIndexOrThrow(BookEntry.COLUMN_UID))); } } return bookUIDs; } /** * Return the name of the currently active book. * Or a generic name if there is no active book (should never happen) * @return Display name of the book */ public @NonNull String getActiveBookDisplayName(){ Cursor cursor = mDb.query(mTableName, new String[]{BookEntry.COLUMN_DISPLAY_NAME}, BookEntry.COLUMN_ACTIVE + " = 1", null, null, null, null); try { if (cursor.moveToFirst()){ return cursor.getString(cursor.getColumnIndexOrThrow(BookEntry.COLUMN_DISPLAY_NAME)); } } finally { cursor.close(); } return "Book1"; } /** * Generates a new default name for a new book * @return String with default name */ public @NonNull String generateDefaultBookName() { long bookCount = getRecordsCount() + 1; String sql = "SELECT COUNT(*) FROM " + mTableName + " WHERE " + BookEntry.COLUMN_DISPLAY_NAME + " = ?"; SQLiteStatement statement = mDb.compileStatement(sql); while (true) { Context context = GnuCashApplication.getAppContext(); String name = context.getString(R.string.book_default_name, bookCount); //String name = "Book" + " " + bookCount; statement.clearBindings(); statement.bindString(1, name); long nameCount = statement.simpleQueryForLong(); if (nameCount == 0) { return name; } bookCount++; } } }