Search in sources :

Example 6 with Time

use of com.ichi2.libanki.utils.Time in project AnkiChinaAndroid by ankichinateam.

the class CollectionTask method doInBackgroundImportReplace.

private TaskData doInBackgroundImportReplace(TaskData param) {
    Timber.d("doInBackgroundImportReplace");
    String path = param.getString();
    Resources res = AnkiDroidApp.getInstance().getBaseContext().getResources();
    // extract the deck from the zip file
    String colPath = CollectionHelper.getCollectionPath(mContext);
    File dir = new File(new File(colPath).getParentFile(), "tmpzip");
    if (dir.exists()) {
        BackupManager.removeDir(dir);
    }
    // from anki2.py
    String colname = "collection.anki21";
    ZipFile zip;
    try {
        zip = new ZipFile(new File(path));
    } catch (IOException e) {
        Timber.e(e, "doInBackgroundImportReplace - Error while unzipping");
        AnkiDroidApp.sendExceptionReport(e, "doInBackgroundImportReplace0");
        return new TaskData(false);
    }
    try {
        // v2 scheduler?
        if (zip.getEntry(colname) == null) {
            colname = CollectionHelper.COLLECTION_FILENAME;
        }
        Utils.unzipFiles(zip, dir.getAbsolutePath(), new String[] { colname, "media" }, null);
    } catch (IOException e) {
        AnkiDroidApp.sendExceptionReport(e, "doInBackgroundImportReplace - unzip");
        return new TaskData(false);
    }
    String colFile = new File(dir, colname).getAbsolutePath();
    if (!(new File(colFile)).exists()) {
        return new TaskData(false);
    }
    Collection tmpCol = null;
    try {
        tmpCol = Storage.Collection(mContext, colFile);
        if (!tmpCol.validCollection()) {
            tmpCol.close();
            return new TaskData(false);
        }
    } catch (Exception e) {
        Timber.e("Error opening new collection file... probably it's invalid");
        try {
            tmpCol.close();
        } catch (Exception e2) {
        // do nothing
        }
        AnkiDroidApp.sendExceptionReport(e, "doInBackgroundImportReplace - open col");
        return new TaskData(false);
    } finally {
        if (tmpCol != null) {
            tmpCol.close();
        }
    }
    publishProgress(new TaskData(res.getString(R.string.importing_collection)));
    if (hasValidCol()) {
        // unload collection and trigger a backup
        Time time = CollectionHelper.getInstance().getTimeSafe(mContext);
        CollectionHelper.getInstance().closeCollection(true, "Importing new collection");
        CollectionHelper.getInstance().lockCollection();
        BackupManager.performBackupInBackground(colPath, true, time);
    }
    // overwrite collection
    File f = new File(colFile);
    if (!f.renameTo(new File(colPath))) {
        // Exit early if this didn't work
        return new TaskData(false);
    }
    int addedCount = -1;
    try {
        CollectionHelper.getInstance().unlockCollection();
        // because users don't have a backup of media, it's safer to import new
        // data and rely on them running a media db check to get rid of any
        // unwanted media. in the future we might also want to duplicate this step
        // import media
        HashMap<String, String> nameToNum = new HashMap<>();
        HashMap<String, String> numToName = new HashMap<>();
        File mediaMapFile = new File(dir.getAbsolutePath(), "media");
        if (mediaMapFile.exists()) {
            JsonReader jr = new JsonReader(new FileReader(mediaMapFile));
            jr.beginObject();
            String name;
            String num;
            while (jr.hasNext()) {
                num = jr.nextName();
                name = jr.nextString();
                nameToNum.put(name, num);
                numToName.put(num, name);
            }
            jr.endObject();
            jr.close();
        }
        String mediaDir = Media.getCollectionMediaPath(colPath);
        int total = nameToNum.size();
        int i = 0;
        for (Map.Entry<String, String> entry : nameToNum.entrySet()) {
            String file = entry.getKey();
            String c = entry.getValue();
            File of = new File(mediaDir, file);
            if (!of.exists()) {
                Utils.unzipFiles(zip, mediaDir, new String[] { c }, numToName);
            }
            ++i;
            publishProgress(new TaskData(res.getString(R.string.import_media_count, (i + 1) * 100 / total)));
        }
        zip.close();
        // delete tmp dir
        BackupManager.removeDir(dir);
        return new TaskData(true);
    } catch (RuntimeException e) {
        Timber.e(e, "doInBackgroundImportReplace - RuntimeException");
        AnkiDroidApp.sendExceptionReport(e, "doInBackgroundImportReplace1");
        return new TaskData(false);
    } catch (FileNotFoundException e) {
        Timber.e(e, "doInBackgroundImportReplace - FileNotFoundException");
        AnkiDroidApp.sendExceptionReport(e, "doInBackgroundImportReplace2");
        return new TaskData(false);
    } catch (IOException e) {
        Timber.e(e, "doInBackgroundImportReplace - IOException");
        AnkiDroidApp.sendExceptionReport(e, "doInBackgroundImportReplace3");
        return new TaskData(false);
    }
}
Also used : HashMap(java.util.HashMap) FileNotFoundException(java.io.FileNotFoundException) Time(com.ichi2.libanki.utils.Time) IOException(java.io.IOException) JSONException(com.ichi2.utils.JSONException) CancellationException(java.util.concurrent.CancellationException) FileNotFoundException(java.io.FileNotFoundException) ConfirmModSchemaException(com.ichi2.anki.exception.ConfirmModSchemaException) ImportExportException(com.ichi2.anki.exception.ImportExportException) IOException(java.io.IOException) ExecutionException(java.util.concurrent.ExecutionException) ZipFile(org.apache.commons.compress.archivers.zip.ZipFile) Collection(com.ichi2.libanki.Collection) JsonReader(com.google.gson.stream.JsonReader) FileReader(java.io.FileReader) Resources(android.content.res.Resources) ZipFile(org.apache.commons.compress.archivers.zip.ZipFile) File(java.io.File) Map(java.util.Map) HashMap(java.util.HashMap) TreeMap(java.util.TreeMap)

Example 7 with Time

use of com.ichi2.libanki.utils.Time in project AnkiChinaAndroid by ankichinateam.

the class BootService method scheduleNotification.

public static void scheduleNotification(Time time, Context context) {
    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    SharedPreferences sp = AnkiDroidApp.getSharedPrefs(context);
    // Don't schedule a notification if the due reminders setting is not enabled
    if (Integer.parseInt(sp.getString("minimumCardsDueForNotification", Integer.toString(Preferences.PENDING_NOTIFICATIONS_ONLY))) >= Preferences.PENDING_NOTIFICATIONS_ONLY) {
        return;
    }
    final Calendar calendar = time.calendar();
    calendar.set(Calendar.HOUR_OF_DAY, sp.getInt("dayOffset", 0));
    calendar.set(Calendar.MINUTE, 0);
    calendar.set(Calendar.SECOND, 0);
    final PendingIntent notificationIntent = PendingIntent.getBroadcast(context, 0, new Intent(context, NotificationService.class), 0);
    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, notificationIntent);
}
Also used : SharedPreferences(android.content.SharedPreferences) Calendar(java.util.Calendar) DeckOptions.reminderToCalendar(com.ichi2.anki.DeckOptions.reminderToCalendar) AlarmManager(android.app.AlarmManager) Intent(android.content.Intent) PendingIntent(android.app.PendingIntent) PendingIntent(android.app.PendingIntent)

Example 8 with Time

use of com.ichi2.libanki.utils.Time in project AnkiChinaAndroid by ankichinateam.

the class Sched method deckDueList.

/**
 * Returns [deckname, did, rev, lrn, new]
 */
@Override
@Nullable
public List<DeckDueTreeNode> deckDueList(@Nullable CollectionTask collectionTask, long did) {
    _checkDay();
    mCol.getDecks().checkIntegrity();
    ArrayList<Deck> decks = deckLimitWithParent(did, mCol);
    // ArrayList<Deck> decks = mCol.getDecks().allSorted();
    Timber.i("show decks" + did + " size:" + decks.size());
    HashMap<String, Integer[]> lims = new HashMap<>();
    ArrayList<DeckDueTreeNode> data = new ArrayList<>();
    long time = SystemClock.elapsedRealtime();
    for (Deck deck : decks) {
        if (collectionTask != null && collectionTask.isCancelled()) {
            return null;
        }
        String deckName = deck.getString("name");
        String p = Decks.parent(deckName);
        // new
        int nlim = _deckNewLimitSingle(deck);
        int rlim = _deckRevLimitSingle(deck);
        if (!TextUtils.isEmpty(p)) {
            Integer[] parentLims = lims.get(Decks.normalizeName(p));
            // 'temporary for diagnosis of bug #6383'
            Assert.that(parentLims != null, "Deck %s is supposed to have parent %s. It has not be found.", deckName, p);
            nlim = Math.min(nlim, parentLims[0]);
            // review
            rlim = Math.min(rlim, parentLims[1]);
        }
        // long time = SystemClock.elapsedRealtime();
        int _new = _newForDeck(deck.getLong("id"), nlim);
        // Timber.i("cost time for new:"+(SystemClock.elapsedRealtime()-time));
        // learning
        int lrn = _lrnForDeck(deck.getLong("id"));
        // Timber.i("cost time for lrn:"+(SystemClock.elapsedRealtime()-time));
        // reviews
        int rev = _revForDeck(deck.getLong("id"), rlim);
        // Timber.i("cost time for rev:"+(SystemClock.elapsedRealtime()-time));
        // save to list
        double[] datas = did == ALL_DECKS_ID ? _getCardDataCount(deck.getLong("id")) : new double[] { 0, 0, 0 };
        // Timber.i("cost time for datas:"+(SystemClock.elapsedRealtime()-time));
        // Timber.i("add deck in tree:%s", deckName);
        data.add(new DeckDueTreeNode(mCol, deck.getString("name"), deck.getLong("id"), rev, lrn, _new, datas));
        // add deck as a parent
        lims.put(Decks.normalizeName(deck.getString("name")), new Integer[] { nlim, rlim });
    }
    Timber.i("cost time for datas:" + (SystemClock.elapsedRealtime() - time));
    return data;
}
Also used : HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) Deck(com.ichi2.libanki.Deck) Nullable(androidx.annotation.Nullable)

Example 9 with Time

use of com.ichi2.libanki.utils.Time in project AnkiChinaAndroid by ankichinateam.

the class SchedV2 method quickDeckDueTree.

/**
 * Similar to deck due tree, but ignore the number of cards.
 *
 *     It may takes a lot of time to compute the number of card, it
 *     requires multiple database access by deck.  Ignoring this number
 *     lead to the creation of a tree more quickly.
 */
@Override
@NonNull
public List<DeckTreeNode> quickDeckDueTree() {
    // Similar to deckDueTree, ignoring the numbers
    // Similar to deckDueList
    ArrayList<DeckTreeNode> data = new ArrayList<>();
    for (JSONObject deck : mCol.getDecks().allSorted()) {
        DeckTreeNode g = new DeckTreeNode(mCol, deck.getString("name"), deck.getLong("id"));
        data.add(g);
    }
    return _groupChildren(data, false);
}
Also used : JSONObject(com.ichi2.utils.JSONObject) ArrayList(java.util.ArrayList) NonNull(androidx.annotation.NonNull)

Example 10 with Time

use of com.ichi2.libanki.utils.Time 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);
            }
        }
    }
}
Also used : HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) Cursor(android.database.Cursor) ConfirmModSchemaException(com.ichi2.anki.exception.ConfirmModSchemaException) ImportExportException(com.ichi2.anki.exception.ImportExportException) IOException(java.io.IOException) FileNotFoundException(java.io.FileNotFoundException) ConfirmModSchemaException(com.ichi2.anki.exception.ConfirmModSchemaException)

Aggregations

Test (org.junit.Test)27 Collection (com.ichi2.libanki.Collection)26 JSONObject (com.ichi2.utils.JSONObject)26 RobolectricTest (com.ichi2.anki.RobolectricTest)19 Card (com.ichi2.libanki.Card)19 Note (com.ichi2.libanki.Note)15 JSONArray (com.ichi2.utils.JSONArray)15 Deck (com.ichi2.libanki.Deck)11 ArrayList (java.util.ArrayList)11 DeckConfig (com.ichi2.libanki.DeckConfig)10 IOException (java.io.IOException)10 ConfirmModSchemaException (com.ichi2.anki.exception.ConfirmModSchemaException)9 HashMap (java.util.HashMap)9 Cursor (android.database.Cursor)8 Nullable (androidx.annotation.Nullable)8 JSONException (com.ichi2.utils.JSONException)8 SharedPreferences (android.content.SharedPreferences)7 Resources (android.content.res.Resources)7 File (java.io.File)7 View (android.view.View)5