Search in sources :

Example 16 with Upgrade

use of com.ichi2.upgrade.Upgrade 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 17 with Upgrade

use of com.ichi2.upgrade.Upgrade in project Anki-Android by ankidroid.

the class CardBrowser method onCollectionLoaded.

// Finish initializing the activity after the collection has been correctly loaded
@Override
protected void onCollectionLoaded(Collection col) {
    super.onCollectionLoaded(col);
    Timber.d("onCollectionLoaded()");
    registerExternalStorageListener();
    SharedPreferences preferences = AnkiDroidApp.getSharedPrefs(getBaseContext());
    // Load reference to action bar title
    mActionBarTitle = findViewById(R.id.toolbar_title);
    mOrder = CARD_ORDER_NONE;
    String colOrder = getCol().get_config_string("sortType");
    for (int c = 0; c < fSortTypes.length; ++c) {
        if (fSortTypes[c].equals(colOrder)) {
            mOrder = c;
            break;
        }
    }
    if (mOrder == 1 && preferences.getBoolean("cardBrowserNoSorting", false)) {
        mOrder = 0;
    }
    // This upgrade should already have been done during
    // setConf. However older version of AnkiDroid didn't call
    // upgradeJSONIfNecessary during setConf, which means the
    // conf saved may still have this bug.
    mOrderAsc = Upgrade.upgradeJSONIfNecessary(getCol(), "sortBackwards", false);
    mCards.reset();
    mCardsListView = findViewById(R.id.card_browser_list);
    // Create a spinner for column1
    Spinner cardsColumn1Spinner = findViewById(R.id.browser_column1_spinner);
    ArrayAdapter<CharSequence> column1Adapter = ArrayAdapter.createFromResource(this, R.array.browser_column1_headings, android.R.layout.simple_spinner_item);
    column1Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    cardsColumn1Spinner.setAdapter(column1Adapter);
    mColumn1Index = AnkiDroidApp.getSharedPrefs(getBaseContext()).getInt("cardBrowserColumn1", 0);
    cardsColumn1Spinner.setOnItemSelectedListener(new OnItemSelectedListener() {

        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
            // If a new column was selected then change the key used to map from mCards to the column TextView
            if (pos != mColumn1Index) {
                mColumn1Index = pos;
                AnkiDroidApp.getSharedPrefs(AnkiDroidApp.getInstance().getBaseContext()).edit().putInt("cardBrowserColumn1", mColumn1Index).apply();
                Column[] fromMap = mCardsAdapter.getFromMapping();
                fromMap[0] = COLUMN1_KEYS[mColumn1Index];
                mCardsAdapter.setFromMapping(fromMap);
            }
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {
        // Do Nothing
        }
    });
    // Load default value for column2 selection
    mColumn2Index = AnkiDroidApp.getSharedPrefs(getBaseContext()).getInt("cardBrowserColumn2", 0);
    // Setup the column 2 heading as a spinner so that users can easily change the column type
    Spinner cardsColumn2Spinner = findViewById(R.id.browser_column2_spinner);
    ArrayAdapter<CharSequence> column2Adapter = ArrayAdapter.createFromResource(this, R.array.browser_column2_headings, android.R.layout.simple_spinner_item);
    column2Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    cardsColumn2Spinner.setAdapter(column2Adapter);
    // Create a new list adapter with updated column map any time the user changes the column
    cardsColumn2Spinner.setOnItemSelectedListener(new OnItemSelectedListener() {

        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
            // If a new column was selected then change the key used to map from mCards to the column TextView
            if (pos != mColumn2Index) {
                mColumn2Index = pos;
                AnkiDroidApp.getSharedPrefs(AnkiDroidApp.getInstance().getBaseContext()).edit().putInt("cardBrowserColumn2", mColumn2Index).apply();
                Column[] fromMap = mCardsAdapter.getFromMapping();
                fromMap[1] = COLUMN2_KEYS[mColumn2Index];
                mCardsAdapter.setFromMapping(fromMap);
            }
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {
        // Do Nothing
        }
    });
    // get the font and font size from the preferences
    int sflRelativeFontSize = preferences.getInt("relativeCardBrowserFontSize", DEFAULT_FONT_SIZE_RATIO);
    String sflCustomFont = preferences.getString("browserEditorFont", "");
    Column[] columnsContent = { COLUMN1_KEYS[mColumn1Index], COLUMN2_KEYS[mColumn2Index] };
    // make a new list adapter mapping the data in mCards to column1 and column2 of R.layout.card_item_browser
    mCardsAdapter = new MultiColumnListAdapter(this, R.layout.card_item_browser, columnsContent, new int[] { R.id.card_sfld, R.id.card_column2 }, sflRelativeFontSize, sflCustomFont);
    // link the adapter to the main mCardsListView
    mCardsListView.setAdapter(mCardsAdapter);
    // make the items (e.g. question & answer) render dynamically when scrolling
    mCardsListView.setOnScrollListener(new RenderOnScroll());
    // set the spinner index
    cardsColumn1Spinner.setSelection(mColumn1Index);
    cardsColumn2Spinner.setSelection(mColumn2Index);
    mCardsListView.setOnItemClickListener((parent, view, position, id) -> {
        if (mInMultiSelectMode) {
            // click on whole cell triggers select
            CheckBox cb = view.findViewById(R.id.card_checkbox);
            cb.toggle();
            onCheck(position, view);
        } else {
            // load up the card selected on the list
            long clickedCardId = getCards().get(position).getId();
            saveScrollingState(position);
            openNoteEditorForCard(clickedCardId);
        }
    });
    mCardsListView.setOnItemLongClickListener((adapterView, view, position, id) -> {
        if (mInMultiSelectMode) {
            boolean hasChanged = false;
            for (int i = Math.min(mLastSelectedPosition, position); i <= Math.max(mLastSelectedPosition, position); i++) {
                CardCache card = (CardCache) mCardsListView.getItemAtPosition(i);
                // Add to the set of checked cards
                hasChanged |= mCheckedCards.add(card);
            }
            if (hasChanged) {
                onSelectionChanged();
            }
        } else {
            mLastSelectedPosition = position;
            saveScrollingState(position);
            loadMultiSelectMode();
            // click on whole cell triggers select
            CheckBox cb = view.findViewById(R.id.card_checkbox);
            cb.toggle();
            onCheck(position, view);
            recenterListView(view);
            mCardsAdapter.notifyDataSetChanged();
        }
        return true;
    });
    getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
    long deckId = getCol().getDecks().selected();
    mDeckSpinnerSelection = new DeckSpinnerSelection(this, col, this.findViewById(R.id.toolbar_spinner), true, false);
    mDeckSpinnerSelection.initializeActionBarDeckSpinner(this.getSupportActionBar());
    selectDeckAndSave(deckId);
    // If a valid value for last deck exists then use it, otherwise use libanki selected deck
    if (getLastDeckId() != null && getLastDeckId() == ALL_DECKS_ID) {
        selectAllDecks();
    } else if (getLastDeckId() != null && getCol().getDecks().get(getLastDeckId(), false) != null) {
        mDeckSpinnerSelection.selectDeckById(getLastDeckId(), false);
    } else {
        mDeckSpinnerSelection.selectDeckById(getCol().getDecks().selected(), false);
    }
}
Also used : SharedPreferences(android.content.SharedPreferences) Spinner(android.widget.Spinner) SearchView(androidx.appcompat.widget.SearchView) CardBrowserSearchView(com.ichi2.ui.CardBrowserSearchView) View(android.view.View) AdapterView(android.widget.AdapterView) TextView(android.widget.TextView) ListView(android.widget.ListView) AbsListView(android.widget.AbsListView) Column(com.ichi2.anki.CardBrowser.Column) CheckBox(android.widget.CheckBox) OnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener)

Aggregations

File (java.io.File)7 Intent (android.content.Intent)6 SharedPreferences (android.content.SharedPreferences)6 View (android.view.View)6 Resources (android.content.res.Resources)5 Bundle (android.os.Bundle)5 TextView (android.widget.TextView)5 Collection (com.ichi2.libanki.Collection)5 JSONException (com.ichi2.utils.JSONException)5 SQLiteDatabaseCorruptException (android.database.sqlite.SQLiteDatabaseCorruptException)4 Message (android.os.Message)4 NonNull (androidx.annotation.NonNull)4 SearchView (androidx.appcompat.widget.SearchView)4 ConfirmModSchemaException (com.ichi2.anki.exception.ConfirmModSchemaException)4 FileInputStream (java.io.FileInputStream)4 FileNotFoundException (java.io.FileNotFoundException)4 IOException (java.io.IOException)4 InputStream (java.io.InputStream)4 List (java.util.List)4 BroadcastReceiver (android.content.BroadcastReceiver)3