/**
* Copyright (C) 2013 by Raphael Michel under the MIT license:
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package de.geeksfactory.opacclient.storage;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import de.geeksfactory.opacclient.objects.Account;
import de.geeksfactory.opacclient.objects.AccountData;
import de.geeksfactory.opacclient.objects.AccountItem;
import de.geeksfactory.opacclient.objects.LentItem;
import de.geeksfactory.opacclient.objects.ReservedItem;
import de.geeksfactory.opacclient.objects.SearchResult;
import de.geeksfactory.opacclient.reminder.Alarm;
public class AccountDataSource {
// Database fields
private SQLiteDatabase database;
private String[] allColumns = AccountDatabase.COLUMNS;
public AccountDataSource(Context context) {
AccountDatabase dbHelper = AccountDatabase.getInstance(context);
database = dbHelper.getWritableDatabase();
// we do not need to close the database, as only one instance is created
// see e.g. http://stackoverflow
// .com/questions/4547461/closing-the-database-in-a-contentprovider/12715032#12715032
}
public long addAccount(Account acc) {
ContentValues values = new ContentValues();
values.put("bib", acc.getLibrary());
values.put("label", acc.getLabel());
values.put("name", acc.getName());
values.put("password", acc.getPassword());
return database.insert("accounts", null, values);
}
public void update(Account acc) {
ContentValues values = new ContentValues();
values.put("bib", acc.getLibrary());
values.put("label", acc.getLabel());
values.put("name", acc.getName());
values.put("password", acc.getPassword());
values.put("passwordValid", acc.isPasswordKnownValid() ? 1 : 0);
database.update("accounts", values, "id = ?", new String[]{acc.getId() + ""});
}
public long addAccount(String bib, String label, String name, String password) {
ContentValues values = new ContentValues();
values.put("bib", bib);
values.put("label", label);
values.put("name", name);
values.put("password", password);
return database.insert("accounts", null, values);
}
public List<Account> getAllAccounts() {
List<Account> accs = new ArrayList<>();
Cursor cursor = database.query("accounts", allColumns, null, null, null, null, null);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
Account acc = cursorToAccount(cursor);
accs.add(acc);
cursor.moveToNext();
}
// Make sure to close the cursor
cursor.close();
return accs;
}
public List<Account> getAllAccounts(String bib) {
List<Account> accs = new ArrayList<>();
String[] selA = {bib};
Cursor cursor = database.query("accounts", allColumns, "bib = ?", selA, null, null, null);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
Account acc = cursorToAccount(cursor);
accs.add(acc);
cursor.moveToNext();
}
// Make sure to close the cursor
cursor.close();
return accs;
}
public List<Account> getAccountsWithPassword() {
List<Account> accs = new ArrayList<>();
Cursor cursor = database.query("accounts", allColumns,
"name is not null AND name != '' AND password is not null", null, null, null, null);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
Account acc = cursorToAccount(cursor);
accs.add(acc);
cursor.moveToNext();
}
// Make sure to close the cursor
cursor.close();
return accs;
}
public Account getAccount(long id) {
String[] selA = {"" + id};
Cursor cursor = database.query("accounts", allColumns, "id = ?", selA, null, null, null);
Account acc = null;
cursor.moveToFirst();
if (!cursor.isAfterLast()) {
acc = cursorToAccount(cursor);
cursor.moveToNext();
}
// Make sure to close the cursor
cursor.close();
return acc;
}
private Account cursorToAccount(Cursor cursor) {
Account acc = new Account();
acc.setId(cursor.getLong(0));
acc.setLibrary(cursor.getString(1));
acc.setLabel(cursor.getString(2));
acc.setName(cursor.getString(3));
acc.setPassword(cursor.getString(4));
acc.setCached(cursor.getLong(5));
acc.setPasswordKnownValid(cursor.getLong(9) > 0);
return acc;
}
public void remove(Account acc) {
deleteAccountData(acc);
String[] selA = {"" + acc.getId()};
database.delete("accounts", "id=?", selA);
}
public int getExpiring(Account account, int tolerance) {
String[] selA = {String.valueOf(account.getId())};
Cursor cursor = database.query(AccountDatabase.TABLENAME_LENT, new String[]{"COUNT(*)"},
"account = ? AND date(deadline) < date('now','-" + tolerance + " days')", selA,
null, null, null);
cursor.moveToFirst();
int result = cursor.getInt(0);
cursor.close();
return result;
}
public AccountData getCachedAccountData(Account account) {
AccountData adata = new AccountData(account.getId());
List<LentItem> lent = new ArrayList<>();
String[] selectionArgs = {"" + account.getId()};
Cursor cursor = database.query(AccountDatabase.TABLENAME_LENT, AccountDatabase.COLUMNS_LENT,
"account = ?", selectionArgs, null, null, null);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
LentItem entry = cursorToLentItem(cursor);
lent.add(entry);
cursor.moveToNext();
}
cursor.close();
adata.setLent(lent);
List<ReservedItem> res = new ArrayList<>();
cursor = database.query(AccountDatabase.TABLENAME_RESERVATION,
AccountDatabase.COLUMNS_RESERVATIONS, "account = ?", selectionArgs, null, null,
null);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
ReservedItem entry = cursorToReservedItem(cursor);
res.add(entry);
cursor.moveToNext();
}
cursor.close();
adata.setReservations(res);
String[] selA = {"" + account.getId()};
cursor = database
.query("accounts", new String[]{"pendingFees", "validUntil", "warning"}, "id = ?",
selA, null, null, null);
cursor.moveToFirst();
if (!cursor.isAfterLast()) {
adata.setPendingFees(cursor.getString(0));
adata.setValidUntil(cursor.getString(1));
adata.setWarning(cursor.getString(2));
cursor.moveToNext();
}
// Make sure to close the cursor
cursor.close();
return adata;
}
private LentItem cursorToLentItem(Cursor cursor) {
LentItem item = new LentItem();
setAccountItemAttributes(cursor, item);
item.setBarcode(cursor.getString(7));
item.setDeadline(cursor.getString(8));
item.setHomeBranch(cursor.getString(9));
item.setLendingBranch(cursor.getString(10));
item.setProlongData(cursor.getString(11));
item.setRenewable(cursor.getInt(12) == 1);
item.setDownloadData(cursor.getString(13));
item.setEbook(cursor.getInt(14) == 1);
return item;
}
private ReservedItem cursorToReservedItem(Cursor cursor) {
ReservedItem item = new ReservedItem();
setAccountItemAttributes(cursor, item);
item.setReadyDate(cursor.getString(7));
item.setExpirationDate(cursor.getString(8));
item.setBranch(cursor.getString(9));
item.setCancelData(cursor.getString(10));
item.setBookingData(cursor.getString(11));
return item;
}
private void setAccountItemAttributes(Cursor cursor, AccountItem item) {
item.setDbId(cursor.getLong(0));
item.setAccount(cursor.getLong(1));
item.setTitle(cursor.getString(2));
item.setAuthor(cursor.getString(3));
item.setFormat(cursor.getString(4));
item.setId(cursor.getString(5));
item.setStatus(cursor.getString(6));
String mediatype = cursor.getString(cursor.getColumnIndex("mediatype"));
item.setMediaType(mediatype != null ? SearchResult.MediaType.valueOf(mediatype) : null);
item.setCover(cursor.getString(cursor.getColumnIndex("cover")));
}
private ContentValues lentItemToContentValues(LentItem item, long accountId) {
ContentValues cv = new ContentValues();
setAccountItemAttributes(item, cv, accountId);
putOrNull(cv, "barcode", item.getBarcode());
putOrNull(cv, "deadline", item.getDeadline());
putOrNull(cv, "homebranch", item.getHomeBranch());
putOrNull(cv, "lending_branch", item.getLendingBranch());
putOrNull(cv, "prolong_data", item.getProlongData());
cv.put("renewable", item.isRenewable() ? 1 : 0);
putOrNull(cv, "download_data", item.getDownloadData());
cv.put("ebook", item.isEbook() ? 1 : 0);
return cv;
}
private ContentValues reservedItemToContentValues(ReservedItem item, long accountId) {
ContentValues cv = new ContentValues();
setAccountItemAttributes(item, cv, accountId);
putOrNull(cv, "ready", item.getReadyDate());
putOrNull(cv, "expiration", item.getExpirationDate());
putOrNull(cv, "branch", item.getBranch());
putOrNull(cv, "cancel_data", item.getCancelData());
putOrNull(cv, "booking_data", item.getBookingData());
return cv;
}
private void setAccountItemAttributes(AccountItem item, ContentValues cv, long accountId) {
if (item.getDbId() != null) cv.put("id", item.getDbId());
cv.put("account", accountId);
putOrNull(cv, "title", item.getTitle());
putOrNull(cv, "author", item.getAuthor());
putOrNull(cv, "format", item.getFormat());
putOrNull(cv, "itemid", item.getId());
putOrNull(cv, "status", item.getStatus());
putOrNull(cv, "cover", item.getCover());
putOrNull(cv, "mediatype",
item.getMediaType() != null ? item.getMediaType().toString() : null);
}
private void putOrNull(ContentValues cv, String key, LocalDate value) {
if (value != null) {
cv.put(key, value.toString());
} else {
cv.putNull(key);
}
}
private void putOrNull(ContentValues cv, String key, String value) {
if (value != null) {
cv.put(key, value);
} else {
cv.putNull(key);
}
}
public void invalidateCachedData() {
database.delete(AccountDatabase.TABLENAME_LENT, null, null);
database.delete(AccountDatabase.TABLENAME_RESERVATION, null, null);
database.delete(AccountDatabase.TABLENAME_ALARMS, null, null);
ContentValues update = new ContentValues();
update.put("cached", 0);
update.put("pendingFees", (String) null);
update.put("validUntil", (String) null);
update.put("warning", (String) null);
database.update(AccountDatabase.TABLENAME_ACCOUNTS, update, null, null);
}
public void deleteAccountData(Account account) {
database.delete(AccountDatabase.TABLENAME_LENT, "account = ?",
new String[]{"" + account.getId()});
database.delete(AccountDatabase.TABLENAME_RESERVATION, "account = ?",
new String[]{"" + account.getId()});
}
public void invalidateCachedAccountData(Account account) {
ContentValues update = new ContentValues();
update.put("cached", 0);
database.update(AccountDatabase.TABLENAME_ACCOUNTS, update, "id = ?",
new String[]{"" + account.getId()});
}
public long getCachedAccountDataTime(Account account) {
return getAccount(account.getId()).getCached();
}
public void storeCachedAccountData(Account account, AccountData adata) {
if (adata == null) {
return;
}
long time = System.currentTimeMillis();
ContentValues update = new ContentValues();
update.put("cached", time);
update.put("pendingFees", adata.getPendingFees());
update.put("validUntil", adata.getValidUntil());
update.put("warning", adata.getWarning());
database.update(AccountDatabase.TABLENAME_ACCOUNTS, update, "id = ?",
new String[]{"" + account.getId()});
database.delete(AccountDatabase.TABLENAME_LENT, "account = ?",
new String[]{"" + account.getId()});
for (LentItem entry : adata.getLent()) {
ContentValues insertmapping = lentItemToContentValues(entry, account.getId());
database.insert(AccountDatabase.TABLENAME_LENT, null, insertmapping);
}
database.delete(AccountDatabase.TABLENAME_RESERVATION, "account = ?",
new String[]{"" + account.getId()});
for (ReservedItem entry : adata.getReservations()) {
ContentValues insertmapping = reservedItemToContentValues(entry, account.getId());
database.insert(AccountDatabase.TABLENAME_RESERVATION, null, insertmapping);
}
}
public List<LentItem> getAllLentItems() {
List<LentItem> items = new ArrayList<>();
Cursor cursor = database
.query(AccountDatabase.TABLENAME_LENT, AccountDatabase.COLUMNS_LENT, null, null,
null, null, null);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
LentItem item = cursorToLentItem(cursor);
items.add(item);
cursor.moveToNext();
}
cursor.close();
return items;
}
public LentItem getLentItem(long id) {
String[] selA = {"" + id};
Cursor cursor = database
.query(AccountDatabase.TABLENAME_LENT, AccountDatabase.COLUMNS_LENT, "id = ?", selA,
null, null, null);
LentItem item = null;
cursor.moveToFirst();
if (!cursor.isAfterLast()) {
item = cursorToLentItem(cursor);
}
// Make sure to close the cursor
cursor.close();
return item;
}
public List<LentItem> getLentItems(long[] ids) {
List<LentItem> items = new ArrayList<>();
Cursor cursor = database
.query(AccountDatabase.TABLENAME_LENT, AccountDatabase.COLUMNS_LENT, "id IN(" +
joinLongs(ids, ",") + ")", null, null, null, null);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
LentItem item = cursorToLentItem(cursor);
items.add(item);
cursor.moveToNext();
}
cursor.close();
return items;
}
public List<Account> getAccountsWithPassword(String ident) {
List<Account> accs = new ArrayList<>();
Cursor cursor = database.query("accounts", allColumns,
"name is not null AND name != '' AND password is not null AND bib = ?",
new String[]{ident}, null, null, null);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
Account acc = cursorToAccount(cursor);
accs.add(acc);
cursor.moveToNext();
}
// Make sure to close the cursor
cursor.close();
return accs;
}
public long addAlarm(LocalDate deadline, long[] media, DateTime alarmTime) {
for (long mid : media) {
if (getLentItem(mid) == null) {
throw new DataIntegrityException(
"Cannot add alarm with deadline " + deadline.toString() +
" that has dependency on the non-existing media item " + mid);
}
}
ContentValues values = new ContentValues();
values.put("deadline", deadline.toString());
values.put("media", joinLongs(media, ","));
values.put("alarm", alarmTime.toString());
values.put("notified", 0);
values.put("finished", 0);
return database.insert(AccountDatabase.TABLENAME_ALARMS, null, values);
}
public void updateAlarm(Alarm alarm) {
for (long mid : alarm.media) {
if (getLentItem(mid) == null) {
throw new DataIntegrityException(
"Cannot update alarm with deadline " + alarm.deadline.toString() +
" that has dependency on the non-existing media item " + mid);
}
}
ContentValues values = new ContentValues();
values.put("deadline", alarm.deadline.toString());
values.put("media", joinLongs(alarm.media, ","));
values.put("alarm", alarm.notificationTime.toString());
values.put("notified", alarm.notified ? 1 : 0);
values.put("finished", alarm.finished ? 1 : 0);
database.update(AccountDatabase.TABLENAME_ALARMS, values, "id = ?",
new String[]{alarm.id + ""});
}
public void resetNotifiedOnAllAlarams() {
ContentValues values = new ContentValues();
values.put("notified", 0);
database.update(AccountDatabase.TABLENAME_ALARMS, values, "finished = 0 AND notified = 1", null);
}
public Alarm getAlarmByDeadline(LocalDate deadline) {
String[] selA = {deadline.toString()};
Cursor cursor = database
.query(AccountDatabase.TABLENAME_ALARMS, AccountDatabase.COLUMNS_ALARMS,
"deadline = ?", selA, null, null, null);
Alarm item = null;
cursor.moveToFirst();
if (!cursor.isAfterLast()) {
item = cursorToAlarm(cursor);
cursor.moveToNext();
}
// Make sure to close the cursor
cursor.close();
return item;
}
public Alarm getAlarm(long id) {
String[] selA = {"" + id};
Cursor cursor = database
.query(AccountDatabase.TABLENAME_ALARMS, AccountDatabase.COLUMNS_ALARMS, "id = ?",
selA, null, null, null);
Alarm item = null;
cursor.moveToFirst();
if (!cursor.isAfterLast()) {
item = cursorToAlarm(cursor);
cursor.moveToNext();
}
// Make sure to close the cursor
cursor.close();
return item;
}
public List<Alarm> getAllAlarms() {
List<Alarm> alarms = new ArrayList<>();
Cursor cursor = database
.query(AccountDatabase.TABLENAME_ALARMS, AccountDatabase.COLUMNS_ALARMS, null, null,
null, null, null);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
Alarm alarm = cursorToAlarm(cursor);
alarms.add(alarm);
cursor.moveToNext();
}
// Make sure to close the cursor
cursor.close();
return alarms;
}
public void clearAlarms() {
database.delete(AccountDatabase.TABLENAME_ALARMS, null, null);
}
private Alarm cursorToAlarm(Cursor cursor) {
Alarm alarm = new Alarm();
alarm.id = cursor.getLong(0);
alarm.deadline = new LocalDate(cursor.getString(1));
alarm.media = splitLongs(cursor.getString(2), ",");
alarm.notificationTime = new DateTime(cursor.getString(3));
alarm.notified = cursor.getInt(4) == 1;
alarm.finished = cursor.getInt(5) == 1;
return alarm;
}
public void removeAlarm(Alarm alarm) {
String[] selA = {"" + alarm.id};
database.delete(AccountDatabase.TABLENAME_ALARMS, "id=?", selA);
}
private String joinLongs(long[] longs, String separator) {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (long l : longs) {
if (first) {
first = false;
} else {
sb.append(separator);
}
sb.append(l);
}
return sb.toString();
}
private long[] splitLongs(String string, String separator) {
String[] strings = string.split(separator);
long[] longs = new long[strings.length];
for (int i = 0; i < strings.length; i++) {
longs[i] = Long.valueOf(strings[i]);
}
return longs;
}
}