Exemple #1
0
 @Override
 public void onCorruption(SQLiteDatabase db) {
   Timber.e("The database has been corrupted...");
   AnkiDroidApp.sendExceptionReport(
       new RuntimeException("Database corrupted"),
       "AnkiDb.MyDbErrorHandler.onCorruption",
       "Db has been corrupted ");
   CollectionHelper.getInstance().closeCollection(false);
   DatabaseErrorDialog.databaseCorruptFlag = true;
 }
Exemple #2
0
      @Override
      public boolean commit() {
        Timber.d("DeckOptions - commit() changes back to database");

        try {
          for (Entry<String, Object> entry : mUpdate.valueSet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            Timber.i("Change value for key '" + key + "': " + value);

            if (key.equals("maxAnswerTime")) {
              mOptions.put("maxTaken", (Integer) value);
            } else if (key.equals("newFactor")) {
              mOptions.getJSONObject("new").put("initialFactor", (Integer) value * 10);
            } else if (key.equals("newOrder")) {
              int newValue = Integer.parseInt((String) value);
              // Sorting is slow, so only do it if we change order
              int oldValue = mOptions.getJSONObject("new").getInt("order");
              if (oldValue != newValue) {
                mOptions.getJSONObject("new").put("order", newValue);
                DeckTask.launchDeckTask(
                    DeckTask.TASK_TYPE_REORDER,
                    mConfChangeHandler,
                    new DeckTask.TaskData(new Object[] {mOptions}));
              }
              mOptions.getJSONObject("new").put("order", Integer.parseInt((String) value));
            } else if (key.equals("newPerDay")) {
              mOptions.getJSONObject("new").put("perDay", (Integer) value);
            } else if (key.equals("newGradIvl")) {
              JSONArray ja = new JSONArray(); // [graduating, easy]
              ja.put((Integer) value);
              ja.put(mOptions.getJSONObject("new").getJSONArray("ints").get(1));
              mOptions.getJSONObject("new").put("ints", ja);
            } else if (key.equals("newEasy")) {
              JSONArray ja = new JSONArray(); // [graduating, easy]
              ja.put(mOptions.getJSONObject("new").getJSONArray("ints").get(0));
              ja.put((Integer) value);
              mOptions.getJSONObject("new").put("ints", ja);
            } else if (key.equals("newBury")) {
              mOptions.getJSONObject("new").put("bury", (Boolean) value);
            } else if (key.equals("revPerDay")) {
              mOptions.getJSONObject("rev").put("perDay", (Integer) value);
            } else if (key.equals("easyBonus")) {
              mOptions.getJSONObject("rev").put("ease4", (Integer) value / 100.0f);
            } else if (key.equals("revIvlFct")) {
              mOptions.getJSONObject("rev").put("ivlFct", (Integer) value / 100.0f);
            } else if (key.equals("revMaxIvl")) {
              mOptions.getJSONObject("rev").put("maxIvl", (Integer) value);
            } else if (key.equals("revBury")) {
              mOptions.getJSONObject("rev").put("bury", (Boolean) value);
            } else if (key.equals("lapMinIvl")) {
              mOptions.getJSONObject("lapse").put("minInt", (Integer) value);
            } else if (key.equals("lapLeechThres")) {
              mOptions.getJSONObject("lapse").put("leechFails", (Integer) value);
            } else if (key.equals("lapLeechAct")) {
              mOptions.getJSONObject("lapse").put("leechAction", Integer.parseInt((String) value));
            } else if (key.equals("lapNewIvl")) {
              mOptions.getJSONObject("lapse").put("mult", (Integer) value / 100.0f);
            } else if (key.equals("showAnswerTimer")) {
              mOptions.put("timer", (Boolean) value ? 1 : 0);
            } else if (key.equals("autoPlayAudio")) {
              mOptions.put("autoplay", (Boolean) value);
            } else if (key.equals("replayQuestion")) {
              mOptions.put("replayq", (Boolean) value);
            } else if (key.equals("desc")) {
              mDeck.put("desc", (String) value);
              mCol.getDecks().save(mDeck);
            } else if (key.equals("newSteps")) {
              mOptions
                  .getJSONObject("new")
                  .put("delays", StepsPreference.convertToJSON((String) value));
            } else if (key.equals("lapSteps")) {
              mOptions
                  .getJSONObject("lapse")
                  .put("delays", StepsPreference.convertToJSON((String) value));
            } else if (key.equals("deckConf")) {
              long newConfId = Long.parseLong((String) value);
              mOptions = mCol.getDecks().getConf(newConfId);
              DeckTask.launchDeckTask(
                  DeckTask.TASK_TYPE_CONF_CHANGE,
                  mConfChangeHandler,
                  new DeckTask.TaskData(new Object[] {mDeck, mOptions}));
              // Restart to reflect the new preference values
              restartActivity();
            } else if (key.equals("confRename")) {
              String newName = (String) value;
              if (!TextUtils.isEmpty(newName)) {
                mOptions.put("name", newName);
              }
            } else if (key.equals("confReset")) {
              if ((Boolean) value) {
                DeckTask.launchDeckTask(
                    DeckTask.TASK_TYPE_CONF_RESET,
                    mConfChangeHandler,
                    new DeckTask.TaskData(new Object[] {mOptions}));
              }
            } else if (key.equals("confAdd")) {
              String newName = (String) value;
              if (!TextUtils.isEmpty(newName)) {
                // New config clones current config
                long id = mCol.getDecks().confId(newName, mOptions.toString());
                mDeck.put("conf", id);
                mCol.getDecks().save(mDeck);
              }
            } else if (key.equals("confRemove")) {
              if (mOptions.getLong("id") == 1) {
                // Don't remove the options group if it's the default group
                Themes.showThemedToast(
                    DeckOptions.this,
                    getResources().getString(R.string.default_conf_delete_error),
                    false);
              } else {
                // Remove options group, handling the case where the user needs to confirm full sync
                try {
                  remConf();
                } catch (ConfirmModSchemaException e) {
                  // Libanki determined that a full sync will be required, so confirm with the user
                  // before proceeding
                  // TODO : Use ConfirmationDialog DialogFragment -- not compatible with
                  // PreferenceActivity
                  new MaterialDialog.Builder(DeckOptions.this)
                      .content(R.string.full_sync_confirmation)
                      .positiveText(R.string.dialog_ok)
                      .negativeText(R.string.dialog_cancel)
                      .callback(
                          new MaterialDialog.ButtonCallback() {
                            @Override
                            public void onPositive(MaterialDialog dialog) {
                              mCol.modSchemaNoCheck();
                              try {
                                remConf();
                              } catch (ConfirmModSchemaException e) {
                                // This should never be reached as we just forced modSchema
                                throw new RuntimeException(e);
                              }
                            }
                          })
                      .build()
                      .show();
                }
              }
            } else if (key.equals("confSetSubdecks")) {
              if ((Boolean) value) {
                DeckTask.launchDeckTask(
                    DeckTask.TASK_TYPE_CONF_SET_SUBDECKS,
                    mConfChangeHandler,
                    new DeckTask.TaskData(new Object[] {mDeck, mOptions}));
              }
            }
          }
        } catch (JSONException e) {
          throw new RuntimeException(e);
        }

        // save conf
        try {
          mCol.getDecks().save(mOptions);
        } catch (RuntimeException e) {
          Timber.e("DeckOptions - RuntimeException on saving conf: " + e);
          AnkiDroidApp.sendExceptionReport(e, "DeckOptionsSaveConf");
          setResult(DeckPicker.RESULT_DB_ERROR);
          finish();
        }

        // make sure we refresh the parent cached values
        cacheValues();
        buildLists();
        updateSummaries();

        // and update any listeners
        for (OnSharedPreferenceChangeListener listener : listeners) {
          listener.onSharedPreferenceChanged(DeckPreferenceHack.this, null);
        }

        return true;
      }
Exemple #3
0
  /**
   * Convenience method for querying the database for an entire column. The column will be returned
   * as an ArrayList of the specified class. See Deck.initUndo() for a usage example.
   *
   * @param type The class of the column's data type. Example: int.class, String.class.
   * @param query The SQL query statement.
   * @param column The column id in the result set to return.
   * @return An ArrayList with the contents of the specified column.
   */
  public <T> ArrayList<T> queryColumn(Class<T> type, String query, int column) {
    int nullExceptionCount = 0;
    InvocationTargetException nullException = null; // to catch the null exception for reporting
    ArrayList<T> results = new ArrayList<T>();
    Cursor cursor = null;

    try {
      cursor = mDatabase.rawQuery(query, null);
      String methodName = getCursorMethodName(type.getSimpleName());
      while (cursor.moveToNext()) {
        try {
          // The magical line. Almost as illegible as python code ;)
          results.add(
              type.cast(Cursor.class.getMethod(methodName, int.class).invoke(cursor, column)));
        } catch (InvocationTargetException e) {
          if (cursor.isNull(column)) { // null value encountered
            nullExceptionCount++;
            if (nullExceptionCount == 1) { // Toast and error report first time only
              nullException = e;
              Toast.makeText(
                      AnkiDroidApp.getInstance().getBaseContext(),
                      "Error report pending: unexpected null in database.",
                      Toast.LENGTH_LONG)
                  .show();
            }
            continue; // attempt to skip this null record
          } else {
            throw new RuntimeException(e);
          }
        }
      }
    } catch (NoSuchMethodException e) {
      // This is really coding error, so it should be revealed if it ever happens
      throw new RuntimeException(e);
    } catch (IllegalArgumentException e) {
      // This is really coding error, so it should be revealed if it ever happens
      throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
      // This is really coding error, so it should be revealed if it ever happens
      throw new RuntimeException(e);
    } finally {
      if (cursor != null) {
        cursor.close();
      }
      if (nullExceptionCount > 0) {
        if (nullException != null) {
          StringBuilder sb = new StringBuilder();
          sb.append("AnkiDb.queryColumn (column " + column + "): ");
          sb.append("Exception due to null. Query: " + query);
          sb.append(" Null occurrences during this query: " + nullExceptionCount);
          AnkiDroidApp.sendExceptionReport(
              nullException, "queryColumn_encounteredNull", sb.toString());
          Timber.w(sb.toString());
        } else { // nullException not properly initialized
          StringBuilder sb = new StringBuilder();
          sb.append("AnkiDb.queryColumn(): Critical error -- ");
          sb.append("unable to pass in the actual exception to error reporting.");
          AnkiDroidApp.sendExceptionReport(
              new RuntimeException("queryColumn null"),
              "queryColumn_encounteredNull",
              sb.toString());
          Timber.e(sb.toString());
        }
      }
    }

    return results;
  }