com.hichinaschool.flashcards.libanki.Decks.java Source code

Java tutorial

Introduction

Here is the source code for com.hichinaschool.flashcards.libanki.Decks.java

Source

/****************************************************************************************
 * Copyright (c) 2009 Daniel Svrd <daniel.svard@gmail.com>                             *
 * Copyright (c) 2009 Casey Link <unnamedrambler@gmail.com>                             *
 * Copyright (c) 2009 Edu Zamora <edu.zasu@gmail.com>                                   *
 * Copyright (c) 2010 Norbert Nagold <norbert.nagold@gmail.com>                         *
 *                                                                                      *
 * This program is free software; you can redistribute it and/or modify it under        *
 * the terms of the GNU General Public License as published by the Free Software        *
 * Foundation; either version 3 of the License, or (at your option) any later           *
 * version.                                                                             *
 *                                                                                      *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
 * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
 *                                                                                      *
 * You should have received a copy of the GNU General Public License along with         *
 * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
 ****************************************************************************************/

package com.hichinaschool.flashcards.libanki;

import android.content.ContentValues;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.TreeMap;

/**
 * A deck stores all of the cards and scheduling information. It is saved in a file with a name ending in .anki See
 * http://ichi2.net/anki/wiki/KeyTermsAndConcepts#Deck
 */
public class Decks {

    // private TreeMap<Integer, Model> mModelCache;
    // private TreeMap<Integer, JSONObject> mGroupConfCache;

    public static final String defaultDeck = "{" + "'newToday': [0, 0], " + // currentDay, count
            "'revToday': [0, 0], " + "'lrnToday': [0, 0], " + "'timeToday': [0, 0], " + // time in ms
            "'conf': 1, " + "'usn': 0, " + "'desc': \"\", 'dyn': 0, 'collapsed': False, " +
            // added in beta11
            "'extendNew': 10, 'extendRev': 50 }";

    private static final String defaultDynamicDeck = "{" + "'newToday': [0, 0], " + // currentDay, count
            "'revToday': [0, 0], " + "'lrnToday': [0, 0], " + "'timeToday': [0, 0], " + // time in ms
            "'collapsed': False, 'dyn': 1, 'desc': \"\", 'usn': 0, 'delays': null, 'separate': True, " +
            // list of (search, limit, order); we only use first element for now
            "'terms': [[\"\", 100, 0]], 'resched': True, " +
            // currently unused
            "'return': True }";

    // default group conf
    public static final String defaultConf = "{" + "'name': \"Default\"," + "'new': {" + "'delays': [1, 10], "
            + "'ints': [1, 4, 7], " + // 7
                                                                                                                                                 // is
                                                                                                                                                 // not
                                                                                                                                                 // currently
                                                                                                                                                 // used
            "'initialFactor': 2500, " + "'separate': True, " + "'order': " + Sched.NEW_CARDS_DUE + ", "
            + "'perDay': 20, 'bury': True }, " + "'lapse': {" + "'delays': [10], " + "'mult': 0, " + "'minInt': 1, "
            + "'leechFails': 8, " + "'leechAction': 0 }, "
            // type 0=suspend, 1=tagonly
            + "'rev': { " + "'perDay': 100, " + "'ease4': 1.3, " + "'fuzz': 0.05, " + "'minSpace': 1, "
            + "'ivlFct': 1, " + "'maxIvl': 36500 }, " + "'maxTaken': 60, " + "'timer': 0, "
            + "'autoplay': True, 'replayq': True, " + "'mod': 0, " + "'usn': 0 }";

    private Collection mCol;
    private HashMap<Long, JSONObject> mDecks;
    private HashMap<Long, JSONObject> mDconf;
    private boolean mChanged;

    /**
     * Registry save/load *******************************************************
     * ****************************************
     */

    public Decks(Collection col) {
        mCol = col;
    }

    public void load(String decks, String dconf) {
        mDecks = new HashMap<Long, JSONObject>();
        mDconf = new HashMap<Long, JSONObject>();
        try {
            JSONObject decksarray = new JSONObject(decks);
            JSONArray ids = decksarray.names();
            for (int i = 0; i < ids.length(); i++) {
                String id = ids.getString(i);
                JSONObject o = decksarray.getJSONObject(id);
                long longId = Long.parseLong(id);
                mDecks.put(longId, o);
            }
            JSONObject confarray = new JSONObject(dconf);
            ids = confarray.names();
            for (int i = 0; i < ids.length(); i++) {
                String id = ids.getString(i);
                mDconf.put(Long.parseLong(id), confarray.getJSONObject(id));
            }
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
        mChanged = false;
    }

    public void save() {
        save(null);
    }

    /** Can be called with either a deck or a deck configuration. */
    public void save(JSONObject g) {
        if (g != null) {
            try {
                g.put("mod", Utils.intNow());
                g.put("usn", mCol.usn());
            } catch (JSONException e) {
                throw new RuntimeException(e);
            }
        }
        mChanged = true;
    }

    public void flush() {
        ContentValues values = new ContentValues();
        if (mChanged) {
            try {
                JSONObject decksarray = new JSONObject();
                for (Map.Entry<Long, JSONObject> d : mDecks.entrySet()) {
                    decksarray.put(Long.toString(d.getKey()), d.getValue());
                }
                values.put("decks", Utils.jsonToString(decksarray));
                JSONObject confarray = new JSONObject();
                for (Map.Entry<Long, JSONObject> d : mDconf.entrySet()) {
                    confarray.put(Long.toString(d.getKey()), d.getValue());
                }
                values.put("dconf", Utils.jsonToString(confarray));
            } catch (JSONException e) {
                throw new RuntimeException(e);
            }
            mCol.getDb().update("col", values);
            mChanged = false;
        }
    }

    /**
     * Deck save/load *********************************************************** ************************************
     */

    public long id(String name) {
        return id(name, true);
    }

    /** Add a deck with NAME. Reuse deck if already exists. Return id as int. */
    public long id(String name, boolean create) {
        return id(name, create, defaultDeck);
    }

    public long id(String name, boolean create, String type) {
        name = name.replace("\'", "").replace("\"", "");
        for (Map.Entry<Long, JSONObject> g : mDecks.entrySet()) {
            try {
                if (g.getValue().getString("name").equalsIgnoreCase(name)) {
                    return g.getKey();
                }
            } catch (JSONException e) {
                throw new RuntimeException(e);
            }
        }
        if (!create) {
            return 0;
        }
        if (name.matches(".*::.*")) {
            // not top level; ensure all parents exist
            name = _ensureParents(name);
        }
        JSONObject g;
        long id;
        try {
            g = new JSONObject(type);
            g.put("name", name);
            id = Utils.intNow(1000);
            while (mDecks.containsKey(id)) {
                id = Utils.intNow();
            }
            g.put("id", id);
            mDecks.put(id, g);
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
        save(g);
        maybeAddToActive();
        return id;
    }

    public void rem(long did) {
        rem(did, false);
    }

    /** Remove the deck. If cardsToo, delete any cards inside. */
    public void rem(long did, boolean cardsToo) {
        rem(did, cardsToo, true);
    }

    public void rem(long did, boolean cardsToo, boolean childrenToo) {
        try {
            if (did == 1) {
                // we won't allow the default deck to be deleted, but if it's a child of an existing deck then it needs to be renamed
                JSONObject deck = get(did);
                if (deck.getString("name").contains("::")) {
                    deck.put("name", "Default");
                    save(deck);
                }
                return;
            }
            // log the removal regardless of whether we have the deck or not
            mCol._logRem(new long[] { did }, Sched.REM_DECK);
            // do nothing else if doesn't exist
            if (!mDecks.containsKey(did)) {
                return;
            }
            JSONObject deck = get(did);
            if (deck.getInt("dyn") != 0) {
                // deleting a cramming deck returns cards to their previous deck rather than deleting the cards
                mCol.getSched().emptyDyn(did);
                if (childrenToo) {
                    for (long id : children(did).values()) {
                        rem(id, cardsToo);
                    }
                }
            } else {
                // delete children first
                if (childrenToo) {
                    // we don't want to delete children when syncing
                    for (long id : children(did).values()) {
                        rem(id, cardsToo);
                    }
                }
                // delete cards too?
                if (cardsToo) {
                    // don't use cids(), as we want cards in cram decks too
                    ArrayList<Long> cids = mCol.getDb().queryColumn(Long.class,
                            "SELECT id FROM cards WHERE did = " + did + " OR odid = " + did, 0);
                    mCol.remCards(Utils.arrayList2array(cids));
                }
            }
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
        // delete the deck and add a grave
        mDecks.remove(did);
        // ensure we have an active deck
        if (active().contains(did)) {
            select((long) (mDecks.keySet().iterator().next()));
        }
        save();
    }

    /** An unsorted list of all deck names. */
    public ArrayList<String> allNames() {
        return allNames(true);
    }

    public ArrayList<String> allNames(boolean dyn) {
        ArrayList<String> list = new ArrayList<String>();
        try {
            if (dyn) {
                for (JSONObject x : mDecks.values()) {
                    list.add(x.getString("name"));
                }
            } else {
                for (JSONObject x : mDecks.values()) {
                    if (x.getInt("dyn") == 0) {
                        list.add(x.getString("name"));
                    }
                }
            }
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
        return list;
    }

    /**
     * A list of all decks.
     */
    public ArrayList<JSONObject> all() {
        ArrayList<JSONObject> decks = new ArrayList<JSONObject>();
        Iterator<JSONObject> it = mDecks.values().iterator();
        while (it.hasNext()) {
            decks.add(it.next());
        }
        return decks;
    }

    public long[] allIds() {
        Iterator<Long> it = mDecks.keySet().iterator();
        long[] ids = new long[mDecks.size()];
        int i = 0;
        while (it.hasNext()) {
            ids[i] = it.next();
            i++;
        }
        return ids;
    }

    /**
     * Return the number of decks.
     */
    public int count() {
        return mDecks.size();
    }

    public JSONObject get(long did) {
        return get(did, true);
    }

    public JSONObject get(long did, boolean defaultvalue) {
        if (mDecks.containsKey(did)) {
            return mDecks.get(did);
        } else if (defaultvalue) {
            JSONObject d = mDecks.get(1);
            if (d == null) {
                d = mDecks.values().iterator().next();
            }
            return d;
        } else {
            return null;
        }
    }

    /** Get deck by NAME. */
    public JSONObject byName(String name) {
        try {
            for (JSONObject m : mDecks.values()) {
                if (m.get("name").equals(name)) {
                    return m;
                }
            }
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
        return null;
    }

    /** Add or update an existing deck. Used for syncing and merging. */
    public void update(JSONObject g) {
        try {
            mDecks.put(g.getLong("id"), g);
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
        maybeAddToActive();
        // mark registry changed, but don't bump mod time
        save();
    }

    /** Rename deck prefix to NAME if not exists. Updates children. */
    public boolean rename(JSONObject g, String newName) {
        // make sure target node doesn't already exist
        if (allNames().contains(newName) || newName.length() == 0) {
            return false;
        }
        // ensure we have parents
        newName = _ensureParents(newName);
        // rename children
        String oldName;
        try {
            oldName = g.getString("name");
            for (JSONObject grp : all()) {
                if (grp.getString("name").startsWith(oldName + "::")) {
                    String on = grp.getString("name");
                    // unlike the related code in libanki python the following replaceFirst call works with regex
                    // pattern, so we need to escape the oldName:: with \Q, \E
                    String nn = on.replaceFirst("\\Q" + oldName + "::\\E", newName + "::");
                    grp.put("name", nn);
                    save(grp);
                }
            }
            // adjust name
            g.put("name", newName);
            // ensure we have parents again, as we may have renamed parent->child
            newName = _ensureParents(newName);
            save(g);
            // renaming may have altered active did order
            maybeAddToActive();
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
        return true;
    }

    /** Ensure parents exist, and return name with case matching parents. */
    public String _ensureParents(String name) {
        String s = "";
        String[] path = name.split("::");
        if (path.length < 2) {
            return name;
        }
        for (int i = 0; i < path.length - 1; i++) {
            if (i == 0) {
                s = path[0];
            } else {
                s = s + "::" + path[i];
            }
            // fetch or create
            long did = id(s);
            // get original case
            s = name(did);
        }
        name = s + "::" + path[path.length - 1];
        return name;
    }

    /**
     * Deck configurations ******************************************************
     * *****************************************
     */

    /** A list of all deck config. */
    public ArrayList<JSONObject> allConf() {
        ArrayList<JSONObject> confs = new ArrayList<JSONObject>();
        for (JSONObject c : mDconf.values()) {
            confs.add(c);
        }
        return confs;
    }

    public JSONObject confForDid(long did) {
        JSONObject deck = get(did);
        if (deck.has("conf")) {
            try {
                JSONObject conf = getConf(deck.getLong("conf"));
                conf.put("dyn", 0);
                return conf;
            } catch (JSONException e) {
                throw new RuntimeException(e);
            }
        }
        // dynamic decks have embedded conf
        return deck;
    }

    public JSONObject getConf(long confId) {
        return mDconf.get(confId);
    }

    public void updateConf(JSONObject g) {
        try {
            mDconf.put(g.getLong("id"), g);
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
        save();
    }

    /**
     * Create a new configuration and return id. Uses defaultConf as template.
     * 
     * @param name Name of the new configuration
     * @return The id of the new configuration
     */
    public long confId(String name) {
        return confId(name, defaultConf);
    }

    /**
     * Create a new configuration and return id.
     * 
     * @param name Name of the new configuration
     * @param cloneFrom Optional parameter to copy configuration from
     * @return The id of the new configuration
     */
    public long confId(String name, String cloneFrom) {
        JSONObject c;
        long id;
        try {
            c = new JSONObject(cloneFrom);
            while (true) {
                id = Utils.intNow(1000);
                if (!mDconf.containsKey(Long.valueOf(id))) {
                    break;
                }
            }
            c.put("id", id);
            c.put("name", name);
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
        mDconf.put(Long.valueOf(id), c);
        save(c);
        return id;
    }

    /**
     * Remove a configuration and update all decks using it.
     */
    public void remConf(long id) {
        mCol.modSchema();
        mDconf.remove(Long.valueOf(id));
        for (JSONObject g : all()) {
            // ignore cram decks
            if (!g.has("conf")) {
                continue;
            }
            try {
                if (g.getString("conf").equals(Long.toString(id))) {
                    g.put("conf", 1);
                    save(g);
                }
            } catch (JSONException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public void setConf(JSONObject deck, long id) {
        try {
            deck.put("conf", id);
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
        save(deck);
    }

    public ArrayList<Long> didsForConf(JSONObject conf) {
        ArrayList<Long> dids = new ArrayList<Long>();
        Iterator<JSONObject> it = mDecks.values().iterator();
        try {
            while (it.hasNext()) {
                JSONObject deck = it.next();
                if (deck.has("conf") && deck.getLong("conf") == conf.getLong("id")) {
                    dids.add(deck.getLong("id"));
                }
            }
            return dids;
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
    }

    public void restoreToDefault(JSONObject conf) {
        try {
            int oldOrder = conf.getJSONObject("new").getInt("order");
            JSONObject newConf = new JSONObject(defaultConf);
            newConf.put("id", conf.getLong("id"));
            newConf.put("name", conf.getString("name"));
            mDconf.put(conf.getLong("id"), newConf);
            save(newConf);
            // if it was previously randomized, resort
            if (oldOrder == 0) {
                mCol.getSched().resortConf(newConf);
            }
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Deck utils *************************************************************** ********************************
     */

    public String name(long did) {
        return name(did, false);
    }

    public String name(long did, boolean def) {
        try {
            JSONObject deck = get(did, def);
            if (deck != null) {
                return deck.getString("name");
            }
            return "[no deck]";
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
    }

    public String nameOrNone(long did) {
        JSONObject deck = get(did, false);
        if (deck != null) {
            try {
                return deck.getString("name");
            } catch (JSONException e) {
                throw new RuntimeException(e);
            }
        }
        return null;
    }

    public void setDeck(long[] cids, long did) {
        mCol.getDb().execute("UPDATE cards SET did = ?, usn = ?, mod = ? WHERE id IN " + Utils.ids2str(cids),
                new Object[] { did, mCol.usn(), Utils.intNow() });
    }

    private void maybeAddToActive() {
        // reselect current deck, or default if current has disappeared
        JSONObject c = current();
        try {
            select(c.getLong("id"));
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
    }

    public long[] cids(long did) {
        return cids(did, false);
    }

    public long[] cids(long did, boolean children) {
        String sql;
        if (children) {
            ArrayList<Long> dids = new ArrayList<Long>();
            dids.add(did);
            for (long id : children(did).values()) {
                dids.add(id);
            }
            sql = "SELECT id FROM cards WHERE did IN " + Utils.ids2str(Utils.arrayList2array(dids));
        } else {
            sql = "SELECT id FROM cards WHERE did = " + did;
        }
        ArrayList<Long> cids = mCol.getDb().queryColumn(Long.class, sql, 0);
        long[] result = new long[cids.size()];
        for (int i = 0; i < cids.size(); i++) {
            result[i] = cids.get(i);
        }
        return result;
    }

    public void recoverOrphans() {
        boolean mod = mCol.getDb().getMod();
        mCol.getDb().execute("UPDATE cards SET did = 1 WHERE did NOT IN " + Utils.ids2str(allIds()));
        mCol.getDb().setMod(mod);
    }

    /**
     * Deck selection *********************************************************** ************************************
     */

    /* The currrently active dids. MAke sure to copy before modifying */
    public LinkedList<Long> active() {
        try {
            String actv = mCol.getConf().getString("activeDecks");
            JSONArray ja = new JSONArray(actv);
            LinkedList<Long> result = new LinkedList<Long>();
            for (int i = 0; i < ja.length(); i++) {
                result.add(ja.getLong(i));
            }
            return result;
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
    }

    /* The currently selected did. */
    public long selected() {
        try {
            return mCol.getConf().getLong("curDeck");
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
    }

    public JSONObject current() {
        return get(selected());
    }

    /* Select a new branch. */
    public void select(long did) {
        try {
            String name = mDecks.get(did).getString("name");
            // current deck
            mCol.getConf().put("curDeck", Long.toString(did));
            // and active decks (current + all children)
            TreeMap<String, Long> actv = children(did);
            actv.put(name, did);
            JSONArray ja = new JSONArray();
            for (Long n : actv.values()) {
                ja.put(n);
            }
            mCol.getConf().put("activeDecks", ja);
            mChanged = true;
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
    }

    /* all children of did as (name, id) */
    public TreeMap<String, Long> children(long did) {
        String name;
        try {
            name = get(did).getString("name");
            TreeMap<String, Long> list = new TreeMap<String, Long>();
            for (JSONObject g : all()) {
                if (g.getString("name").startsWith(name + "::")) {
                    list.put(g.getString("name"), g.getLong("id"));
                }
            }
            return list;
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
    }

    /* all parents of did */
    public ArrayList<JSONObject> parents(long did) {
        // get parent and grandparent names
        ArrayList<JSONObject> list = new ArrayList<JSONObject>();
        try {
            String[] path = get(did).getString("name").split("::");
            String deckpath = null;
            for (int i = 0; i < path.length - 1; i++) {
                if (i == 0) {
                    deckpath = path[0];
                } else {
                    deckpath = deckpath + "::" + path[i];
                }
                list.add(get(id(deckpath, false)));
            }
            return list;
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Sync handling ************************************************************ ***********************************
     */

    public void beforeUpload() {
        try {
            for (JSONObject d : all()) {
                d.put("usn", 0);
            }
            for (JSONObject c : allConf()) {
                c.put("usn", 0);
            }
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
        save();
    }

    /**
     * Deck utils
     ***************************************************************/

    /* Return a new dynamic deck and set it as the current deck. */
    public long newDyn(String name) {
        long did = id(name, true, defaultDynamicDeck);
        select(did);
        return did;
    }

    public boolean isDyn(long did) {
        try {
            return get(did).getInt("dyn") != 0;
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
    }

    public String getActualDescription() {
        try {
            return current().getString("desc");
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
    }

    // /**
    // * Yes counts
    // * @return todayAnswers, todayNoAnswers, matureAnswers, matureNoAnswers
    // */
    // public int[] yesCounts() {
    // int dayStart = (getSched().mDayCutoff - 86400) * 10000;
    // int todayAnswers = (int)
    // getDB().queryScalar("SELECT count() FROM revlog WHERE time >= " +
    // dayStart);
    // int todayNoAnswers = (int)
    // getDB().queryScalar("SELECT count() FROM revlog WHERE time >= " +
    // dayStart + " AND ease = 1");
    // int matureAnswers = (int)
    // getDB().queryScalar("SELECT count() FROM revlog WHERE lastivl >= 21");
    // int matureNoAnswers = (int)
    // getDB().queryScalar("SELECT count() FROM revlog WHERE lastivl >= 21 AND ease = 1");
    // return new int[] { todayAnswers, todayNoAnswers, matureAnswers,
    // matureNoAnswers };
    // }
    //
    //
    // /**
    // * Yes rates for today's and mature cards
    // * @return todayRate, matureRate
    // */
    // public double[] yesRates() {
    // int[] counts = yesCounts();
    // return new double[] { 1 - (double)counts[1]/counts[0], 1 -
    // (double)counts[3]/counts[2] };
    // }
    //
    //
    // // Media
    // // *****
    //
    // /**
    // * Return the media directory if exists, none if couldn't be created.
    // *
    // * @param create If true it will attempt to create the folder if it
    // doesn't exist
    // * @param rename This is used to simulate the python with create=None that
    // is only used when renaming the mediaDir
    // * @return The path of the media directory
    // */
    // public String mediaDir() {
    // return mediaDir(false, false);
    // }
    // public String mediaDir(boolean create) {
    // return mediaDir(create, false);
    // }
    // public String mediaDir(boolean create, boolean rename) {
    // String dir = null;
    // File mediaDir = null;
    // if (mDeckPath != null && !mDeckPath.equals("")) {
    // // Log.i(AnkiDroidApp.TAG, "mediaDir - mediaPrefix = " + mMediaPrefix);
    // if (mMediaPrefix != null) {
    // dir = mMediaPrefix + "/" + mDeckName + ".media";
    // } else {
    // dir = mDeckPath.replaceAll("\\.anki$", ".media");
    // }
    // if (rename) {
    // // Don't create, but return dir
    // return dir;
    // }
    // mediaDir = new File(dir);
    // if (!mediaDir.exists() && create) {
    // try {
    // if (!mediaDir.mkdir()) {
    // Log.e(AnkiDroidApp.TAG, "Couldn't create media directory " + dir);
    // return null;
    // }
    // } catch (SecurityException e) {
    // Log.e(AnkiDroidApp.TAG,
    // "Security restriction: Couldn't create media directory " + dir);
    // return null;
    // }
    // }
    // }
    //
    // if (dir == null) {
    // return null;
    // } else {
    // if (!mediaDir.exists() || !mediaDir.isDirectory()) {
    // return null;
    // }
    // }
    // // Log.i(AnkiDroidApp.TAG, "mediaDir - mediaDir = " + dir);
    // return dir;
    // }
    //
    // public String getMediaPrefix() {
    // return mMediaPrefix;
    // }
    // public void setMediaPrefix(String mediaPrefix) {
    // mMediaPrefix = mediaPrefix;
    // }
    // //
    // //
    // //
    // // private boolean hasLaTeX() {
    // // Cursor cursor = null;
    // // try {
    // // cursor = getDB().getDatabase().rawQuery(
    // // "SELECT Id FROM fields WHERE " +
    // // "(value like '%[latex]%[/latex]%') OR " +
    // // "(value like '%[$]%[/$]%') OR " +
    // // "(value like '%[$$]%[/$$]%') LIMIT 1 ", null);
    // // if (cursor.moveToFirst()) {
    // // return true;
    // // }
    // // } finally {
    // // if (cursor != null) {
    // // cursor.close();
    // // }
    // // }
    // // return false;
    // // }

    public HashMap<Long, JSONObject> getDconf() {
        return mDconf;
    }

    public HashMap<Long, JSONObject> getDecks() {
        return mDecks;
    }
}