Search in sources :

Example 56 with START

use of com.ichi2.anim.ActivityTransitionAnimation.Direction.START in project Anki-Android by ankidroid.

the class ZipFile method exportInto.

/**
 * Export source database into new destination database Note: The following python syntax isn't supported in
 * Android: for row in mSrc.db.execute("select * from cards where id in "+ids2str(cids)): therefore we use a
 * different method for copying tables
 *
 * @param path String path to destination database
 * @throws JSONException
 * @throws IOException
 */
public void exportInto(@NonNull String path, Context context) throws JSONException, IOException, ImportExportException {
    // create a new collection at the target
    new File(path).delete();
    Collection dst = Storage.Collection(context, path);
    mSrc = mCol;
    // find cards
    Long[] cids = cardIds();
    // attach dst to src so we can copy data between them. This isn't done in original libanki as Python more
    // flexible
    dst.close();
    Timber.d("Attach DB");
    mSrc.getDb().getDatabase().execSQL("ATTACH '" + path + "' AS DST_DB");
    // copy cards, noting used nids (as unique set)
    Timber.d("Copy cards");
    mSrc.getDb().getDatabase().execSQL("INSERT INTO DST_DB.cards select * from cards where id in " + Utils.ids2str(cids));
    List<Long> uniqueNids = mSrc.getDb().queryLongList("select distinct nid from cards where id in " + Utils.ids2str(cids));
    // notes
    Timber.d("Copy notes");
    String strnids = Utils.ids2str(uniqueNids);
    mSrc.getDb().getDatabase().execSQL("INSERT INTO DST_DB.notes select * from notes where id in " + strnids);
    // remove system tags if not exporting scheduling info
    if (!mIncludeSched) {
        Timber.d("Stripping system tags from list");
        ArrayList<String> srcTags = mSrc.getDb().queryStringList("select tags from notes where id in " + strnids);
        ArrayList<Object[]> args = new ArrayList<>(srcTags.size());
        Object[] arg = new Object[2];
        for (int row = 0; row < srcTags.size(); row++) {
            arg[0] = removeSystemTags(srcTags.get(row));
            arg[1] = uniqueNids.get(row);
            args.add(row, arg);
        }
        mSrc.getDb().executeMany("UPDATE DST_DB.notes set tags=? where id=?", args);
    }
    // models used by the notes
    Timber.d("Finding models used by notes");
    ArrayList<Long> mids = mSrc.getDb().queryLongList("select distinct mid from DST_DB.notes where id in " + strnids);
    // card history and revlog
    if (mIncludeSched) {
        Timber.d("Copy history and revlog");
        mSrc.getDb().getDatabase().execSQL("insert into DST_DB.revlog select * from revlog where cid in " + Utils.ids2str(cids));
        // reopen collection to destination database (different from original python code)
        mSrc.getDb().getDatabase().execSQL("DETACH DST_DB");
        dst.reopen();
    } else {
        Timber.d("Detaching destination db and reopening");
        // first reopen collection to destination database (different from original python code)
        mSrc.getDb().getDatabase().execSQL("DETACH DST_DB");
        dst.reopen();
        // then need to reset card state
        Timber.d("Resetting cards");
        dst.getSched().resetCards(cids);
    }
    // models - start with zero
    Timber.d("Copy models");
    for (Model m : mSrc.getModels().all()) {
        if (mids.contains(m.getLong("id"))) {
            dst.getModels().update(m);
        }
    }
    // decks
    Timber.d("Copy decks");
    java.util.Collection<Long> dids = null;
    if (mDid != null) {
        dids = new HashSet<>(mSrc.getDecks().children(mDid).values());
        dids.add(mDid);
    }
    JSONObject dconfs = new JSONObject();
    for (Deck d : mSrc.getDecks().all()) {
        if ("1".equals(d.getString("id"))) {
            continue;
        }
        if (dids != null && !dids.contains(d.getLong("id"))) {
            continue;
        }
        if (d.isStd() && d.getLong("conf") != 1L) {
            if (mIncludeSched) {
                dconfs.put(Long.toString(d.getLong("conf")), true);
            }
        }
        Deck destinationDeck = d.deepClone();
        if (!mIncludeSched) {
            // scheduling not included, so reset deck settings to default
            destinationDeck.put("conf", 1);
        }
        dst.getDecks().update(destinationDeck);
    }
    // copy used deck confs
    Timber.d("Copy deck options");
    for (DeckConfig dc : mSrc.getDecks().allConf()) {
        if (dconfs.has(dc.getString("id"))) {
            dst.getDecks().updateConf(dc);
        }
    }
    // find used media
    Timber.d("Find used media");
    JSONObject media = new JSONObject();
    mMediaDir = mSrc.getMedia().dir();
    if (mIncludeMedia) {
        ArrayList<Long> mid = mSrc.getDb().queryLongList("select mid from notes where id in " + strnids);
        ArrayList<String> flds = mSrc.getDb().queryStringList("select flds from notes where id in " + strnids);
        for (int idx = 0; idx < mid.size(); idx++) {
            for (String file : mSrc.getMedia().filesInStr(mid.get(idx), flds.get(idx))) {
                // skip files in subdirs
                if (file.contains(File.separator)) {
                    continue;
                }
                media.put(file, true);
            }
        }
        if (mMediaDir != null) {
            for (File f : new File(mMediaDir).listFiles()) {
                if (f.isDirectory()) {
                    continue;
                }
                String fname = f.getName();
                if (fname.startsWith("_")) {
                    // Loop through every model that will be exported, and check if it contains a reference to f
                    for (JSONObject model : mSrc.getModels().all()) {
                        if (_modelHasMedia(model, fname)) {
                            media.put(fname, true);
                            break;
                        }
                    }
                }
            }
        }
    }
    JSONArray keys = media.names();
    if (keys != null) {
        mMediaFiles.ensureCapacity(keys.length());
        addAll(mMediaFiles, keys.stringIterable());
    }
    Timber.d("Cleanup");
    dst.setCrt(mSrc.getCrt());
    // todo: tags?
    mCount = dst.cardCount();
    dst.setMod();
    postExport();
    dst.close();
}
Also used : ArrayList(java.util.ArrayList) JSONArray(com.ichi2.utils.JSONArray) SuppressLint(android.annotation.SuppressLint) JSONObject(com.ichi2.utils.JSONObject) JSONObject(com.ichi2.utils.JSONObject) File(java.io.File)

Example 57 with START

use of com.ichi2.anim.ActivityTransitionAnimation.Direction.START in project Anki-Android by ankidroid.

the class DeckPicker method showStartupScreensAndDialogs.

private void showStartupScreensAndDialogs(SharedPreferences preferences, int skip) {
    // For Android 8/8.1 we want to use software rendering by default or the Reviewer UI is broken #7369
    if (CompatHelper.getSdkVersion() == Build.VERSION_CODES.O || CompatHelper.getSdkVersion() == Build.VERSION_CODES.O_MR1) {
        if (!preferences.contains("softwareRender")) {
            Timber.i("Android 8/8.1 detected with no render preference. Turning on software render.");
            preferences.edit().putBoolean("softwareRender", true).apply();
        } else {
            Timber.i("Android 8/8.1 detected, software render preference already exists.");
        }
    }
    if (!BackupManager.enoughDiscSpace(CollectionHelper.getCurrentAnkiDroidDirectory(this))) {
        Timber.i("Not enough space to do backup");
        showDialogFragment(DeckPickerNoSpaceLeftDialog.newInstance());
    } else if (preferences.getBoolean("noSpaceLeft", false)) {
        Timber.i("No space left");
        showDialogFragment(DeckPickerBackupNoSpaceLeftDialog.newInstance());
        preferences.edit().remove("noSpaceLeft").apply();
    } else if (InitialActivity.performSetupFromFreshInstallOrClearedPreferences(preferences)) {
        onFinishedStartup();
    } else if (skip < 2 && !InitialActivity.isLatestVersion(preferences)) {
        Timber.i("AnkiDroid is being updated and a collection already exists.");
        // The user might appreciate us now, see if they will help us get better?
        if (!preferences.contains(UsageAnalytics.ANALYTICS_OPTIN_KEY)) {
            displayAnalyticsOptInDialog();
        }
        // For upgrades, we check if we are upgrading
        // to a version that contains additions to the database integrity check routine that we would
        // like to run on all collections. A missing version number is assumed to be a fresh
        // installation of AnkiDroid and we don't run the check.
        long current = VersionUtils.getPkgVersionCode();
        Timber.i("Current AnkiDroid version: %s", current);
        long previous;
        if (preferences.contains(UPGRADE_VERSION_KEY)) {
            // Upgrading currently installed app
            previous = getPreviousVersion(preferences, current);
        } else {
            // Fresh install
            previous = current;
        }
        preferences.edit().putLong(UPGRADE_VERSION_KEY, current).apply();
        // It is rebuilt on the next sync or media check
        if (previous < 20300200) {
            Timber.i("Deleting media database");
            File mediaDb = new File(CollectionHelper.getCurrentAnkiDroidDirectory(this), "collection.media.ad.db2");
            if (mediaDb.exists()) {
                mediaDb.delete();
            }
        }
        // Recommend the user to do a full-sync if they're upgrading from before 2.3.1beta8
        if (previous < 20301208) {
            Timber.i("Recommend the user to do a full-sync");
            mRecommendFullSync = true;
        }
        // Fix "font-family" definition in templates created by AnkiDroid before 2.6alhpa23
        if (previous < 20600123) {
            Timber.i("Fixing font-family definition in templates");
            try {
                ModelManager models = getCol().getModels();
                for (Model m : models.all()) {
                    String css = m.getString("css");
                    if (css.contains("font-familiy")) {
                        m.put("css", css.replace("font-familiy", "font-family"));
                        models.save(m);
                    }
                }
                models.flush();
            } catch (JSONException e) {
                Timber.e(e, "Failed to upgrade css definitions.");
            }
        }
        // Check if preference upgrade or database check required, otherwise go to new feature screen
        int upgradeDbVersion = AnkiDroidApp.CHECK_DB_AT_VERSION;
        // Specifying a checkpoint in the future is not supported, please don't do it!
        if (current < upgradeDbVersion) {
            Timber.e("Invalid value for CHECK_DB_AT_VERSION");
            UIUtils.showSimpleSnackbar(this, "Invalid value for CHECK_DB_AT_VERSION", false);
            onFinishedStartup();
            return;
        }
        // Skip full DB check if the basic check is OK
        // TODO: remove this variable if we really want to do the full db check on every user
        boolean skipDbCheck = false;
        // if (previous < upgradeDbVersion && getCol().basicCheck()) {
        // skipDbCheck = true;
        // }
        boolean upgradedPreferences = InitialActivity.upgradePreferences(this, previous);
        // noinspection ConstantConditions
        if (!skipDbCheck && previous < upgradeDbVersion) {
            Timber.i("showStartupScreensAndDialogs() running integrityCheck()");
            // #5852 - since we may have a warning about disk space, we don't want to force a check database
            // and show a warning before the user knows what is happening.
            new MaterialDialog.Builder(this).title(R.string.integrity_check_startup_title).content(R.string.integrity_check_startup_content).positiveText(R.string.check_db).negativeText(R.string.close).onPositive((materialDialog, dialogAction) -> integrityCheck()).onNeutral((materialDialog, dialogAction) -> restartActivity()).onNegative((materialDialog, dialogAction) -> restartActivity()).canceledOnTouchOutside(false).cancelable(false).build().show();
            return;
        }
        if (upgradedPreferences) {
            Timber.i("Updated preferences with no integrity check - restarting activity");
            // If integrityCheck() doesn't occur, but we did update preferences we should restart DeckPicker to
            // proceed
            restartActivity();
            return;
        }
        // There the "lastVersion" is set, so that this code is not reached again
        if (VersionUtils.isReleaseVersion()) {
            Timber.i("Displaying new features");
            Intent infoIntent = new Intent(this, Info.class);
            infoIntent.putExtra(Info.TYPE_EXTRA, Info.TYPE_NEW_VERSION);
            if (skip != 0) {
                startActivityForResultWithAnimation(infoIntent, SHOW_INFO_NEW_VERSION, START);
            } else {
                startActivityForResultWithoutAnimation(infoIntent, SHOW_INFO_NEW_VERSION);
            }
        } else {
            Timber.i("Dev Build - not showing 'new features'");
            // Don't show new features dialog for development builds
            InitialActivity.setUpgradedToLatestVersion(preferences);
            String ver = getResources().getString(R.string.updated_version, VersionUtils.getPkgVersionName());
            UIUtils.showSnackbar(this, ver, true, -1, null, findViewById(R.id.root_layout), null);
            showStartupScreensAndDialogs(preferences, 2);
        }
    } else {
        // This is the main call when there is nothing special required
        Timber.i("No startup screens required");
        onFinishedStartup();
    }
}
Also used : DividerItemDecoration(androidx.recyclerview.widget.DividerItemDecoration) StartupFailure(com.ichi2.anki.InitialActivity.StartupFailure) Bundle(android.os.Bundle) NonNull(androidx.annotation.NonNull) Uri(android.net.Uri) DialogHandler(com.ichi2.anki.dialogs.DialogHandler) Drawable(android.graphics.drawable.Drawable) ShortcutManagerCompat(androidx.core.content.pm.ShortcutManagerCompat) Manifest(android.Manifest) Decks(com.ichi2.libanki.Decks) Fragment(androidx.fragment.app.Fragment) JSONException(com.ichi2.utils.JSONException) ContextCompat(androidx.core.content.ContextCompat) DeckPickerBackupNoSpaceLeftDialog(com.ichi2.anki.dialogs.DeckPickerBackupNoSpaceLeftDialog) IntentFilter(android.content.IntentFilter) Triple(com.ichi2.utils.Triple) SearchView(androidx.appcompat.widget.SearchView) DeckPickerContextMenu(com.ichi2.anki.dialogs.DeckPickerContextMenu) Cancellable(com.ichi2.async.Cancellable) DeckRenameException(com.ichi2.libanki.backend.exception.DeckRenameException) StringRes(androidx.annotation.StringRes) Unit(kotlin.Unit) Nullable(androidx.annotation.Nullable) Message(android.os.Message) HostNumFactory(com.ichi2.anki.web.HostNumFactory) CompatHelper(com.ichi2.compat.CompatHelper) DeckAdapter(com.ichi2.anki.widgets.DeckAdapter) LinearLayoutManager(androidx.recyclerview.widget.LinearLayoutManager) DeckPickerNoSpaceToDowngradeDialog(com.ichi2.anki.dialogs.DeckPickerNoSpaceToDowngradeDialog) DeckPickerConfirmDeleteDeckDialog(com.ichi2.anki.dialogs.DeckPickerConfirmDeleteDeckDialog) Direction(com.ichi2.anim.ActivityTransitionAnimation.Direction) FULL_DOWNLOAD(com.ichi2.async.Connection.ConflictResolution.FULL_DOWNLOAD) SdCardReceiver(com.ichi2.anki.receiver.SdCardReceiver) Editor(android.content.SharedPreferences.Editor) CustomSyncServerUrlException(com.ichi2.libanki.sync.CustomSyncServerUrlException) FileSizeFormatter(com.ichi2.anki.dialogs.DeckPickerNoSpaceToDowngradeDialog.FileSizeFormatter) DeckPickerNoSpaceLeftDialog(com.ichi2.anki.dialogs.DeckPickerNoSpaceLeftDialog) StudyOptionsListener(com.ichi2.anki.StudyOptionsFragment.StudyOptionsListener) BadgeDrawableBuilder(com.ichi2.ui.BadgeDrawableBuilder) Menu(android.view.Menu) DeckService(com.ichi2.anki.servicelayer.DeckService) Connection(com.ichi2.async.Connection) Settings(android.provider.Settings) AnkiPackageImporter(com.ichi2.libanki.importer.AnkiPackageImporter) SwipeRefreshLayout(androidx.swiperefreshlayout.widget.SwipeRefreshLayout) CollectionIntegrityStorageCheck(com.ichi2.anki.CollectionHelper.CollectionIntegrityStorageCheck) ActivityExportingDelegate(com.ichi2.anki.export.ActivityExportingDelegate) TextUtils(android.text.TextUtils) File(java.io.File) SharedPreferences(android.content.SharedPreferences) TypedValue(android.util.TypedValue) IconCompat(androidx.core.graphics.drawable.IconCompat) ImportUtils(com.ichi2.utils.ImportUtils) EditText(android.widget.EditText) SchedulerService(com.ichi2.anki.servicelayer.SchedulerService) LinearLayout(android.widget.LinearLayout) AsyncDialogFragment(com.ichi2.anki.dialogs.AsyncDialogFragment) PackageManager(android.content.pm.PackageManager) TaskManager(com.ichi2.async.TaskManager) WindowManager(android.view.WindowManager) UsageAnalytics(com.ichi2.anki.analytics.UsageAnalytics) ModelManager(com.ichi2.libanki.ModelManager) ConfirmationDialog(com.ichi2.anki.dialogs.ConfirmationDialog) AnkiStatsTaskHandler(com.ichi2.anki.stats.AnkiStatsTaskHandler) Permissions(com.ichi2.utils.Permissions) View(android.view.View) RecyclerView(androidx.recyclerview.widget.RecyclerView) SyncStatus(com.ichi2.utils.SyncStatus) FragmentTransaction(androidx.fragment.app.FragmentTransaction) BroadcastReceiver(android.content.BroadcastReceiver) DatabaseErrorDialog(com.ichi2.anki.dialogs.DatabaseErrorDialog) CustomStudyDialogFactory(com.ichi2.anki.dialogs.customstudy.CustomStudyDialogFactory) Timber(timber.log.Timber) UndoService(com.ichi2.anki.servicelayer.UndoService) List(java.util.List) TextView(android.widget.TextView) ImportFileSelectionFragment(com.ichi2.anki.dialogs.ImportFileSelectionFragment) RelativeLayout(android.widget.RelativeLayout) Filterable(android.widget.Filterable) TaskListenerWithContext(com.ichi2.async.TaskListenerWithContext) ViewPropertyAnimator(android.view.ViewPropertyAnimator) MaterialDialog(com.afollestad.materialdialogs.MaterialDialog) Snackbar(com.google.android.material.snackbar.Snackbar) Window(android.view.Window) VersionUtils(com.ichi2.utils.VersionUtils) Context(android.content.Context) TaskListener(com.ichi2.async.TaskListener) KeyEvent(android.view.KeyEvent) GravityEnum(com.afollestad.materialdialogs.GravityEnum) Pair(android.util.Pair) DeckPickerAnalyticsOptInDialog(com.ichi2.anki.dialogs.DeckPickerAnalyticsOptInDialog) ImportDialog(com.ichi2.anki.dialogs.ImportDialog) Intent(android.content.Intent) Collection(com.ichi2.libanki.Collection) StyledProgressDialog(com.ichi2.themes.StyledProgressDialog) PixelFormat(android.graphics.PixelFormat) TypedArray(android.content.res.TypedArray) MenuItem(android.view.MenuItem) WidgetStatus(com.ichi2.widget.WidgetStatus) SyncErrorDialog(com.ichi2.anki.dialogs.SyncErrorDialog) Payload(com.ichi2.async.Connection.Payload) Model(com.ichi2.libanki.Model) Build(android.os.Build) ShortcutInfoCompat(androidx.core.content.pm.ShortcutInfoCompat) Utils(com.ichi2.libanki.Utils) DialogInterface(android.content.DialogInterface) ConfirmModSchemaException(com.ichi2.anki.exception.ConfirmModSchemaException) Computation(com.ichi2.utils.Computation) AbstractDeckTreeNode(com.ichi2.libanki.sched.AbstractDeckTreeNode) ActivityCompat(androidx.core.app.ActivityCompat) CreateDeckDialog(com.ichi2.anki.dialogs.CreateDeckDialog) CollectionTask(com.ichi2.async.CollectionTask) SQLException(android.database.SQLException) CustomStudyDialog(com.ichi2.anki.dialogs.customstudy.CustomStudyDialog) Syncer(com.ichi2.libanki.sync.Syncer) MediaCheckDialog(com.ichi2.anki.dialogs.MediaCheckDialog) AdaptionUtil(com.ichi2.utils.AdaptionUtil) VisibleForTesting(androidx.annotation.VisibleForTesting) Resources(android.content.res.Resources) OnClickListener(android.view.View.OnClickListener) BadgeDrawableBuilder(com.ichi2.ui.BadgeDrawableBuilder) Model(com.ichi2.libanki.Model) JSONException(com.ichi2.utils.JSONException) Intent(android.content.Intent) ModelManager(com.ichi2.libanki.ModelManager) File(java.io.File)

Example 58 with START

use of com.ichi2.anim.ActivityTransitionAnimation.Direction.START in project Anki-Android by ankidroid.

the class TemporaryModelTest method testTempModelStorage.

@Test
public void testTempModelStorage() throws Exception {
    // Start off with clean state in the cache dir
    TemporaryModel.clearTempModelFiles();
    // Make sure save / retrieve works
    String tempModelPath = TemporaryModel.saveTempModel(getTargetContext(), new JSONObject("{\"foo\": \"bar\"}"));
    Assert.assertNotNull("Saving temp model unsuccessful", tempModelPath);
    JSONObject tempModel = TemporaryModel.getTempModel(tempModelPath);
    Assert.assertNotNull("Temp model not read successfully", tempModel);
    Assert.assertEquals(new JSONObject("{\"foo\": \"bar\"}").toString(), tempModel.toString());
    // Make sure clearing works
    Assert.assertEquals(1, TemporaryModel.clearTempModelFiles());
    Timber.i("The following logged NoSuchFileException is an expected part of verifying a file delete.");
    try {
        TemporaryModel.getTempModel(tempModelPath);
        Assert.fail("Should have caught an exception here because the file is missing");
    } catch (IOException e) {
    // this is expected
    }
}
Also used : JSONObject(com.ichi2.utils.JSONObject) IOException(java.io.IOException) Test(org.junit.Test)

Aggregations

Test (org.junit.Test)23 Intent (android.content.Intent)21 JSONObject (com.ichi2.utils.JSONObject)17 Model (com.ichi2.libanki.Model)14 View (android.view.View)13 ArrayList (java.util.ArrayList)13 Bundle (android.os.Bundle)12 Collection (com.ichi2.libanki.Collection)11 Note (com.ichi2.libanki.Note)9 File (java.io.File)9 JSONArray (com.ichi2.utils.JSONArray)8 SharedPreferences (android.content.SharedPreferences)7 TextView (android.widget.TextView)7 EditText (android.widget.EditText)6 Deck (com.ichi2.libanki.Deck)6 Dialog (android.app.Dialog)5 NonNull (androidx.annotation.NonNull)5 MaterialDialog (com.afollestad.materialdialogs.MaterialDialog)5 RobolectricTest (com.ichi2.anki.RobolectricTest)5 TaskData (com.ichi2.async.TaskData)5