Java tutorial
/**************************************************************************************** * Copyright (c) 2009 Andrew Dubya <andrewdubya@gmail.com> * * Copyright (c) 2009 Nicolas Raoul <nicolas.raoul@gmail.com> * * Copyright (c) 2009 Edu Zamora <edu.zasu@gmail.com> * * Copyright (c) 2009 Daniel Svrd <daniel.svard@gmail.com> * * Copyright (c) 2010 Norbert Nagold <norbert.nagold@gmail.com> * * * * 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.hichinaschool.flashcards.anki; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.TreeSet; import org.json.JSONException; import org.json.JSONObject; import android.app.Activity; import android.app.Dialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnCancelListener; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.database.SQLException; import android.graphics.PixelFormat; import android.graphics.Rect; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentTransaction; import android.text.TextUtils; import android.util.Log; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.view.Window; import android.widget.AdapterView; import android.widget.Button; import android.widget.EditText; import android.widget.ImageButton; import android.widget.ListView; import android.widget.SimpleAdapter; import android.widget.TextView; import com.google.analytics.tracking.android.EasyTracker; import com.google.analytics.tracking.android.MapBuilder; import com.hichinaschool.flashcards.anim.ActivityTransitionAnimation; import com.hichinaschool.flashcards.anki.receiver.SdCardReceiver; import com.hichinaschool.flashcards.async.Connection; import com.hichinaschool.flashcards.async.DeckTask; import com.hichinaschool.flashcards.async.Connection.OldAnkiDeckFilter; import com.hichinaschool.flashcards.async.Connection.Payload; import com.hichinaschool.flashcards.async.DeckTask.Listener; import com.hichinaschool.flashcards.async.DeckTask.TaskData; import com.hichinaschool.flashcards.charts.ChartBuilder; import com.hichinaschool.flashcards.libanki.Collection; import com.hichinaschool.flashcards.libanki.Utils; import com.hichinaschool.flashcards.themes.StyledDialog; import com.hichinaschool.flashcards.themes.StyledOpenCollectionDialog; import com.hichinaschool.flashcards.themes.StyledProgressDialog; import com.hichinaschool.flashcards.themes.Themes; import com.hichinaschool.flashcards.widget.WidgetStatus; public class DeckPicker extends FragmentActivity { public static final int CRAM_DECK_FRAGMENT = -1; /** * Dialogs */ private static final int DIALOG_NO_SDCARD = 0; private static final int DIALOG_USER_NOT_LOGGED_IN_SYNC = 1; private static final int DIALOG_USER_NOT_LOGGED_IN_ADD_SHARED_DECK = 2; private static final int DIALOG_NO_CONNECTION = 3; private static final int DIALOG_DELETE_DECK = 4; private static final int DIALOG_SELECT_STATISTICS_TYPE = 5; private static final int DIALOG_CONTEXT_MENU = 9; private static final int DIALOG_REPAIR_COLLECTION = 10; private static final int DIALOG_NO_SPACE_LEFT = 11; private static final int DIALOG_SYNC_CONFLICT_RESOLUTION = 12; private static final int DIALOG_CONNECTION_ERROR = 13; private static final int DIALOG_SYNC_LOG = 15; private static final int DIALOG_SELECT_HELP = 16; private static final int DIALOG_BACKUP_NO_SPACE_LEFT = 17; private static final int DIALOG_OK = 18; private static final int DIALOG_DB_ERROR = 19; private static final int DIALOG_ERROR_HANDLING = 20; private static final int DIALOG_LOAD_FAILED = 21; private static final int DIALOG_RESTORE_BACKUP = 22; private static final int DIALOG_SD_CARD_NOT_MOUNTED = 23; private static final int DIALOG_NEW_COLLECTION = 24; private static final int DIALOG_FULL_SYNC_FROM_SERVER = 25; private static final int DIALOG_SYNC_SANITY_ERROR = 26; private static final int DIALOG_SYNC_UPGRADE_REQUIRED = 27; private static final int DIALOG_IMPORT = 28; private static final int DIALOG_IMPORT_LOG = 29; private static final int DIALOG_IMPORT_HINT = 30; private static final int DIALOG_IMPORT_SELECT = 31; public static final String UPGRADE_OLD_COLLECTION_RENAME = "oldcollection.apkg"; public static final String IMPORT_REPLACE_COLLECTION_NAME = "collection.apkg"; private static final int IMPORT_METHOD_ASK = 0; private static final int IMPORT_METHOD_ADD = 1; private static final int IMPORT_METHOD_REPLACE = 2; private String mSyncMessage; private String mDialogMessage; private int[] mRepairValues; private boolean mLoadFailed; private String mImportPath; private String[] mImportValues; private int mImportMethod = IMPORT_METHOD_ASK; /** * Menus */ private static final int MENU_ABOUT = 0; private static final int MENU_CREATE_DECK = 1; private static final int MENU_ADD_SHARED_DECK = 2; private static final int MENU_PREFERENCES = 3; private static final int MENU_FEEDBACK = 5; private static final int MENU_HELP = 6; private static final int CHECK_DATABASE = 7; private static final int MENU_SYNC = 8; private static final int MENU_ADD_NOTE = 9; public static final int MENU_CREATE_DYNAMIC_DECK = 10; private static final int MENU_STATISTICS = 12; private static final int MENU_CARDBROWSER = 13; private static final int MENU_IMPORT = 14; private static final int MENU_REUPGRADE = 15; /** * Context Menus */ private static final int CONTEXT_MENU_COLLAPSE_DECK = 2; private static final int CONTEXT_MENU_RENAME_DECK = 1; private static final int CONTEXT_MENU_DELETE_DECK = 0; private static final int CONTEXT_MENU_DECK_SUMMARY = 3; private static final int CONTEXT_MENU_CUSTOM_DICTIONARY = 4; private static final int CONTEXT_MENU_RESET_LANGUAGE = 5; public static final String EXTRA_START = "start"; public static final String EXTRA_DECK_ID = "deckId"; public static final int EXTRA_START_NOTHING = 0; public static final int EXTRA_START_REVIEWER = 1; public static final int EXTRA_START_DECKPICKER = 2; public static final int EXTRA_DB_ERROR = 3; public static final int RESULT_MEDIA_EJECTED = 202; public static final int RESULT_DB_ERROR = 203; public static final int RESULT_RESTART = 204; /** * Available options performed by other activities */ private static final int PREFERENCES_UPDATE = 0; private static final int DOWNLOAD_SHARED_DECK = 3; public static final int REPORT_FEEDBACK = 4; private static final int LOG_IN_FOR_DOWNLOAD = 5; private static final int LOG_IN_FOR_SYNC = 6; private static final int STUDYOPTIONS = 7; private static final int SHOW_INFO_WELCOME = 8; private static final int SHOW_INFO_NEW_VERSION = 9; private static final int REPORT_ERROR = 10; public static final int SHOW_STUDYOPTIONS = 11; private static final int ADD_NOTE = 12; private static final int LOG_IN = 13; private static final int BROWSE_CARDS = 14; private static final int ADD_SHARED_DECKS = 15; private static final int LOG_IN_FOR_SHARED_DECK = 16; private static final int ADD_CRAM_DECK = 17; private static final int SHOW_INFO_UPGRADE_DECKS = 18; private static final int REQUEST_REVIEW = 19; private StyledProgressDialog mProgressDialog; private StyledOpenCollectionDialog mOpenCollectionDialog; private StyledOpenCollectionDialog mNotMountedDialog; private ImageButton mAddButton; private ImageButton mCardsButton; private ImageButton mStatsButton; private ImageButton mSyncButton; private File[] mBackups; /* ListView variables */ private SimpleAdapter mDeckListAdapter; private ArrayList<HashMap<String, String>> mDeckList; private ListView mDeckListView; private int mDeckLastItemSelected; /* HiChina! variables */ private boolean lesson1added = false; private boolean mDontSaveOnStop = false; private BroadcastReceiver mUnmountReceiver = null; private String mPrefDeckPath = null; private long mLastTimeOpened; private long mCurrentDid; private int mSyncMediaUsn = 0; private EditText mDialogEditText; int mStatisticType; public boolean mFragmented; private boolean mInvalidateMenu; boolean mCompletionBarRestrictToActive = false; // set this to true in order to calculate completion bar only for // active cards private int[] mDictValues; private int mContextMenuPosition; /** Swipe Detection */ private GestureDetector gestureDetector; View.OnTouchListener gestureListener; private boolean mSwipeEnabled; // ---------------------------------------------------------------------------- // LISTENERS // ---------------------------------------------------------------------------- private AdapterView.OnItemClickListener mDeckSelHandler = new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View v, int p, long id) { handleDeckSelection(p); } }; private DialogInterface.OnClickListener mContextMenuListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int item) { Resources res = getResources(); @SuppressWarnings("unchecked") HashMap<String, String> data = (HashMap<String, String>) mDeckListAdapter.getItem(mContextMenuPosition); switch (item) { case CONTEXT_MENU_COLLAPSE_DECK: try { JSONObject deck = AnkiDroidApp.getCol().getDecks().get(mCurrentDid); if (AnkiDroidApp.getCol().getDecks().children(mCurrentDid).size() > 0) { deck.put("collapsed", !deck.getBoolean("collapsed")); AnkiDroidApp.getCol().getDecks().save(deck); loadCounts(); } } catch (JSONException e1) { // do nothing } return; case CONTEXT_MENU_DELETE_DECK: showDialog(DIALOG_DELETE_DECK); return; case CONTEXT_MENU_RESET_LANGUAGE: // resetDeckLanguages(data.get("filepath")); return; case CONTEXT_MENU_CUSTOM_DICTIONARY: // String[] dicts = res.getStringArray(R.array.dictionary_labels); // String[] vals = res.getStringArray(R.array.dictionary_values); // int currentSet = MetaDB.getLookupDictionary(DeckPicker.this, data.get("filepath")); // // mCurrentDeckPath = data.get("filepath"); // String[] labels = new String[dicts.length + 1]; // mDictValues = new int[dicts.length + 1]; // int currentChoice = 0; // labels[0] = res.getString(R.string.deckpicker_select_dictionary_default); // mDictValues[0] = -1; // for (int i = 1; i < labels.length; i++) { // labels[i] = dicts[i-1]; // mDictValues[i] = Integer.parseInt(vals[i-1]); // if (currentSet == mDictValues[i]) { // currentChoice = i; // } // } // StyledDialog.Builder builder = new StyledDialog.Builder(DeckPicker.this); // builder.setTitle(res.getString(R.string.deckpicker_select_dictionary_title)); // builder.setSingleChoiceItems(labels, currentChoice, new DialogInterface.OnClickListener() { // public void onClick(DialogInterface dialog, int item) { // MetaDB.storeLookupDictionary(DeckPicker.this, mCurrentDeckPath, mDictValues[item]); // } // }); // StyledDialog alert = builder.create(); // alert.show(); return; case CONTEXT_MENU_RENAME_DECK: StyledDialog.Builder builder2 = new StyledDialog.Builder(DeckPicker.this); builder2.setTitle(res.getString(R.string.contextmenu_deckpicker_rename_deck)); mDialogEditText = (EditText) new EditText(DeckPicker.this); mDialogEditText.setSingleLine(); mDialogEditText.setText(AnkiDroidApp.getCol().getDecks().name(mCurrentDid)); // mDialogEditText.setFilters(new InputFilter[] { mDeckNameFilter }); builder2.setView(mDialogEditText, false, false); builder2.setPositiveButton(res.getString(R.string.rename), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { String newName = mDialogEditText.getText().toString().replaceAll("['\"]", ""); Collection col = AnkiDroidApp.getCol(); if (col != null) { if (col.getDecks().rename(col.getDecks().get(mCurrentDid), newName)) { for (HashMap<String, String> d : mDeckList) { if (d.get("did").equals(Long.toString(mCurrentDid))) { d.put("name", newName); } } mDeckListAdapter.notifyDataSetChanged(); loadCounts(); } else { try { Themes.showThemedToast(DeckPicker.this, getResources().getString(R.string.rename_error, col.getDecks().get(mCurrentDid).get("name")), false); } catch (JSONException e) { throw new RuntimeException(e); } } } } }); builder2.setNegativeButton(res.getString(R.string.cancel), null); builder2.create().show(); return; case CONTEXT_MENU_DECK_SUMMARY: // mStatisticType = 0; // DeckTask.launchDeckTask(DeckTask.TASK_TYPE_LOAD_STATISTICS, mLoadStatisticsHandler, new // DeckTask.TaskData(DeckPicker.this, new String[]{data.get("filepath")}, mStatisticType, 0)); return; } } }; private Connection.TaskListener mSyncListener = new Connection.TaskListener() { String currentMessage; long countUp; long countDown; @Override public void onDisconnected() { showDialog(DIALOG_NO_CONNECTION); } @Override public void onPreExecute() { mDialogMessage = ""; mSyncMessage = ""; mDontSaveOnStop = true; countUp = 0; countDown = 0; if (mProgressDialog == null || !mProgressDialog.isShowing()) { mProgressDialog = StyledProgressDialog.show(DeckPicker.this, getResources().getString(R.string.sync_title), getResources().getString(R.string.sync_prepare_syncing) + "\n" + getResources().getString(R.string.sync_up_down_size, countUp, countDown), true, false); } } @Override public void onProgressUpdate(Object... values) { Resources res = getResources(); if (values[0] instanceof Boolean) { // This is the part Download missing media of syncing int total = ((Integer) values[1]).intValue(); int done = ((Integer) values[2]).intValue(); values[0] = ((String) values[3]); values[1] = res.getString(R.string.sync_downloading_media, done, total); } else if (values[0] instanceof Integer) { int id = (Integer) values[0]; if (id != 0) { currentMessage = res.getString(id); } if (values.length >= 3) { countUp = (Long) values[1]; countDown = (Long) values[2]; } } if (mProgressDialog != null && mProgressDialog.isShowing()) { // mProgressDialog.setTitle((String) values[0]); mProgressDialog.setMessage(currentMessage + "\n" + res.getString(R.string.sync_up_down_size, countUp / 1024, countDown / 1024)); } } @Override public void onPostExecute(Payload data) { // Log.i(AnkiDroidApp.TAG, "onPostExecute"); Resources res = DeckPicker.this.getResources(); mDontSaveOnStop = false; if (mProgressDialog != null) { mProgressDialog.dismiss(); } mSyncMessage = data.message; if (!data.success) { Object[] result = (Object[]) data.result; if (result[0] instanceof String) { String resultType = (String) result[0]; if (resultType.equals("badAuth")) { // delete old auth information SharedPreferences preferences = AnkiDroidApp.getSharedPrefs(getBaseContext()); Editor editor = preferences.edit(); editor.putString("username", ""); editor.putString("hkey", ""); editor.commit(); // then show showDialog(DIALOG_USER_NOT_LOGGED_IN_SYNC); } else if (resultType.equals("noChanges")) { mDialogMessage = res.getString(R.string.sync_no_changes_message); showDialog(DIALOG_SYNC_LOG); } else if (resultType.equals("clockOff")) { long diff = (Long) result[1]; if (diff >= 86100) { // The difference if more than a day minus 5 minutes acceptable by ankiweb error mDialogMessage = res.getString(R.string.sync_log_clocks_unsynchronized, diff, res.getString(R.string.sync_log_clocks_unsynchronized_date)); } else if (Math.abs((diff % 3600.0) - 1800.0) >= 1500.0) { // The difference would be within limit if we adjusted the time by few hours // It doesn't work for all timezones, but it covers most and it's a guess anyway mDialogMessage = res.getString(R.string.sync_log_clocks_unsynchronized, diff, res.getString(R.string.sync_log_clocks_unsynchronized_tz)); } else { mDialogMessage = res.getString(R.string.sync_log_clocks_unsynchronized, diff, ""); } showDialog(DIALOG_SYNC_LOG); } else if (resultType.equals("fullSync")) { if (data.data != null && data.data.length >= 1 && data.data[0] instanceof Integer) { mSyncMediaUsn = (Integer) data.data[0]; } showDialog(DIALOG_SYNC_CONFLICT_RESOLUTION); } else if (resultType.equals("dbError")) { mDialogMessage = res.getString(R.string.sync_corrupt_database, R.string.repair_deck); showDialog(DIALOG_SYNC_LOG); } else if (resultType.equals("overwriteError")) { mDialogMessage = res.getString(R.string.sync_overwrite_error); showDialog(DIALOG_SYNC_LOG); } else if (resultType.equals("remoteDbError")) { mDialogMessage = res.getString(R.string.sync_remote_db_error); showDialog(DIALOG_SYNC_LOG); } else if (resultType.equals("sdAccessError")) { mDialogMessage = res.getString(R.string.sync_write_access_error); showDialog(DIALOG_SYNC_LOG); } else if (resultType.equals("finishError")) { mDialogMessage = res.getString(R.string.sync_log_finish_error); showDialog(DIALOG_SYNC_LOG); } else if (resultType.equals("IOException")) { handleDbError(); } else if (resultType.equals("genericError")) { mDialogMessage = res.getString(R.string.sync_generic_error); showDialog(DIALOG_SYNC_LOG); } else if (resultType.equals("OutOfMemoryError")) { mDialogMessage = res.getString(R.string.error_insufficient_memory); showDialog(DIALOG_SYNC_LOG); } else if (resultType.equals("upgradeRequired")) { showDialog(DIALOG_SYNC_UPGRADE_REQUIRED); } else if (resultType.equals("sanityCheckError")) { Collection col = AnkiDroidApp.getCol(); col.modSchema(); col.save(); mDialogMessage = res.getString(R.string.sync_sanity_failed); showDialog(DIALOG_SYNC_SANITY_ERROR); } else if (resultType.equals("serverAbort")) { // syncMsg has already been set above, no need to fetch it here. showDialog(DIALOG_SYNC_LOG); } else { if (result.length > 1 && result[1] instanceof Integer) { int type = (Integer) result[1]; switch (type) { case 503: mDialogMessage = res.getString(R.string.sync_too_busy); break; default: mDialogMessage = res.getString(R.string.sync_log_error_specific, Integer.toString(type), (String) result[2]); break; } } else if (result[0] instanceof String) { mDialogMessage = res.getString(R.string.sync_log_error_specific, -1, (String) result[0]); } else { mDialogMessage = res.getString(R.string.sync_generic_error); } showDialog(DIALOG_SYNC_LOG); } } } else { updateDecksList((TreeSet<Object[]>) data.result, (Integer) data.data[2], (Integer) data.data[3]); if (data.data[4] != null) { mDialogMessage = (String) data.data[4]; } else if (data.data.length > 0 && data.data[0] instanceof String && ((String) data.data[0]).length() > 0) { String dataString = (String) data.data[0]; if (dataString.equals("upload")) { mDialogMessage = res.getString(R.string.sync_log_uploading_message); } else if (dataString.equals("download")) { mDialogMessage = res.getString(R.string.sync_log_downloading_message); // set downloaded collection as current one } else { mDialogMessage = res.getString(R.string.sync_database_success); } } else { mDialogMessage = res.getString(R.string.sync_database_success); } showDialog(DIALOG_SYNC_LOG); // close opening dialog in case it's open if (mOpenCollectionDialog != null && mOpenCollectionDialog.isShowing()) { mOpenCollectionDialog.dismiss(); } if (mFragmented) { try { // Pick the correct deck after sync. Updates the values in the fragment if same deck. long did = AnkiDroidApp.getCol().getDecks().current().getLong("id"); selectDeck(did); } catch (JSONException e) { throw new RuntimeException(); } } } } }; DeckTask.TaskListener mOpenCollectionHandler = new DeckTask.TaskListener() { @Override public void onPostExecute(DeckTask.TaskData result) { Collection col = result.getCollection(); Object[] res = result.getObjArray(); if (col == null || res == null) { AnkiDatabaseManager.closeDatabase(AnkiDroidApp.getCollectionPath()); // showDialog(DIALOG_LOAD_FAILED); loadCollection(); return; } updateDecksList((TreeSet<Object[]>) res[0], (Integer) res[1], (Integer) res[2]); // select last loaded deck if any if (mFragmented) { long did = col.getDecks().selected(); selectDeck(did); } if (AnkiDroidApp.colIsOpen() && mImportPath != null) { showDialog(DIALOG_IMPORT); } if (mOpenCollectionDialog.isShowing()) { try { mOpenCollectionDialog.dismiss(); } catch (Exception e) { Log.e(AnkiDroidApp.TAG, "onPostExecute - Dialog dismiss Exception = " + e.getMessage()); } } } @Override public void onPreExecute() { if (mOpenCollectionDialog == null || !mOpenCollectionDialog.isShowing()) { mOpenCollectionDialog = StyledOpenCollectionDialog.show(DeckPicker.this, getResources().getString(R.string.open_collection), new OnCancelListener() { @Override public void onCancel(DialogInterface arg0) { // TODO: close dbs? DeckTask.cancelTask(); finishWithAnimation(); } }); } if (mNotMountedDialog != null && mNotMountedDialog.isShowing()) { try { mNotMountedDialog.dismiss(); } catch (Exception e) { Log.e(AnkiDroidApp.TAG, "onPostExecute - Dialog dismiss Exception = " + e.getMessage()); } } } @Override public void onProgressUpdate(DeckTask.TaskData... values) { String message = values[0].getString(); if (message != null) { mOpenCollectionDialog.setMessage(message); } } }; DeckTask.TaskListener mLoadCountsHandler = new DeckTask.TaskListener() { @Override public void onPostExecute(DeckTask.TaskData result) { if (result == null) { return; } Object[] res = result.getObjArray(); updateDecksList((TreeSet<Object[]>) res[0], (Integer) res[1], (Integer) res[2]); if (mOpenCollectionDialog != null && mOpenCollectionDialog.isShowing()) { mOpenCollectionDialog.dismiss(); } try { // Ensure we have the correct deck selected in the deck list after we have updated it. Check first // if the collection is open since it might have been closed before this task completes. if (AnkiDroidApp.getCol() != null) { setSelectedDeck(AnkiDroidApp.getCol().getDecks().current().getLong("id")); } } catch (JSONException e) { throw new RuntimeException(); } } @Override public void onPreExecute() { } @Override public void onProgressUpdate(DeckTask.TaskData... values) { } }; DeckTask.TaskListener mCloseCollectionHandler = new DeckTask.TaskListener() { @Override public void onPostExecute(DeckTask.TaskData result) { } @Override public void onPreExecute() { } @Override public void onProgressUpdate(DeckTask.TaskData... values) { } }; DeckTask.TaskListener mLoadStatisticsHandler = new DeckTask.TaskListener() { @Override public void onPostExecute(DeckTask.TaskData result) { if (mProgressDialog.isShowing()) { try { mProgressDialog.dismiss(); } catch (Exception e) { Log.e(AnkiDroidApp.TAG, "onPostExecute - Dialog dismiss Exception = " + e.getMessage()); } } if (result.getBoolean()) { // if (mStatisticType == Statistics.TYPE_DECK_SUMMARY) { // Statistics.showDeckSummary(DeckPicker.this); // } else { Intent intent = new Intent(DeckPicker.this, com.hichinaschool.flashcards.charts.ChartBuilder.class); startActivity(intent); if (AnkiDroidApp.SDK_VERSION > 4) { ActivityTransitionAnimation.slide(DeckPicker.this, ActivityTransitionAnimation.DOWN); } // } } else { // TODO: db error handling } } @Override public void onPreExecute() { mProgressDialog = StyledProgressDialog.show(DeckPicker.this, "", getResources().getString(R.string.calculating_statistics), true); } @Override public void onProgressUpdate(DeckTask.TaskData... values) { } }; DeckTask.TaskListener mRepairDeckHandler = new DeckTask.TaskListener() { @Override public void onPreExecute() { mProgressDialog = StyledProgressDialog.show(DeckPicker.this, "", getResources().getString(R.string.backup_repair_deck_progress), true); } @Override public void onPostExecute(DeckTask.TaskData result) { if (mProgressDialog != null && mProgressDialog.isShowing()) { mProgressDialog.dismiss(); } if (result.getBoolean()) { loadCollection(); } else { Themes.showThemedToast(DeckPicker.this, getResources().getString(R.string.deck_repair_error), true); showDialog(DIALOG_ERROR_HANDLING); } } @Override public void onProgressUpdate(TaskData... values) { } }; DeckTask.TaskListener mRestoreDeckHandler = new DeckTask.TaskListener() { @Override public void onPreExecute() { mProgressDialog = StyledProgressDialog.show(DeckPicker.this, "", getResources().getString(R.string.backup_restore_deck), true); } @Override public void onPostExecute(DeckTask.TaskData result) { switch (result.getInt()) { case BackupManager.RETURN_DECK_RESTORED: loadCollection(); Collection col = AnkiDroidApp.getCol(); if (col != null) { col.modSchema(false); } break; case BackupManager.RETURN_ERROR: Themes.showThemedToast(DeckPicker.this, getResources().getString(R.string.backup_restore_error), true); showDialog(DIALOG_ERROR_HANDLING); break; case BackupManager.RETURN_NOT_ENOUGH_SPACE: showDialog(DIALOG_NO_SPACE_LEFT); break; } if (mProgressDialog != null && mProgressDialog.isShowing()) { mProgressDialog.dismiss(); } } @Override public void onProgressUpdate(TaskData... values) { } }; DeckTask.TaskListener mImportAddListener = new DeckTask.TaskListener() { @Override public void onPostExecute(DeckTask.TaskData result) { if (mProgressDialog != null && mProgressDialog.isShowing()) { mProgressDialog.dismiss(); } if (result.getBoolean()) { Resources res = getResources(); int count = result.getInt(); if (count < 0) { if (count == -2) { mDialogMessage = res.getString(R.string.import_log_no_apkg); } else { mDialogMessage = res.getString(R.string.import_log_error); } showDialog(DIALOG_IMPORT_LOG); } else { mDialogMessage = res.getString(R.string.import_log_success, count); showDialog(DIALOG_IMPORT_LOG); Object[] info = result.getObjArray(); updateDecksList((TreeSet<Object[]>) info[0], (Integer) info[1], (Integer) info[2]); } } else { handleDbError(); } } @Override public void onPreExecute() { if (mProgressDialog == null || !mProgressDialog.isShowing()) { mProgressDialog = StyledProgressDialog.show(DeckPicker.this, getResources().getString(R.string.import_title), getResources().getString(R.string.import_importing), true, false); } } @Override public void onProgressUpdate(DeckTask.TaskData... values) { mProgressDialog.setMessage(values[0].getString()); } }; DeckTask.TaskListener mImportReplaceListener = new DeckTask.TaskListener() { @Override public void onPostExecute(DeckTask.TaskData result) { if (mProgressDialog != null && mProgressDialog.isShowing()) { mProgressDialog.dismiss(); } if (result.getBoolean()) { Resources res = getResources(); int code = result.getInt(); if (code == -2) { mDialogMessage = res.getString(R.string.import_log_no_apkg); } Object[] info = result.getObjArray(); updateDecksList((TreeSet<Object[]>) info[0], (Integer) info[1], (Integer) info[2]); if (mOpenCollectionDialog != null && mOpenCollectionDialog.isShowing()) { mOpenCollectionDialog.dismiss(); } } else { mDialogMessage = getResources().getString(R.string.import_log_no_apkg); showDialog(DIALOG_IMPORT_LOG); } } @Override public void onPreExecute() { if (mProgressDialog == null || !mProgressDialog.isShowing()) { mProgressDialog = StyledProgressDialog.show(DeckPicker.this, getResources().getString(R.string.import_title), getResources().getString(R.string.import_importing), true, false); } } @Override public void onProgressUpdate(DeckTask.TaskData... values) { mProgressDialog.setMessage(values[0].getString()); } }; DeckTask.TaskListener mUpgradeExportListener = new DeckTask.TaskListener() { @Override public void onPreExecute() { if (mProgressDialog == null || !mProgressDialog.isShowing()) { mProgressDialog = StyledProgressDialog.show(DeckPicker.this, getString(R.string.export_progress_title), getString(R.string.export_progress_exporting), true, false); } } @Override public void onPostExecute(DeckTask.TaskData result) { if (mProgressDialog != null && mProgressDialog.isShowing()) { mProgressDialog.dismiss(); } StyledDialog.Builder builder = new StyledDialog.Builder(DeckPicker.this); StyledDialog dialog; if (result.getBoolean()) { builder.setIcon(R.drawable.ic_dialog_info); builder.setTitle(getString(R.string.export_progress_title)); builder.setMessage(getString(R.string.upgrade_deck_export_successful)); } else { builder.setIcon(R.drawable.ic_dialog_alert); builder.setTitle(R.string.export_progress_title); builder.setMessage(getString(R.string.upgrade_deck_export_error)); builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { AnkiDroidApp.getSharedPrefs(AnkiDroidApp.getInstance().getBaseContext()).edit() .putInt("lastUpgradeVersion", AnkiDroidApp.getPkgVersionCode()).commit(); } }); } builder.setPositiveButton(R.string.btn_continue, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { showUpgradeScreen(true, Info.UPGRADE_SCREEN_BASIC1); AnkiDroidApp.getSharedPrefs(AnkiDroidApp.getInstance().getBaseContext()).edit() .putInt("lastUpgradeVersion", AnkiDroidApp.getPkgVersionCode()).commit(); } }); dialog = builder.create(); dialog.show(); } @Override public void onProgressUpdate(TaskData... values) { } }; // ---------------------------------------------------------------------------- // ANDROID METHODS // ---------------------------------------------------------------------------- /** Called when the activity is first created. */ @Override protected void onCreate(Bundle savedInstanceState) throws SQLException { // Log.i(AnkiDroidApp.TAG, "DeckPicker - onCreate"); Intent intent = getIntent(); if (!isTaskRoot()) { // Log.i(AnkiDroidApp.TAG, "DeckPicker - onCreate: Detected multiple instance of this activity, closing it and return to root activity"); Intent reloadIntent = new Intent(DeckPicker.this, DeckPicker.class); reloadIntent.setAction(Intent.ACTION_MAIN); if (intent != null && intent.getExtras() != null) { reloadIntent.putExtras(intent.getExtras()); } if (intent != null && intent.getData() != null) { reloadIntent.setData(intent.getData()); } reloadIntent.addCategory(Intent.CATEGORY_LAUNCHER); reloadIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); finish(); startActivityIfNeeded(reloadIntent, 0); } if (intent.getData() != null) { mImportPath = getIntent().getData().getEncodedPath(); } // need to start this here in order to avoid showing deckpicker before splashscreen if (AnkiDroidApp.colIsOpen()) { setTitle(getResources().getString(R.string.app_name)); } else { setTitle(""); mOpenCollectionHandler.onPreExecute(); } Themes.applyTheme(this); super.onCreate(savedInstanceState); // mStartedByBigWidget = intent.getIntExtra(EXTRA_START, EXTRA_START_NOTHING); SharedPreferences preferences = restorePreferences(); // activate broadcast messages if first start of a day if (mLastTimeOpened < UIUtils.getDayStart()) { preferences.edit().putBoolean("showBroadcastMessageToday", true).commit(); } preferences.edit().putLong("lastTimeOpened", System.currentTimeMillis()).commit(); // if (intent != null && intent.hasExtra(EXTRA_DECK_ID)) { // openStudyOptions(intent.getLongExtra(EXTRA_DECK_ID, 1)); // } // BroadcastMessages.checkForNewMessages(this); View mainView = getLayoutInflater().inflate(R.layout.deck_picker, null); setContentView(mainView); // check, if tablet layout View studyoptionsFrame = findViewById(R.id.studyoptions_fragment); mFragmented = studyoptionsFrame != null && studyoptionsFrame.getVisibility() == View.VISIBLE; Themes.setContentStyle(mFragmented ? mainView : mainView.findViewById(R.id.deckpicker_view), Themes.CALLER_DECKPICKER); registerExternalStorageListener(); if (!mFragmented) { // mAddButton = (ImageButton) findViewById(R.id.deckpicker_add); // mAddButton.setOnClickListener(new OnClickListener() { // @Override // public void onClick(View v) { // addNote(); // } // }); // // mCardsButton = (ImageButton) findViewById(R.id.deckpicker_card_browser); // mCardsButton.setOnClickListener(new OnClickListener() { // @Override // public void onClick(View v) { // openCardBrowser(); // } // }); // // mStatsButton = (ImageButton) findViewById(R.id.statistics_all_button); // mStatsButton.setOnClickListener(new OnClickListener() { // @Override // public void onClick(View v) { // showDialog(DIALOG_SELECT_STATISTICS_TYPE); // } // }); // // mSyncButton = (ImageButton) findViewById(R.id.sync_all_button); // mSyncButton.setOnClickListener(new OnClickListener() { // @Override // public void onClick(View v) { // sync(); // } // }); } mInvalidateMenu = false; mDeckList = new ArrayList<HashMap<String, String>>(); mDeckListView = (ListView) findViewById(R.id.files); // mDeckListAdapter = new SimpleAdapter(this, mDeckList, R.layout.deck_item, new String[] { "name", "new", "lrn", // "rev", // "complMat", "complAll", // "sep", "dyn" }, new int[] { R.id.DeckPickerName, R.id.deckpicker_new, R.id.deckpicker_lrn, // R.id.deckpicker_rev, // R.id.deckpicker_bar_mat, R.id.deckpicker_bar_all, // R.id.deckpicker_deck, R.id.DeckPickerName }); mDeckListAdapter = new SimpleAdapter(this, mDeckList, R.layout.deck_item, new String[] { "name", "new", "lrn", "rev", "sep", "dyn", "url" }, new int[] { R.id.DeckPickerName, R.id.deckpicker_new, R.id.deckpicker_lrn, R.id.deckpicker_rev, R.id.deckpicker_deck, R.id.DeckPickerName, R.id.deckpicker_url }); mDeckListAdapter.setViewBinder(new SimpleAdapter.ViewBinder() { @Override public boolean setViewValue(final View view, final Object data, String text) { if (view.getId() == R.id.deckpicker_url) { // If "url" field has text, it means the deck must be downloaded // so we put the downloadButton visible and add its listener if (!text.equals("")) { final View parentView = (View) view.getParent(); Button downloadButton = (Button) parentView.findViewById(R.id.deckpicker_button); downloadButton.setVisibility(View.VISIBLE); parentView.findViewById(R.id.deckpicker_new).setVisibility(View.GONE); parentView.findViewById(R.id.deckpicker_rev).setVisibility(View.GONE); parentView.findViewById(R.id.deckpicker_lrn).setVisibility(View.GONE); downloadButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // ListView lv = (ListView) parentView.getParent().getParent(); // int i; // for (i = 0; i < mDeckList.size(); i++) { // if (mDeckListView.getItemAtPosition(i).equals(view)){ // mDeckLastItemSelected = i; // } // } // Gets url from the selected deck and download it TextView txt = (TextView) ((View) view.getParent()) .findViewById(R.id.deckpicker_url); String url = txt.getText().toString(); Connection.downloadSharedDeck(mDownloadDeckListener, new Connection.Payload(new Object[] { url })); } }); } else { Button downloadButton = (Button) ((View) view.getParent()) .findViewById(R.id.deckpicker_button); downloadButton.setVisibility(View.GONE); ((View) view.getParent()).findViewById(R.id.deckpicker_new).setVisibility(View.VISIBLE); ((View) view.getParent()).findViewById(R.id.deckpicker_rev).setVisibility(View.VISIBLE); ((View) view.getParent()).findViewById(R.id.deckpicker_lrn).setVisibility(View.VISIBLE); } } if (view.getId() == R.id.deckpicker_deck) { if (text.equals("top")) { view.setBackgroundResource(R.drawable.white_deckpicker_top); return true; } else if (text.equals("bot")) { view.setBackgroundResource(R.drawable.white_deckpicker_bottom); return true; } else if (text.equals("ful")) { view.setBackgroundResource(R.drawable.white_deckpicker_full); return true; } else if (text.equals("cen")) { view.setBackgroundResource(R.drawable.white_deckpicker_center); return true; } } else if (view.getId() == R.id.DeckPickerName) { if (text.equals("d0")) { ((TextView) view).setTextColor(getResources().getColor(R.color.non_dyn_deck)); return true; } else if (text.equals("d1")) { ((TextView) view).setTextColor(getResources().getColor(R.color.dyn_deck)); return true; } } // } else if (view.getId() == R.id.deckpicker_bar_mat || view.getId() == R.id.deckpicker_bar_all) { // if (text.length() > 0 && !text.equals("-1.0")) { // View parent = (View)view.getParent().getParent(); // if (text.equals("-2")) { // parent.setVisibility(View.GONE); // } else { // Utils.updateProgressBars(view, (int) UIUtils.getDensityAdjustedValue(DeckPicker.this, 3.4f), // (int) (Double.parseDouble(text) * ((View)view.getParent().getParent().getParent()).getHeight())); // if (parent.getVisibility() == View.INVISIBLE) { // parent.setVisibility(View.VISIBLE); // parent.setAnimation(ViewAnimation.fade(ViewAnimation.FADE_IN, 500, 0)); // } // } // } // return true; // } else if (view.getVisibility() == View.INVISIBLE) { // if (!text.equals("-1")) { // view.setVisibility(View.VISIBLE); // view.setAnimation(ViewAnimation.fade(ViewAnimation.FADE_IN, 500, 0)); // return false; // } // } else if (text.equals("-1")){ // view.setVisibility(View.INVISIBLE); // return false; return false; } }); mDeckListView.setOnItemClickListener(mDeckSelHandler); mDeckListView.setAdapter(mDeckListAdapter); if (mFragmented) { mDeckListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); } registerForContextMenu(mDeckListView); showStartupScreensAndDialogs(preferences, 0); if (mSwipeEnabled) { gestureDetector = new GestureDetector(new MyGestureDetector()); mDeckListView.setOnTouchListener(new View.OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { if (gestureDetector.onTouchEvent(event)) { return true; } return false; } }); } } @Override public void onStart() { super.onStart(); EasyTracker.getInstance(this).activityStart(this); } @Override protected void onResume() { super.onResume(); if (AnkiDroidApp.getCol() != null) { if (Utils.now() > AnkiDroidApp.getCol().getSched().getDayCutoff() && AnkiDroidApp.isSdCardMounted()) { loadCounts(); } } } @Override public void onSaveInstanceState(Bundle savedInstanceState) { super.onSaveInstanceState(savedInstanceState); savedInstanceState.putLong("mCurrentDid", mCurrentDid); // savedInstanceState.putSerializable("mDeckList", mDeckList); } @Override public void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); mCurrentDid = savedInstanceState.getLong("mCurrentDid"); // mDeckList = (ArrayList<HashMap<String, String>>) savedInstanceState.getSerializable("mDeckList"); } private void loadCollection() { if (!AnkiDroidApp.isSdCardMounted()) { showDialog(DIALOG_SD_CARD_NOT_MOUNTED); return; } String path = AnkiDroidApp.getCollectionPath(); Collection col = AnkiDroidApp.getCol(); if (col == null || !col.getPath().equals(path) || mDeckListView == null || mDeckListView.getChildCount() == 0) { DeckTask.launchDeckTask(DeckTask.TASK_TYPE_OPEN_COLLECTION, mOpenCollectionHandler, new DeckTask.TaskData(path)); } else { loadCounts(); } // HashMap<String, String> hashmap = new HashMap<String, String>(); // hashmap.put("HiChina!", "HiChina! Lesson 1"); // hashmap.put("HiChina!", "HiChina! Lesson 2"); // hashmap.put("HiChina!", "HiChina! Lesson 3"); // // mDeckList.add(hashmap); // TaskData result = new TaskData(mDeckList); // Object[] res = result.getObjArray(); // TreeSet<Object[]> decks = new TreeSet<Object[]>(); // decks.add(res); // // // updateDecksList(decks, 0, 3); // close opening dialog in case it's open // if (mOpenCollectionDialog != null && mOpenCollectionDialog.isShowing()) { // mOpenCollectionDialog.dismiss(); // } } public void loadCounts() { if (AnkiDroidApp.colIsOpen()) { DeckTask.launchDeckTask(DeckTask.TASK_TYPE_LOAD_DECK_COUNTS, mLoadCountsHandler, new TaskData(AnkiDroidApp.getCol())); } } private void addNote() { Intent intent = new Intent(DeckPicker.this, CardEditor.class); intent.putExtra(CardEditor.EXTRA_CALLER, CardEditor.CALLER_DECKPICKER); startActivityForResult(intent, ADD_NOTE); if (AnkiDroidApp.SDK_VERSION > 4) { ActivityTransitionAnimation.slide(DeckPicker.this, ActivityTransitionAnimation.LEFT); } } private void openCardBrowser() { Intent cardBrowser = new Intent(DeckPicker.this, CardBrowser.class); cardBrowser.putExtra("fromDeckpicker", true); startActivityForResult(cardBrowser, BROWSE_CARDS); if (AnkiDroidApp.SDK_VERSION > 4) { ActivityTransitionAnimation.slide(DeckPicker.this, ActivityTransitionAnimation.LEFT); } } private boolean hasErrorFiles() { // for (String file : this.fileList()) { // if (file.endsWith(".stacktrace")) { // return true; // } // } return false; } private void showUpgradeScreen(boolean animation, int stage) { showUpgradeScreen(animation, stage, true); } private void showUpgradeScreen(boolean animation, int stage, boolean left) { Intent upgradeIntent = new Intent(this, Info.class); upgradeIntent.putExtra(Info.TYPE_EXTRA, Info.TYPE_UPGRADE_DECKS); upgradeIntent.putExtra(Info.TYPE_UPGRADE_STAGE, stage); startActivityForResult(upgradeIntent, SHOW_INFO_UPGRADE_DECKS); if (animation && AnkiDroidApp.SDK_VERSION > 4) { ActivityTransitionAnimation.slide(this, left ? ActivityTransitionAnimation.LEFT : ActivityTransitionAnimation.RIGHT); } } private boolean upgradeNeeded() { if (!AnkiDroidApp.isSdCardMounted()) { showDialog(DIALOG_SD_CARD_NOT_MOUNTED); return false; } File dir = new File(AnkiDroidApp.getCurrentAnkiDroidDirectory()); if (!dir.isDirectory()) { dir.mkdirs(); } if ((new File(AnkiDroidApp.getCollectionPath())).exists()) { // collection file exists return false; } if (dir.listFiles(new OldAnkiDeckFilter()).length > 0) { return true; } return false; } private void restartUpgradeProcess() { StyledDialog.Builder builder = new StyledDialog.Builder(DeckPicker.this); builder.setTitle(R.string.deck_upgrade_title); builder.setIcon(R.drawable.ic_dialog_alert); builder.setMessage(getString(R.string.deck_upgrade_rename_warning, UPGRADE_OLD_COLLECTION_RENAME)); builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { AnkiDroidApp.closeCollection(true); String path = AnkiDroidApp.getCurrentAnkiDroidDirectory(); File apkgExport = new File(path, UPGRADE_OLD_COLLECTION_RENAME); if (apkgExport.exists()) { // rename any existing oldcollection.apkg files oldcollection.apkgi, i = 1, 2, 3,... int i = 0; File altname; do { altname = new File(path, UPGRADE_OLD_COLLECTION_RENAME + i); ++i; } while (altname.exists()); apkgExport.renameTo(altname); } DeckTask.launchDeckTask(DeckTask.TASK_TYPE_EXPORT_APKG, mUpgradeExportListener, new DeckTask.TaskData(new Object[] { AnkiDroidApp.getCollectionPath(), apkgExport.getAbsolutePath(), false })); } }); builder.setCancelable(false); builder.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { AnkiDroidApp.getSharedPrefs(AnkiDroidApp.getInstance().getBaseContext()).edit() .putInt("lastUpgradeVersion", AnkiDroidApp.getPkgVersionCode()).commit(); } }); builder.show(); } private SharedPreferences restorePreferences() { SharedPreferences preferences = AnkiDroidApp.getSharedPrefs(getBaseContext()); mPrefDeckPath = AnkiDroidApp.getCurrentAnkiDroidDirectory(); mLastTimeOpened = preferences.getLong("lastTimeOpened", 0); mSwipeEnabled = AnkiDroidApp.initiateGestures(this, preferences); // mInvertedColors = preferences.getBoolean("invertedColors", false); // mSwap = preferences.getBoolean("swapqa", false); // mLocale = preferences.getString("language", ""); // setLanguage(mLocale); return preferences; } private void showStartupScreensAndDialogs(SharedPreferences preferences, int skip) { if (skip < 1 && preferences.getLong("lastTimeOpened", 0) == 0) { showStartupScreensAndDialogs(preferences, 2); // Intent infoIntent = new Intent(this, Info.class); // infoIntent.putExtra(Info.TYPE_EXTRA, Info.TYPE_WELCOME); // startActivityForResult(infoIntent, SHOW_INFO_WELCOME); // if (skip != 0 && AnkiDroidApp.SDK_VERSION > 4) { // ActivityTransitionAnimation.slide(this, ActivityTransitionAnimation.LEFT); // } } else if (skip < 2 && !preferences.getString("lastVersion", "").equals(AnkiDroidApp.getPkgVersionName())) { showStartupScreensAndDialogs(preferences, 2); // preferences.edit().putBoolean("showBroadcastMessageToday", true).commit(); // Intent infoIntent = new Intent(this, Info.class); // infoIntent.putExtra(Info.TYPE_EXTRA, Info.TYPE_NEW_VERSION); // startActivityForResult(infoIntent, SHOW_INFO_NEW_VERSION); // if (skip != 0 && AnkiDroidApp.SDK_VERSION > 4) { // ActivityTransitionAnimation.slide(this, ActivityTransitionAnimation.LEFT); // } } else if (skip < 3 && upgradeNeeded()) { // Note that the "upgrade needed" refers to upgrading Anki 1.x decks, not to newer // versions of AnkiDroid. // AnkiDroidApp.getSharedPrefs(AnkiDroidApp.getInstance().getBaseContext()) // .edit().putInt("lastUpgradeVersion", AnkiDroidApp.getPkgVersionCode()).commit(); // showUpgradeScreen(skip != 0, Info.UPGRADE_SCREEN_BASIC1); showStartupScreensAndDialogs(preferences, 2); } else if (skip < 4 && hasErrorFiles()) { showStartupScreensAndDialogs(preferences, 2); // HiChina // Intent i = new Intent(this, Feedback.class); // startActivityForResult(i, REPORT_ERROR); // if (skip != 0 && AnkiDroidApp.SDK_VERSION > 4) { // ActivityTransitionAnimation.slide(this, ActivityTransitionAnimation.LEFT); // } } else if (!AnkiDroidApp.isSdCardMounted()) { showDialog(DIALOG_SD_CARD_NOT_MOUNTED); } else if (!BackupManager.enoughDiscSpace(mPrefDeckPath)) {// && !preferences.getBoolean("dontShowLowMemory", // false)) { showDialog(DIALOG_NO_SPACE_LEFT); } else if (preferences.getBoolean("noSpaceLeft", false)) { showDialog(DIALOG_BACKUP_NO_SPACE_LEFT); preferences.edit().putBoolean("noSpaceLeft", false).commit(); } else if (mImportPath != null && AnkiDroidApp.colIsOpen()) { showDialog(DIALOG_IMPORT); } else { // AnkiDroid is being updated and a collection already exists. 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. int current = AnkiDroidApp.getPkgVersionCode(); int previous; if (!preferences.contains("lastUpgradeVersion")) { // Fresh install previous = current; } else { try { previous = preferences.getInt("lastUpgradeVersion", current); } catch (ClassCastException e) { // Previous versions stored this as a string. String s = preferences.getString("lastUpgradeVersion", ""); // The last version of AnkiDroid that stored this as a string was 2.0.2. // We manually set the version here, but anything older will force a DB // check. if (s.equals("2.0.2")) { previous = 40; } else { previous = 0; } } } preferences.edit().putInt("lastUpgradeVersion", current).commit(); if (previous < AnkiDroidApp.CHECK_DB_AT_VERSION) { DeckTask.launchDeckTask(DeckTask.TASK_TYPE_OPEN_COLLECTION, new Listener() { @Override public void onPostExecute(DeckTask task, TaskData result) { mOpenCollectionHandler.onPostExecute(result); integrityCheck(); } @Override public void onPreExecute(DeckTask task) { } @Override public void onProgressUpdate(DeckTask task, TaskData... values) { } }, new DeckTask.TaskData(AnkiDroidApp.getCollectionPath())); } else { loadCollection(); } } } protected void sendKey(int keycode) { this.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keycode)); this.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keycode)); } @Override protected void onPause() { // Log.i(AnkiDroidApp.TAG, "DeckPicker - onPause"); super.onPause(); } @Override protected void onStop() { // Log.i(AnkiDroidApp.TAG, "DeckPicker - onStop"); super.onStop(); EasyTracker.getInstance(this).activityStop(this); if (!mDontSaveOnStop) { if (isFinishing()) { DeckTask.launchDeckTask(DeckTask.TASK_TYPE_CLOSE_DECK, mCloseCollectionHandler, new TaskData(AnkiDroidApp.getCol())); } else { StudyOptionsFragment frag = getFragment(); if (!(frag != null && !frag.dbSaveNecessary())) { UIUtils.saveCollectionInBackground(); } } } } @Override protected void onDestroy() { super.onDestroy(); if (mUnmountReceiver != null) { unregisterReceiver(mUnmountReceiver); } // Log.i(AnkiDroidApp.TAG, "DeckPicker - onDestroy()"); } @Override protected Dialog onCreateDialog(int id) { StyledDialog dialog; Resources res = getResources(); StyledDialog.Builder builder = new StyledDialog.Builder(this); switch (id) { case DIALOG_OK: builder.setPositiveButton(R.string.ok, null); dialog = builder.create(); break; case DIALOG_NO_SDCARD: builder.setMessage("The SD card could not be read. Please, turn off USB storage."); builder.setPositiveButton(R.string.ok, null); dialog = builder.create(); break; case DIALOG_SELECT_HELP: builder.setTitle(res.getString(R.string.help_title)); builder.setItems(new String[] { res.getString(R.string.help_tutorial), res.getString(R.string.help_online), res.getString(R.string.help_faq) }, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface arg0, int arg1) { if (arg1 == 0) { createTutorialDeck(); } else { if (Utils.isIntentAvailable(DeckPicker.this, "android.intent.action.VIEW")) { Intent intent = new Intent("android.intent.action.VIEW", Uri.parse(getResources().getString( arg1 == 0 ? R.string.link_help : R.string.link_faq))); startActivity(intent); } else { startActivity(new Intent(DeckPicker.this, Info.class)); } } } }); dialog = builder.create(); break; case DIALOG_CONNECTION_ERROR: builder.setTitle(res.getString(R.string.connection_error_title)); builder.setIcon(R.drawable.ic_dialog_alert); builder.setMessage(res.getString(R.string.connection_error_message)); builder.setPositiveButton(res.getString(R.string.retry), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { sync(); } }); builder.setNegativeButton(res.getString(R.string.cancel), null); dialog = builder.create(); break; case DIALOG_SYNC_CONFLICT_RESOLUTION: builder.setTitle(res.getString(R.string.sync_conflict_title)); builder.setIcon(android.R.drawable.ic_input_get); builder.setMessage(res.getString(R.string.sync_conflict_message)); builder.setPositiveButton(res.getString(R.string.sync_conflict_local), mSyncConflictResolutionListener); builder.setNeutralButton(res.getString(R.string.sync_conflict_remote), mSyncConflictResolutionListener); builder.setNegativeButton(res.getString(R.string.sync_conflict_cancel), mSyncConflictResolutionListener); builder.setCancelable(true); dialog = builder.create(); break; case DIALOG_LOAD_FAILED: builder.setMessage(res.getString(R.string.open_collection_failed_message, BackupManager.BROKEN_DECKS_SUFFIX, res.getString(R.string.repair_deck))); builder.setTitle(R.string.open_collection_failed_title); builder.setIcon(R.drawable.ic_dialog_alert); builder.setPositiveButton(res.getString(R.string.error_handling_options), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { showDialog(DIALOG_ERROR_HANDLING); } }); builder.setNegativeButton(res.getString(R.string.close), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finishWithAnimation(); } }); builder.setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { finishWithAnimation(); } }); dialog = builder.create(); break; case DIALOG_DB_ERROR: builder.setMessage(R.string.answering_error_message); builder.setTitle(R.string.answering_error_title); builder.setIcon(R.drawable.ic_dialog_alert); builder.setPositiveButton(res.getString(R.string.error_handling_options), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { showDialog(DIALOG_ERROR_HANDLING); } }); builder.setNeutralButton(res.getString(R.string.answering_error_report), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent i = new Intent(DeckPicker.this, Feedback.class); i.putExtra("request", RESULT_DB_ERROR); dialog.dismiss(); startActivityForResult(i, REPORT_ERROR); if (AnkiDroidApp.SDK_VERSION > 4) { ActivityTransitionAnimation.slide(DeckPicker.this, ActivityTransitionAnimation.RIGHT); } } }); builder.setNegativeButton(res.getString(R.string.close), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (!AnkiDroidApp.colIsOpen()) { finishWithAnimation(); } } }); builder.setCancelable(true); dialog = builder.create(); break; case DIALOG_ERROR_HANDLING: builder.setTitle(res.getString(R.string.error_handling_title)); builder.setIcon(R.drawable.ic_dialog_alert); builder.setSingleChoiceItems(new String[] { "1" }, 0, null); builder.setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { if (mLoadFailed) { // dialog has been called because collection could not be opened showDialog(DIALOG_LOAD_FAILED); } else { // dialog has been called because a db error happened showDialog(DIALOG_DB_ERROR); } } }); builder.setNegativeButton(res.getString(R.string.cancel), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (mLoadFailed) { // dialog has been called because collection could not be opened showDialog(DIALOG_LOAD_FAILED); } else { // dialog has been called because a db error happened showDialog(DIALOG_DB_ERROR); } } }); dialog = builder.create(); break; case DIALOG_USER_NOT_LOGGED_IN_ADD_SHARED_DECK: builder.setTitle(res.getString(R.string.connection_error_title)); builder.setIcon(R.drawable.ic_dialog_alert); builder.setMessage(res.getString(R.string.no_user_password_error_message)); builder.setNegativeButton(res.getString(R.string.cancel), null); builder.setPositiveButton(res.getString(R.string.log_in), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent myAccount = new Intent(DeckPicker.this, MyAccount.class); myAccount.putExtra("notLoggedIn", true); startActivityForResult(myAccount, LOG_IN_FOR_SHARED_DECK); if (AnkiDroidApp.SDK_VERSION > 4) { ActivityTransitionAnimation.slide(DeckPicker.this, ActivityTransitionAnimation.FADE); } } }); dialog = builder.create(); break; case DIALOG_USER_NOT_LOGGED_IN_SYNC: builder.setTitle(res.getString(R.string.connection_error_title)); builder.setIcon(R.drawable.ic_dialog_alert); builder.setMessage(res.getString(R.string.no_user_password_error_message)); builder.setNegativeButton(res.getString(R.string.cancel), null); builder.setPositiveButton(res.getString(R.string.log_in), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent myAccount = new Intent(DeckPicker.this, MyAccount.class); myAccount.putExtra("notLoggedIn", true); startActivityForResult(myAccount, LOG_IN_FOR_SYNC); if (AnkiDroidApp.SDK_VERSION > 4) { ActivityTransitionAnimation.slide(DeckPicker.this, ActivityTransitionAnimation.FADE); } } }); dialog = builder.create(); break; // case DIALOG_USER_NOT_LOGGED_IN_DOWNLOAD: // if (id == DIALOG_USER_NOT_LOGGED_IN_SYNC) { // } else { // builder.setPositiveButton(res.getString(R.string.log_in), // new DialogInterface.OnClickListener() { // // @Override // public void onClick(DialogInterface dialog, int which) { // Intent myAccount = new Intent(DeckPicker.this, // MyAccount.class); // myAccount.putExtra("notLoggedIn", true); // startActivityForResult(myAccount, LOG_IN_FOR_DOWNLOAD); // if (UIUtils.getApiLevel() > 4) { // ActivityTransitionAnimation.slide(DeckPicker.this, ActivityTransitionAnimation.LEFT); // } // } // }); // } // builder.setNegativeButton(res.getString(R.string.cancel), null); // dialog = builder.create(); // break; case DIALOG_NO_CONNECTION: builder.setTitle(res.getString(R.string.connection_error_title)); builder.setIcon(R.drawable.ic_dialog_alert); builder.setMessage(res.getString(R.string.connection_needed)); builder.setPositiveButton(res.getString(R.string.ok), null); dialog = builder.create(); break; case DIALOG_DELETE_DECK: if (!AnkiDroidApp.colIsOpen() || mDeckList == null) { return null; } // Message is set in onPrepareDialog builder.setTitle(res.getString(R.string.delete_deck_title)); builder.setIcon(R.drawable.ic_dialog_alert); builder.setPositiveButton(res.getString(R.string.yes), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { DeckTask.launchDeckTask(DeckTask.TASK_TYPE_DELETE_DECK, new DeckTask.TaskListener() { @Override public void onPreExecute() { mProgressDialog = StyledProgressDialog.show(DeckPicker.this, "", getResources().getString(R.string.delete_deck), true); } @Override public void onPostExecute(TaskData result) { if (result == null) { return; } Object[] res = result.getObjArray(); updateDecksList((TreeSet<Object[]>) res[0], (Integer) res[1], (Integer) res[2]); if (mFragmented) { selectDeck(AnkiDroidApp.getCol().getDecks().selected()); } if (mProgressDialog.isShowing()) { try { mProgressDialog.dismiss(); } catch (Exception e) { Log.e(AnkiDroidApp.TAG, "onPostExecute - Dialog dismiss Exception = " + e.getMessage()); } } } @Override public void onProgressUpdate(TaskData... values) { } }, new TaskData(AnkiDroidApp.getCol(), mCurrentDid)); } }); builder.setNegativeButton(res.getString(R.string.cancel), null); dialog = builder.create(); break; case DIALOG_SELECT_STATISTICS_TYPE: dialog = ChartBuilder.getStatisticsDialog(this, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { boolean muh = mFragmented ? AnkiDroidApp.getSharedPrefs(AnkiDroidApp.getInstance().getBaseContext()).getBoolean( "statsRange", true) : true; DeckTask.launchDeckTask(DeckTask.TASK_TYPE_LOAD_STATISTICS, mLoadStatisticsHandler, new DeckTask.TaskData(AnkiDroidApp.getCol(), which, mFragmented ? AnkiDroidApp .getSharedPrefs(AnkiDroidApp.getInstance().getBaseContext()) .getBoolean("statsRange", true) : true)); } }, mFragmented); break; case DIALOG_CONTEXT_MENU: String[] entries = new String[1]; // entries[CONTEXT_MENU_DECK_SUMMARY] = // "XXXsum";//res.getStringArray(R.array.statistics_type_labels)[0]; // entries[CONTEXT_MENU_CUSTOM_DICTIONARY] = // res.getString(R.string.contextmenu_deckpicker_set_custom_dictionary); // entries[CONTEXT_MENU_RESET_LANGUAGE] = // res.getString(R.string.contextmenu_deckpicker_reset_language_assignments); // entries[CONTEXT_MENU_COLLAPSE_DECK] = res.getString(R.string.contextmenu_deckpicker_collapse_deck); // entries[CONTEXT_MENU_RENAME_DECK] = res.getString(R.string.contextmenu_deckpicker_rename_deck); entries[CONTEXT_MENU_DELETE_DECK] = res.getString(R.string.contextmenu_deckpicker_delete_deck); builder.setTitle("Context Menu"); builder.setIcon(R.drawable.ic_menu_manage); builder.setItems(entries, mContextMenuListener); dialog = builder.create(); break; case DIALOG_REPAIR_COLLECTION: builder.setTitle(res.getString(R.string.backup_repair_deck)); builder.setMessage(res.getString(R.string.repair_deck_dialog, BackupManager.BROKEN_DECKS_SUFFIX)); builder.setIcon(R.drawable.ic_dialog_alert); builder.setPositiveButton(res.getString(R.string.yes), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { DeckTask.launchDeckTask(DeckTask.TASK_TYPE_REPAIR_DECK, mRepairDeckHandler, new DeckTask.TaskData(AnkiDroidApp.getCol(), AnkiDroidApp.getCollectionPath())); } }); builder.setNegativeButton(res.getString(R.string.no), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { showDialog(DIALOG_ERROR_HANDLING); } }); builder.setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface arg0) { showDialog(DIALOG_ERROR_HANDLING); } }); dialog = builder.create(); break; case DIALOG_SYNC_SANITY_ERROR: builder.setPositiveButton(getString(R.string.sync_sanity_local), mSyncSanityFailListener); builder.setNeutralButton(getString(R.string.sync_sanity_remote), mSyncSanityFailListener); builder.setNegativeButton(res.getString(R.string.sync_conflict_cancel), mSyncSanityFailListener); builder.setTitle(res.getString(R.string.sync_log_title)); dialog = builder.create(); break; case DIALOG_SYNC_UPGRADE_REQUIRED: builder.setMessage(res.getString(R.string.upgrade_required, res.getString(R.string.link_anki))); builder.setPositiveButton(res.getString(R.string.retry), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { sync("download", mSyncMediaUsn); } }); builder.setNegativeButton(res.getString(R.string.cancel), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (mLoadFailed) { // dialog has been called because collection could not be opened showDialog(DIALOG_LOAD_FAILED); } else { // dialog has been called because a db error happened showDialog(DIALOG_DB_ERROR); } } }); builder.setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface arg0) { if (mLoadFailed) { // dialog has been called because collection could not be opened showDialog(DIALOG_LOAD_FAILED); } else { // dialog has been called because a db error happened showDialog(DIALOG_DB_ERROR); } } }); builder.setTitle(res.getString(R.string.sync_log_title)); dialog = builder.create(); break; case DIALOG_SYNC_LOG: builder.setTitle(res.getString(R.string.sync_log_title)); builder.setPositiveButton(res.getString(R.string.ok), null); dialog = builder.create(); break; case DIALOG_BACKUP_NO_SPACE_LEFT: builder.setTitle(res.getString(R.string.attention)); builder.setMessage(res.getString(R.string.backup_deck_no_space_left)); builder.setPositiveButton(res.getString(R.string.ok), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { loadCollection(); } }); // builder.setNegativeButton(res.getString(R.string.dont_show_again), new // DialogInterface.OnClickListener() { // @Override // public void onClick(DialogInterface arg0, int arg1) { // PrefSettings.getSharedPrefs(getBaseContext()).edit().putBoolean("dontShowLowMemory", true).commit(); // } // }); builder.setCancelable(true); builder.setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface arg0) { loadCollection(); } }); dialog = builder.create(); break; case DIALOG_SD_CARD_NOT_MOUNTED: if (mNotMountedDialog == null || !mNotMountedDialog.isShowing()) { mNotMountedDialog = StyledOpenCollectionDialog.show(DeckPicker.this, getResources().getString(R.string.sd_card_not_mounted), new OnCancelListener() { @Override public void onCancel(DialogInterface arg0) { finishWithAnimation(); } }, new View.OnClickListener() { @Override public void onClick(View v) { startActivityForResult(new Intent(DeckPicker.this, Preferences.class), PREFERENCES_UPDATE); } }); } dialog = null; break; case DIALOG_IMPORT: builder.setTitle(res.getString(R.string.import_title)); builder.setMessage(res.getString(R.string.import_message, mImportPath)); builder.setPositiveButton(res.getString(R.string.import_message_add), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { DeckTask.launchDeckTask(DeckTask.TASK_TYPE_IMPORT, mImportAddListener, new TaskData(AnkiDroidApp.getCol(), mImportPath, false)); mImportPath = null; } }); builder.setNeutralButton(res.getString(R.string.import_message_replace), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Resources res = getResources(); StyledDialog.Builder builder = new StyledDialog.Builder(DeckPicker.this); builder.setTitle(res.getString(R.string.import_title)); builder.setMessage(res.getString(R.string.import_message_replace_confirm, mImportPath)); builder.setPositiveButton(res.getString(R.string.yes), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { DeckTask.launchDeckTask(DeckTask.TASK_TYPE_IMPORT_REPLACE, mImportReplaceListener, new TaskData(AnkiDroidApp.getCol(), mImportPath)); mImportPath = null; } }); builder.setNegativeButton(res.getString(R.string.no), null); builder.show(); } }); builder.setNegativeButton(res.getString(R.string.cancel), null); builder.setCancelable(true); dialog = builder.create(); break; case DIALOG_IMPORT_SELECT: builder.setTitle(res.getString(R.string.import_title)); dialog = builder.create(); break; case DIALOG_IMPORT_HINT: builder.setTitle(res.getString(R.string.import_title)); builder.setMessage(res.getString(R.string.import_hint, AnkiDroidApp.getCurrentAnkiDroidDirectory())); builder.setPositiveButton(res.getString(R.string.ok), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { showDialog(DIALOG_IMPORT_SELECT); } }); builder.setNegativeButton(res.getString(R.string.cancel), null); dialog = builder.create(); break; case DIALOG_IMPORT_LOG: builder.setIcon(R.drawable.ic_dialog_alert); builder.setTitle(res.getString(R.string.import_title)); builder.setPositiveButton(res.getString(R.string.ok), null); dialog = builder.create(); break; case DIALOG_NO_SPACE_LEFT: builder.setTitle(res.getString(R.string.attention)); builder.setMessage(res.getString(R.string.sd_space_warning, BackupManager.MIN_FREE_SPACE)); builder.setPositiveButton(res.getString(R.string.ok), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finishWithAnimation(); } }); // builder.setNegativeButton(res.getString(R.string.dont_show_again), new // DialogInterface.OnClickListener() { // @Override // public void onClick(DialogInterface arg0, int arg1) { // PrefSettings.getSharedPrefs(getBaseContext()).edit().putBoolean("dontShowLowMemory", true).commit(); // } // }); builder.setCancelable(true); builder.setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface arg0) { finishWithAnimation(); } }); dialog = builder.create(); break; case DIALOG_RESTORE_BACKUP: File[] files = BackupManager.getBackups(new File(AnkiDroidApp.getCollectionPath())); mBackups = new File[files.length]; for (int i = 0; i < files.length; i++) { mBackups[i] = files[files.length - 1 - i]; } if (mBackups.length == 0) { builder.setTitle(getResources().getString(R.string.backup_restore)); builder.setMessage(res.getString(R.string.backup_restore_no_backups)); builder.setPositiveButton(res.getString(R.string.ok), new Dialog.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { showDialog(DIALOG_ERROR_HANDLING); } }); builder.setCancelable(true).setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface arg0) { showDialog(DIALOG_ERROR_HANDLING); } }); } else { String[] dates = new String[mBackups.length]; for (int i = 0; i < mBackups.length; i++) { dates[i] = mBackups[i].getName().replaceAll(".*-(\\d{4}-\\d{2}-\\d{2})-(\\d{2})-(\\d{2}).anki2", "$1 ($2:$3 h)"); } builder.setTitle(res.getString(R.string.backup_restore_select_title)); builder.setIcon(android.R.drawable.ic_input_get); builder.setSingleChoiceItems(dates, dates.length, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { DeckTask.launchDeckTask(DeckTask.TASK_TYPE_RESTORE_DECK, mRestoreDeckHandler, new DeckTask.TaskData(new Object[] { AnkiDroidApp.getCol(), AnkiDroidApp.getCollectionPath(), mBackups[which].getPath() })); } }); builder.setCancelable(true).setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface arg0) { showDialog(DIALOG_ERROR_HANDLING); } }); } dialog = builder.create(); break; case DIALOG_NEW_COLLECTION: builder.setTitle(res.getString(R.string.backup_new_collection)); builder.setMessage(res.getString(R.string.backup_del_collection_question)); builder.setPositiveButton(res.getString(R.string.ok), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { AnkiDroidApp.closeCollection(false); String path = AnkiDroidApp.getCollectionPath(); AnkiDatabaseManager.closeDatabase(path); if (BackupManager.moveDatabaseToBrokenFolder(path, false)) { loadCollection(); } else { showDialog(DIALOG_ERROR_HANDLING); } } }); builder.setNegativeButton(res.getString(R.string.no), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface arg0, int arg1) { showDialog(DIALOG_ERROR_HANDLING); } }); builder.setCancelable(true); builder.setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface arg0) { showDialog(DIALOG_ERROR_HANDLING); } }); dialog = builder.create(); break; case DIALOG_FULL_SYNC_FROM_SERVER: builder.setTitle(res.getString(R.string.backup_full_sync_from_server)); builder.setMessage(res.getString(R.string.backup_full_sync_from_server_question)); builder.setPositiveButton(res.getString(R.string.ok), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { sync("download", mSyncMediaUsn); } }); builder.setNegativeButton(res.getString(R.string.no), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface arg0, int arg1) { showDialog(DIALOG_ERROR_HANDLING); } }); builder.setCancelable(true); builder.setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface arg0) { showDialog(DIALOG_ERROR_HANDLING); } }); dialog = builder.create(); break; default: dialog = null; } if (dialog != null) { dialog.setOwnerActivity(this); } return dialog; } @Override protected void onPrepareDialog(int id, Dialog dialog) { Resources res = getResources(); StyledDialog ad = (StyledDialog) dialog; switch (id) { case DIALOG_DELETE_DECK: if (!AnkiDroidApp.colIsOpen() || mDeckList == null || mDeckList.size() == 0) { return; } boolean isDyn = AnkiDroidApp.getCol().getDecks().isDyn(mCurrentDid); if (isDyn) { ad.setMessage(String.format(res.getString(R.string.delete_cram_deck_message), "\'" + AnkiDroidApp.getCol().getDecks().name(mCurrentDid) + "\'")); } else { ad.setMessage(String.format(res.getString(R.string.delete_deck_message), "\'" + AnkiDroidApp.getCol().getDecks().name(mCurrentDid) + "\'")); } break; case DIALOG_CONTEXT_MENU: if (!AnkiDroidApp.colIsOpen() || mDeckList == null || mDeckList.size() == 0) { return; } mCurrentDid = Long.parseLong(mDeckList.get(mContextMenuPosition).get("did")); // try { // ad.changeListItem(CONTEXT_MENU_COLLAPSE_DECK, getResources().getString(AnkiDroidApp.getCol().getDecks().get(mCurrentDid).getBoolean("collapsed") ? R.string.contextmenu_deckpicker_inflate_deck : R.string.contextmenu_deckpicker_collapse_deck)); // } catch (NotFoundException e) { // // do nothing // } catch (JSONException e) { // // do nothing // } ad.setTitle(AnkiDroidApp.getCol().getDecks().name(mCurrentDid)); break; case DIALOG_IMPORT_LOG: case DIALOG_SYNC_LOG: case DIALOG_SYNC_SANITY_ERROR: // If both have text, separate them by a new line. if (!TextUtils.isEmpty(mDialogMessage) && !TextUtils.isEmpty(mSyncMessage)) { ad.setMessage(mDialogMessage + "\n\n" + mSyncMessage); } else if (!TextUtils.isEmpty(mDialogMessage)) { ad.setMessage(mDialogMessage); } else { ad.setMessage(mSyncMessage); } break; case DIALOG_DB_ERROR: mLoadFailed = false; ad.getButton(Dialog.BUTTON3).setEnabled(hasErrorFiles()); break; case DIALOG_LOAD_FAILED: mLoadFailed = true; if (mOpenCollectionDialog != null && mOpenCollectionDialog.isShowing()) { mOpenCollectionDialog.setMessage(res.getString(R.string.col_load_failed)); } break; case DIALOG_ERROR_HANDLING: ArrayList<String> options = new ArrayList<String>(); ArrayList<Integer> values = new ArrayList<Integer>(); if (AnkiDroidApp.getCol() == null) { // retry options.add(res.getString(R.string.backup_retry_opening)); values.add(0); } else { // fix integrity options.add(res.getString(R.string.check_db)); values.add(1); } // repair db with sqlite options.add(res.getString(R.string.backup_error_menu_repair)); values.add(2); // // restore from backup options.add(res.getString(R.string.backup_restore)); values.add(3); // delete old collection and build new one options.add(res.getString(R.string.backup_full_sync_from_server)); values.add(4); // delete old collection and build new one options.add(res.getString(R.string.backup_del_collection)); values.add(5); String[] titles = new String[options.size()]; mRepairValues = new int[options.size()]; for (int i = 0; i < options.size(); i++) { titles[i] = options.get(i); mRepairValues[i] = values.get(i); } ad.setItems(titles, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch (mRepairValues[which]) { case 0: loadCollection(); return; case 1: integrityCheck(); return; case 2: showDialog(DIALOG_REPAIR_COLLECTION); return; case 3: showDialog(DIALOG_RESTORE_BACKUP); return; case 4: showDialog(DIALOG_FULL_SYNC_FROM_SERVER); return; case 5: showDialog(DIALOG_NEW_COLLECTION); return; } } }); break; case DIALOG_IMPORT_SELECT: List<File> fileList = Utils.getImportableDecks(); if (fileList.size() == 0) { Themes.showThemedToast(DeckPicker.this, getResources().getString(R.string.upgrade_import_no_file_found), false); } ad.setEnabled(fileList.size() != 0); String[] tts = new String[fileList.size()]; mImportValues = new String[fileList.size()]; for (int i = 0; i < tts.length; i++) { tts[i] = fileList.get(i).getName().replace(".apkg", ""); mImportValues[i] = fileList.get(i).getAbsolutePath(); } ad.setItems(tts, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mImportPath = mImportValues[which]; switch (mImportMethod) { case IMPORT_METHOD_ADD: DeckTask.launchDeckTask(DeckTask.TASK_TYPE_IMPORT, mImportAddListener, new TaskData(AnkiDroidApp.getCol(), mImportPath, false)); mImportPath = null; break; case IMPORT_METHOD_REPLACE: DeckTask.launchDeckTask(DeckTask.TASK_TYPE_IMPORT_REPLACE, mImportReplaceListener, new TaskData(AnkiDroidApp.getCol(), mImportPath)); mImportPath = null; break; case IMPORT_METHOD_ASK: default: showDialog(DIALOG_IMPORT); } mImportMethod = IMPORT_METHOD_ASK; } }); break; } } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { mContextMenuPosition = ((AdapterView.AdapterContextMenuInfo) menuInfo).position; showDialog(DIALOG_CONTEXT_MENU); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) { // Log.i(AnkiDroidApp.TAG, "DeckPicker - onBackPressed()"); if (mFragmented && getFragment().congratsShowing()) { getFragment().finishCongrats(); return true; } finishWithAnimation(); return true; } return super.onKeyDown(keyCode, event); } private void finishWithAnimation() { finish(); if (AnkiDroidApp.SDK_VERSION > 4) { ActivityTransitionAnimation.slide(this, ActivityTransitionAnimation.DIALOG_EXIT); } } // ---------------------------------------------------------------------------- // CUSTOM METHODS // ---------------------------------------------------------------------------- public void setStudyContentView(long deckId, Bundle cramConfig) { StudyOptionsFragment details = StudyOptionsFragment.newInstance(deckId, false, cramConfig); FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); // ft.setCustomAnimations(R.anim.fade_in, R.anim.fade_out); ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE); ft.replace(R.id.studyoptions_fragment, details); ft.commit(); } public StudyOptionsFragment getFragment() { Fragment frag = (Fragment) getSupportFragmentManager().findFragmentById(R.id.studyoptions_fragment); if (frag != null && (frag instanceof StudyOptionsFragment)) { return (StudyOptionsFragment) frag; } return null; } /** * Show/dismiss dialog when sd card is ejected/remounted (collection is saved by SdCardReceiver) */ private void registerExternalStorageListener() { if (mUnmountReceiver == null) { mUnmountReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (AnkiDroidApp.getSharedPrefs(getBaseContext()).getBoolean("internalMemory", false)) { return; } if (intent.getAction().equals(SdCardReceiver.MEDIA_EJECT)) { showDialog(DIALOG_SD_CARD_NOT_MOUNTED); } else if (intent.getAction().equals(SdCardReceiver.MEDIA_MOUNT)) { if (mNotMountedDialog != null && mNotMountedDialog.isShowing()) { mNotMountedDialog.dismiss(); } loadCollection(); } } }; IntentFilter iFilter = new IntentFilter(); iFilter.addAction(SdCardReceiver.MEDIA_EJECT); iFilter.addAction(SdCardReceiver.MEDIA_MOUNT); registerReceiver(mUnmountReceiver, iFilter); } } /** * Creates an intent to load a deck given the full pathname of it. The constructed intent is equivalent (modulo the * extras) to the open used by the launcher shortcut, which means it will not open a new study options window but * bring the existing one to the front. */ public static Intent getLoadDeckIntent(Context context, long deckId) { Intent loadDeckIntent = new Intent(context, DeckPicker.class); loadDeckIntent.setAction(Intent.ACTION_MAIN); loadDeckIntent.addCategory(Intent.CATEGORY_LAUNCHER); loadDeckIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); loadDeckIntent.putExtra(EXTRA_DECK_ID, deckId); return loadDeckIntent; } // private void handleRestoreDecks(boolean reloadIfEmpty) { // if (mBrokenDecks.size() != 0) { // while (true) { // mCurrentDeckPath = mBrokenDecks.remove(0); // if (!mAlreadyDealtWith.contains(mCurrentDeckPath) || mBrokenDecks.size() == 0) { // break; // } // } // mDeckNotLoadedAlert.setMessage(getResources().getString(R.string.open_deck_failed, "\'" + new // File(mCurrentDeckPath).getName() + "\'", BackupManager.BROKEN_DECKS_SUFFIX.replace("/", ""), // getResources().getString(R.string.repair_deck))); // mDeckNotLoadedAlert.show(); // } else if (reloadIfEmpty) { // if (mRestoredOrDeleted) { // mBrokenDecks = new ArrayList<String>(); // // populateDeckList(mPrefDeckPath); // } // } // } private void sync() { sync(null, 0); } /** * The mother of all syncing attempts. This might be called from sync() as first attempt to sync a collection OR * from the mSyncConflictResolutionListener if the first attempt determines that a full-sync is required. In the * second case, we have passed the mediaUsn that was obtained during the first attempt. * * @param syncConflictResolution Either "upload" or "download", depending on the user's choice. * @param syncMediaUsn The media Usn, as determined during the prior sync() attempt that determined that full * syncing was required. */ private void sync(String syncConflictResolution, int syncMediaUsn) { SharedPreferences preferences = AnkiDroidApp.getSharedPrefs(getBaseContext()); String hkey = preferences.getString("hkey", ""); if (hkey.length() == 0) { showDialog(DIALOG_USER_NOT_LOGGED_IN_SYNC); } else { Connection.sync(mSyncListener, new Connection.Payload(new Object[] { hkey, preferences.getBoolean("syncFetchesMedia", true), syncConflictResolution, syncMediaUsn })); } } private void addSharedDeck() { Intent intent = new Intent(DeckPicker.this, Info.class); intent.putExtra(Info.TYPE_EXTRA, Info.TYPE_SHARED_DECKS); startActivityForResult(intent, ADD_SHARED_DECKS); if (AnkiDroidApp.SDK_VERSION > 4) { ActivityTransitionAnimation.slide(DeckPicker.this, ActivityTransitionAnimation.RIGHT); } } private DialogInterface.OnClickListener mSyncSanityFailListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Resources res = getResources(); StyledDialog.Builder builder; switch (which) { case DialogInterface.BUTTON_POSITIVE: builder = new StyledDialog.Builder(DeckPicker.this); builder.setPositiveButton(res.getString(R.string.yes), new Dialog.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Collection col = AnkiDroidApp.getCol(); if (col != null) { col.modSchema(true); col.setMod(); sync("upload", 0); } } }); builder.setNegativeButton(res.getString(R.string.no), null); builder.setTitle(res.getString(R.string.sync_log_title)); builder.setMessage(res.getString(R.string.sync_conflict_local_confirm)); builder.show(); break; case DialogInterface.BUTTON_NEUTRAL: builder = new StyledDialog.Builder(DeckPicker.this); builder.setPositiveButton(res.getString(R.string.yes), new Dialog.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Collection col = AnkiDroidApp.getCol(); if (col != null) { col.modSchema(true); col.setMod(); sync("download", 0); } } }); builder.setNegativeButton(res.getString(R.string.no), null); builder.setTitle(res.getString(R.string.sync_log_title)); builder.setMessage(res.getString(R.string.sync_conflict_remote_confirm)); builder.show(); break; case DialogInterface.BUTTON_NEGATIVE: default: } } }; private DialogInterface.OnClickListener mSyncConflictResolutionListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Resources res = getResources(); StyledDialog.Builder builder; switch (which) { case DialogInterface.BUTTON_POSITIVE: builder = new StyledDialog.Builder(DeckPicker.this); builder.setPositiveButton(res.getString(R.string.yes), new Dialog.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { sync("upload", mSyncMediaUsn); } }); builder.setNegativeButton(res.getString(R.string.no), null); builder.setTitle(res.getString(R.string.sync_log_title)); builder.setMessage(res.getString(R.string.sync_conflict_local_confirm)); builder.show(); break; case DialogInterface.BUTTON_NEUTRAL: builder = new StyledDialog.Builder(DeckPicker.this); builder.setPositiveButton(res.getString(R.string.yes), new Dialog.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { sync("download", mSyncMediaUsn); } }); builder.setNegativeButton(res.getString(R.string.no), null); builder.setTitle(res.getString(R.string.sync_log_title)); builder.setMessage(res.getString(R.string.sync_conflict_remote_confirm)); builder.show(); break; case DialogInterface.BUTTON_NEGATIVE: default: } } }; @Override public boolean onCreateOptionsMenu(Menu menu) { MenuItem item; if (mFragmented) { // UIUtils.addMenuItemInActionBar(menu, Menu.NONE, MENU_SYNC, Menu.NONE, R.string.sync_title, // R.drawable.ic_menu_refresh); // // UIUtils.addMenuItemInActionBar(menu, Menu.NONE, MENU_ADD_NOTE, Menu.NONE, R.string.add, // R.drawable.ic_menu_add); // int icon; // SharedPreferences preferences = AnkiDroidApp.getSharedPrefs(this); // if (preferences.getBoolean("invertedColors", false)) { // icon = R.drawable.ic_menu_night_checked; // } else { // icon = R.drawable.ic_menu_night; // } // UIUtils.addMenuItemInActionBar(menu, Menu.NONE, StudyOptionsActivity.MENU_NIGHT, Menu.NONE, R.string.night_mode, // icon); // UIUtils.addMenuItemInActionBar(menu, Menu.NONE, MENU_STATISTICS, Menu.NONE, R.string.statistics_menu, // R.drawable.ic_menu_statistics); // // UIUtils.addMenuItemInActionBar(menu, Menu.NONE, MENU_CARDBROWSER, Menu.NONE, R.string.menu_cardbrowser, // R.drawable.ic_menu_cardbrowser); } // UIUtils.addMenuItemInActionBar(menu, Menu.NONE, MENU_HELP, Menu.NONE, R.string.help_title, // R.drawable.ic_menu_help); // // item = menu.add(Menu.NONE, MENU_PREFERENCES, Menu.NONE, R.string.menu_preferences); // item.setIcon(R.drawable.ic_menu_preferences); // item = menu.add(Menu.NONE, MENU_ADD_SHARED_DECK, Menu.NONE, R.string.menu_get_shared_decks); // item.setIcon(R.drawable.ic_menu_download); // item = menu.add(Menu.NONE, MENU_CREATE_DECK, Menu.NONE, R.string.new_deck); // item.setIcon(R.drawable.ic_menu_add); // item = menu.add(Menu.NONE, MENU_CREATE_DYNAMIC_DECK, Menu.NONE, R.string.new_dynamic_deck); // item.setIcon(R.drawable.ic_menu_add); // item = menu.add(Menu.NONE, MENU_IMPORT, Menu.NONE, R.string.menu_import); // item.setIcon(R.drawable.ic_menu_download); // UIUtils.addMenuItem(menu, Menu.NONE, CHECK_DATABASE, Menu.NONE, R.string.check_db, R.drawable.ic_menu_search); // item = menu.add(Menu.NONE, StudyOptionsActivity.MENU_ROTATE, Menu.NONE, R.string.menu_rotate); // item.setIcon(R.drawable.ic_menu_always_landscape_portrait); // item = menu.add(Menu.NONE, MENU_FEEDBACK, Menu.NONE, R.string.studyoptions_feedback); // item.setIcon(R.drawable.ic_menu_send); // item = menu.add(Menu.NONE, MENU_ABOUT, Menu.NONE, R.string.menu_about); // item.setIcon(R.drawable.ic_menu_info_details); // item = menu.add(Menu.NONE, MENU_REUPGRADE, Menu.NONE, R.string.restart_upgrade_process); // item.setIcon(R.drawable.ic_menu_preferences); return true; } @Override public boolean onMenuOpened(int featureId, Menu menu) { if (mInvalidateMenu) { if (menu != null) { menu.clear(); onCreateOptionsMenu(menu); } mInvalidateMenu = false; } return super.onMenuOpened(featureId, menu); } @Override public boolean onPrepareOptionsMenu(Menu menu) { boolean sdCardAvailable = AnkiDroidApp.isSdCardMounted(); if (mFragmented) { menu.findItem(MENU_SYNC).setEnabled(sdCardAvailable); menu.findItem(MENU_ADD_NOTE).setEnabled(sdCardAvailable); menu.findItem(MENU_STATISTICS).setEnabled(sdCardAvailable); menu.findItem(MENU_CARDBROWSER).setEnabled(sdCardAvailable); } // menu.findItem(MENU_CREATE_DECK).setEnabled(sdCardAvailable); // menu.findItem(MENU_CREATE_DYNAMIC_DECK).setEnabled(sdCardAvailable); // menu.findItem(CHECK_DATABASE).setEnabled(sdCardAvailable); return true; } /** Handles item selections */ @Override public boolean onOptionsItemSelected(MenuItem item) { Resources res = getResources(); switch (item.getItemId()) { case MENU_HELP: showDialog(DIALOG_SELECT_HELP); return true; case MENU_SYNC: sync(); return true; case MENU_ADD_NOTE: addNote(); return true; case MENU_STATISTICS: showDialog(DIALOG_SELECT_STATISTICS_TYPE); return true; case MENU_CARDBROWSER: openCardBrowser(); return true; case MENU_CREATE_DECK: StyledDialog.Builder builder2 = new StyledDialog.Builder(DeckPicker.this); builder2.setTitle(res.getString(R.string.new_deck)); mDialogEditText = (EditText) new EditText(DeckPicker.this); // mDialogEditText.setFilters(new InputFilter[] { mDeckNameFilter }); builder2.setView(mDialogEditText, false, false); builder2.setPositiveButton(res.getString(R.string.create), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { String deckName = mDialogEditText.getText().toString().replaceAll("[\'\"\\n\\r\\[\\]\\(\\)]", ""); // Log.i(AnkiDroidApp.TAG, "Creating deck: " + deckName); AnkiDroidApp.getCol().getDecks().id(deckName, true); loadCounts(); } }); builder2.setNegativeButton(res.getString(R.string.cancel), null); builder2.create().show(); return true; case MENU_CREATE_DYNAMIC_DECK: StyledDialog.Builder builder3 = new StyledDialog.Builder(DeckPicker.this); builder3.setTitle(res.getString(R.string.new_deck)); mDialogEditText = (EditText) new EditText(DeckPicker.this); ArrayList<String> names = AnkiDroidApp.getCol().getDecks().allNames(); int n = 1; String cramDeckName = "Cram 1"; while (names.contains(cramDeckName)) { n++; cramDeckName = "Cram " + n; } mDialogEditText.setText(cramDeckName); // mDialogEditText.setFilters(new InputFilter[] { mDeckNameFilter }); builder3.setView(mDialogEditText, false, false); builder3.setPositiveButton(res.getString(R.string.create), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { long id = AnkiDroidApp.getCol().getDecks().newDyn(mDialogEditText.getText().toString()); openStudyOptions(id, new Bundle()); } }); builder3.setNegativeButton(res.getString(R.string.cancel), null); builder3.create().show(); return true; case MENU_ABOUT: startActivity(new Intent(DeckPicker.this, Info.class)); if (AnkiDroidApp.SDK_VERSION > 4) { ActivityTransitionAnimation.slide(DeckPicker.this, ActivityTransitionAnimation.RIGHT); } return true; case MENU_ADD_SHARED_DECK: if (AnkiDroidApp.getCol() != null) { SharedPreferences preferences = AnkiDroidApp.getSharedPrefs(getBaseContext()); String hkey = preferences.getString("hkey", ""); if (hkey.length() == 0) { showDialog(DIALOG_USER_NOT_LOGGED_IN_ADD_SHARED_DECK); } else { addSharedDeck(); } } return true; case MENU_IMPORT: showDialog(DIALOG_IMPORT_HINT); return true; case MENU_PREFERENCES: startActivityForResult(new Intent(DeckPicker.this, Preferences.class), PREFERENCES_UPDATE); return true; case MENU_FEEDBACK: Intent i = new Intent(DeckPicker.this, Feedback.class); i.putExtra("request", REPORT_FEEDBACK); startActivityForResult(i, REPORT_FEEDBACK); if (AnkiDroidApp.SDK_VERSION > 4) { ActivityTransitionAnimation.slide(this, ActivityTransitionAnimation.RIGHT); } return true; case CHECK_DATABASE: integrityCheck(); return true; case StudyOptionsActivity.MENU_ROTATE: if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } else { this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } return true; case StudyOptionsActivity.MENU_NIGHT: SharedPreferences preferences = AnkiDroidApp.getSharedPrefs(this); if (preferences.getBoolean("invertedColors", false)) { preferences.edit().putBoolean("invertedColors", false).commit(); item.setIcon(R.drawable.ic_menu_night); } else { preferences.edit().putBoolean("invertedColors", true).commit(); item.setIcon(R.drawable.ic_menu_night_checked); } return true; case MENU_REUPGRADE: restartUpgradeProcess(); return true; default: return super.onOptionsItemSelected(item); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent); mDontSaveOnStop = false; if (resultCode == RESULT_MEDIA_EJECTED) { showDialog(DIALOG_SD_CARD_NOT_MOUNTED); return; } else if (resultCode == RESULT_DB_ERROR) { handleDbError(); return; } if (requestCode == SHOW_STUDYOPTIONS && resultCode == RESULT_OK) { loadCounts(); } else if (requestCode == ADD_NOTE && resultCode != RESULT_CANCELED) { loadCounts(); } else if (requestCode == BROWSE_CARDS && (resultCode == Activity.RESULT_OK || resultCode == Activity.RESULT_CANCELED)) { loadCounts(); } else if (requestCode == ADD_CRAM_DECK) { // TODO: check, if ok has been clicked loadCounts(); } else if (requestCode == REPORT_ERROR) { showStartupScreensAndDialogs(AnkiDroidApp.getSharedPrefs(getBaseContext()), 4); } else if (requestCode == SHOW_INFO_UPGRADE_DECKS) { if (intent != null && intent.hasExtra(Info.TYPE_UPGRADE_STAGE)) { int type = intent.getIntExtra(Info.TYPE_UPGRADE_STAGE, Info.UPGRADE_SCREEN_BASIC1); if (type == Info.UPGRADE_CONTINUE) { showStartupScreensAndDialogs(AnkiDroidApp.getSharedPrefs(getBaseContext()), 3); } else { showUpgradeScreen(true, type, !intent.hasExtra(Info.TYPE_ANIMATION_RIGHT)); } } else { if (resultCode == RESULT_OK) { if (mOpenCollectionDialog != null && mOpenCollectionDialog.isShowing()) { mOpenCollectionDialog.dismiss(); } if (AnkiDroidApp.colIsOpen()) { AnkiDroidApp.closeCollection(true); } AnkiDroidApp.openCollection(AnkiDroidApp.getCollectionPath()); loadCounts(); } else { finishWithAnimation(); } } } else if (requestCode == SHOW_INFO_WELCOME || requestCode == SHOW_INFO_NEW_VERSION) { if (resultCode == RESULT_OK) { showStartupScreensAndDialogs(AnkiDroidApp.getSharedPrefs(getBaseContext()), requestCode == SHOW_INFO_WELCOME ? 1 : 2); } else { finishWithAnimation(); } } else if (requestCode == PREFERENCES_UPDATE) { String oldPath = mPrefDeckPath; SharedPreferences pref = restorePreferences(); String newLanguage = pref.getString("language", ""); if (AnkiDroidApp.setLanguage(newLanguage)) { mInvalidateMenu = true; } if (mNotMountedDialog != null && mNotMountedDialog.isShowing() && pref.getBoolean("internalMemory", false)) { showStartupScreensAndDialogs(pref, 0); } else if (!mPrefDeckPath.equals(oldPath)) { loadCollection(); } // if (resultCode == StudyOptions.RESULT_RESTART) { // setResult(StudyOptions.RESULT_RESTART); // finishWithAnimation(); // } else { // SharedPreferences preferences = PrefSettings.getSharedPrefs(getBaseContext()); // BackupManager.initBackup(); // if (!mPrefDeckPath.equals(preferences.getString("deckPath", AnkiDroidApp.getStorageDirectory())) || // mPrefDeckOrder != Integer.parseInt(preferences.getString("deckOrder", "0"))) { // // populateDeckList(preferences.getString("deckPath", AnkiDroidApp.getStorageDirectory())); // } // } } else if (requestCode == REPORT_FEEDBACK && resultCode == RESULT_OK) { } else if (requestCode == LOG_IN_FOR_SYNC && resultCode == RESULT_OK) { sync(); } else if (requestCode == LOG_IN_FOR_SHARED_DECK && resultCode == RESULT_OK) { addSharedDeck(); } else if (requestCode == ADD_SHARED_DECKS) { if (intent != null) { mImportPath = intent.getStringExtra("importPath"); } if (AnkiDroidApp.colIsOpen() && mImportPath != null) { DeckTask.launchDeckTask(DeckTask.TASK_TYPE_IMPORT, mImportAddListener, new TaskData(AnkiDroidApp.getCol(), mImportPath, true)); mImportPath = null; } } else if (requestCode == REQUEST_REVIEW) { // Log.i(AnkiDroidApp.TAG, "Result code = " + resultCode); switch (resultCode) { default: // do not reload counts, if activity is created anew because it has been before destroyed by android loadCounts(); break; case Reviewer.RESULT_NO_MORE_CARDS: mDontSaveOnStop = true; Intent i = new Intent(); i.setClass(this, StudyOptionsActivity.class); i.putExtra("onlyFnsMsg", true); startActivityForResult(i, SHOW_STUDYOPTIONS); if (AnkiDroidApp.SDK_VERSION > 4) { ActivityTransitionAnimation.slide(this, ActivityTransitionAnimation.RIGHT); } break; } } // workaround for hidden dialog on return BroadcastMessages.showDialog(); } private void integrityCheck() { if (!AnkiDroidApp.colIsOpen()) { loadCollection(); return; } DeckTask.launchDeckTask(DeckTask.TASK_TYPE_CHECK_DATABASE, new DeckTask.TaskListener() { @Override public void onPreExecute() { mProgressDialog = StyledProgressDialog.show(DeckPicker.this, "", getResources().getString(R.string.check_db_message), true); } @Override public void onPostExecute(TaskData result) { if (mProgressDialog.isShowing()) { mProgressDialog.dismiss(); } if (result.getBoolean()) { // StyledDialog dialog = (StyledDialog) onCreateDialog(DIALOG_OK); // dialog.setTitle(getResources().getString(R.string.check_db_title)); // dialog.setMessage(String.format(Utils.ENGLISH_LOCALE, // getResources().getString(R.string.check_db_result_message), // Math.round(result.getLong() / 1024))); // dialog.show(); } else { handleDbError(); } } @Override public void onProgressUpdate(TaskData... values) { } }, new DeckTask.TaskData(AnkiDroidApp.getCol())); } // private void resetDeckLanguages(String deckPath) { // if (MetaDB.resetDeckLanguages(this, deckPath)) { // Themes.showThemedToast(this, getResources().getString(R.string.contextmenu_deckpicker_reset_reset_message), // true); // } // } // // // public void openSharedDeckPicker() { // // deckLoaded = false; // startActivityForResult(new Intent(this, SharedDeckPicker.class), DOWNLOAD_SHARED_DECK); // if (UIUtils.getApiLevel() > 4) { // ActivityTransitionAnimation.slide(this, ActivityTransitionAnimation.RIGHT); // } // } public void handleDbError() { AnkiDatabaseManager.closeDatabase(AnkiDroidApp.getCollectionPath()); DeckTask.launchDeckTask(DeckTask.TASK_TYPE_RESTORE_IF_MISSING, new DeckTask.TaskListener() { @Override public void onPreExecute() { mProgressDialog = StyledProgressDialog.show(DeckPicker.this, "", getResources().getString(R.string.backup_restore_if_missing), true); } @Override public void onPostExecute(TaskData result) { if (mProgressDialog.isShowing()) { try { mProgressDialog.dismiss(); } catch (Exception e) { Log.e(AnkiDroidApp.TAG, "onPostExecute - Dialog dismiss Exception = " + e.getMessage()); } } showDialog(DIALOG_DB_ERROR); } @Override public void onProgressUpdate(TaskData... values) { } }, new DeckTask.TaskData(AnkiDroidApp.getCollectionPath())); } private void openStudyOptions(long deckId) { openStudyOptions(deckId, null); } private void openStudyOptions(long deckId, Bundle cramInitialConfig) { if (mFragmented) { setStudyContentView(deckId, cramInitialConfig); } else { mDontSaveOnStop = true; Intent intent = new Intent(); intent.putExtra("index", deckId); intent.putExtra("cramInitialConfig", cramInitialConfig); intent.setClass(this, StudyOptionsActivity.class); // if (deckId != 0) { // intent.putExtra(EXTRA_DECK_ID, deckId); // } startActivityForResult(intent, SHOW_STUDYOPTIONS); // if (deckId != 0) { // if (UIUtils.getApiLevel() > 4) { // ActivityTransitionAnimation.slide(this, ActivityTransitionAnimation.NONE); // } // } else { if (AnkiDroidApp.SDK_VERSION > 4) { ActivityTransitionAnimation.slide(this, ActivityTransitionAnimation.LEFT); } // } } } /** * Programmatically click on a deck in the deck list. * @param did The deck ID of the deck to select. */ private void selectDeck(long did) { // Log.i(AnkiDroidApp.TAG, "Selected deck with ID " + did); for (int i = 0; i < mDeckList.size(); i++) { if (Long.parseLong(mDeckList.get(i).get("did")) == did) { final int lastPosition = i; mDeckListView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { mDeckListView.getViewTreeObserver().removeGlobalOnLayoutListener(this); mDeckListView.performItemClick(null, lastPosition, 0); } }); break; } } } /** * Set which deck is selected (highlighted) in the deck list. * <p> * Note that this method does not change the currently selected deck in the collection, only the highlighted * deck in the deck list. To select a deck, see {@link #selectDeck(long)}. * @param did The deck ID of the deck to select. */ public void setSelectedDeck(long did) { for (int i = 0; i < mDeckList.size(); i++) { if (Long.parseLong(mDeckList.get(i).get("did")) == did) { mDeckListView.setItemChecked(i, true); break; } } } private void handleDeckSelection(int id) { if (!AnkiDroidApp.colIsOpen()) { loadCollection(); } @SuppressWarnings("unchecked") HashMap<String, String> data = (HashMap<String, String>) mDeckListAdapter.getItem(id); // Log.i(AnkiDroidApp.TAG, "Selected " + deckFilename); long deckId = Long.parseLong(data.get("did")); if (deckId == -10 || deckId == -20 || deckId == -30 || deckId == -40 || deckId == -50) { EasyTracker easyTracker = EasyTracker.getInstance(this); easyTracker.send(MapBuilder.createEvent(getString(R.string.ga_event_download_deck_category), getString(R.string.ga_event_download_deck_action), null, deckId).build()); String url = data.get("url").toString(); Connection.downloadSharedDeck(mDownloadDeckListener, new Connection.Payload(new Object[] { url })); } else { AnkiDroidApp.getCol().getDecks().select(deckId); openStudyOptions(deckId); } } private void createTutorialDeck() { DeckTask.launchDeckTask(DeckTask.TASK_TYPE_LOAD_TUTORIAL, new DeckTask.TaskListener() { @Override public void onPreExecute() { mProgressDialog = StyledProgressDialog.show(DeckPicker.this, "", getResources().getString(R.string.tutorial_load), true); } @Override public void onPostExecute(TaskData result) { if (result.getBoolean()) { loadCounts(); openStudyOptions(AnkiDroidApp.getCol().getDecks().selected()); } else { Themes.showThemedToast(DeckPicker.this, getResources().getString(R.string.tutorial_loading_error), false); } if (mProgressDialog.isShowing()) { try { mProgressDialog.dismiss(); } catch (Exception e) { Log.e(AnkiDroidApp.TAG, "onPostExecute - Dialog dismiss Exception = " + e.getMessage()); } } } @Override public void onProgressUpdate(TaskData... values) { } }, new DeckTask.TaskData(AnkiDroidApp.getCol())); } private void updateDecksList(TreeSet<Object[]> decks, int eta, int count) { if (decks == null) { Log.e(AnkiDroidApp.TAG, "updateDecksList: empty decks list"); return; } ArrayList<String> deckTitles = new ArrayList<String>(); String currentName = null; Object[] defaultDeck = null; for (Object[] d : decks) { currentName = readableDeckName(((String[]) d[0])); if (!currentName.equals("Default")) deckTitles.add(currentName); else defaultDeck = d; } decks.remove(defaultDeck); if (!deckTitles.contains("HSK 1 Vocabulary")) { String[] strings = { "HSK 1 Vocabulary" }; decks.add(new Object[] { strings, Long.valueOf(-10), 0, 0, 0, false, "https://s3.amazonaws.com/s3.hichinaschool.com.br/HSK+1+Vocabulary.apkg" }); } if (!deckTitles.contains("HSK 2 Vocabulary")) { String[] strings = { "HSK 2 Vocabulary" }; decks.add(new Object[] { strings, Long.valueOf(-20), 0, 0, 0, false, "https://s3.amazonaws.com/s3.hichinaschool.com.br/HSK+2+Vocabulary.apkg" }); } if (!deckTitles.contains("HSK 3 Vocabulary")) { String[] strings = { "HSK 3 Vocabulary" }; decks.add(new Object[] { strings, Long.valueOf(-30), 0, 0, 0, false, "https://s3.amazonaws.com/s3.hichinaschool.com.br/HSK+3+Vocabulary.apkg" }); } if (!deckTitles.contains("HSK 4 Vocabulary")) { String[] strings = { "HSK 4 Vocabulary" }; decks.add(new Object[] { strings, Long.valueOf(-40), 0, 0, 0, false, "https://s3.amazonaws.com/s3.hichinaschool.com.br/HSK+4+Vocabulary.apkg" }); } if (!deckTitles.contains("HSK 5 Vocabulary")) { String[] strings = { "HSK 5 Vocabulary" }; decks.add(new Object[] { strings, Long.valueOf(-50), 0, 0, 0, false, "https://s3.amazonaws.com/s3.hichinaschool.com.br/HSK+5+Vocabulary.apkg" }); } mDeckList.clear(); int due = 0; for (Object[] d : decks) { HashMap<String, String> m = new HashMap<String, String>(); String[] name = ((String[]) d[0]); m.put("name", readableDeckName(name)); m.put("did", ((Long) d[1]).toString()); m.put("new", ((Integer) d[2]).toString()); m.put("lrn", ((Integer) d[3]).toString()); m.put("rev", ((Integer) d[4]).toString()); m.put("dyn", ((Boolean) d[5]) ? "d1" : "d0"); if (d.length > 6) m.put("url", ((String) d[6].toString())); // m.put("complMat", ((Float)d[5]).toString()); // m.put("complAll", ((Float)d[6]).toString()); if (name.length == 1) { due += Integer.parseInt(m.get("new")) + Integer.parseInt(m.get("lrn")) + Integer.parseInt(m.get("rev")); // top position m.put("sep", "top"); // correct previous deck if (mDeckList.size() > 0) { HashMap<String, String> map = mDeckList.get(mDeckList.size() - 1); if (map.get("sep").equals("top")) { map.put("sep", "ful"); } else { map.put("sep", "bot"); } } } else { // center position m.put("sep", "cen"); } if (mDeckList.size() > 0 && mDeckList.size() == decks.size() - 1) { // bottom position if (name.length == 1) { m.put("sep", "ful"); } else { m.put("sep", "bot"); } } mDeckList.add(m); } mDeckListAdapter.notifyDataSetChanged(); // set title Resources res = getResources(); // if (count != -1) { // String time = "-"; // if (eta != -1) { // time = res.getQuantityString(R.plurals.deckpicker_title_minutes, eta, eta); // } // AnkiDroidApp.getCompat().setSubtitle(this, res.getQuantityString(R.plurals.deckpicker_title, due, due, count, time)); // } setTitle(res.getString(R.string.app_name)); // update widget WidgetStatus.update(this, decks); } // private void restartApp() { // // restarts application in order to apply new themes or localisations // Intent i = getBaseContext().getPackageManager() // .getLaunchIntentForPackage(getBaseContext().getPackageName()); // mCompat.invalidateOptionsMenu(this); // MetaDB.closeDB(); // StudyOptions.this.finishWithAnimation(); // startActivity(i); // } // ---------------------------------------------------------------------------- // INNER CLASSES // ---------------------------------------------------------------------------- class MyGestureDetector extends SimpleOnGestureListener { @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { if (mSwipeEnabled && !mFragmented) { try { if (e1.getX() - e2.getX() > AnkiDroidApp.sSwipeMinDistance && Math.abs(velocityX) > AnkiDroidApp.sSwipeThresholdVelocity && Math.abs(e1.getY() - e2.getY()) < AnkiDroidApp.sSwipeMaxOffPath) { mDontSaveOnStop = true; float pos = e1.getY(); for (int j = 0; j < mDeckListView.getChildCount(); j++) { View v = mDeckListView.getChildAt(j); Rect rect = new Rect(); v.getHitRect(rect); if (rect.top < pos && rect.bottom > pos) { HashMap<String, String> data = (HashMap<String, String>) mDeckListAdapter .getItem(mDeckListView.getPositionForView(v)); Collection col = AnkiDroidApp.getCol(); if (col != null) { col.getDecks().select(Long.parseLong(data.get("did"))); col.reset(); Intent reviewer = new Intent(DeckPicker.this, Reviewer.class); startActivityForResult(reviewer, REQUEST_REVIEW); if (AnkiDroidApp.SDK_VERSION > 4) { ActivityTransitionAnimation.slide(DeckPicker.this, ActivityTransitionAnimation.LEFT); } return true; } } } } } catch (Exception e) { Log.e(AnkiDroidApp.TAG, "onFling Exception = " + e.getMessage()); } } return false; } } // @Override // protected void onNewIntent(Intent intent) { // super.onNewIntent(intent); // String deck = intent.getStringExtra(EXTRA_DECK); // // Log.d(AnkiDroidApp.TAG, "StudyOptions.onNewIntent: " + intent // + ", deck=" + deck); // // if (deck != null && !deck.equals(mDeckFilename)) { // // mDeckFilename = deck; // // // loadPreviousDeck(); // // } // } public static String readableDeckName(String[] name) { int len = name.length; StringBuilder sb = new StringBuilder(); for (int i = 0; i < len; i++) { if (i == len - 1) { sb.append(name[i]); } else if (i == len - 2) { sb.append("\u21aa"); } else { sb.append(" "); } } return sb.toString(); } @Override public void onAttachedToWindow() { if (!mFragmented) { Window window = getWindow(); window.setFormat(PixelFormat.RGBA_8888); } } Connection.TaskListener mDownloadDeckListener = new Connection.TaskListener() { int countDown = 0; @Override public void onProgressUpdate(Object... values) { countDown = ((Integer) values[0]).intValue(); if (mProgressDialog != null && mProgressDialog.isShowing()) { // mProgressDialog.setTitle((String) values[0]); mProgressDialog.setMessage(getResources().getString(R.string.download_deck, countDown)); } } @Override public void onPreExecute() { // Log.i(AnkiDroidApp.TAG, "Info: mDownloadDeckListener - onPreExcecute"); if (mProgressDialog == null || !mProgressDialog.isShowing()) { mProgressDialog = StyledProgressDialog.show(DeckPicker.this, "", getResources().getString(R.string.download_deck, countDown / 1024), true); } } @Override public void onPostExecute(Payload data) { // Log.i(AnkiDroidApp.TAG, "Info: mDownloadDeckListener - onPostExecute, success = " + data.success); Resources res = getResources(); try { if (mProgressDialog != null && mProgressDialog.isShowing()) { mProgressDialog.dismiss(); } } catch (IllegalArgumentException e) { Log.e(AnkiDroidApp.TAG, "Info - IllegalArgumentException: " + e); } if (data.success) { mImportPath = (String) data.result; if (AnkiDroidApp.colIsOpen() && mImportPath != null) { DeckTask.launchDeckTask(DeckTask.TASK_TYPE_IMPORT, mImportAddListener, new TaskData(AnkiDroidApp.getCol(), mImportPath, true)); mImportPath = null; // mDeckList.remove(mDeckListView.getItemAtPosition(mDeckLastItemSelected+1)); // mDeckListAdapter.notifyDataSetChanged(); } } else { StyledDialog.Builder builder = new StyledDialog.Builder(DeckPicker.this); builder.setTitle(res.getString(R.string.connection_error_title)); builder.setIcon(R.drawable.ic_dialog_alert); builder.setMessage(res.getString(R.string.register_error)); builder.setPositiveButton(res.getString(R.string.ok), null); builder.show(); } } @Override public void onDisconnected() { Themes.showThemedToast(DeckPicker.this, getResources().getString(R.string.connection_needed), true); // Toast.makeText(DeckPicker.this, getString(R.string.connection_needed), Toast.LENGTH_SHORT).show(); } }; }