package org.jorge.cmp.io.database; /* * This file is part of LoLin1. * * LoLin1 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * LoLin1 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with LoLin1. If not, see <http://www.gnu.org/licenses/>. * * Created by Jorge Antonio Diaz-Benito Soriano. */ import android.content.ContentValues; import android.content.Context; import android.content.res.Resources; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.support.annotation.NonNull; import com.crashlytics.android.Crashlytics; import org.jorge.cmp.BuildConfig; import org.jorge.cmp.R; import org.jorge.cmp.datamodel.FeedArticle; import org.jorge.cmp.datamodel.Realm; import org.jorge.cmp.io.backup.LoLin1BackupAgent; import org.jorge.cmp.util.Utils; import java.util.ArrayList; import java.util.List; import java.util.Locale; public class SQLiteDAO extends RobustSQLiteOpenHelper { public static final Object DB_LOCK = new Object(); private static final String TABLE_KEY_TIMESTAMP = "TABLE_KEY_TIMESTAMP"; private static final String TABLE_KEY_TITLE = "TABLE_KEY_TITLE"; private static final String TABLE_KEY_URL = "TABLE_KEY_URL"; private static final String TABLE_KEY_DESC = "TABLE_KEY_DESC"; private static final String TABLE_KEY_READ = "TABLE_KEY_READ"; private static final String TABLE_KEY_IMG_URL = "TABLE_KEY_IMG_URL"; private static final String COMMUNITY_TABLE_NAME = "COMMUNITY", SCHOOL_TABLE_NAME = "SCHOOL"; private final int LOLIN1_V1_59_DB_VERSION; private static Context mContext; private static SQLiteDAO singleton; public static String getCommunityTableName() { return COMMUNITY_TABLE_NAME; } public static String getNewsTableName(Realm r, String l) { return String.format(Locale.ENGLISH, mContext.getString(R.string.news_table_name_pattern) , r, l).toUpperCase(Locale.ENGLISH); } public static String getSchoolTableName() { return SCHOOL_TABLE_NAME; } private SQLiteDAO(@NonNull Context _context) { super(_context, _context.getString(R.string.database_name), null, BuildConfig.VERSION_CODE); mContext = _context; LOLIN1_V1_59_DB_VERSION = mContext.getResources().getInteger(R.integer .lolin1_v1_v59_db_version); } public synchronized static void setup(@NonNull Context _context) { if (singleton == null) { singleton = new SQLiteDAO(_context); mContext = _context; getInstance().getWritableDatabase(); //Force database creation } } public synchronized static SQLiteDAO getInstance() { if (singleton == null) throw new IllegalStateException("SQLiteDAO.setup(Context) must be called before " + "trying to retrieve the instance."); return singleton; } @Override public void onRobustUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) throws SQLiteException { if (oldVersion == LOLIN1_V1_59_DB_VERSION) { //Clean the stored data which is no longer necessary. //Stored news and account data will be lost, // but neither of them are an issue. final Resources resources = mContext.getResources(); final String[] oldTableNames = resources.getStringArray(R.array .lolin1_v1_59_news_table_names); synchronized (DB_LOCK) { for (String oldTableName : oldTableNames) db.execSQL("DROP TABLE IF EXISTS " + oldTableName); } } LoLin1BackupAgent.requestBackup(mContext); } @Override public void onCreate(SQLiteDatabase db) { super.onCreate(db); final Realm[] allRealms = Realm.getAllRealms(); final List<String> createTableCommands = new ArrayList<>(); for (Realm realm : allRealms) { for (String locale : realm.getLocales()) createTableCommands.add(("CREATE TABLE IF NOT EXISTS " + SQLiteDAO.getNewsTableName (realm, locale) + " ( " + TABLE_KEY_TIMESTAMP + " INTEGER NOT NULL ON CONFLICT IGNORE, " + TABLE_KEY_TITLE + " TEXT NOT NULL ON CONFLICT IGNORE, " + TABLE_KEY_URL + " TEXT PRIMARY KEY ON CONFLICT IGNORE, " + TABLE_KEY_DESC + " TEXT, " + TABLE_KEY_READ + " INTEGER NOT NULL ON CONFLICT IGNORE, " + TABLE_KEY_IMG_URL + " TEXT NOT NULL ON CONFLICT IGNORE " + ")").toUpperCase (Locale.ENGLISH)); } createTableCommands.add(("CREATE TABLE IF NOT EXISTS " + COMMUNITY_TABLE_NAME + " ( " + TABLE_KEY_TIMESTAMP + " INTEGER DEFAULT CURRENT_TIMESTAMP, " + TABLE_KEY_TITLE + " TEXT PRIMARY KEY ON CONFLICT IGNORE, " + TABLE_KEY_URL + " TEXT NOT NULL ON CONFLICT REPLACE, " + TABLE_KEY_DESC + " TEXT, " + TABLE_KEY_READ + " INTEGER NOT NULL ON CONFLICT IGNORE, " + TABLE_KEY_IMG_URL + " TEXT NOT NULL ON CONFLICT IGNORE " + ")").toUpperCase (Locale.ENGLISH)); createTableCommands.add(("CREATE TABLE IF NOT EXISTS " + SCHOOL_TABLE_NAME + " ( " + TABLE_KEY_TIMESTAMP + " INTEGER DEFAULT CURRENT_TIMESTAMP, " + TABLE_KEY_TITLE + " TEXT PRIMARY KEY ON CONFLICT IGNORE, " + TABLE_KEY_URL + " TEXT NOT NULL ON CONFLICT REPLACE, " + TABLE_KEY_DESC + " TEXT, " + TABLE_KEY_READ + " INTEGER NOT NULL ON CONFLICT IGNORE, " + TABLE_KEY_IMG_URL + " TEXT NOT NULL ON CONFLICT IGNORE " + ")").toUpperCase (Locale.ENGLISH)); synchronized (DB_LOCK) { for (String cmd : createTableCommands) db.execSQL(cmd); for (Realm realm : allRealms) for (String locale : realm.getLocales()) RobustSQLiteOpenHelper.addTableName(getNewsTableName(realm, locale)); RobustSQLiteOpenHelper.addTableName(COMMUNITY_TABLE_NAME); RobustSQLiteOpenHelper.addTableName(SCHOOL_TABLE_NAME); } LoLin1BackupAgent.requestBackup(mContext); } public void insertArticlesIntoTable(@NonNull List<FeedArticle> articles, @NonNull String tableName) { if (Utils.isMainThread()) { throw new IllegalStateException("Attempted call to insertArticlesIntoTable on main " + "thread!"); } SQLiteDatabase db = getWritableDatabase(); List<ContentValues> storableArticles = new ArrayList<>(); for (FeedArticle article : articles) storableArticles.add(mapFeedArticleToStorable(article)); synchronized (DB_LOCK) { db.beginTransaction(); for (ContentValues storableArticle : storableArticles) db.insert(tableName, null, storableArticle); db.setTransactionSuccessful(); db.endTransaction(); LoLin1BackupAgent.requestBackup(mContext); } } private ContentValues mapFeedArticleToStorable(FeedArticle article) { ContentValues ret = new ContentValues(); ret.put(TABLE_KEY_TITLE, article.getTitle()); ret.put(TABLE_KEY_TIMESTAMP, System.currentTimeMillis()); try { Thread.sleep(1); //To make sure that the millis time is always unique } catch (InterruptedException e) { Crashlytics.logException(e); } ret.put(TABLE_KEY_URL, article.getUrl()); ret.put(TABLE_KEY_DESC, article.getPreviewText()); ret.put(TABLE_KEY_IMG_URL, article.getImageUrl()); ret.put(TABLE_KEY_READ, article.isRead() ? 1 : 0); return ret; } private FeedArticle mapStorableToFeedArticle(Cursor articleCursor) { return new FeedArticle(articleCursor.getString(articleCursor.getColumnIndex (TABLE_KEY_TITLE)), articleCursor.getString(articleCursor.getColumnIndex(TABLE_KEY_URL)), articleCursor.getString(articleCursor.getColumnIndex(TABLE_KEY_IMG_URL)), articleCursor.getString(articleCursor.getColumnIndex(TABLE_KEY_DESC)), articleCursor.getInt(articleCursor.getColumnIndex(TABLE_KEY_READ)) == 0 ? Boolean .FALSE : Boolean.TRUE); } public List<FeedArticle> getFeedArticlesFromTable(String tableName) { List<FeedArticle> ret; SQLiteDatabase db = getReadableDatabase(); synchronized (DB_LOCK) { db.beginTransaction(); Cursor allStorableArticles = db.query(tableName, null, null, null, null, null, TABLE_KEY_TIMESTAMP + " DESC"); ret = new ArrayList<>(); if (allStorableArticles != null && allStorableArticles.moveToFirst()) { do { ret.add(mapStorableToFeedArticle(allStorableArticles)); } while (allStorableArticles.moveToNext()); } if (allStorableArticles != null) allStorableArticles.close(); db.setTransactionSuccessful(); db.endTransaction(); } return ret; } public void markArticleAsRead(FeedArticle article, String tableName) { if (Utils.isMainThread()) { throw new IllegalStateException("Attempted call to markArticleAsRead on main " + "thread!"); } SQLiteDatabase db = getWritableDatabase(); ContentValues newReadContainer = new ContentValues(); newReadContainer.put(TABLE_KEY_READ, 1); synchronized (DB_LOCK) { db.beginTransaction(); db.update(tableName, newReadContainer, TABLE_KEY_URL + " = '" + article.getUrl() + "'", null); db.setTransactionSuccessful(); db.endTransaction(); LoLin1BackupAgent.requestBackup(mContext); } } }