use of com.ichi2.libanki.sched.Counts.Queue in project AnkiChinaAndroid by ankichinateam.
the class StudyOptionsFragment method getCollectionTaskListener.
/**
* Returns a listener that rebuilds the interface after execute.
*
* @param refreshDecklist If true, the listener notifies the parent activity to update its deck list
* to reflect the latest values.
*/
private TaskListener getCollectionTaskListener(final boolean refreshDecklist) {
return new TaskListener() {
@Override
public void onPreExecute() {
}
@Override
public void onPostExecute(TaskData result) {
dismissProgressDialog();
if (result != null) {
// Get the return values back from the AsyncTask
Object[] obj = result.getObjArray();
int newCards = (Integer) obj[0];
int lrnCards = (Integer) obj[1];
int revCards = (Integer) obj[1] + (Integer) obj[2];
int totalNew = (Integer) obj[3];
int totalCards = (Integer) obj[4];
Timber.i("start refresh list data:" + newCards + "," + lrnCards + "," + revCards + "," + totalNew + "," + totalCards);
// Don't do anything if the fragment is no longer attached to it's Activity or col has been closed
if (getActivity() == null) {
Timber.e("StudyOptionsFragment.mRefreshFragmentListener :: can't refresh");
return;
}
// #5506 If we have no view, short circuit all UI logic
if (mStudyOptionsView == null) {
tryOpenCramDeckOptions();
return;
}
// Reinitialize controls incase changed to filtered deck
initAllContentViews(mStudyOptionsView);
// Set the deck name
String fullName;
Deck deck = getCol().getDecks().current();
// Main deck name
fullName = deck.getString("name");
String[] name = Decks.path(fullName);
StringBuilder nameBuilder = new StringBuilder();
if (name.length > 0) {
nameBuilder.append(name[name.length - 1]);
}
// if (name.length > 1) {
// nameBuilder.append("\n").append(name[1]);
// }
// if (name.length > 3) {
// nameBuilder.append("...");
// }
// if (name.length > 2) {
// nameBuilder.append("\n").append(name[name.length - 1]);
// }
// mTextDeckName.setText(nameBuilder.toString());
mDeckListAdapter.mTextDeckName = nameBuilder.toString();
if (tryOpenCramDeckOptions()) {
return;
}
// Switch between the empty view, the ordinary view, and the "congratulations" view
boolean isDynamic = deck.optInt("dyn", 0) != 0;
if (totalCards == 0 && !isDynamic) {
mCurrentContentView = CONTENT_EMPTY;
mDeckListAdapter.mDeckInfoLayoutVisible = View.VISIBLE;
mDeckListAdapter.mTextCongratsMessageVisible = View.VISIBLE;
// mDeckListAdapter.mTextCongratsMessage=getString(R.string.studyoptions_empty);
mDeckListAdapter.mButtonStartEnable = false;
mDeckListAdapter.mTextButtonStart = getString(R.string.studyoptions_start);
} else if (newCards + lrnCards + revCards == 0) {
mCurrentContentView = CONTENT_CONGRATS;
if (!isDynamic) {
mDeckListAdapter.mDeckInfoLayoutVisible = View.GONE;
mDeckListAdapter.mButtonStartEnable = true;
mDeckListAdapter.mTextButtonStart = getString(R.string.add_today_study_amount);
} else {
mDeckListAdapter.mButtonStartEnable = true;
mDeckListAdapter.mTextButtonStart = getString(R.string.add_today_study_amount);
}
mDeckListAdapter.mTextCongratsMessageVisible = View.VISIBLE;
// mDeckListAdapter.mTextCongratsMessage=getCol().getSched().finishedMsg(getActivity()).toString();
// mTextCongratsMessage.setText(getCol().getSched().finishedMsg(getActivity()));
} else {
mCurrentContentView = CONTENT_STUDY_OPTIONS;
mDeckListAdapter.mDeckInfoLayoutVisible = View.VISIBLE;
mDeckListAdapter.mTextCongratsMessageVisible = View.GONE;
mDeckListAdapter.mButtonStartEnable = true;
mDeckListAdapter.mTextButtonStart = getString(R.string.studyoptions_start);
}
mDeckListAdapter.setButtonStartClickListener(mButtonClickListener);
mDeckListAdapter.setSelfStudyClickListener(mSelfStudyListener);
// Set deck description
String desc;
if (isDynamic) {
desc = getResources().getString(R.string.dyn_deck_desc);
} else {
desc = "";
// desc = getCol().getDecks().getActualDescription();
}
if (desc.length() > 0) {
mDeckListAdapter.mTextDeckDescription = desc;
mDeckListAdapter.mTextDeckDescriptionVisible = View.VISIBLE;
// mTextDeckDescription.setText(formatDescription(desc));
// mTextDeckDescription.setVisibility(View.VISIBLE);
} else {
mDeckListAdapter.mTextDeckDescriptionVisible = View.GONE;
}
// Set new/learn/review card counts
mDeckListAdapter.mTextTodayNew = String.valueOf(newCards);
mDeckListAdapter.mTextTodayRev = String.valueOf(revCards);
// Set the total number of new cards in deck
if (totalNew < NEW_CARD_COUNT_TRUNCATE_THRESHOLD) {
// if it hasn't been truncated by libanki then just set it usually
// mTextNewTotal.setText(String.valueOf(totalNew));
} else {
// mTextNewTotal.setText(">1000");
if (mFullNewCountThread != null) {
// a thread was previously made -- interrupt it
mFullNewCountThread.interrupt();
}
// mFullNewCountThread = new Thread(() -> {
// Collection collection = getCol();
// TODO: refactor code to not rewrite this query, add to Sched.totalNewForCurrentDeck()
// String query = "SELECT count(*) FROM cards WHERE did IN " +
// Utils.ids2str(collection.getDecks().active()) +
// " AND queue = " + Consts.QUEUE_TYPE_NEW;
// final int fullNewCount = collection.getDb().queryScalar(query);
// if (fullNewCount > 0) {
// Runnable setNewTotalText = new Runnable() {
// @Override
// public void run() {
// mTextNewTotal.setText(String.valueOf(fullNewCount));
// }
// };
// if (!Thread.currentThread().isInterrupted()) {
// mTextNewTotal.post(setNewTotalText);
// }
// }
// });
// mFullNewCountThread.start();
}
// Set total number of cards
// mTextTotal.setText(String.valueOf(totalCards));
double[] data = calculateStat(getCol(), getCol().getDecks().current().optLong("id"));
mNewCardsNum = (int) data[2];
mRevCardsNum = revCards;
mShouldConfigBeforeStudy = mNewCardsNum == totalCards && mShouldConfigBeforeStudy;
int hardNum = getLapses(getCol(), getCol().getDecks().current().optLong("id"));
mDeckListAdapter.mTextCountHandled = String.format(Locale.CHINA, "%d", (int) data[0]);
mDeckListAdapter.mTextCountLearning = String.format(Locale.CHINA, "%d", (int) data[1]);
mDeckListAdapter.mTextCountNew = String.format(Locale.CHINA, "%d", (int) data[2]);
mDeckListAdapter.mTextCountHard = String.format(Locale.CHINA, "%d", hardNum);
mDeckListAdapter.mTextTotal = String.format(Locale.CHINA, "共%d张卡牌", totalCards);
double percent = 0;
if (data[2] == 0) {
// 新卡已学完,显示已掌握
percent = (data[0] + data[1] + data[2] <= 0) ? 0 : (data[0] / (data[0] + data[1] + data[2]) * 100);
mDeckListAdapter.mTextHandledNum = String.format(Locale.CHINA, "%.0f/%.0f", data[0], (data[0] + data[1] + data[2]));
// holder.handled_percent.setText((String.format(Locale.CHINA, "已掌握 %.1f", percent)) + "%");
} else {
percent = (data[0] + data[1] + data[2] <= 0) ? 0 : ((data[0] + data[1]) / (data[0] + data[1] + data[2]) * 100);
mDeckListAdapter.mTextHandledNum = String.format(Locale.CHINA, "%.0f/%.0f", data[0] + data[1], data[0] + data[1] + data[2]);
// holder.handled_percent.setText((String.format(Locale.CHINA, "已学 %.1f", percent)) + "%");
}
// double percent = (data[0] + data[1] + data[2] <= 0) ? 0 : (data[0] / (data[0] + data[1] + data[2]) * 100);
// mStudyProgress.setMax(100*100);
mDeckListAdapter.mStudyProgress = (int) (percent * 100);
mDeckListAdapter.mTextHandledPercent = (String.format(Locale.CHINA, data[2] == 0 ? "已掌握 %.1f" : "已学 %.1f", percent)) + "%";
// Set estimated time remaining
int eta = (newCards + revCards) * 10 / 60;
if ((newCards + revCards) % 60 != 0) {
eta++;
}
if (eta != -1) {
mDeckListAdapter.mTextETA = "" + eta;
} else {
mDeckListAdapter.mTextETA = "-";
}
mDeckListAdapter.notifyDataSetChangedAll();
// Rebuild the options menu
configureToolbar();
}
updateDeckList();
// If in fragmented mode, refresh the deck list
if (mFragmented && refreshDecklist) {
mListener.onRequireDeckListUpdate();
}
}
};
}
use of com.ichi2.libanki.sched.Counts.Queue in project AnkiChinaAndroid by ankichinateam.
the class Sched method _fillRev.
@Override
protected boolean _fillRev(boolean allowSibling) {
if (!mRevQueue.isEmpty()) {
return true;
}
if (mRevCount == 0) {
return false;
}
SupportSQLiteDatabase db = mCol.getDb().getDatabase();
while (!mRevDids.isEmpty()) {
long did = mRevDids.getFirst();
int lim = Math.min(mQueueLimit, _deckRevLimit(did));
Cursor cur = null;
if (lim != 0) {
mRevQueue.clear();
// fill the queue with the current did
try {
/* Difference with upstream: we take current card into account.
*
* When current card is answered, the card is not due anymore, so does not belong to the queue.
* Furthermore, _burySiblings ensure that the siblings of the current cards are removed from the
* queue to ensure same day spacing. We simulate this action by ensuring that those siblings are not
* filled, except if we know there are cards and we didn't find any non-sibling card. This way, the
* queue is not empty if it should not be empty (important for the conditional belows), but the
* front of the queue contains distinct card.
*/
String idName = (allowSibling) ? "id" : "nid";
long id = (allowSibling) ? currentCardId() : currentCardNid();
cur = db.query("SELECT id FROM cards WHERE did = ? AND queue = " + Consts.QUEUE_TYPE_REV + " AND due <= ?" + " AND " + idName + " != ? LIMIT ?", new Object[] { did, mToday, id, lim });
while (cur.moveToNext()) {
mRevQueue.add(cur.getLong(0));
}
} finally {
if (cur != null && !cur.isClosed()) {
cur.close();
}
}
if (!mRevQueue.isEmpty()) {
// ordering
if (mCol.getDecks().get(did).getInt("dyn") != 0) {
// dynamic decks need due order preserved
// Note: libanki reverses mRevQueue and returns the last element in _getRevCard().
// AnkiDroid differs by leaving the queue intact and returning the *first* element
// in _getRevCard().
} else {
Random r = new Random();
r.setSeed(mToday);
mRevQueue.shuffle(r);
}
// is the current did empty?
if (mRevQueue.size() < lim) {
mRevDids.remove();
}
return true;
}
}
// nothing left in the deck; move to next
mRevDids.remove();
}
// Since we didn't get a card and the count is non-zero, we
// need to check again for any cards that were removed from
// the queue but not buried
_resetRev();
return _fillRev(true);
}
use of com.ichi2.libanki.sched.Counts.Queue in project AnkiChinaAndroid by ankichinateam.
the class SchedV2 method currentCardIsInQueueWithDeck.
protected boolean currentCardIsInQueueWithDeck(@Consts.CARD_QUEUE int queue, long did) {
// mCurrentCard may be set to null when the reviewer gets closed. So we copy it to be sure to avoid NullPointerException
Card currentCard = mCurrentCard;
List<Long> currentCardParentsDid = mCurrentCardParentsDid;
return currentCard != null && currentCard.getQueue() == queue && currentCardParentsDid != null && currentCardParentsDid.contains(did);
}
use of com.ichi2.libanki.sched.Counts.Queue in project AnkiChinaAndroid by ankichinateam.
the class Anki2Importer method _importCards.
private void _importCards() {
if (mMustResetLearning) {
try {
mSrc.changeSchedulerVer(2);
} catch (ConfirmModSchemaException e) {
throw new RuntimeException("Changing the scheduler of an import should not cause schema modification", e);
}
}
// build map of guid -> (ord -> cid) and used id cache
mCards = new HashMap<>();
Map<Long, Boolean> existing = new HashMap<>();
Cursor cur = null;
try {
cur = mDst.getDb().getDatabase().query("select f.guid, c.ord, c.id from cards c, notes f " + "where c.nid = f.id", null);
while (cur.moveToNext()) {
String guid = cur.getString(0);
int ord = cur.getInt(1);
long cid = cur.getLong(2);
existing.put(cid, true);
if (mCards.containsKey(guid)) {
mCards.get(guid).put(ord, cid);
} else {
Map<Integer, Long> map = new HashMap<>();
map.put(ord, cid);
mCards.put(guid, map);
}
}
} finally {
if (cur != null) {
cur.close();
}
}
// loop through src
List<Object[]> cards = new ArrayList<>();
int totalCardCount = 0;
final int thresExecCards = 1000;
List<Object[]> revlog = new ArrayList<>();
int totalRevlogCount = 0;
final int thresExecRevlog = 1000;
int usn = mDst.usn();
long aheadBy = mSrc.getSched().getToday() - mDst.getSched().getToday();
try {
mDst.getDb().getDatabase().beginTransaction();
cur = mSrc.getDb().getDatabase().query("select f.guid, f.mid, c.* from cards c, notes f " + "where c.nid = f.id", null);
// Counters for progress updates
int total = cur.getCount();
boolean largeCollection = total > 200;
int onePercent = total / 100;
int i = 0;
while (cur.moveToNext()) {
Object[] card = new Object[] { cur.getString(0), cur.getLong(1), cur.getLong(2), cur.getLong(3), cur.getLong(4), cur.getInt(5), cur.getLong(6), cur.getInt(7), cur.getInt(8), cur.getInt(9), cur.getLong(10), cur.getLong(11), cur.getLong(12), cur.getInt(13), cur.getInt(14), cur.getInt(15), cur.getLong(16), cur.getLong(17), cur.getInt(18), cur.getString(19) };
String guid = (String) card[0];
if (mChangedGuids.containsKey(guid)) {
guid = mChangedGuids.get(guid);
}
if (mIgnoredGuids.containsKey(guid)) {
continue;
}
// does the card's note exist in dst col?
if (!mNotes.containsKey(guid)) {
continue;
}
Object[] dnid = mNotes.get(guid);
// does the card already exist in the dst col?
int ord = (Integer) card[5];
if (mCards.containsKey(guid) && mCards.get(guid).containsKey(ord)) {
// fixme: in future, could update if newer mod time
continue;
}
// doesn't exist. strip off note info, and save src id for later
Object[] oc = card;
card = new Object[oc.length - 2];
System.arraycopy(oc, 2, card, 0, card.length);
long scid = (Long) card[0];
// ensure the card id is unique
while (existing.containsKey(card[0])) {
card[0] = (Long) card[0] + 999;
}
existing.put((Long) card[0], true);
// update cid, nid, etc
card[1] = mNotes.get(guid)[0];
card[2] = _did((Long) card[2]);
if (mTopID < 0) {
mTopID = (long) card[2];
}
card[4] = mCol.getTime().intTime();
card[5] = usn;
// review cards have a due date relative to collection
if ((Integer) card[7] == 2 || (Integer) card[7] == 3 || (Integer) card[6] == 2) {
card[8] = (Long) card[8] - aheadBy;
}
// odue needs updating too
if (((Long) card[14]).longValue() != 0) {
card[14] = (Long) card[14] - aheadBy;
}
// if odid true, convert card from filtered to normal
if ((Long) card[15] != 0) {
// odid
card[15] = 0;
// odue
card[8] = card[14];
card[14] = 0;
// queue
if ((Integer) card[6] == 1) {
// type
card[7] = 0;
} else {
card[7] = card[6];
}
// type
if ((Integer) card[6] == 1) {
card[6] = 0;
}
}
cards.add(card);
// we need to import revlog, rewriting card ids and bumping usn
try (Cursor cur2 = mSrc.getDb().getDatabase().query("select * from revlog where cid = " + scid, null)) {
while (cur2.moveToNext()) {
Object[] rev = new Object[] { cur2.getLong(0), cur2.getLong(1), cur2.getInt(2), cur2.getInt(3), cur2.getLong(4), cur2.getLong(5), cur2.getLong(6), cur2.getLong(7), cur2.getInt(8) };
rev[1] = card[0];
rev[2] = mDst.usn();
revlog.add(rev);
}
}
i++;
// apply card changes partially
if (cards.size() >= thresExecCards) {
totalCardCount += cards.size();
insertCards(cards);
cards.clear();
Timber.d("add cards: %d", totalCardCount);
}
// apply revlog changes partially
if (revlog.size() >= thresExecRevlog) {
totalRevlogCount += revlog.size();
insertRevlog(revlog);
revlog.clear();
Timber.d("add revlog: %d", totalRevlogCount);
}
if (total != 0 && (!largeCollection || i % onePercent == 0)) {
publishProgress(100, i * 100 / total, 0);
}
}
publishProgress(100, 100, 0);
// count total values
totalCardCount += cards.size();
totalRevlogCount += revlog.size();
Timber.d("add cards total: %d", totalCardCount);
Timber.d("add revlog total: %d", totalRevlogCount);
// apply (for last chunk)
insertCards(cards);
cards.clear();
insertRevlog(revlog);
revlog.clear();
mLog.add(getRes().getString(R.string.import_complete_count, totalCardCount));
mDst.getDb().getDatabase().setTransactionSuccessful();
} finally {
if (cur != null) {
cur.close();
}
if (mDst.getDb().getDatabase().inTransaction()) {
try {
mDst.getDb().getDatabase().endTransaction();
} catch (Exception e) {
Timber.w(e);
}
}
}
}
use of com.ichi2.libanki.sched.Counts.Queue in project AnkiChinaAndroid by ankichinateam.
the class SchedV2 method _burySiblings.
/**
* Sibling spacing
* ********************
*/
protected void _burySiblings(@NonNull Card card) {
ArrayList<Long> toBury = new ArrayList<>();
JSONObject nconf = _newConf(card);
boolean buryNew = nconf.optBoolean("bury", true);
JSONObject rconf = _revConf(card);
boolean buryRev = rconf.optBoolean("bury", true);
// loop through and remove from queues
Cursor cur = null;
try {
cur = mCol.getDb().query("select id, queue from cards where nid=? and id!=? " + "and (queue=" + Consts.QUEUE_TYPE_NEW + " or (queue=" + Consts.QUEUE_TYPE_REV + " and due<=?))", card.getNid(), card.getId(), mToday);
while (cur.moveToNext()) {
long cid = cur.getLong(0);
int queue = cur.getInt(1);
SimpleCardQueue queue_object;
if (queue == Consts.QUEUE_TYPE_REV) {
queue_object = mRevQueue;
if (buryRev) {
toBury.add(cid);
}
} else {
queue_object = mNewQueue;
if (buryNew) {
toBury.add(cid);
}
}
// even if burying disabled, we still discard to give
// same-day spacing
queue_object.remove(cid);
}
} finally {
if (cur != null && !cur.isClosed()) {
cur.close();
}
}
// then bury
if (!toBury.isEmpty()) {
buryCards(Utils.collection2Array(toBury), false);
}
}
Aggregations