Search in sources :

Example 46 with Time

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

the class BackupManager method repairCollection.

/**
 * Run the sqlite3 command-line-tool (if it exists) on the collection to dump to a text file
 * and reload as a new database. Recently this command line tool isn't available on many devices
 *
 * @param col Collection
 * @return whether the repair was successful
 */
public static boolean repairCollection(Collection col) {
    String deckPath = col.getPath();
    File deckFile = new File(deckPath);
    Time time = col.getTime();
    Timber.i("BackupManager - RepairCollection - Closing Collection");
    col.close();
    // repair file
    String execString = "sqlite3 " + deckPath + " .dump | sqlite3 " + deckPath + ".tmp";
    Timber.i("repairCollection - Execute: %s", execString);
    try {
        String[] cmd = { "/system/bin/sh", "-c", execString };
        Process process = Runtime.getRuntime().exec(cmd);
        process.waitFor();
        if (!new File(deckPath + ".tmp").exists()) {
            Timber.e("repairCollection - dump to " + deckPath + ".tmp failed");
            return false;
        }
        if (!moveDatabaseToBrokenFolder(deckPath, false, time)) {
            Timber.e("repairCollection - could not move corrupt file to broken folder");
            return false;
        }
        Timber.i("repairCollection - moved corrupt file to broken folder");
        File repairedFile = new File(deckPath + ".tmp");
        return repairedFile.renameTo(deckFile);
    } catch (IOException | InterruptedException e) {
        Timber.e(e, "repairCollection - error");
    }
    return false;
}
Also used : Time(com.ichi2.libanki.utils.Time) IOException(java.io.IOException) File(java.io.File)

Example 47 with Time

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

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 = (TextView) findViewById(R.id.toolbar_title);
    // Add drop-down menu to select deck to action bar.
    mDropDownDecks = getCol().getDecks().allSorted();
    mDropDownAdapter = new DeckDropDownAdapter(this, mDropDownDecks, R.layout.dropdown_deck_selected_item, this);
    Toolbar toolbar = findViewById(R.id.toolbar);
    if (toolbar != null) {
        setSupportActionBar(toolbar);
        // enable ActionBar app icon to behave as action to toggle nav drawer
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setHomeButtonEnabled(true);
        // Decide which action to take when the navigation button is tapped.
        toolbar.setNavigationOnClickListener(v -> finishActivityWithFade(this, ActivityTransitionAnimation.RIGHT));
    }
    ActionBar mActionBar = getSupportActionBar();
    if (mActionBar != null) {
        mActionBar.setDisplayShowTitleEnabled(false);
    }
    mActionBarSpinner = (Spinner) findViewById(R.id.toolbar_spinner);
    mActionBarSpinner.setAdapter(mDropDownAdapter);
    mActionBarSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {

        @Override
        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
            deckDropDownItemChanged(position);
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {
        // do nothing
        }
    });
    mActionBarSpinner.setVisibility(View.VISIBLE);
    mOrder = CARD_ORDER_NONE;
    String colOrder = getCol().getConf().getString("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(), getCol().getConf(), "sortBackwards", false);
    mCards = new ArrayList<>();
    mCardsListView = (ListView) findViewById(R.id.card_browser_list);
    // Create a spinner for column1
    Spinner cardsColumn1Spinner = (Spinner) 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).commit();
                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 = (Spinner) 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).commit();
                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(new ListView.OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            if (mInMultiSelectMode) {
                // click on whole cell triggers select
                CheckBox cb = (CheckBox) 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();
                openNoteEditorForCard(clickedCardId);
            }
        }
    });
    mCardsListView.setOnItemLongClickListener(new ListView.OnItemLongClickListener() {

        @Override
        public boolean onItemLongClick(AdapterView<?> adapterView, View view, final int position, long id) {
            mLastSelectedPosition = position;
            loadMultiSelectMode();
            // click on whole cell triggers select
            CheckBox cb = (CheckBox) 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);
    // 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) {
        selectDeckById(getLastDeckId());
    } else {
        selectDeckById(getCol().getDecks().selected());
    }
}
Also used : SharedPreferences(android.content.SharedPreferences) Spinner(android.widget.Spinner) View(android.view.View) AdapterView(android.widget.AdapterView) SearchView(androidx.appcompat.widget.SearchView) TextView(android.widget.TextView) ListView(android.widget.ListView) AbsListView(android.widget.AbsListView) DeckDropDownAdapter(com.ichi2.anki.widgets.DeckDropDownAdapter) ListView(android.widget.ListView) AbsListView(android.widget.AbsListView) Column(com.ichi2.anki.CardBrowser.Column) CheckBox(android.widget.CheckBox) OnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener) ActionBar(androidx.appcompat.app.ActionBar) Toolbar(androidx.appcompat.widget.Toolbar)

Example 48 with Time

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

the class AnkiActivity method showImportDialog.

@TargetApi(Build.VERSION_CODES.KITKAT)
@Override
public void showImportDialog(int id, String message) {
    // On API19+ we only use import dialog to confirm, otherwise we use it the whole time
    if ((CompatHelper.getSdkVersion() < 19) || (id == ImportDialog.DIALOG_IMPORT_ADD_CONFIRM) || (id == ImportDialog.DIALOG_IMPORT_REPLACE_CONFIRM)) {
        Timber.d("showImportDialog() delegating to ImportDialog");
        AsyncDialogFragment newFragment = ImportDialog.newInstance(id, message, this);
        showAsyncDialogFragment(newFragment);
    } else {
        Timber.d("showImportDialog() delegating to file picker intent");
        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("*/*");
        intent.putExtra("android.content.extra.SHOW_ADVANCED", true);
        intent.putExtra("android.content.extra.FANCY", true);
        intent.putExtra("android.content.extra.SHOW_FILESIZE", true);
        startActivityForResultWithoutAnimation(intent, PICK_APKG_FILE);
    }
}
Also used : AsyncDialogFragment(com.ichi2.anki.dialogs.AsyncDialogFragment) PendingIntent(android.app.PendingIntent) CustomTabsIntent(androidx.browser.customtabs.CustomTabsIntent) Intent(android.content.Intent) TargetApi(android.annotation.TargetApi)

Example 49 with Time

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

the class OverView method loadDataBuilder.

public void loadDataBuilder(OverviewStatsBuilder builder) {
    Timber.d("loadDataBuilder");
    Resources res = getResources();
    findTextViewById(R.id.tv_today).setText(res.getString(R.string.stats_today));
    final int minutes = (int) Math.round(builder.mTodayStats[THETIME_INDEX] / 60.0);
    final String span = res.getQuantityString(R.plurals.time_span_minutes, minutes, minutes);
    findTextViewById(R.id.tv_today_minute).setText(res.getQuantityString(R.plurals.stats_today_cards, builder.mTodayStats[CARDS_INDEX], builder.mTodayStats[CARDS_INDEX], span));
    findTextViewById(R.id.tv_today_repeat).setText(res.getString(R.string.stats_today_again_count, builder.mTodayStats[FAILED_INDEX]));
    if (builder.mTodayStats[CARDS_INDEX] > 0) {
        findTextViewById(R.id.tv_today_correct_rate).setVisibility(VISIBLE);
        findTextViewById(R.id.tv_today_correct_rate).setText(res.getString(R.string.stats_today_correct_count, builder.mTodayStats[CARDS_INDEX] - builder.mTodayStats[FAILED_INDEX], builder.mTodayStats[CARDS_INDEX], (((1 - builder.mTodayStats[FAILED_INDEX] / (float) (builder.mTodayStats[CARDS_INDEX])) * 100.0))));
    } else {
        findTextViewById(R.id.tv_today_correct_rate).setVisibility(GONE);
    }
    findTextViewById(R.id.tv_today_study).setText(res.getString(R.string.stats_today_type_breakdown, builder.mTodayStats[LRN_INDEX], builder.mTodayStats[REV_INDEX], builder.mTodayStats[RELRN_INDEX], builder.mTodayStats[FILT_INDEX]));
    if (builder.mTodayStats[MCNT_INDEX] != 0) {
        findTextViewById(R.id.tv_today_hint).setText(res.getString(R.string.stats_today_mature_cards, builder.mTodayStats[MSUM_INDEX], builder.mTodayStats[MCNT_INDEX], (builder.mTodayStats[MSUM_INDEX] / (float) (builder.mTodayStats[MCNT_INDEX]) * 100.0)));
    } else {
        findTextViewById(R.id.tv_today_hint).setText(res.getString(R.string.stats_today_no_mature_cards));
    }
    findTextViewById(R.id.description).setText(res.getString(builder.mType.descriptionId));
    OverviewStatsBuilder.OverviewStats oStats = builder.mOverViewStats;
    // FORECAST
    findTextViewById(R.id.tv_predict).setText(res.getString(R.string.stats_forecast).toUpperCase());
    findTextViewById(R.id.tv_predict_all_content).setText(res.getString(R.string.stats_overview_forecast_total, oStats.forecastTotalReviews));
    findTextViewById(R.id.tv_predict_avg_content).setText(res.getString(R.string.stats_overview_forecast_average, oStats.forecastAverageReviews));
    findTextViewById(R.id.tv_predict_deadline_content).setText(res.getString(R.string.stats_overview_forecast_due_tomorrow, oStats.forecastDueTomorrow));
    // REVIEW COUNT
    findTextViewById(R.id.tv_review).setText(res.getString(R.string.stats_review_count).toUpperCase());
    findTextViewById(R.id.tv_review_day_content).setText(oStats.daysStudied + "/" + oStats.allDays);
    findTextViewById(R.id.tv_review_day_percent).setText("(" + (int) ((float) oStats.daysStudied / (float) oStats.allDays * 100) + "%)");
    findTextViewById(R.id.tv_review_all_content).setText(res.getString(R.string.stats_overview_total_reviews, oStats.totalReviews));
    findTextViewById(R.id.tv_review_avg_content).setText(res.getString(R.string.stats_overview_reviews_per_day_studydays, oStats.reviewsPerDayOnStudyDays));
    boolean allDaysStudied = oStats.daysStudied == oStats.allDays;
    if (!allDaysStudied) {
        findTextViewById(R.id.tv_review_hint).setText(res.getString(R.string.stats_overview_reviews_per_day_all, oStats.reviewsPerDayOnAll));
        findTextViewById(R.id.tv_review_hint).setVisibility(VISIBLE);
    } else {
        findTextViewById(R.id.tv_review_hint).setVisibility(GONE);
    }
    // REVIEW TIME
    findTextViewById(R.id.tv_review_time).setText(res.getString(R.string.stats_review_time).toUpperCase());
    findTextViewById(R.id.tv_review_time_count_content).setText(oStats.daysStudied + "/" + oStats.allDays);
    findTextViewById(R.id.tv_review_time_count_percent).setText("(" + (int) ((float) oStats.daysStudied / (float) oStats.allDays * 100) + "%)");
    findTextViewById(R.id.tv_review_time_all_content).setText(res.getString(R.string.stats_overview_total_time_in_period, Math.round(oStats.totalTime)));
    findTextViewById(R.id.tv_review_time_avg_content).setText(res.getString(R.string.stats_overview_time_per_day_studydays, oStats.timePerDayOnStudyDays));
    double cardsPerMinute = oStats.totalTime == 0 ? 0 : ((double) oStats.totalReviews) / oStats.totalTime;
    double averageAnswerTime = oStats.totalReviews == 0 ? 0 : (oStats.totalTime * 60) / ((double) oStats.totalReviews);
    if (!allDaysStudied) {
        findTextViewById(R.id.tv_review_time_hint).setText(res.getString(R.string.stats_overview_time_per_day_all, oStats.timePerDayOnAll) + "," + res.getString(R.string.stats_overview_average_answer_time, averageAnswerTime, cardsPerMinute));
    } else {
        findTextViewById(R.id.tv_review_time_hint).setText(res.getString(R.string.stats_overview_average_answer_time, averageAnswerTime, cardsPerMinute));
    }
    // ADDED
    findTextViewById(R.id.tv_add).setText(res.getString(R.string.stats_added).toUpperCase());
    findTextViewById(R.id.tv_add_all_content).setText(res.getString(R.string.stats_overview_total_new_cards, oStats.totalNewCards));
    findTextViewById(R.id.tv_add_avg_content).setText(res.getString(R.string.stats_overview_new_cards_per_day, oStats.newCardsPerDay));
    // INTERVALS
    findTextViewById(R.id.tv_distance).setText(res.getString(R.string.stats_review_intervals).toUpperCase());
    findTextViewById(R.id.tv_distance_avg).setText(res.getString(R.string.stats_overview_average_interval));
    findTextViewById(R.id.tv_distance_avg_content).setText(Utils.roundedTimeSpan(getContext(), (int) Math.round(oStats.averageInterval * Stats.SECONDS_PER_DAY)));
    findTextViewById(R.id.tv_distance_max).setText(res.getString(R.string.stats_overview_longest_interval));
    findTextViewById(R.id.tv_distance_all_content).setText(Utils.roundedTimeSpan(getContext(), (int) Math.round(oStats.longestInterval * Stats.SECONDS_PER_DAY)));
    // ANSWER BUTTONS
    findTextViewById(R.id.tv_answer_button).setText(res.getString(R.string.stats_answer_buttons).toUpperCase());
    findTextViewById(R.id.tv_answer_study_content).setText(res.getString(R.string.stats_overview_answer_buttons_learn, oStats.newCardsOverview.getPercentage()));
    findTextViewById(R.id.tv_answer_study_percent).setText("(" + oStats.newCardsOverview.correct + "/" + oStats.newCardsOverview.total + ")");
    findTextViewById(R.id.tv_answer_not_familiar_content).setText(res.getString(R.string.stats_overview_answer_buttons_young, oStats.youngCardsOverview.getPercentage()));
    findTextViewById(R.id.tv_answer_not_familiar_percent).setText("(" + oStats.youngCardsOverview.correct + "/" + oStats.youngCardsOverview.total + ")");
    findTextViewById(R.id.tv_answer_familiar_content).setText(res.getString(R.string.stats_overview_answer_buttons_mature, oStats.matureCardsOverview.getPercentage()));
    findTextViewById(R.id.tv_answer_familiar_percent).setText("(" + oStats.matureCardsOverview.correct + "/" + oStats.matureCardsOverview.total + ")");
    // CARD TYPES
    findTextViewById(R.id.tv_note_type).setText(res.getString(R.string.stats_cards_types).toUpperCase());
    findTextViewById(R.id.tv_note_type_all_card).setText(R.string.stats_overview_card_types_total_cards);
    findTextViewById(R.id.tv_note_type_all_card_content).setText("" + oStats.totalCards);
    findTextViewById(R.id.tv_note_type_all).setText(R.string.stats_overview_card_types_total_notes);
    findTextViewById(R.id.tv_note_type_all_content).setText("" + oStats.totalNotes);
    findTextViewById(R.id.tv_note_low_level).setText(R.string.stats_overview_card_types_lowest_ease);
    findTextViewById(R.id.tv_note_low_level_content).setText(oStats.lowestEase + "%");
    findTextViewById(R.id.tv_note_avg_level).setText(R.string.stats_overview_card_types_average_ease);
    findTextViewById(R.id.tv_note_avg_level_content).setText(oStats.averageEase + "%");
    findTextViewById(R.id.tv_note_high_level).setText(R.string.stats_overview_card_types_highest_ease);
    findTextViewById(R.id.tv_note_high_level_content).setText(oStats.highestEase + "%");
}
Also used : OverviewStatsBuilder(com.ichi2.anki.stats.OverviewStatsBuilder) Resources(android.content.res.Resources) SuppressLint(android.annotation.SuppressLint)

Example 50 with Time

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

the class Connection method doInBackgroundSync.

private Payload doInBackgroundSync(Payload data) {
    sIsCancellable = true;
    Timber.d("doInBackgroundSync()");
    // Block execution until any previous background task finishes, or timeout after 5s
    boolean ok = CollectionTask.waitToFinish(5);
    String hkey = (String) data.data[0];
    boolean media = (Boolean) data.data[1];
    String conflictResolution = (String) data.data[2];
    HostNum hostNum = (HostNum) data.data[3];
    long restServerSpace = (long) data.data[4];
    // Use safe version that catches exceptions so that full sync is still possible
    Collection col = CollectionHelper.getInstance().getColSafe(AnkiDroidApp.getInstance());
    boolean colCorruptFullSync = false;
    if (!CollectionHelper.getInstance().colIsOpen() || !ok) {
        if (conflictResolution != null && "download".equals(conflictResolution)) {
            colCorruptFullSync = true;
        } else {
            data.success = false;
            data.result = new Object[] { "genericError" };
            return data;
        }
    }
    try {
        CollectionHelper.getInstance().lockCollection();
        HttpSyncer server = new RemoteServer(this, hkey, hostNum);
        Syncer client = new Syncer(col, server, hostNum);
        // run sync and check state
        boolean noChanges = false;
        if (conflictResolution == null) {
            Timber.i("Sync - starting sync");
            publishProgress(R.string.sync_prepare_syncing);
            Object[] ret = client.sync(this, restServerSpace);
            data.message = client.getSyncMsg();
            if (ret == null) {
                data.success = false;
                data.result = new Object[] { "genericError" };
                return data;
            }
            String retCode = (String) ret[0];
            if (!"noChanges".equals(retCode) && !"success".equals(retCode)) {
                data.success = false;
                data.result = ret;
                // Check if there was a sanity check error
                if ("sanityCheckError".equals(retCode)) {
                    // Force full sync next time
                    col.modSchemaNoCheck();
                    col.save();
                }
                return data;
            }
            // save and note success state
            if ("noChanges".equals(retCode)) {
                // publishProgress(R.string.sync_no_changes_message);
                noChanges = true;
            }
            restServerSpace = client.getRestSpace();
        } else {
            try {
                // Disable sync cancellation for full-sync
                sIsCancellable = false;
                server = new FullSyncer(col, hkey, this, hostNum);
                if ("upload".equals(conflictResolution)) {
                    Timber.i("Sync - fullsync - upload collection");
                    publishProgress(R.string.sync_preparing_full_sync_message);
                    Object[] ret = server.upload(restServerSpace);
                    col.reopen();
                    if (ret == null) {
                        data.success = false;
                        data.result = new Object[] { "genericError" };
                        return data;
                    }
                    if (!ret[0].equals(HttpSyncer.ANKIWEB_STATUS_OK)) {
                        data.success = false;
                        data.result = ret;
                        return data;
                    }
                } else if ("download".equals(conflictResolution)) {
                    Timber.i("Sync - fullsync - download collection");
                    publishProgress(R.string.sync_downloading_message);
                    Object[] ret = server.download();
                    if (ret == null) {
                        Timber.w("Sync - fullsync - unknown error");
                        data.success = false;
                        data.result = new Object[] { "genericError" };
                        return data;
                    }
                    if ("success".equals(ret[0])) {
                        data.success = true;
                        col.reopen();
                    }
                    if (!"success".equals(ret[0])) {
                        Timber.w("Sync - fullsync - download failed");
                        data.success = false;
                        data.result = ret;
                        if (!colCorruptFullSync) {
                            col.reopen();
                        }
                        return data;
                    }
                }
            } catch (OutOfMemoryError e) {
                AnkiDroidApp.sendExceptionReport(e, "doInBackgroundSync-fullSync");
                data.success = false;
                data.result = new Object[] { "OutOfMemoryError" };
                return data;
            } catch (RuntimeException e) {
                if (timeoutOccurred(e)) {
                    data.result = new Object[] { "connectionError", e };
                } else if ("UserAbortedSync".equals(e.getMessage())) {
                    data.result = new Object[] { "UserAbortedSync", e };
                } else {
                    AnkiDroidApp.sendExceptionReport(e, "doInBackgroundSync-fullSync");
                    data.result = new Object[] { "IOException", e };
                }
                data.success = false;
                return data;
            }
        }
        // clear undo to avoid non syncing orphans (because undo resets usn too
        if (!noChanges) {
            col.clearUndo();
        }
        // then move on to media sync
        sIsCancellable = true;
        boolean noMediaChanges = false;
        boolean enoughServerSpace = true;
        String mediaError = null;
        if (media) {
            server = new RemoteMediaServer(col, hkey, this, hostNum);
            MediaSyncer mediaClient = new MediaSyncer(col, (RemoteMediaServer) server, this);
            String ret;
            try {
                // ������ܻ���Ϊ�ϴ��ļ������ռ��С���׳��쳣���ڴ�֮ǰ��Ҫ����ʣ��ռ��С
                ret = mediaClient.sync(restServerSpace);
                Timber.e("sync media ret is null");
                if (ret == null) {
                    mediaError = AnkiDroidApp.getAppResources().getString(R.string.sync_media_error);
                } else {
                    if ("corruptMediaDB".equals(ret)) {
                        mediaError = AnkiDroidApp.getAppResources().getString(R.string.sync_media_db_error);
                        noMediaChanges = true;
                    }
                    if ("noChanges".equals(ret)) {
                        publishProgress(R.string.sync_media_no_changes);
                        noMediaChanges = true;
                    }
                    if ("sanityFailed".equals(ret)) {
                        mediaError = AnkiDroidApp.getAppResources().getString(R.string.sync_media_sanity_failed);
                    } else {
                        publishProgress(R.string.sync_media_success);
                    }
                }
            } catch (RuntimeException e) {
                if (timeoutOccurred(e)) {
                    data.result = new Object[] { "connectionError", e };
                } else if ("UserAbortedSync".equals(e.getMessage())) {
                    data.result = new Object[] { "UserAbortedSync", e };
                }
                mediaError = AnkiDroidApp.getAppResources().getString(R.string.sync_media_error) + "\n\n" + e.getLocalizedMessage();
            }
        }
        if (noChanges && (!media || noMediaChanges)) {
            data.success = false;
            data.result = new Object[] { "noChanges" };
            return data;
        } else {
            data.success = true;
            data.data = new Object[] { conflictResolution, col, mediaError };
            return data;
        }
    } catch (MediaSyncException e) {
        Timber.e("Media sync rejected by server");
        data.success = false;
        data.result = new Object[] { "mediaSyncServerError", e };
        AnkiDroidApp.sendExceptionReport(e, "doInBackgroundSync");
        return data;
    } catch (UnknownHttpResponseException e) {
        Timber.e(e, "doInBackgroundSync -- unknown response code error");
        data.success = false;
        int code = e.getResponseCode();
        String msg = e.getLocalizedMessage();
        data.result = new Object[] { "error", code, msg };
        return data;
    } catch (NoEnoughServerSpaceException e) {
        Timber.e("NoEnoughServerSpaceException ");
        e.printStackTrace();
        data.success = false;
        data.result = new Object[] { "noServerSpace", e.rest, e.need };
        return data;
    } catch (Exception e) {
        // Global error catcher.
        // Try to give a human readable error, otherwise print the raw error message
        Timber.e(e, "doInBackgroundSync error");
        data.success = false;
        if (timeoutOccurred(e)) {
            data.result = new Object[] { "connectionError", e };
        } else if ("UserAbortedSync".equals(e.getMessage())) {
            data.result = new Object[] { "UserAbortedSync", e };
        } else {
            AnkiDroidApp.sendExceptionReport(e, "doInBackgroundSync");
            data.result = new Object[] { e.getLocalizedMessage(), e };
        }
        return data;
    } finally {
        Timber.i("Sync Finished - Closing Collection");
        // don't bump mod time unless we explicitly save
        if (col != null) {
            col.close(false);
        }
        CollectionHelper.getInstance().unlockCollection();
    }
}
Also used : HostNum(com.ichi2.libanki.sync.HostNum) FullSyncer(com.ichi2.libanki.sync.FullSyncer) UnknownHttpResponseException(com.ichi2.anki.exception.UnknownHttpResponseException) JSONException(com.ichi2.utils.JSONException) NoEnoughServerSpaceException(com.ichi2.anki.exception.NoEnoughServerSpaceException) MediaSyncException(com.ichi2.anki.exception.MediaSyncException) IOException(java.io.IOException) UnknownHttpResponseException(com.ichi2.anki.exception.UnknownHttpResponseException) HttpSyncer(com.ichi2.libanki.sync.HttpSyncer) FullSyncer(com.ichi2.libanki.sync.FullSyncer) Syncer(com.ichi2.libanki.sync.Syncer) MediaSyncer(com.ichi2.libanki.sync.MediaSyncer) MediaSyncException(com.ichi2.anki.exception.MediaSyncException) Collection(com.ichi2.libanki.Collection) MediaSyncer(com.ichi2.libanki.sync.MediaSyncer) JSONObject(com.ichi2.utils.JSONObject) HttpSyncer(com.ichi2.libanki.sync.HttpSyncer) RemoteServer(com.ichi2.libanki.sync.RemoteServer) RemoteMediaServer(com.ichi2.libanki.sync.RemoteMediaServer) NoEnoughServerSpaceException(com.ichi2.anki.exception.NoEnoughServerSpaceException)

Aggregations

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