/*
* Overchan Android (Meta Imageboard Client)
* Copyright (C) 2014-2016 miku-nyan <https://github.com/miku-nyan>
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
package nya.miku.wishmaster.ui;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import nya.miku.wishmaster.api.ChanModule;
import nya.miku.wishmaster.api.models.UrlPageModel;
import nya.miku.wishmaster.common.Logger;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;
/**
* Работа с базой данных
* @author miku-nyan
*
*/
public class Database {
private static final String TAG = "Database";
private static final int DB_VERSION = 1000;
private static final String DB_NAME = "database.db";
private static final String TABLE_HIDDEN = "hiddenitems";
private static final String TABLE_HISTORY = "history";
private static final String TABLE_FAVORITES = "favorites";
private static final String TABLE_SAVED = "saved";
private static final String COL_CHAN = "chan";
private static final String COL_BOARD = "board";
private static final String COL_BOARDPAGE = "boardpage";
private static final String COL_THREAD = "thread";
private static final String COL_POST = "post";
private static final String COL_TITLE = "title";
private static final String COL_URL = "url";
private static final String COL_DATE = "date";
private static final String COL_FILEPATH = "filepath";
private static final String NULL = "NULL";
private final DBHelper dbHelper;
public Database(Context context) {
dbHelper = new DBHelper(context);
}
private static String fixNull(String s) {
return s != null && s.length() > 0 ? s : NULL;
}
public static boolean isNull(String s) {
return s == null || s.equals(NULL);
}
/* *********************** HIDDEN ITEMS *********************** */
public interface IsHiddenDelegate {
public boolean isHidden(String chan, String board, String thread, String post);
}
private IsHiddenDelegate isHiddenDelegate = new IsHiddenDelegate() {
@Override
public boolean isHidden(String chan, String board, String thread, String post) {
return Database.this.isHidden(chan, board, thread, post);
}
};
public IsHiddenDelegate getDefaultIsHiddenDelegate() {
return isHiddenDelegate;
}
public IsHiddenDelegate getCachedIsHiddenDelegate(final String fchan, final String fboard, final String fthread) {
Cursor c = dbHelper.getReadableDatabase().query(TABLE_HIDDEN, null,
COL_CHAN + " = ? AND " + COL_BOARD + " = ? AND " + COL_THREAD + " = ?",
new String[] { fixNull(fchan), fixNull(fboard), fixNull(fthread) }, null, null, null);
final List<String> hiddenList;
if (c != null && c.moveToFirst()) {
hiddenList = new ArrayList<String>(c.getCount());
int postIndex = c.getColumnIndex(COL_POST);
do {
hiddenList.add(c.getString(postIndex));
} while (c.moveToNext());
} else {
hiddenList = Collections.emptyList();
}
if (c != null) c.close();
return new IsHiddenDelegate() {
@Override
public boolean isHidden(String chan, String board, String thread, String post) {
if (fchan != chan && !fchan.equals(chan)) return false;
if (fboard != board && !fboard.equals(board)) return false;
if (fthread != thread && !fthread.equals(thread)) return false;
return hiddenList.contains(post);
}
};
}
public void addHidden(String chan, String board, String thread, String post) {
if (isHidden(chan, board, thread, post)) {
Logger.d(TAG, "entry is already exists");
return;
}
ContentValues value = new ContentValues(4);
value.put(COL_CHAN, fixNull(chan));
value.put(COL_BOARD, fixNull(board));
value.put(COL_THREAD, fixNull(thread));
value.put(COL_POST, fixNull(post));
dbHelper.getWritableDatabase().insert(TABLE_HIDDEN, null, value);
}
public void removeHidden(String chan, String board, String thread, String post) {
dbHelper.getWritableDatabase().delete(TABLE_HIDDEN,
COL_CHAN + " = ? AND " + COL_BOARD + " = ? AND " + COL_THREAD + " = ? AND " + COL_POST + " = ?",
new String[] { fixNull(chan), fixNull(board), fixNull(thread), fixNull(post) });
}
public boolean isHidden(String chan, String board, String thread, String post) {
Cursor c = dbHelper.getReadableDatabase().query(TABLE_HIDDEN, null,
COL_CHAN + " = ? AND " + COL_BOARD + " = ? AND " + COL_THREAD + " = ? AND " + COL_POST + " = ?",
new String[] { fixNull(chan), fixNull(board), fixNull(thread), fixNull(post) }, null, null, null);
boolean result = false;
if (c != null && c.moveToFirst()) result = true;
if (c != null) c.close();
return result;
}
/* *********************** HISTORY *********************** */
public static class HistoryEntry {
public final String chan;
public final String board;
public final String boardPage;
public final String thread;
public final String title;
public final String url;
public final long date;
public HistoryEntry(String chan, String board, String boardPage, String thread, String title, String url, long date) {
this.chan = chan;
this.board = board;
this.boardPage = boardPage;
this.thread = thread;
this.title = title;
this.url = url;
this.date = date;
}
}
public void addHistory(String chan, String board, String boardPage, String thread, String title, String url) {
removeHistory(chan, board, boardPage, thread);
ContentValues values = new ContentValues(6);
values.put(COL_CHAN, fixNull(chan));
values.put(COL_BOARD, fixNull(board));
values.put(COL_BOARDPAGE, fixNull(boardPage));
values.put(COL_THREAD, fixNull(thread));
values.put(COL_TITLE, fixNull(title));
values.put(COL_URL, fixNull(url));
values.put(COL_DATE, System.currentTimeMillis());
dbHelper.getWritableDatabase().insert(TABLE_HISTORY, null, values);
}
public void removeHistory(String chan, String board, String boardPage, String thread) {
dbHelper.getWritableDatabase().delete(TABLE_HISTORY,
COL_CHAN + " = ? AND " + COL_BOARD + " = ? AND " + COL_BOARDPAGE + " = ? AND " + COL_THREAD + " = ?",
new String[] { fixNull(chan), fixNull(board), fixNull(boardPage), fixNull(thread) });
}
public List<HistoryEntry> getHistory() {
List<HistoryEntry> list = new ArrayList<HistoryEntry>();
Cursor c = dbHelper.getReadableDatabase().query(TABLE_HISTORY, null, null, null, null, null, COL_DATE + " desc", "200");
if (c != null && c.moveToFirst()) {
int chanIndex = c.getColumnIndex(COL_CHAN);
int boardIndex = c.getColumnIndex(COL_BOARD);
int boardpageIndex = c.getColumnIndex(COL_BOARDPAGE);
int threadIndex = c.getColumnIndex(COL_THREAD);
int titleIndex = c.getColumnIndex(COL_TITLE);
int urlIndex = c.getColumnIndex(COL_URL);
int dateIndex = c.getColumnIndex(COL_DATE);
do {
list.add(new HistoryEntry(c.getString(chanIndex), c.getString(boardIndex), c.getString(boardpageIndex),
c.getString(threadIndex), c.getString(titleIndex), c.getString(urlIndex), c.getLong(dateIndex)));
} while (c.moveToNext());
}
if (c != null) c.close();
return list;
}
public void clearHistory() {
dbHelper.recreateHistory();
}
/* *********************** FAVORITES *********************** */
public class FavoritesEntry {
public final String chan;
public final String board;
public final String boardPage;
public final String thread;
public final String title;
public final String url;
private FavoritesEntry(String chan, String board, String boardPage, String thread, String title, String url) {
this.chan = chan;
this.board = board;
this.boardPage = boardPage;
this.thread = thread;
this.title = title;
this.url = url;
}
}
public int getCnf() {
Cursor c = dbHelper.getReadableDatabase().query(TABLE_FAVORITES, null, null, null, null, null, BaseColumns._ID + " desc");
int r = c.getCount();
if (c != null) c.close();
return r;
}
public void addFavorite(String chan, String board, String boardPage, String thread, String title, String url) {
removeFavorite(chan, board, boardPage, thread);
ContentValues values = new ContentValues(6);
values.put(COL_CHAN, fixNull(chan));
values.put(COL_BOARD, fixNull(board));
values.put(COL_BOARDPAGE, fixNull(boardPage));
values.put(COL_THREAD, fixNull(thread));
values.put(COL_TITLE, fixNull(title));
values.put(COL_URL, fixNull(url));
dbHelper.getWritableDatabase().insert(TABLE_FAVORITES, null, values);
}
public void removeFavorite(String chan, String board, String boardPage, String thread) {
dbHelper.getWritableDatabase().delete(TABLE_FAVORITES,
COL_CHAN + " = ? AND " + COL_BOARD + " = ? AND " + COL_BOARDPAGE + " = ? AND " + COL_THREAD + " = ?",
new String[] { fixNull(chan), fixNull(board), fixNull(boardPage), fixNull(thread) });
}
public List<FavoritesEntry> getFavorites() {
List<FavoritesEntry> list = new ArrayList<FavoritesEntry>();
Cursor c = dbHelper.getReadableDatabase().query(TABLE_FAVORITES, null, null, null, null, null, BaseColumns._ID + " desc", "200");
if (c != null && c.moveToFirst()) {
int chanIndex = c.getColumnIndex(COL_CHAN);
int boardIndex = c.getColumnIndex(COL_BOARD);
int boardpageIndex = c.getColumnIndex(COL_BOARDPAGE);
int threadIndex = c.getColumnIndex(COL_THREAD);
int titleIndex = c.getColumnIndex(COL_TITLE);
int urlIndex = c.getColumnIndex(COL_URL);
do {
list.add(new FavoritesEntry(c.getString(chanIndex), c.getString(boardIndex), c.getString(boardpageIndex),
c.getString(threadIndex), c.getString(titleIndex), c.getString(urlIndex)));
} while (c.moveToNext());
}
if (c != null) c.close();
return list;
}
public List<String> getFavoriteBoards(ChanModule chan) {
List<String> list = new ArrayList<>();
Cursor c =
dbHelper.getReadableDatabase().query(TABLE_FAVORITES, null, COL_CHAN + " = ? AND " + COL_BOARD + " != ? AND " + COL_THREAD + " = ?",
new String[] { fixNull(chan.getChanName()), fixNull(null), fixNull(null) }, null, null, BaseColumns._ID + " desc", "200");
if (c != null && c.moveToFirst()) {
int boardIndex = c.getColumnIndex(COL_BOARD);
int boardpageIndex = c.getColumnIndex(COL_BOARDPAGE);
do {
String boardName = c.getString(boardIndex);
String boardPage = c.getString(boardpageIndex);
if (isFirstPage(chan, boardName, boardPage)) list.add(boardName);
} while (c.moveToNext());
}
if (c != null) c.close();
return list;
}
private boolean isFirstPage(ChanModule chan, String boardName, String boardPage) {
try {
UrlPageModel urlModel = new UrlPageModel();
urlModel.chanName = chan.getChanName();
urlModel.type = UrlPageModel.TYPE_BOARDPAGE;
urlModel.boardName = boardName;
urlModel.boardPage = UrlPageModel.DEFAULT_FIRST_PAGE;
if (chan.parseUrl(chan.buildUrl(urlModel)).boardPage == Integer.parseInt(boardPage)) return true;
} catch (Exception e) {
Logger.e(TAG, e);
}
return false;
}
public boolean isFavorite(String chan, String board, String boardPage, String thread) {
Cursor c = dbHelper.getReadableDatabase().query(TABLE_FAVORITES, null,
COL_CHAN + " = ? AND " + COL_BOARD + " = ? AND " + COL_BOARDPAGE + " = ? AND " + COL_THREAD + " = ?",
new String[] { fixNull(chan), fixNull(board), fixNull(boardPage), fixNull(thread) }, null, null, null);
boolean result = false;
if (c != null && c.moveToFirst()) result = true;
if (c != null) c.close();
return result;
}
public void clearFavorites() {
dbHelper.recreateFavorites();
}
/* *********************** HISTORY & FAVORITES *********************** */
public void updateHistoryFavoritesEntries(String chan, String board, String boardPage, String thread, String title) {
ContentValues values = new ContentValues(1);
values.put(COL_TITLE, title);
for (String table : new String[] { TABLE_HISTORY, TABLE_FAVORITES }) {
dbHelper.getWritableDatabase().update(table, values,
COL_CHAN + " = ? AND " + COL_BOARD + " = ? AND " + COL_BOARDPAGE + " = ? AND " + COL_THREAD + " = ?",
new String[] { fixNull(chan), fixNull(board), fixNull(boardPage), fixNull(thread) });
}
}
/* *********************** SAVED THREADS *********************** */
public class SavedThreadEntry {
public final String chan;
public final String title;
public final String filepath;
private SavedThreadEntry(String chan, String title, String filepath) {
this.chan = chan;
this.title = title;
this.filepath = filepath;
}
}
public void addSavedThread(String chan, String title, String filepath) {
removeSavedThread(filepath);
ContentValues values = new ContentValues(3);
values.put(COL_CHAN, fixNull(chan));
values.put(COL_TITLE, fixNull(title));
values.put(COL_FILEPATH, fixNull(filepath));
dbHelper.getWritableDatabase().insert(TABLE_SAVED, null, values);
}
public void removeSavedThread(String filepath) {
dbHelper.getWritableDatabase().delete(TABLE_SAVED, COL_FILEPATH + " = ?", new String[] { fixNull(filepath) });
}
public List<SavedThreadEntry> getSavedThreads() {
List<SavedThreadEntry> list = new ArrayList<SavedThreadEntry>();
Cursor c = dbHelper.getReadableDatabase().query(TABLE_SAVED, null, null, null, null, null, BaseColumns._ID + " desc", "200");
if (c != null && c.moveToFirst()) {
int chanIndex = c.getColumnIndex(COL_CHAN);
int titleIndex = c.getColumnIndex(COL_TITLE);
int filepathIndex = c.getColumnIndex(COL_FILEPATH);
do {
list.add(new SavedThreadEntry(c.getString(chanIndex), c.getString(titleIndex), c.getString(filepathIndex)));
} while (c.moveToNext());
}
if (c != null) c.close();
return list;
}
/* *********************** DB HELPER *********************** */
private static class DBHelper extends SQLiteOpenHelper implements BaseColumns {
public DBHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(createTable(TABLE_HIDDEN, new String[] { COL_CHAN, COL_BOARD, COL_THREAD, COL_POST }));
db.execSQL(createTable(TABLE_HISTORY,
new String[] { COL_CHAN, COL_BOARD, COL_BOARDPAGE, COL_THREAD, COL_TITLE, COL_URL, COL_DATE },
new String[] { "text", "text", "text", "text", "text", "text", "integer" }));
db.execSQL(createTable(TABLE_FAVORITES, new String[] { COL_CHAN, COL_BOARD, COL_BOARDPAGE, COL_THREAD, COL_TITLE, COL_URL }));
db.execSQL(createTable(TABLE_SAVED, new String[] { COL_CHAN, COL_TITLE, COL_FILEPATH }));
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion < newVersion) {
db.execSQL(dropTable(TABLE_HIDDEN));
db.execSQL(dropTable(TABLE_HISTORY));
db.execSQL(dropTable(TABLE_FAVORITES));
db.execSQL(dropTable(TABLE_SAVED));
onCreate(db);
}
}
@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
onUpgrade(db, oldVersion, newVersion);
}
private static String createTable(String tableName, String[] columns) {
return createTable(tableName, columns, null);
}
private static String createTable(String tableName, String[] columns, String[] types) {
StringBuilder sql = new StringBuilder(110).append("create table ").append(tableName).append(" (").
append(_ID).append(" integer primary key autoincrement,");
for (int i=0; i<columns.length; ++i) {
sql.append(columns[i]).append(' ').append(types == null ? "text" : types[i]).append(',');
}
sql.setCharAt(sql.length()-1, ')');
return sql.append(';').toString();
}
private static String dropTable(String tableName) {
return "DROP TABLE IF EXISTS " + tableName;
}
public void recreateHistory() {
getWritableDatabase().execSQL(dropTable(TABLE_HISTORY));
getWritableDatabase().execSQL(createTable(TABLE_HISTORY,
new String[] { COL_CHAN, COL_BOARD, COL_BOARDPAGE, COL_THREAD, COL_TITLE, COL_URL, COL_DATE },
new String[] { "text", "text", "text", "text", "text", "text", "integer" }));
}
public void recreateFavorites() {
getWritableDatabase().execSQL(dropTable(TABLE_FAVORITES));
getWritableDatabase().execSQL(createTable(TABLE_FAVORITES,
new String[] { COL_CHAN, COL_BOARD, COL_BOARDPAGE, COL_THREAD, COL_TITLE, COL_URL }));
}
}
}