private static File getBackupDirectory() {
   SharedPreferences prefs =
       AnkiDroidApp.getSharedPrefs(AnkiDroidApp.getInstance().getBaseContext());
   File directory =
       new File(prefs.getString("deckPath", AnkiDroidApp.getStorageDirectory()) + BACKUP_SUFFIX);
   if (!directory.isDirectory()) {
     directory.mkdirs();
   }
   return directory;
 }
 public void setCard(Card card) {
   mCurrentCard = card;
   Long cardId = 0l;
   if (card != null) {
     cardId = card.getId();
   }
   AnkiDroidApp.getSharedPrefs(AnkiDroidApp.getInstance().getBaseContext())
       .edit()
       .putLong("lastWidgetCard", cardId)
       .commit();
 }
 @Override
 public void onCreate() {
   super.onCreate();
   SharedPreferences prefs =
       AnkiDroidApp.getSharedPrefs(AnkiDroidApp.getInstance().getBaseContext());
   String path = prefs.getString("lastWidgetDeck", "");
   if (path != null && path.length() > 0 && AnkiDroidApp.isSdCardMounted()) {
     Log.i(AnkiDroidApp.TAG, "BigWidget: reloading deck " + path);
     mCol = Collection.currentCollection();
     if (mCol != null) {
       mCurrentCard = mCol.getSched().getCard();
     }
   }
 }
Beispiel #4
0
 public static boolean initialize(Context context) {
   mContext = context;
   SharedPreferences preferences =
       AnkiDroidApp.getSharedPrefs(AnkiDroidApp.getInstance().getBaseContext());
   mDictionary =
       Integer.parseInt(preferences.getString("dictionary", Integer.toString(DICTIONARY_NONE)));
   switch (mDictionary) {
     case DICTIONARY_NONE:
       mIsDictionaryAvailable = false;
       break;
     case DICTIONARY_AEDICT:
       mDictionaryAction = "sk.baka.aedict.action.ACTION_SEARCH_EDICT";
       mIsDictionaryAvailable = Utils.isIntentAvailable(mContext, mDictionaryAction);
       break;
     case DICTIONARY_LEO_WEB:
     case DICTIONARY_NCIKU_WEB:
     case DICTIONARY_EIJIRO_WEB:
       mDictionaryAction = "android.intent.action.VIEW";
       mIsDictionaryAvailable = Utils.isIntentAvailable(mContext, mDictionaryAction);
       break;
     case DICTIONARY_LEO_APP:
       mDictionaryAction = "android.intent.action.SEND";
       mIsDictionaryAvailable =
           Utils.isIntentAvailable(
               mContext,
               mDictionaryAction,
               new ComponentName("org.leo.android.dict", "org.leo.android.dict.LeoDict"));
       break;
     case DICTIONARY_COLORDICT:
       mDictionaryAction = "colordict.intent.action.SEARCH";
       mIsDictionaryAvailable = Utils.isIntentAvailable(mContext, mDictionaryAction);
       break;
     case DICTIONARY_FORA:
       mDictionaryAction = "com.ngc.fora.action.LOOKUP";
       mIsDictionaryAvailable = Utils.isIntentAvailable(mContext, mDictionaryAction);
       break;
     default:
       mIsDictionaryAvailable = false;
       break;
   }
   Log.i(AnkiDroidApp.TAG, "Is intent available = " + mIsDictionaryAvailable);
   return mIsDictionaryAvailable;
 }
 @Override
 public void onDestroy() {
   // // TODO: this does not seem to be reliably called
   // String path = "";
   long cardId = 0l;
   if (mCol != null) {
     // path = mLoadedDeck.getDeckPath();
     // DeckManager.closeDeck(path, DeckManager.REQUESTING_ACTIVITY_BIGWIDGET);
     if (mCurrentCard != null) {
       cardId = mCurrentCard.getId();
     }
   }
   // PrefSettings.getSharedPrefs(AnkiDroidApp.getInstance().getBaseContext()).edit().putString("lastWidgetDeck",
   // path).commit();
   AnkiDroidApp.getSharedPrefs(AnkiDroidApp.getInstance().getBaseContext())
       .edit()
       .putLong("lastWidgetCard", cardId)
       .commit();
 }
  /**
   * If collection has not been opened for a long time, we perform a backup here because Android
   * deleted sometimes corrupted decks
   */
  public static boolean safetyBackupNeeded(String path, int days) {
    if (!AnkiDroidApp.getSharedPrefs(AnkiDroidApp.getInstance().getBaseContext())
        .getBoolean("useBackup", true)) {
      return false;
    }

    File collectionFile = new File(path);
    File[] deckBackups = getBackups(collectionFile);
    int len = deckBackups.length;
    if (len == 0) {
      // no backup available
      return true;
    } else if (deckBackups[len - 1].lastModified() == collectionFile.lastModified()) {
      return false;
    }

    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd-HH-mm");
    Calendar cal = new GregorianCalendar();
    cal.setTimeInMillis(System.currentTimeMillis());

    Date lastBackupDate = null;
    while (lastBackupDate == null && len > 0) {
      try {
        len--;
        lastBackupDate =
            df.parse(
                deckBackups[len]
                    .getName()
                    .replaceAll("^.*-(\\d{4}-\\d{2}-\\d{2}-\\d{2}-\\d{2}).anki2$", "$1"));
      } catch (ParseException e) {
        lastBackupDate = null;
      }
    }
    if (lastBackupDate == null) {
      return true;
    } else if (lastBackupDate.getTime() + days * 24 * 3600000 < Utils.intNow(1000)) {
      return true;
    } else {
      return false;
    }
  }
Beispiel #7
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;
  }
  public static void performBackup(String path, int interval, boolean force) {
    SharedPreferences prefs =
        AnkiDroidApp.getSharedPrefs(AnkiDroidApp.getInstance().getBaseContext());
    if (!prefs.getBoolean("useBackup", true) && !force) {
      return;
    }
    File collectionFile = new File(path);
    File[] deckBackups = getBackups(collectionFile);
    int len = deckBackups.length;
    if (len > 0 && deckBackups[len - 1].lastModified() == collectionFile.lastModified()) {
      Log.i(AnkiDroidApp.TAG, "performBackup: No backup necessary due to no collection changes");
      return;
    }

    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd-HH-mm");
    Calendar cal = new GregorianCalendar();
    cal.setTimeInMillis(System.currentTimeMillis());

    Date lastBackupDate = null;
    while (lastBackupDate == null && len > 0) {
      try {
        len--;
        lastBackupDate =
            df.parse(
                deckBackups[len]
                    .getName()
                    .replaceAll("^.*-(\\d{4}-\\d{2}-\\d{2}-\\d{2}-\\d{2}).anki2$", "$1"));
      } catch (ParseException e) {
        lastBackupDate = null;
      }
    }
    if (lastBackupDate != null
        && lastBackupDate.getTime() + interval * 3600000 > Utils.intNow(1000)
        && !force) {
      Log.i(AnkiDroidApp.TAG, "performBackup: No backup created. Last backup younger than 5 hours");
      return;
    }

    String backupFilename;
    try {
      backupFilename =
          String.format(
              Utils.ENGLISH_LOCALE,
              collectionFile.getName().replace(".anki2", "") + "-%s.anki2",
              df.format(cal.getTime()));
    } catch (UnknownFormatConversionException e) {
      Log.e(AnkiDroidApp.TAG, "performBackup: error on creating backup filename: " + e);
      return;
    }

    File backupFile = new File(getBackupDirectory().getPath(), backupFilename);
    if (backupFile.exists()) {
      Log.i(AnkiDroidApp.TAG, "performBackup: No new backup created. File already exists");
      return;
    }

    if (getFreeDiscSpace(collectionFile)
        < collectionFile.length() + (MIN_FREE_SPACE * 1024 * 1024)) {
      Log.e(AnkiDroidApp.TAG, "performBackup: Not enough space on sd card to backup.");
      prefs.edit().putBoolean("noSpaceLeft", true).commit();
      return;
    }

    try {
      InputStream stream = new FileInputStream(collectionFile);
      Utils.writeToFile(stream, backupFile.getAbsolutePath());
      stream.close();

      // set timestamp of file in order to avoid creating a new backup unless its changed
      backupFile.setLastModified(collectionFile.lastModified());
    } catch (IOException e) {
      Log.e(AnkiDroidApp.TAG, Log.getStackTraceString(e));
      Log.e(AnkiDroidApp.TAG, "performBackup: Copying of file failed.");
      return;
    }

    // delete old backups
    deleteDeckBackups(path, prefs.getInt("backupMax", 3));
  }