use of com.ichi2.libanki.Deck in project AnkiChinaAndroid by ankichinateam.
the class ImportTest method testAnki2Mediadupes.
@Test
public void testAnki2Mediadupes() throws IOException, JSONException, ImportExportException {
List<String> expected;
List<String> actual;
// add a note that references a sound
Note n = testCol.newNote();
n.setField(0, "[sound:foo.mp3]");
long mid = n.model().getLong("id");
testCol.addNote(n);
// add that sound to the media folder
FileOutputStream os;
os = new FileOutputStream(new File(testCol.getMedia().dir(), "foo.mp3"), false);
os.write("foo".getBytes());
os.close();
testCol.close();
// it should be imported correctly into an empty deck
Collection empty = Shared.getEmptyCol(InstrumentationRegistry.getInstrumentation().getTargetContext());
Importer imp = new Anki2Importer(empty, testCol.getPath());
imp.run();
expected = Collections.singletonList("foo.mp3");
actual = Arrays.asList(new File(empty.getMedia().dir()).list());
actual.retainAll(expected);
assertEquals(expected.size(), actual.size());
// and importing again will not duplicate, as the file content matches
empty.remCards(empty.getDb().queryLongList("select id from cards"));
imp = new Anki2Importer(empty, testCol.getPath());
imp.run();
expected = Collections.singletonList("foo.mp3");
actual = Arrays.asList(new File(empty.getMedia().dir()).list());
actual.retainAll(expected);
assertEquals(expected.size(), actual.size());
n = empty.getNote(empty.getDb().queryLongScalar("select id from notes"));
assertTrue(n.getFields()[0].contains("foo.mp3"));
// if the local file content is different, and import should trigger a rename
empty.remCards(empty.getDb().queryLongList("select id from cards"));
os = new FileOutputStream(new File(empty.getMedia().dir(), "foo.mp3"), false);
os.write("bar".getBytes());
os.close();
imp = new Anki2Importer(empty, testCol.getPath());
imp.run();
expected = Arrays.asList("foo.mp3", String.format("foo_%s.mp3", mid));
actual = Arrays.asList(new File(empty.getMedia().dir()).list());
actual.retainAll(expected);
assertEquals(expected.size(), actual.size());
n = empty.getNote(empty.getDb().queryLongScalar("select id from notes"));
assertTrue(n.getFields()[0].contains("_"));
// if the localized media file already exists, we rewrite the note and media
empty.remCards(empty.getDb().queryLongList("select id from cards"));
os = new FileOutputStream(new File(empty.getMedia().dir(), "foo.mp3"));
os.write("bar".getBytes());
os.close();
imp = new Anki2Importer(empty, testCol.getPath());
imp.run();
expected = Arrays.asList("foo.mp3", String.format("foo_%s.mp3", mid));
actual = Arrays.asList(new File(empty.getMedia().dir()).list());
actual.retainAll(expected);
assertEquals(expected.size(), actual.size());
n = empty.getNote(empty.getDb().queryLongScalar("select id from notes"));
assertTrue(n.getFields()[0].contains("_"));
empty.close();
}
use of com.ichi2.libanki.Deck in project AnkiChinaAndroid by ankichinateam.
the class ImportTest method testDupeIgnore.
/**
* Custom tests for AnkiDroid.
*/
@Test
public void testDupeIgnore() throws IOException, ImportExportException {
// create a new empty deck
String tmp = Shared.getTestFilePath(InstrumentationRegistry.getInstrumentation().getTargetContext(), "update1.apkg");
AnkiPackageImporter imp = new AnkiPackageImporter(testCol, tmp);
imp.run();
tmp = Shared.getTestFilePath(InstrumentationRegistry.getInstrumentation().getTargetContext(), "update3.apkg");
imp = new AnkiPackageImporter(testCol, tmp);
imp.run();
// there is a dupe, but it was ignored
assertEquals(1, imp.getDupes());
assertEquals(0, imp.getAdded());
assertEquals(0, imp.getUpdated());
}
use of com.ichi2.libanki.Deck in project AnkiChinaAndroid by ankichinateam.
the class ImportTest method testAnki2Updates.
@Test
public void testAnki2Updates() throws IOException, ImportExportException {
// create a new empty deck
String tmp = Shared.getTestFilePath(InstrumentationRegistry.getInstrumentation().getTargetContext(), "update1.apkg");
AnkiPackageImporter imp = new AnkiPackageImporter(testCol, tmp);
imp.run();
assertEquals(0, imp.getDupes());
assertEquals(1, imp.getAdded());
assertEquals(0, imp.getUpdated());
// importing again should be idempotent
imp = new AnkiPackageImporter(testCol, tmp);
imp.run();
assertEquals(1, imp.getDupes());
assertEquals(0, imp.getAdded());
assertEquals(0, imp.getUpdated());
// importing a newer note should update
assertEquals(1, testCol.noteCount());
assertTrue(testCol.getDb().queryString("select flds from notes").startsWith("hello"));
tmp = Shared.getTestFilePath(InstrumentationRegistry.getInstrumentation().getTargetContext(), "update2.apkg");
imp = new AnkiPackageImporter(testCol, tmp);
imp.run();
assertEquals(1, imp.getDupes());
assertEquals(0, imp.getAdded());
assertEquals(1, imp.getUpdated());
assertTrue(testCol.getDb().queryString("select flds from notes").startsWith("goodbye"));
}
use of com.ichi2.libanki.Deck in project AnkiChinaAndroid by ankichinateam.
the class NoteEditor method saveNote.
@VisibleForTesting
void saveNote() {
final Resources res = getResources();
if (mSelectedTags == null) {
mSelectedTags = new ArrayList<>(0);
}
// treat add new note and edit existing note independently
if (mAddNote) {
// DEFECT: This does not block addition if cloze transpositions are in non-cloze fields.
if (isClozeType() && !hasClozeDeletions()) {
displayErrorSavingNote();
return;
}
// load all of the fields into the note
for (FieldEditText f : mEditFields) {
updateField(f);
}
// Save deck to model
mEditorNote.model().put("did", mCurrentDid);
// Save tags to model
mEditorNote.setTagsFromStr(tagsAsString(mSelectedTags));
JSONArray tags = new JSONArray();
for (String t : mSelectedTags) {
tags.put(t);
}
getCol().getModels().current().put("tags", tags);
getCol().getModels().setChanged();
mReloadRequired = true;
CollectionTask.launchCollectionTask(ADD_NOTE, saveNoteHandler(), new TaskData(mEditorNote));
} else {
// Check whether note type has been changed
final Model newModel = getCurrentlySelectedModel();
final Model oldModel = (mCurrentEditedCard == null) ? null : mCurrentEditedCard.model();
File target = new File(FileUtil.createTmpDir(this), mCurrentEditedCard.getId() + ".wav");
if (target.exists()) {
Timber.i("editing card audio is exists,delete it");
target.delete();
}
if (!newModel.equals(oldModel)) {
mReloadRequired = true;
if (mModelChangeCardMap.size() < mEditorNote.numberOfCards() || mModelChangeCardMap.containsValue(null)) {
// If cards will be lost via the new mapping then show a confirmation dialog before proceeding with the change
ConfirmationDialog dialog = new ConfirmationDialog();
dialog.setArgs(res.getString(R.string.confirm_map_cards_to_nothing));
Runnable confirm = () -> {
// Bypass the check once the user confirms
changeNoteTypeWithErrorHandling(oldModel, newModel);
};
dialog.setConfirm(confirm);
showDialogFragment(dialog);
} else {
// Otherwise go straight to changing note type
changeNoteTypeWithErrorHandling(oldModel, newModel);
}
return;
}
// Regular changes in note content
boolean modified = false;
// changed did? this has to be done first as remFromDyn() involves a direct write to the database
if (mCurrentEditedCard != null && mCurrentEditedCard.getDid() != mCurrentDid) {
mReloadRequired = true;
// remove card from filtered deck first (if relevant)
getCol().getSched().remFromDyn(new long[] { mCurrentEditedCard.getId() });
// refresh the card object to reflect the database changes in remFromDyn()
mCurrentEditedCard.load();
// also reload the note object
mEditorNote = mCurrentEditedCard.note();
// then set the card ID to the new deck
mCurrentEditedCard.setDid(mCurrentDid);
modified = true;
}
// now load any changes to the fields from the form
for (FieldEditText f : mEditFields) {
modified = modified | updateField(f);
}
// added tag?
for (String t : mSelectedTags) {
modified = modified || !mEditorNote.hasTag(t);
}
// removed tag?
modified = modified || mEditorNote.getTags().size() > mSelectedTags.size();
if (modified) {
mEditorNote.setTagsFromStr(tagsAsString(mSelectedTags));
mChanged = true;
}
closeNoteEditor();
}
}
use of com.ichi2.libanki.Deck in project AnkiChinaAndroid by ankichinateam.
the class DeckPickerFragment method onCreateView.
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (mRoot != null) {
return mRoot;
}
Timber.i("on create view in deck picker fragment");
mRoot = inflater.inflate(R.layout.deck_picker, container, false);
mToolbar = mRoot.findViewById(R.id.toolbar);
mPullToSyncWrapper = mRoot.findViewById(R.id.pull_to_sync_wrapper);
if (!Permissions.hasStorageAccessPermission(getAnkiActivity())) {
mPullToSyncWrapper.setVisibility(View.GONE);
mToolbar.setVisibility(View.GONE);
mRoot.findViewById(R.id.no_permission_layout).setVisibility(View.VISIBLE);
mRoot.findViewById(R.id.hint_button).setOnClickListener(v -> ActivityCompat.requestPermissions(getAnkiActivity(), new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, REQUEST_STORAGE_PERMISSION));
// return mRoot;
}
if (mToolbar != null) {
setHasOptionsMenu(true);
mToolbar.setNavigationIcon(null);
mToolbar.setTitle("");
mTabLayout = mToolbar.findViewById(R.id.tab_layout);
mVipLogo = mToolbar.findViewById(R.id.vip_logo);
mVipLogo.setOnClickListener(v -> onNavigationPressed());
TabLayout.Tab tab = mTabLayout.newTab();
View view = getLayoutInflater().inflate(R.layout.item_deckpicker_tab, null);
((TextView) view.findViewById(R.id.name)).setText("学习");
tab.setCustomView(view);
mTabLayout.addTab(tab);
TabLayout.Tab tab2 = mTabLayout.newTab();
View view2 = getLayoutInflater().inflate(R.layout.item_deckpicker_tab, null);
((TextView) view2.findViewById(R.id.name)).setText("笔记");
tab2.setCustomView(view2);
mTabLayout.addTab(tab2);
mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
if (tab == tab2) {
getAnkiActivity().openCardBrowser(ALL_DECKS_ID);
}
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
// check, if tablet layout
mStudyoptionsFrame = mRoot.findViewById(R.id.studyoptions_fragment);
if (mFragmented) {
loadStudyOptionsFragment(false);
}
mRecyclerView = mRoot.findViewById(R.id.files);
mRecyclerViewLayoutManager = new LinearLayoutManager(getAnkiActivity());
mRecyclerView.setLayoutManager(mRecyclerViewLayoutManager);
View view = mFragmented ? mRoot.findViewById(R.id.deckpicker_view) : mRoot.findViewById(R.id.root_layout);
boolean hasDeckPickerBackground = false;
try {
hasDeckPickerBackground = applyDeckPickerBackground(view);
} catch (OutOfMemoryError e) {
// 6608 - OOM should be catchable here.
Timber.w(e, "Failed to apply background - OOM");
UIUtils.showThemedToast(getAnkiActivity(), getString(R.string.background_image_too_large), false);
} catch (Exception e) {
Timber.w(e, "Failed to apply background");
UIUtils.showThemedToast(getAnkiActivity(), getString(R.string.failed_to_apply_background_image, e.getLocalizedMessage()), false);
}
// create and set an adapter for the RecyclerView
mDeckListAdapter = new DeckAdapter(getLayoutInflater(), getAnkiActivity());
mDeckListAdapter.setDeckClickListener(mDeckClickListener);
mDeckListAdapter.setCountsClickListener(mCountsClickListener);
mDeckListAdapter.setDeckExpanderClickListener(mDeckExpanderClickListener);
mDeckListAdapter.setDeckLongClickListener(mDeckLongClickListener);
mDeckListAdapter.enablePartialTransparencyForBackground(hasDeckPickerBackground);
mDeckListAdapter.setMarketClickListener(v -> getAnkiActivity().openSourceMarket());
mRecyclerView.setAdapter(mDeckListAdapter);
mPullToSyncWrapper.setDistanceToTriggerSync(SWIPE_TO_SYNC_TRIGGER_DISTANCE);
mPullToSyncWrapper.setOnRefreshListener(() -> {
Timber.i("Pull to Sync: Syncing");
mPullToSyncWrapper.setRefreshing(false);
updateDeckList();
// getAnkiActivity().sync();
});
mPullToSyncWrapper.getViewTreeObserver().addOnScrollChangedListener(() -> mPullToSyncWrapper.setEnabled(mRecyclerViewLayoutManager.findFirstCompletelyVisibleItemPosition() == 0));
// Setup the FloatingActionButtons, should work everywhere with min API >= 15
mActionsMenu = mRoot.findViewById(R.id.add_content_menu);
mActionsMenu.findViewById(R.id.fab_expand_menu_button).setContentDescription(getString(R.string.menu_add));
// configureFloatingActionsMenu();
mReviewSummaryTextView = mRoot.findViewById(R.id.today_stats_text_view);
// mRoot.findViewById(R.id.tv_resource).setOnClickListener(v -> getAnkiActivity().openSourceMarket());
mShortAnimDuration = getResources().getInteger(android.R.integer.config_shortAnimTime);
fetchAds();
return mRoot;
}
Aggregations