use of com.ichi2.libanki.DB in project AnkiChinaAndroid by ankichinateam.
the class ContentProviderTest method testInsertAndRemoveNote.
/**
* Check that inserting and removing a note into default deck works as expected
*/
@Test
public void testInsertAndRemoveNote() {
// Get required objects for test
final ContentResolver cr = InstrumentationRegistry.getInstrumentation().getTargetContext().getContentResolver();
// Add the note
ContentValues values = new ContentValues();
values.put(FlashCardsContract.Note.MID, mModelId);
values.put(FlashCardsContract.Note.FLDS, Utils.joinFields(TEST_NOTE_FIELDS));
values.put(FlashCardsContract.Note.TAGS, TEST_TAG);
Uri newNoteUri = cr.insert(FlashCardsContract.Note.CONTENT_URI, values);
assertNotNull("Check that URI returned from addNewNote is not null", newNoteUri);
// test that the changes are physically saved to the DB
final Collection col = reopenCol();
// Check that it looks as expected
assertNotNull("check note URI path", newNoteUri.getLastPathSegment());
Note addedNote = new Note(col, Long.parseLong(newNoteUri.getLastPathSegment()));
addedNote.load();
assertArrayEquals("Check that fields were set correctly", addedNote.getFields(), TEST_NOTE_FIELDS);
assertEquals("Check that tag was set correctly", TEST_TAG, addedNote.getTags().get(0));
JSONObject model = col.getModels().get(mModelId);
assertNotNull("Check model", model);
int expectedNumCards = model.getJSONArray("tmpls").length();
assertEquals("Check that correct number of cards generated", expectedNumCards, addedNote.numberOfCards());
// Now delete the note
cr.delete(newNoteUri, null, null);
try {
addedNote.load();
fail("Expected RuntimeException to be thrown when deleting note");
} catch (RuntimeException e) {
// Expect RuntimeException to be thrown when loading deleted note
}
}
use of com.ichi2.libanki.DB in project AnkiChinaAndroid by ankichinateam.
the class ContentProviderTest method testInsertField.
/**
* Check that inserting and removing a note into default deck works as expected
*/
@Test
public void testInsertField() throws Exception {
// Get required objects for test
final ContentResolver cr = InstrumentationRegistry.getInstrumentation().getTargetContext().getContentResolver();
Collection col = getCol();
Model model = StdModels.basicModel.add(col, BASIC_MODEL_NAME);
long modelId = model.getLong("id");
JSONArray initialFieldsArr = model.getJSONArray("flds");
int initialFieldCount = initialFieldsArr.length();
Uri noteTypeUri = ContentUris.withAppendedId(FlashCardsContract.Model.CONTENT_URI, modelId);
ContentValues insertFieldValues = new ContentValues();
insertFieldValues.put(FlashCardsContract.Model.FIELD_NAME, TEST_FIELD_NAME);
Uri fieldUri = cr.insert(Uri.withAppendedPath(noteTypeUri, "fields"), insertFieldValues);
assertNotNull("Check field uri", fieldUri);
// Ensure that the changes are physically saved to the DB
col = reopenCol();
model = col.getModels().get(modelId);
// Test the field is as expected
long fieldId = ContentUris.parseId(fieldUri);
assertEquals("Check field id", initialFieldCount, fieldId);
assertNotNull("Check model", model);
JSONArray fldsArr = model.getJSONArray("flds");
assertEquals("Check fields length", initialFieldCount + 1, fldsArr.length());
assertEquals("Check last field name", TEST_FIELD_NAME, fldsArr.getJSONObject(fldsArr.length() - 1).optString("name", ""));
col.getModels().rem(model);
}
use of com.ichi2.libanki.DB in project AnkiChinaAndroid by ankichinateam.
the class DatabaseErrorDialog method onCreateDialog.
@Override
public MaterialDialog onCreateDialog(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int mType = getArguments().getInt("dialogType");
Resources res = getResources();
MaterialDialog.Builder builder = new MaterialDialog.Builder(getActivity());
builder.cancelable(true).title(getTitle());
boolean sqliteInstalled = false;
try {
sqliteInstalled = Runtime.getRuntime().exec("sqlite3 --version").waitFor() == 0;
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
switch(mType) {
case DIALOG_LOAD_FAILED:
{
// the activity
return builder.cancelable(false).content(getMessage()).iconAttr(R.attr.dialogErrorIcon).positiveText(res.getString(R.string.error_handling_options)).negativeText(res.getString(R.string.close)).onPositive((inner_dialog, which) -> ((DeckPicker) getActivity()).showDatabaseErrorDialog(DIALOG_ERROR_HANDLING)).onNegative((inner_dialog, which) -> exit()).show();
}
case DIALOG_DB_ERROR:
{
// Database Check failed to execute successfully; give user the option of either choosing from repair
// options, submitting an error report, or closing the activity
MaterialDialog dialog = builder.cancelable(false).content(getMessage()).iconAttr(R.attr.dialogErrorIcon).positiveText(res.getString(R.string.error_handling_options)).negativeText(res.getString(R.string.answering_error_report)).neutralText(res.getString(R.string.close)).onPositive((inner_dialog, which) -> ((DeckPicker) getActivity()).showDatabaseErrorDialog(DIALOG_ERROR_HANDLING)).onNegative((inner_dialog, which) -> {
((DeckPicker) getActivity()).sendErrorReport();
dismissAllDialogFragments();
}).onNeutral((inner_dialog, which) -> exit()).show();
dialog.getCustomView().findViewById(R.id.md_buttonDefaultNegative).setEnabled(((DeckPicker) getActivity()).hasErrorFiles());
return dialog;
}
case DIALOG_ERROR_HANDLING:
{
// The user has asked to see repair options; allow them to choose one of the repair options or go back
// to the previous dialog
ArrayList<String> options = new ArrayList<>();
ArrayList<Integer> values = new ArrayList<>();
if (!((AnkiActivity) getActivity()).colIsOpen()) {
// retry
options.add(res.getString(R.string.backup_retry_opening));
values.add(0);
} else {
// fix integrity
options.add(res.getString(R.string.check_db));
values.add(1);
}
// repair db with sqlite
if (sqliteInstalled) {
options.add(res.getString(R.string.backup_error_menu_repair));
values.add(2);
}
// // restore from backup
options.add(res.getString(R.string.backup_restore));
values.add(3);
// delete old collection and build new one
options.add(res.getString(R.string.backup_full_sync_from_server));
values.add(4);
// delete old collection and build new one
options.add(res.getString(R.string.backup_del_collection));
values.add(5);
String[] titles = new String[options.size()];
mRepairValues = new int[options.size()];
for (int i = 0; i < options.size(); i++) {
titles[i] = options.get(i);
mRepairValues[i] = values.get(i);
}
return builder.iconAttr(R.attr.dialogErrorIcon).negativeText(res.getString(R.string.dialog_cancel)).items(titles).itemsCallback((materialDialog, view, which, charSequence) -> {
switch(mRepairValues[which]) {
case 0:
((DeckPicker) getActivity()).restartActivity();
return;
case 1:
((DeckPicker) getActivity()).showDatabaseErrorDialog(DIALOG_CONFIRM_DATABASE_CHECK);
return;
case 2:
((DeckPicker) getActivity()).showDatabaseErrorDialog(DIALOG_REPAIR_COLLECTION);
return;
case 3:
((DeckPicker) getActivity()).showDatabaseErrorDialog(DIALOG_RESTORE_BACKUP);
return;
case 4:
((DeckPicker) getActivity()).showDatabaseErrorDialog(DIALOG_FULL_SYNC_FROM_SERVER);
return;
case 5:
((DeckPicker) getActivity()).showDatabaseErrorDialog(DIALOG_NEW_COLLECTION);
return;
default:
throw new RuntimeException("Unknown dialog selection: " + mRepairValues[which]);
}
}).show();
}
case DIALOG_REPAIR_COLLECTION:
{
// Allow user to run BackupManager.repairCollection()
return builder.content(getMessage()).iconAttr(R.attr.dialogErrorIcon).positiveText(res.getString(R.string.dialog_positive_repair)).negativeText(res.getString(R.string.dialog_cancel)).onPositive((inner_dialog, which) -> {
((DeckPicker) getActivity()).repairCollection();
dismissAllDialogFragments();
}).show();
}
case DIALOG_RESTORE_BACKUP:
{
// Allow user to restore one of the backups
String path = CollectionHelper.getInstance().getCollectionPath(getActivity());
File[] files = BackupManager.getBackups(new File(path));
mBackups = new File[files.length];
for (int i = 0; i < files.length; i++) {
mBackups[i] = files[files.length - 1 - i];
}
if (mBackups.length == 0) {
builder.title(res.getString(R.string.backup_restore)).content(getMessage()).positiveText(res.getString(R.string.dialog_ok)).onPositive((inner_dialog, which) -> ((DeckPicker) getActivity()).showDatabaseErrorDialog(DIALOG_ERROR_HANDLING));
} else {
String[] dates = new String[mBackups.length];
for (int i = 0; i < mBackups.length; i++) {
dates[i] = mBackups[i].getName().replaceAll(".*-(\\d{4}-\\d{2}-\\d{2})-(\\d{2})-(\\d{2}).apkg", "$1 ($2:$3 h)");
}
builder.title(res.getString(R.string.backup_restore_select_title)).negativeText(res.getString(R.string.dialog_cancel)).onNegative((inner_dialog, which) -> dismissAllDialogFragments()).items(dates).itemsCallbackSingleChoice(dates.length, (materialDialog, view, which, charSequence) -> {
if (mBackups[which].length() > 0) {
// restore the backup if it's valid
((DeckPicker) getActivity()).restoreFromBackup(mBackups[which].getPath());
dismissAllDialogFragments();
} else {
// otherwise show an error dialog
new MaterialDialog.Builder(getActivity()).title(R.string.backup_error).content(R.string.backup_invalid_file_error).positiveText(R.string.dialog_ok).build().show();
}
return true;
});
}
MaterialDialog materialDialog = builder.build();
materialDialog.setOnKeyListener((dialog, keyCode, event) -> {
if (keyCode == KeyEvent.KEYCODE_BACK) {
Timber.i("DIALOG_RESTORE_BACKUP caught hardware back button");
dismissAllDialogFragments();
return true;
}
return false;
});
return materialDialog;
}
case DIALOG_NEW_COLLECTION:
{
// Allow user to create a new empty collection
return builder.content(getMessage()).positiveText(res.getString(R.string.dialog_positive_create)).negativeText(res.getString(R.string.dialog_cancel)).onPositive((inner_dialog, which) -> {
CollectionHelper ch = CollectionHelper.getInstance();
Time time = ch.getTimeSafe(getContext());
ch.closeCollection(false, "DatabaseErrorDialog: Before Create New Collection");
String path1 = CollectionHelper.getCollectionPath(getActivity());
if (BackupManager.moveDatabaseToBrokenFolder(path1, false, time)) {
((DeckPicker) getActivity()).restartActivity();
} else {
((DeckPicker) getActivity()).showDatabaseErrorDialog(DIALOG_LOAD_FAILED);
}
}).show();
}
case DIALOG_CONFIRM_DATABASE_CHECK:
{
// Confirmation dialog for database check
return builder.content(getMessage()).positiveText(res.getString(R.string.dialog_ok)).negativeText(res.getString(R.string.dialog_cancel)).onPositive((inner_dialog, which) -> {
((DeckPicker) getActivity()).integrityCheck();
dismissAllDialogFragments();
}).show();
}
case DIALOG_CONFIRM_RESTORE_BACKUP:
{
// Confirmation dialog for backup restore
return builder.content(getMessage()).positiveText(res.getString(R.string.dialog_continue)).negativeText(res.getString(R.string.dialog_cancel)).onPositive((inner_dialog, which) -> ((DeckPicker) getActivity()).showDatabaseErrorDialog(DIALOG_RESTORE_BACKUP)).show();
}
case DIALOG_FULL_SYNC_FROM_SERVER:
{
// Allow user to do a full-sync from the server
return builder.content(getMessage()).positiveText(res.getString(R.string.dialog_positive_overwrite)).negativeText(res.getString(R.string.dialog_cancel)).onPositive((inner_dialog, which) -> {
((DeckPicker) getActivity()).sync("download");
dismissAllDialogFragments();
}).show();
}
case DIALOG_DB_LOCKED:
{
// If the database is locked, all we can do is ask the user to exit.
return builder.content(getMessage()).positiveText(res.getString(R.string.close)).cancelable(false).onPositive((inner_dialog, which) -> exit()).show();
}
default:
return null;
}
}
use of com.ichi2.libanki.DB in project AnkiChinaAndroid by ankichinateam.
the class CardContentProvider method buryOrSuspendCard.
private void buryOrSuspendCard(Collection col, AbstractSched sched, Card card, boolean bury) {
try {
DB db = col.getDb();
db.getDatabase().beginTransaction();
try {
if (card != null) {
if (bury) {
// bury
sched.buryCards(new long[] { card.getId() });
} else {
// suspend
sched.suspendCards(new long[] { card.getId() });
}
}
db.getDatabase().setTransactionSuccessful();
} finally {
db.getDatabase().endTransaction();
}
} catch (RuntimeException e) {
Timber.e(e, "buryOrSuspendCard - RuntimeException on burying or suspending card");
AnkiDroidApp.sendExceptionReport(e, "doInBackgroundBurySuspendCard");
return;
}
}
use of com.ichi2.libanki.DB in project AnkiChinaAndroid by ankichinateam.
the class CardContentProvider method answerCard.
private void answerCard(Collection col, AbstractSched sched, Card cardToAnswer, @Consts.BUTTON_TYPE int ease, long timeTaken) {
try {
DB db = col.getDb();
db.getDatabase().beginTransaction();
try {
if (cardToAnswer != null) {
if (timeTaken != -1) {
cardToAnswer.setTimerStarted(col.getTime().intTime() - timeTaken / 1000);
}
sched.answerCard(cardToAnswer, ease);
}
db.getDatabase().setTransactionSuccessful();
} finally {
db.getDatabase().endTransaction();
}
} catch (RuntimeException e) {
Timber.e(e, "answerCard - RuntimeException on answering card");
AnkiDroidApp.sendExceptionReport(e, "doInBackgroundAnswerCard");
return;
}
}
Aggregations