com.ichi2.anki.StudyOptionsFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.ichi2.anki.StudyOptionsFragment.java

Source

/****************************************************************************************
 * This program is free software; you can redistribute it and/or modify it under        *
 * the terms of the GNU General Public License as published by the Free Software        *
 * Foundation; either version 3 of the License, or (at your option) any later           *
 * version.                                                                             *
 *                                                                                      *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
 * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
 *                                                                                      *
 * You should have received a copy of the GNU General Public License along with         *
 * this program. If not, see <http://www.gnu.org/licenses/>.                            *
 ****************************************************************************************/

package com.ichi2.anki;

import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.widget.Toolbar;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;

import com.afollestad.materialdialogs.MaterialDialog;
import com.ichi2.anim.ActivityTransitionAnimation;
import com.ichi2.anki.dialogs.CustomStudyDialog;
import com.ichi2.async.DeckTask;
import com.ichi2.libanki.Collection;
import com.ichi2.libanki.Utils;
import com.ichi2.themes.StyledProgressDialog;

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

import timber.log.Timber;

public class StudyOptionsFragment extends Fragment implements Toolbar.OnMenuItemClickListener {

    /**
     * Available options performed by other activities
     */
    private static final int BROWSE_CARDS = 3;
    private static final int STATISTICS = 4;
    private static final int DECK_OPTIONS = 5;

    /**
     * Constants for selecting which content view to display
     */
    public static final int CONTENT_STUDY_OPTIONS = 0;
    public static final int CONTENT_CONGRATS = 1;
    public static final int CONTENT_EMPTY = 2;

    // Threshold at which the total number of new cards is truncated by libanki
    private static final int NEW_CARD_COUNT_TRUNCATE_THRESHOLD = 1000;

    /**
     * Preferences
     */
    private int mCurrentContentView = CONTENT_STUDY_OPTIONS;

    /** Alerts to inform the user about different situations */
    private MaterialDialog mProgressDialog;

    /**
     * UI elements for "Study Options" view
     */
    private View mStudyOptionsView;
    private View mDeckInfoLayout;
    private Button mButtonStart;
    private TextView mTextDeckName;
    private TextView mTextDeckDescription;
    private TextView mTextTodayNew;
    private TextView mTextTodayLrn;
    private TextView mTextTodayRev;
    private TextView mTextNewTotal;
    private TextView mTextTotal;
    private TextView mTextETA;
    private TextView mTextCongratsMessage;
    private Toolbar mToolbar;

    // Flag to indicate if the fragment should load the deck options immediately after it loads
    private boolean mLoadWithDeckOptions;

    private boolean mFragmented;

    private Thread mFullNewCountThread = null;

    StudyOptionsListener mListener;

    public interface StudyOptionsListener {
        void onRequireDeckListUpdate();
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mListener = (StudyOptionsListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement StudyOptionsListener");
        }
    }

    /**
     * Callbacks for UI events
     */
    private View.OnClickListener mButtonClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // long timeLimit = 0;
            switch (v.getId()) {
            case R.id.studyoptions_start:
                Timber.i("StudyOptionsFragment:: start study button pressed");
                if (mCurrentContentView != CONTENT_CONGRATS) {
                    openReviewer();
                } else {
                    showCustomStudyContextMenu();
                }
                return;
            default:
            }
        }
    };

    private void openFilteredDeckOptions() {
        openFilteredDeckOptions(false);
    }

    /**
     * Open the FilteredDeckOptions activity to allow the user to modify the parameters of the
     * filtered deck.
     * @param defaultConfig If true, signals to the FilteredDeckOptions activity that the filtered
     *                      deck has no options associated with it yet and should use a default
     *                      set of values.
     */
    private void openFilteredDeckOptions(boolean defaultConfig) {
        Intent i = new Intent(getActivity(), FilteredDeckOptions.class);
        i.putExtra("defaultConfig", defaultConfig);
        startActivityForResult(i, DECK_OPTIONS);
        ActivityTransitionAnimation.slide(getActivity(), ActivityTransitionAnimation.FADE);
    }

    /**
     * Get a new instance of the fragment.
     * @param withDeckOptions If true, the fragment will load a new activity on top of itself
     *                        which shows the current deck's options. Set to true when programmatically
     *                        opening a new filtered deck for the first time.
     */
    public static StudyOptionsFragment newInstance(boolean withDeckOptions) {
        StudyOptionsFragment f = new StudyOptionsFragment();
        Bundle args = new Bundle();
        args.putBoolean("withDeckOptions", withDeckOptions);
        f.setArguments(args);
        return f;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        if (container == null) {
            // Currently in a layout without a container, so no reason to create our view.
            return null;
        }
        restorePreferences();
        mStudyOptionsView = inflater.inflate(R.layout.studyoptions_fragment, container, false);
        mFragmented = getActivity().getClass() != StudyOptionsActivity.class;
        initAllContentViews();
        if (getArguments() != null) {
            mLoadWithDeckOptions = getArguments().getBoolean("withDeckOptions");
        }
        mToolbar = (Toolbar) mStudyOptionsView.findViewById(R.id.studyOptionsToolbar);
        mToolbar.inflateMenu(R.menu.study_options_fragment);
        if (mToolbar != null) {
            configureToolbar();
        }
        refreshInterface(true);
        return mStudyOptionsView;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mFullNewCountThread != null) {
            mFullNewCountThread.interrupt();
        }
        Timber.d("onDestroy()");
    }

    @Override
    public void onResume() {
        super.onResume();
        Timber.d("onResume()");
        refreshInterface(true);
    }

    private void closeStudyOptions(int result) {
        Activity a = getActivity();
        if (!mFragmented && a != null) {
            a.setResult(result);
            a.finish();
            ActivityTransitionAnimation.slide(a, ActivityTransitionAnimation.RIGHT);
        } else if (a == null) {
            // getActivity() can return null if reference to fragment lingers after parent activity has been closed,
            // which is particularly relevant when using AsyncTasks.
            Timber.e("closeStudyOptions() failed due to getActivity() returning null");
        }
    }

    private void openReviewer() {
        Intent reviewer = new Intent(getActivity(), Reviewer.class);
        if (mFragmented) {
            startActivityForResult(reviewer, AnkiActivity.REQUEST_REVIEW);
        } else {
            // Go to DeckPicker after studying when not tablet
            reviewer.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
            startActivity(reviewer);
            getActivity().finish();
        }
        animateLeft();
        getCol().startTimebox();
    }

    private void animateLeft() {
        ActivityTransitionAnimation.slide(getActivity(), ActivityTransitionAnimation.LEFT);
    }

    private void initAllContentViews() {
        if (mFragmented) {
            mStudyOptionsView.findViewById(R.id.studyoptions_gradient).setVisibility(View.VISIBLE);
        }
        mDeckInfoLayout = mStudyOptionsView.findViewById(R.id.studyoptions_deckinformation);
        mTextDeckName = (TextView) mStudyOptionsView.findViewById(R.id.studyoptions_deck_name);
        mTextDeckDescription = (TextView) mStudyOptionsView.findViewById(R.id.studyoptions_deck_description);
        // make links clickable
        mTextDeckDescription.setMovementMethod(LinkMovementMethod.getInstance());
        mButtonStart = (Button) mStudyOptionsView.findViewById(R.id.studyoptions_start);
        mTextCongratsMessage = (TextView) mStudyOptionsView.findViewById(R.id.studyoptions_congrats_message);
        // Code common to both fragmented and non-fragmented view
        mTextTodayNew = (TextView) mStudyOptionsView.findViewById(R.id.studyoptions_new);
        mTextTodayLrn = (TextView) mStudyOptionsView.findViewById(R.id.studyoptions_lrn);
        mTextTodayRev = (TextView) mStudyOptionsView.findViewById(R.id.studyoptions_rev);
        mTextNewTotal = (TextView) mStudyOptionsView.findViewById(R.id.studyoptions_total_new);
        mTextTotal = (TextView) mStudyOptionsView.findViewById(R.id.studyoptions_total);
        mTextETA = (TextView) mStudyOptionsView.findViewById(R.id.studyoptions_eta);
        mButtonStart.setOnClickListener(mButtonClickListener);
    }

    /**
     * Show the context menu for the custom study options
     */
    private void showCustomStudyContextMenu() {
        CustomStudyDialog d = CustomStudyDialog.newInstance(CustomStudyDialog.CONTEXT_MENU_STANDARD,
                getCol().getDecks().selected());
        ((AnkiActivity) getActivity()).showDialogFragment(d);
    }

    void setFragmentContentView(View newView) {
        ViewGroup parent = (ViewGroup) this.getView();
        parent.removeAllViews();
        parent.addView(newView);
    }

    @Override
    public boolean onMenuItemClick(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.action_undo:
            Timber.i("StudyOptionsFragment:: Undo button pressed");
            getCol().undo();
            openReviewer();
            return true;
        case R.id.action_deck_options:
            Timber.i("StudyOptionsFragment:: Deck options button pressed");
            if (getCol().getDecks().isDyn(getCol().getDecks().selected())) {
                openFilteredDeckOptions();
            } else {
                Intent i = new Intent(getActivity(), DeckOptions.class);
                startActivityForResult(i, DECK_OPTIONS);
                ActivityTransitionAnimation.slide(getActivity(), ActivityTransitionAnimation.FADE);
            }
            return true;
        case R.id.action_custom_study:
            Timber.i("StudyOptionsFragment:: custom study button pressed");
            showCustomStudyContextMenu();
            return true;
        case R.id.action_unbury:
            Timber.i("StudyOptionsFragment:: unbury button pressed");
            getCol().getSched().unburyCardsForDeck();
            refreshInterfaceAndDecklist(true);
            item.setVisible(false);
            return true;
        case R.id.action_rebuild:
            Timber.i("StudyOptionsFragment:: rebuild cram deck button pressed");
            mProgressDialog = StyledProgressDialog.show(getActivity(), "",
                    getResources().getString(R.string.rebuild_cram_deck), true);
            DeckTask.launchDeckTask(DeckTask.TASK_TYPE_REBUILD_CRAM, getDeckTaskListener(true),
                    new DeckTask.TaskData(mFragmented));
            return true;
        case R.id.action_empty:
            Timber.i("StudyOptionsFragment:: empty cram deck button pressed");
            mProgressDialog = StyledProgressDialog.show(getActivity(), "",
                    getResources().getString(R.string.empty_cram_deck), false);
            DeckTask.launchDeckTask(DeckTask.TASK_TYPE_EMPTY_CRAM, getDeckTaskListener(true),
                    new DeckTask.TaskData(mFragmented));
            return true;
        case R.id.action_rename:
            ((DeckPicker) getActivity()).renameDeckDialog(getCol().getDecks().selected());
            return true;
        case R.id.action_delete:
            ((DeckPicker) getActivity()).confirmDeckDeletion(getCol().getDecks().selected());
            return true;
        case R.id.action_export:
            ((DeckPicker) getActivity()).exportDeck(getCol().getDecks().selected());
            return true;
        default:
            return false;
        }
    }

    public void configureToolbar() {
        mToolbar.setOnMenuItemClickListener(this);
        Menu menu = mToolbar.getMenu();
        // Switch on or off rebuild/empty/custom study depending on whether or not filtered deck
        if (getCol().getDecks().isDyn(getCol().getDecks().selected())) {
            menu.findItem(R.id.action_rebuild).setVisible(true);
            menu.findItem(R.id.action_empty).setVisible(true);
            menu.findItem(R.id.action_custom_study).setVisible(false);
        } else {
            menu.findItem(R.id.action_rebuild).setVisible(false);
            menu.findItem(R.id.action_empty).setVisible(false);
            menu.findItem(R.id.action_custom_study).setVisible(true);
        }
        // Don't show custom study icon if congrats shown
        if (mCurrentContentView == CONTENT_CONGRATS) {
            menu.findItem(R.id.action_custom_study).setVisible(false);
        }
        // Switch on rename / delete / export if tablet layout
        if (mFragmented) {
            menu.findItem(R.id.action_rename).setVisible(true);
            menu.findItem(R.id.action_delete).setVisible(true);
            menu.findItem(R.id.action_export).setVisible(true);
        } else {
            menu.findItem(R.id.action_rename).setVisible(false);
            menu.findItem(R.id.action_delete).setVisible(false);
            menu.findItem(R.id.action_export).setVisible(false);
        }
        // Switch on or off unbury depending on if there are cards to unbury
        menu.findItem(R.id.action_unbury).setVisible(getCol().getSched().haveBuried());
        // Switch on or off undo depending on whether undo is available
        if (!getCol().undoAvailable()) {
            menu.findItem(R.id.action_undo).setVisible(false);
        } else {
            menu.findItem(R.id.action_undo).setVisible(true);
            Resources res = AnkiDroidApp.getAppResources();
            menu.findItem(R.id.action_undo)
                    .setTitle(res.getString(R.string.studyoptions_congrats_undo, getCol().undoName(res)));
        }
        // Set the back button listener
        if (!mFragmented) {
            mToolbar.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp);
            mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    ((AnkiActivity) getActivity()).finishWithAnimation(ActivityTransitionAnimation.RIGHT);
                }
            });
        }
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        Timber.d("onActivityResult (requestCode = %d, resultCode = %d)", requestCode, resultCode);

        // rebuild action bar
        configureToolbar();

        // boot back to deck picker if there was an error
        if (resultCode == DeckPicker.RESULT_DB_ERROR || resultCode == DeckPicker.RESULT_MEDIA_EJECTED) {
            closeStudyOptions(resultCode);
            return;
        }

        // perform some special actions depending on which activity we're returning from
        if (requestCode == STATISTICS || requestCode == BROWSE_CARDS) {
            // select original deck if the statistics or card browser were opened,
            // which can change the selected deck
            if (intent.hasExtra("originalDeck")) {
                getCol().getDecks().select(intent.getLongExtra("originalDeck", 0L));
            }
        }
        if (requestCode == DECK_OPTIONS) {
            if (mLoadWithDeckOptions == true) {
                mLoadWithDeckOptions = false;
                try {
                    JSONObject deck = getCol().getDecks().current();
                    if (deck.getInt("dyn") != 0 && deck.has("empty")) {
                        deck.remove("empty");
                    }
                } catch (JSONException e) {
                    throw new RuntimeException(e);
                }
                mProgressDialog = StyledProgressDialog.show(getActivity(), "",
                        getResources().getString(R.string.rebuild_cram_deck), true);
                DeckTask.launchDeckTask(DeckTask.TASK_TYPE_REBUILD_CRAM, getDeckTaskListener(true),
                        new DeckTask.TaskData(mFragmented));
            } else {
                DeckTask.waitToFinish();
                refreshInterface(true);
            }
        } else if (requestCode == AnkiActivity.REQUEST_REVIEW) {
            if (resultCode == Reviewer.RESULT_NO_MORE_CARDS) {
                // If no more cards getting returned while counts > 0 (due to learn ahead limit) then show a snackbar
                int[] counts = getCol().getSched().counts();
                if ((counts[0] + counts[1] + counts[2]) > 0 && mStudyOptionsView != null) {
                    View rootLayout = mStudyOptionsView.findViewById(R.id.studyoptions_main);
                    AnkiActivity activity = (AnkiActivity) getActivity();
                    activity.showSnackbar(R.string.studyoptions_no_cards_due, false, 0, null, rootLayout);
                }
            }
        } else if (requestCode == STATISTICS && mCurrentContentView == CONTENT_CONGRATS) {
            mCurrentContentView = CONTENT_STUDY_OPTIONS;
            setFragmentContentView(mStudyOptionsView);
        }
    }

    private void dismissProgressDialog() {
        if (mStudyOptionsView != null && mStudyOptionsView.findViewById(R.id.progress_bar) != null) {
            mStudyOptionsView.findViewById(R.id.progress_bar).setVisibility(View.GONE);
        }
        // for rebuilding cram decks
        if (mProgressDialog != null && mProgressDialog.isShowing()) {
            try {
                mProgressDialog.dismiss();
            } catch (Exception e) {
                Timber.e("onPostExecute - Dialog dismiss Exception = " + e.getMessage());
            }
        }
    }

    public SharedPreferences restorePreferences() {
        SharedPreferences preferences = AnkiDroidApp.getSharedPrefs(getActivity().getBaseContext());
        return preferences;
    }

    private void refreshInterfaceAndDecklist(boolean resetSched) {
        refreshInterface(resetSched, true);
    }

    protected void refreshInterface() {
        refreshInterface(false, false);
    }

    protected void refreshInterface(boolean resetSched) {
        refreshInterface(resetSched, false);
    }

    /**
     * Rebuild the fragment's interface to reflect the status of the currently selected deck.
     *
     * @param resetSched    Indicates whether to rebuild the queues as well. Set to true for any
     *                      task that modifies queues (e.g., unbury or empty filtered deck).
     * @param resetDecklist Indicates whether to call back to the parent activity in order to
     *                      also refresh the deck list.
     */
    protected void refreshInterface(boolean resetSched, boolean resetDecklist) {
        Timber.d("Refreshing StudyOptionsFragment");
        // Load the deck counts for the deck from Collection asynchronously
        DeckTask.launchDeckTask(DeckTask.TASK_TYPE_UPDATE_VALUES_FROM_DECK, getDeckTaskListener(resetDecklist),
                new DeckTask.TaskData(new Object[] { resetSched }));
    }

    /**
     * Returns a listener that rebuilds the interface after execute.
     *
     * @param refreshDecklist If true, the listener notifies the parent activity to update its deck list
     *                        to reflect the latest values.
     */
    private DeckTask.TaskListener getDeckTaskListener(final boolean refreshDecklist) {
        return new DeckTask.TaskListener() {
            @Override
            public void onPreExecute() {

            }

            @Override
            public void onPostExecute(DeckTask.TaskData result) {
                dismissProgressDialog();
                if (result != null) {
                    // Get the return values back from the AsyncTask
                    Object[] obj = result.getObjArray();
                    int newCards = (Integer) obj[0];
                    int lrnCards = (Integer) obj[1];
                    int revCards = (Integer) obj[2];
                    int totalNew = (Integer) obj[3];
                    int totalCards = (Integer) obj[4];
                    int eta = (Integer) obj[7];

                    // Don't do anything if the fragment is no longer attached to it's Activity or col has been closed
                    if (getActivity() == null) {
                        Timber.e("StudyOptionsFragment.mRefreshFragmentListener :: can't refresh");
                        return;
                    }
                    // Reinitialize controls incase changed to filtered deck
                    initAllContentViews();
                    // Set the deck name
                    String fullName;
                    JSONObject deck = getCol().getDecks().current();
                    try {
                        // Main deck name
                        fullName = deck.getString("name");
                        String[] name = fullName.split("::");
                        StringBuilder nameBuilder = new StringBuilder();
                        if (name.length > 0) {
                            nameBuilder.append(name[0]);
                        }
                        if (name.length > 1) {
                            nameBuilder.append("\n").append(name[1]);
                        }
                        if (name.length > 3) {
                            nameBuilder.append("...");
                        }
                        if (name.length > 2) {
                            nameBuilder.append("\n").append(name[name.length - 1]);
                        }
                        mTextDeckName.setText(nameBuilder.toString());

                    } catch (JSONException e) {
                        throw new RuntimeException(e);
                    }

                    // open cram deck option if deck is opened for the first time
                    if (mLoadWithDeckOptions == true) {
                        openFilteredDeckOptions(mLoadWithDeckOptions);
                        return;
                    }
                    // Switch between the empty view, the ordinary view, and the "congratulations" view
                    boolean isDynamic = deck.optInt("dyn", 0) != 0;
                    if (totalCards == 0 && !isDynamic) {
                        mCurrentContentView = CONTENT_EMPTY;
                        mDeckInfoLayout.setVisibility(View.VISIBLE);
                        mTextCongratsMessage.setVisibility(View.VISIBLE);
                        mTextCongratsMessage.setText(R.string.studyoptions_empty);
                        mButtonStart.setVisibility(View.GONE);
                    } else if (newCards + lrnCards + revCards == 0) {
                        mCurrentContentView = CONTENT_CONGRATS;
                        if (!isDynamic) {
                            mDeckInfoLayout.setVisibility(View.GONE);
                            mButtonStart.setVisibility(View.VISIBLE);
                            mButtonStart.setText(R.string.custom_study);
                        } else {
                            mButtonStart.setVisibility(View.GONE);
                        }
                        mTextCongratsMessage.setVisibility(View.VISIBLE);
                        mTextCongratsMessage.setText(getCol().getSched().finishedMsg(getActivity()));
                    } else {
                        mCurrentContentView = CONTENT_STUDY_OPTIONS;
                        mDeckInfoLayout.setVisibility(View.VISIBLE);
                        mTextCongratsMessage.setVisibility(View.GONE);
                        mButtonStart.setVisibility(View.VISIBLE);
                        mButtonStart.setText(R.string.studyoptions_start);
                    }

                    // Set deck description
                    String desc;
                    if (isDynamic) {
                        desc = getResources().getString(R.string.dyn_deck_desc);
                    } else {
                        desc = getCol().getDecks().getActualDescription();
                    }
                    if (desc.length() > 0) {
                        mTextDeckDescription.setText(Html.fromHtml(desc));
                        mTextDeckDescription.setVisibility(View.VISIBLE);
                    } else {
                        mTextDeckDescription.setVisibility(View.GONE);
                    }

                    // Set new/learn/review card counts
                    mTextTodayNew.setText(String.valueOf(newCards));
                    mTextTodayLrn.setText(String.valueOf(lrnCards));
                    mTextTodayRev.setText(String.valueOf(revCards));

                    // Set the total number of new cards in deck
                    if (totalNew < NEW_CARD_COUNT_TRUNCATE_THRESHOLD) {
                        // if it hasn't been truncated by libanki then just set it usually
                        mTextNewTotal.setText(String.valueOf(totalNew));
                    } else {
                        // if truncated then make a thread to allow full count to load
                        mTextNewTotal.setText(">1000");
                        if (mFullNewCountThread != null) {
                            // a thread was previously made -- interrupt it
                            mFullNewCountThread.interrupt();
                        }
                        mFullNewCountThread = new Thread(new Runnable() {
                            @Override
                            public void run() {
                                Collection collection = getCol();
                                // TODO: refactor code to not rewrite this query, add to Sched.totalNewForCurrentDeck()
                                StringBuilder sbQuery = new StringBuilder();
                                sbQuery.append("SELECT count(*) FROM cards WHERE did IN ");
                                sbQuery.append(Utils.ids2str(collection.getDecks().active()));
                                sbQuery.append(" AND queue = 0");
                                final int fullNewCount = collection.getDb().queryScalar(sbQuery.toString());
                                if (fullNewCount > 0) {
                                    Runnable setNewTotalText = new Runnable() {
                                        @Override
                                        public void run() {
                                            mTextNewTotal.setText(String.valueOf(fullNewCount));
                                        }
                                    };
                                    if (!Thread.currentThread().isInterrupted()) {
                                        mTextNewTotal.post(setNewTotalText);
                                    }
                                }
                            }
                        });
                        mFullNewCountThread.start();
                    }

                    // Set total number of cards
                    mTextTotal.setText(String.valueOf(totalCards));
                    // Set estimated time remaining
                    if (eta != -1) {
                        mTextETA.setText(Integer.toString(eta));
                    } else {
                        mTextETA.setText("-");
                    }
                    // Rebuild the options menu
                    configureToolbar();
                }

                // If in fragmented mode, refresh the deck list
                if (mFragmented && refreshDecklist) {
                    mListener.onRequireDeckListUpdate();
                }
            }

            @Override
            public void onProgressUpdate(DeckTask.TaskData... values) {

            }

            @Override
            public void onCancelled() {

            }
        };
    }

    private Collection getCol() {
        return CollectionHelper.getInstance().getCol(getContext());
    }
}