Search in sources :

Example 36 with Media

use of com.ichi2.libanki.Media in project Anki-Android by Ramblurr.

the class Media method _changes.

private Pair<List<String>, List<String>> _changes() {
    Map<String, Pair<String, Long>> cache = new HashMap<String, Pair<String, Long>>();
    Map<String, Boolean> used = new HashMap<String, Boolean>();
    Cursor cur = null;
    try {
        cur = mMediaDb.getDatabase().query("media", new String[] { "fname", "csum", "mod" }, null, null, null, null, null);
        while (cur.moveToNext()) {
            cache.put(cur.getString(0), new Pair<String, Long>(cur.getString(1), cur.getLong(1)));
            used.put(cur.getString(0), false);
        }
    } catch (SQLException e) {
        throw new RuntimeException(e);
    } finally {
        if (cur != null) {
            cur.close();
        }
    }
    List<String> added = new ArrayList<String>();
    List<String> removed = new ArrayList<String>();
    File mediaDir = new File(getDir());
    for (File f : mediaDir.listFiles()) {
        // ignore folders and thumbs.db
        if (f.isDirectory()) {
            continue;
        }
        String fname = f.getName();
        if (fname.compareTo("thumbs.db") == 0) {
            continue;
        }
        // and files with invalid chars
        boolean bad = false;
        for (String c : new String[] { "\0", "/", "\\", ":" }) {
            if (fname.contains(c)) {
                bad = true;
                break;
            }
        }
        if (bad) {
            continue;
        }
        // empty files are invalid; clean them up and continue
        if (f.length() == 0) {
            f.delete();
            continue;
        }
        // newly added?
        if (!cache.containsKey(fname)) {
            added.add(fname);
        } else {
            // modified since last time?
            if ((f.lastModified() / 1000) != cache.get(fname).second) {
                // and has different checksum?
                if (_checksum(f.getAbsolutePath()).compareTo(cache.get(fname).first) != 0) {
                    added.add(fname);
                }
            }
            // mark as used
            used.put(fname, true);
        }
    }
    // look for any entries in the cache that no longer exist on disk
    for (String fname : used.keySet()) {
        if (!used.get(fname)) {
            removed.add(fname);
        }
    }
    return new Pair<List<String>, List<String>>(added, removed);
}
Also used : HashMap(java.util.HashMap) SQLException(android.database.SQLException) ArrayList(java.util.ArrayList) Cursor(android.database.Cursor) ZipFile(java.util.zip.ZipFile) File(java.io.File) Pair(com.ichi2.anki.Pair)

Example 37 with Media

use of com.ichi2.libanki.Media in project Anki-Android by Ramblurr.

the class Connection method doInBackgroundSync.

private Payload doInBackgroundSync(Payload data) {
    // for for doInBackgroundLoadDeckCounts if any
    DeckTask.waitToFinish();
    String hkey = (String) data.data[0];
    boolean media = (Boolean) data.data[1];
    String conflictResolution = (String) data.data[2];
    int mediaUsn = (Integer) data.data[3];
    boolean colCorruptFullSync = false;
    Collection col = AnkiDroidApp.getCol();
    if (!AnkiDroidApp.colIsOpen()) {
        if (conflictResolution != null && conflictResolution.equals("download")) {
            colCorruptFullSync = true;
        } else {
            data.success = false;
            data.result = new Object[] { "genericError" };
            return data;
        }
    }
    String path = AnkiDroidApp.getCollectionPath();
    BasicHttpSyncer server = new RemoteServer(this, hkey);
    Syncer client = new Syncer(col, server);
    // run sync and check state
    boolean noChanges = false;
    if (conflictResolution == null) {
        // Log.i(AnkiDroidApp.TAG, "Sync - starting sync");
        publishProgress(R.string.sync_prepare_syncing);
        Object[] ret = client.sync(this);
        data.message = client.getSyncMsg();
        mediaUsn = client.getmMediaUsn();
        if (ret == null) {
            data.success = false;
            data.result = new Object[] { "genericError" };
            return data;
        }
        String retCode = (String) ret[0];
        if (!retCode.equals("noChanges") && !retCode.equals("success")) {
            data.success = false;
            data.result = ret;
            // note mediaUSN for later
            data.data = new Object[] { mediaUsn };
            return data;
        }
        // save and note success state
        if (retCode.equals("noChanges")) {
            // publishProgress(R.string.sync_no_changes_message);
            noChanges = true;
        } else {
        // publishProgress(R.string.sync_database_success);
        }
    } else {
        try {
            server = new FullSyncer(col, hkey, this);
            if (conflictResolution.equals("upload")) {
                // Log.i(AnkiDroidApp.TAG, "Sync - fullsync - upload collection");
                publishProgress(R.string.sync_preparing_full_sync_message);
                Object[] ret = server.upload();
                if (ret == null) {
                    data.success = false;
                    data.result = new Object[] { "genericError" };
                    AnkiDroidApp.openCollection(path);
                    return data;
                }
                if (!((String) ret[0]).equals(BasicHttpSyncer.ANKIWEB_STATUS_OK)) {
                    data.success = false;
                    data.result = ret;
                    AnkiDroidApp.openCollection(path);
                    return data;
                }
            } else if (conflictResolution.equals("download")) {
                // Log.i(AnkiDroidApp.TAG, "Sync - fullsync - download collection");
                publishProgress(R.string.sync_downloading_message);
                Object[] ret = server.download();
                if (ret == null) {
                    data.success = false;
                    data.result = new Object[] { "genericError" };
                    AnkiDroidApp.openCollection(path);
                    return data;
                }
                if (!((String) ret[0]).equals("success")) {
                    data.success = false;
                    data.result = ret;
                    if (!colCorruptFullSync) {
                        AnkiDroidApp.openCollection(path);
                    }
                    return data;
                }
            }
            col = AnkiDroidApp.openCollection(path);
        } catch (OutOfMemoryError e) {
            AnkiDroidApp.saveExceptionReportFile(e, "doInBackgroundSync-fullSync");
            data.success = false;
            data.result = new Object[] { "OutOfMemoryError" };
            data.data = new Object[] { mediaUsn };
            return data;
        } catch (RuntimeException e) {
            AnkiDroidApp.saveExceptionReportFile(e, "doInBackgroundSync-fullSync");
            data.success = false;
            data.result = new Object[] { "IOException" };
            data.data = new Object[] { mediaUsn };
            return data;
        }
    }
    // clear undo to avoid non syncing orphans (because undo resets usn too
    if (!noChanges) {
        col.clearUndo();
    }
    // then move on to media sync
    boolean noMediaChanges = false;
    String mediaError = null;
    if (media) {
        server = new RemoteMediaServer(hkey, this);
        MediaSyncer mediaClient = new MediaSyncer(col, (RemoteMediaServer) server);
        String ret;
        try {
            ret = mediaClient.sync(mediaUsn, this);
            if (ret == null) {
                mediaError = AnkiDroidApp.getAppResources().getString(R.string.sync_media_error);
            } else {
                if (ret.equals("noChanges")) {
                    publishProgress(R.string.sync_media_no_changes);
                    noMediaChanges = true;
                }
                if (ret.equals("sanityFailed")) {
                    mediaError = AnkiDroidApp.getAppResources().getString(R.string.sync_media_sanity_failed);
                } else {
                    publishProgress(R.string.sync_media_success);
                }
            }
        } catch (RuntimeException e) {
            AnkiDroidApp.saveExceptionReportFile(e, "doInBackgroundSync-mediaSync");
            mediaError = e.getLocalizedMessage();
        }
    }
    if (noChanges && noMediaChanges) {
        data.success = false;
        data.result = new Object[] { "noChanges" };
        return data;
    } else {
        data.success = true;
        TreeSet<Object[]> decks = col.getSched().deckDueTree();
        int[] counts = new int[] { 0, 0, 0 };
        for (Object[] deck : decks) {
            if (((String[]) deck[0]).length == 1) {
                counts[0] += (Integer) deck[2];
                counts[1] += (Integer) deck[3];
                counts[2] += (Integer) deck[4];
            }
        }
        Object[] dc = col.getSched().deckCounts();
        data.result = dc[0];
        data.data = new Object[] { conflictResolution, col, dc[1], dc[2], mediaError };
        return data;
    }
}
Also used : FullSyncer(com.ichi2.libanki.sync.FullSyncer) OutOfMemoryError(java.lang.OutOfMemoryError) BasicHttpSyncer(com.ichi2.libanki.sync.BasicHttpSyncer) FullSyncer(com.ichi2.libanki.sync.FullSyncer) MediaSyncer(com.ichi2.libanki.sync.MediaSyncer) BasicHttpSyncer(com.ichi2.libanki.sync.BasicHttpSyncer) Syncer(com.ichi2.libanki.sync.Syncer) Collection(com.ichi2.libanki.Collection) MediaSyncer(com.ichi2.libanki.sync.MediaSyncer) JSONObject(org.json.JSONObject) RemoteServer(com.ichi2.libanki.sync.RemoteServer) RemoteMediaServer(com.ichi2.libanki.sync.RemoteMediaServer)

Example 38 with Media

use of com.ichi2.libanki.Media in project Anki-Android by Ramblurr.

the class DeckPicker method sync.

/**
 * The mother of all syncing attempts. This might be called from sync() as first attempt to sync a collection OR
 * from the mSyncConflictResolutionListener if the first attempt determines that a full-sync is required. In the
 * second case, we have passed the mediaUsn that was obtained during the first attempt.
 *
 * @param syncConflictResolution Either "upload" or "download", depending on the user's choice.
 * @param syncMediaUsn The media Usn, as determined during the prior sync() attempt that determined that full
 *            syncing was required.
 */
private void sync(String syncConflictResolution, int syncMediaUsn) {
    SharedPreferences preferences = AnkiDroidApp.getSharedPrefs(getBaseContext());
    String hkey = preferences.getString("hkey", "");
    if (hkey.length() == 0) {
        showDialog(DIALOG_USER_NOT_LOGGED_IN_SYNC);
    } else {
        Connection.sync(mSyncListener, new Connection.Payload(new Object[] { hkey, preferences.getBoolean("syncFetchesMedia", true), syncConflictResolution, syncMediaUsn }));
    }
}
Also used : Payload(com.ichi2.async.Connection.Payload) SharedPreferences(android.content.SharedPreferences) Connection(com.ichi2.async.Connection) JSONObject(org.json.JSONObject)

Example 39 with Media

use of com.ichi2.libanki.Media in project AnkiChinaAndroid by ankichinateam.

the class SettingFragment method onClick.

@Override
public void onClick(View v) {
    int id = v.getId();
    if (id == R.id.user_protocol) {
        WebViewActivity.openUrlInApp(getAnkiActivity(), URL_USER_PROTOCOL, "");
    } else if (id == R.id.user_private) {
        WebViewActivity.openUrlInApp(getAnkiActivity(), URL_PRIVATE, "");
    } else if (id == R.id.rl_market_like) {
        goAppShop(getAnkiActivity(), BuildConfig.APPLICATION_ID, "");
    } else if (id == R.id.vip_power) {
        Timber.i("click vip button");
        getAnkiActivity().openVipUrl(mVipUrl);
    } else if (id == R.id.rl_anki_course || id == R.id.rl_team || id == R.id.rl_version || id == R.id.rl_feedback) {
        if (id == R.id.rl_anki_course) {
            WebViewActivity.openUrlInApp(getAnkiActivity(), URL_ANKI_COURSE, "");
        } else if (id == R.id.rl_version) {
            WebViewActivity.openUrlInApp(getAnkiActivity(), URL_VERSION, "");
        } else if (id == R.id.rl_team) {
            WebViewActivity.openUrlInApp(getAnkiActivity(), URL_VOLUNTEER, "");
        } else {
            WebViewActivity.openUrlInApp(getAnkiActivity(), URL_FEEDBACK, "");
        }
    } else if (id == R.id.ll_switch_server) {
        final Dialog dialog = new Dialog(getAnkiActivity(), R.style.DialogTheme);
        dialog.setContentView(R.layout.dialog_switch_server);
        Window dialogWindow = dialog.getWindow();
        dialogWindow.setGravity(Gravity.TOP | Gravity.LEFT);
        WindowManager.LayoutParams lp = dialogWindow.getAttributes();
        int notificationBar = Resources.getSystem().getDimensionPixelSize(Resources.getSystem().getIdentifier("status_bar_height", "dimen", "android"));
        int[] location = new int[2];
        // 获取在当前窗体内的绝对坐标
        mLl_switch_server.getLocationInWindow(location);
        // 获取在整个屏幕内的绝对坐标
        mLl_switch_server.getLocationOnScreen(location);
        lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
        lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
        lp.x = location[0];
        lp.y = location[1] + mLl_switch_server.getHeight() - notificationBar + 20;
        dialogWindow.setAttributes(lp);
        updateSwitchServerDialog(dialog);
        dialog.findViewById(R.id.rl_over_sea_server).setOnClickListener(view -> {
            String account = AnkiDroidApp.getSharedPrefs(getAnkiActivity()).getString(Consts.KEY_SAVED_ANKI_WEB_ACCOUNT, "");
            if (account.isEmpty()) {
                // 未登陆,跳转到登陆
                Intent myAccount = new Intent(getAnkiActivity(), MyAccount2.class);
                myAccount.putExtra("notLoggedIn", true);
                getAnkiActivity().startActivityWithAnimation(myAccount, ActivityTransitionAnimation.FADE);
            } else if (Consts.LOGIN_SERVER == Consts.LOGIN_SERVER_ANKICHINA) {
                // 已登陆,切换为当前状态
                SharedPreferences preferences = AnkiDroidApp.getSharedPrefs(getAnkiActivity());
                SharedPreferences.Editor editor = preferences.edit();
                editor.putString("username", preferences.getString(Consts.KEY_SAVED_ANKI_WEB_ACCOUNT, ""));
                editor.putString("hkey", preferences.getString(Consts.KEY_SAVED_ANKI_WEB_HKEY, ""));
                editor.putString("token", "");
                Consts.LOGIN_SERVER = Consts.LOGIN_SERVER_ANKIWEB;
                editor.putInt(Consts.KEY_ANKI_ACCOUNT_SERVER, Consts.LOGIN_SERVER);
                editor.apply();
                HostNumFactory.getInstance(getAnkiActivity()).reset();
                // force media resync on deauth
                getAnkiActivity().getCol().getMedia().forceResync();
                MobclickAgent.onProfileSignOff();
                updateSwitchServerLayout();
            }
            checkRestServerSpace();
            dialog.dismiss();
        });
        dialog.findViewById(R.id.rl_china_server).setOnClickListener(view -> {
            String account = AnkiDroidApp.getSharedPrefs(getAnkiActivity()).getString(Consts.KEY_SAVED_ANKI_CHINA_PHONE, "");
            if (account.isEmpty()) {
                // 未登陆,跳转到登陆
                Intent myAccount = new Intent(getAnkiActivity(), MyAccount.class);
                myAccount.putExtra("notLoggedIn", true);
                getAnkiActivity().startActivityWithAnimation(myAccount, ActivityTransitionAnimation.FADE);
            } else if (Consts.LOGIN_SERVER == Consts.LOGIN_SERVER_ANKIWEB) {
                // 已登陆,切换为当前状态
                SharedPreferences preferences = AnkiDroidApp.getSharedPrefs(getAnkiActivity());
                SharedPreferences.Editor editor = preferences.edit();
                // 切换为ankichina的登陆状态
                editor.putString("username", preferences.getString(Consts.KEY_SAVED_ANKI_CHINA_PHONE, ""));
                editor.putString("hkey", preferences.getString(Consts.KEY_SAVED_ANKI_CHINA_HKEY, ""));
                editor.putString("token", preferences.getString(Consts.KEY_SAVED_ANKI_CHINA_TOKEN, ""));
                Consts.LOGIN_SERVER = Consts.LOGIN_SERVER_ANKICHINA;
                editor.putInt(Consts.KEY_ANKI_ACCOUNT_SERVER, Consts.LOGIN_SERVER);
                editor.apply();
                HostNumFactory.getInstance(getAnkiActivity()).reset();
                getAnkiActivity().getCol().getMedia().forceResync();
                MobclickAgent.onProfileSignOff();
                updateSwitchServerLayout();
            }
            checkRestServerSpace();
            dialog.dismiss();
        });
        dialog.show();
    }
}
Also used : Window(android.view.Window) StringFormat(org.acra.data.StringFormat) Text(org.w3c.dom.Text) LinearLayout(android.widget.LinearLayout) Bundle(android.os.Bundle) URL(java.net.URL) NonNull(androidx.annotation.NonNull) WindowManager(android.view.WindowManager) ImageView(android.widget.ImageView) ForeGroundColorSpan(com.ichi2.anki.DeckPicker.ForeGroundColorSpan) Drawable(android.graphics.drawable.Drawable) JSONException(org.json.JSONException) JSONObject(org.json.JSONObject) Handler(android.os.Handler) View(android.view.View) ForegroundColorSpan(android.text.style.ForegroundColorSpan) CHANGE_ACCOUNT(com.ichi2.anki.DeckPicker.CHANGE_ACCOUNT) URL_ANKI_COURSE(com.ichi2.libanki.Consts.URL_ANKI_COURSE) ViewGroup(android.view.ViewGroup) Timber(timber.log.Timber) List(java.util.List) TextView(android.widget.TextView) Nullable(androidx.annotation.Nullable) HostNumFactory(com.ichi2.anki.web.HostNumFactory) Consts(com.ichi2.libanki.Consts) OKHttpUtil(com.ichi2.utils.OKHttpUtil) MobclickAgent(com.umeng.analytics.MobclickAgent) RelativeLayout(android.widget.RelativeLayout) Window(android.view.Window) DeckPicker.goAppShop(com.ichi2.anki.DeckPicker.goAppShop) URL_FEEDBACK(com.ichi2.libanki.Consts.URL_FEEDBACK) Spanned(android.text.Spanned) Dialog(android.app.Dialog) Intent(android.content.Intent) Collection(com.ichi2.libanki.Collection) URL_PRIVATE(com.ichi2.libanki.Consts.URL_PRIVATE) TypedArray(android.content.res.TypedArray) ArrayList(java.util.ArrayList) SpannableStringBuilder(android.text.SpannableStringBuilder) SyncErrorDialog(com.ichi2.anki.dialogs.SyncErrorDialog) Toast(android.widget.Toast) Connection(com.ichi2.async.Connection) Response(okhttp3.Response) Call(okhttp3.Call) Callback(okhttp3.Callback) ActionBar(android.app.ActionBar) URL_VERSION(com.ichi2.libanki.Consts.URL_VERSION) SettingItem(com.ichi2.ui.SettingItem) SwitchCompat(androidx.appcompat.widget.SwitchCompat) SpannableString(android.text.SpannableString) LayoutInflater(android.view.LayoutInflater) URL_VOLUNTEER(com.ichi2.libanki.Consts.URL_VOLUNTEER) URL_USER_PROTOCOL(com.ichi2.libanki.Consts.URL_USER_PROTOCOL) TextUtils(android.text.TextUtils) BackgroundColorSpan(android.text.style.BackgroundColorSpan) IOException(java.io.IOException) Themes(com.ichi2.themes.Themes) Color(android.graphics.Color) Gravity(android.view.Gravity) TaskStackBuilder(androidx.core.app.TaskStackBuilder) SharedPreferences(android.content.SharedPreferences) ActivityTransitionAnimation(com.ichi2.anim.ActivityTransitionAnimation) JSONArray(org.json.JSONArray) Resources(android.content.res.Resources) SharedPreferences(android.content.SharedPreferences) Dialog(android.app.Dialog) SyncErrorDialog(com.ichi2.anki.dialogs.SyncErrorDialog) Intent(android.content.Intent) SpannableString(android.text.SpannableString)

Example 40 with Media

use of com.ichi2.libanki.Media in project AnkiChinaAndroid by ankichinateam.

the class NoteEditor method populateEditFields.

private void populateEditFields(FieldChangeType type, boolean editModelMode) {
    List<FieldEditLine> editLines = mFieldState.loadFieldEditLines(type);
    mFieldsLayoutContainer.removeAllViews();
    mCustomViewIds.clear();
    mEditFields = new LinkedList<>();
    // Use custom font if selected from preferences
    Typeface mCustomTypeface = null;
    SharedPreferences preferences = AnkiDroidApp.getSharedPrefs(getBaseContext());
    String customFont = preferences.getString("browserEditorFont", "");
    if (!"".equals(customFont)) {
        mCustomTypeface = AnkiFont.getTypeface(this, customFont);
    }
    ClipboardManager clipboard = ContextCompat.getSystemService(this, ClipboardManager.class);
    FieldEditLine previous = null;
    mCustomViewIds.ensureCapacity(editLines.size());
    for (int i = 0; i < editLines.size(); i++) {
        FieldEditLine edit_line_view = editLines.get(i);
        mCustomViewIds.add(edit_line_view.getId());
        FieldEditText newTextbox = edit_line_view.getEditText();
        newTextbox.setImagePasteListener(this::onImagePaste);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
            if (i == 0) {
                findViewById(R.id.note_deck_spinner).setNextFocusForwardId(newTextbox.getId());
            }
            if (previous != null) {
                previous.getLastViewInTabOrder().setNextFocusForwardId(newTextbox.getId());
            }
        }
        previous = edit_line_view;
        edit_line_view.setEnableAnimation(animationEnabled());
        // TODO: Remove the >= M check - one callback works on API 11.
        if (CompatHelper.getSdkVersion() >= Build.VERSION_CODES.M) {
            // Use custom implementation of ActionMode.Callback customize selection and insert menus
            Field f = new Field(getFieldByIndex(i), getCol());
            ActionModeCallback actionModeCallback = new ActionModeCallback(newTextbox, f);
            edit_line_view.setActionModeCallbacks(actionModeCallback);
        }
        edit_line_view.setTypeface(mCustomTypeface);
        edit_line_view.setHintLocale(getHintLocaleForField(edit_line_view.getName()));
        initFieldEditText(newTextbox, i, !editModelMode);
        mEditFields.add(newTextbox);
        SharedPreferences prefs = AnkiDroidApp.getSharedPrefs(this);
        if (prefs.getInt("note_editor_font_size", -1) > 0) {
            newTextbox.setTextSize(prefs.getInt("note_editor_font_size", -1));
        }
        newTextbox.setCapitalize(prefs.getBoolean("note_editor_capitalize", true));
        ImageButton mediaButton = edit_line_view.getMediaButton();
        // Load icons from attributes
        int[] icons = Themes.getResFromAttr(this, new int[] { R.attr.attachFileImage, R.attr.upDownImage });
        // Make the icon change between media icon and switch field icon depending on whether editing note type
        if (editModelMode && allowFieldRemapping()) {
            // Allow remapping if originally more than two fields
            mediaButton.setBackgroundResource(icons[1]);
            setRemapButtonListener(mediaButton, i);
        } else if (editModelMode && !allowFieldRemapping()) {
            mediaButton.setBackgroundResource(0);
        } else {
            // Use media editor button if not changing note type
            mediaButton.setBackgroundResource(icons[0]);
            setMMButtonListener(mediaButton, i);
        }
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O && previous != null) {
            previous.getLastViewInTabOrder().setNextFocusForwardId(R.id.CardEditorTagButton);
        }
        mediaButton.setContentDescription(getString(R.string.multimedia_editor_attach_mm_content, edit_line_view.getName()));
        mFieldsLayoutContainer.addView(edit_line_view);
    }
}
Also used : ClipboardManager(android.content.ClipboardManager) Typeface(android.graphics.Typeface) SharedPreferences(android.content.SharedPreferences) SuppressLint(android.annotation.SuppressLint) AudioRecordingField(com.ichi2.anki.multimediacard.fields.AudioRecordingField) IField(com.ichi2.anki.multimediacard.fields.IField) ImageField(com.ichi2.anki.multimediacard.fields.ImageField) TextField(com.ichi2.anki.multimediacard.fields.TextField) AudioClipField(com.ichi2.anki.multimediacard.fields.AudioClipField) ImageButton(android.widget.ImageButton)

Aggregations

File (java.io.File)43 IOException (java.io.IOException)26 Collection (com.ichi2.libanki.Collection)25 JSONObject (com.ichi2.utils.JSONObject)19 ArrayList (java.util.ArrayList)17 Test (org.junit.Test)17 ZipFile (java.util.zip.ZipFile)14 JSONException (com.ichi2.utils.JSONException)10 FileOutputStream (java.io.FileOutputStream)10 List (java.util.List)10 JSONArray (com.ichi2.utils.JSONArray)9 FileInputStream (java.io.FileInputStream)9 FileNotFoundException (java.io.FileNotFoundException)9 SharedPreferences (android.content.SharedPreferences)8 JSONObject (org.json.JSONObject)8 Resources (android.content.res.Resources)7 Uri (android.net.Uri)7 RobolectricTest (com.ichi2.anki.RobolectricTest)7 ConfirmModSchemaException (com.ichi2.anki.exception.ConfirmModSchemaException)7 AnkiPackageImporter (com.ichi2.libanki.importer.AnkiPackageImporter)7