예제 #1
0
 /** Check that updating the flds column works as expected */
 public void testUpdateNoteFields() {
   final ContentResolver cr = getContext().getContentResolver();
   ContentValues cv = new ContentValues();
   // Change the fields so that the first field is now "newTestValue"
   String[] dummyFields2 = mDummyFields.clone();
   dummyFields2[0] = TEST_FIELD_VALUE;
   for (Uri uri : mCreatedNotes) {
     // Update the flds
     cv.put(FlashCardsContract.Note.FLDS, Utils.joinFields(dummyFields2));
     cr.update(uri, cv, null, null);
     // Query the table again
     Cursor noteCursor =
         cr.query(uri, FlashCardsContract.Note.DEFAULT_PROJECTION, null, null, null);
     try {
       assertNotNull(
           "Check that there is a valid cursor for detail data after update", noteCursor);
       assertEquals(
           "Check that there is one and only one entry after update", 1, noteCursor.getCount());
       assertTrue("Move to first item in cursor", noteCursor.moveToFirst());
       String[] newFlds =
           Utils.splitFields(
               noteCursor.getString(noteCursor.getColumnIndex(FlashCardsContract.Note.FLDS)));
       assertTrue(
           "Check that the flds have been updated correctly",
           Arrays.equals(newFlds, dummyFields2));
     } finally {
       noteCursor.close();
     }
   }
 }
  private String unzipSharedDeckFile(String zipFilename, String title) {
    ZipInputStream zipInputStream = null;
    Log.i(AnkiDroidApp.TAG, "unzipSharedDeckFile");
    if (zipFilename.endsWith(".zip")) {
      Log.i(AnkiDroidApp.TAG, "zipFilename ends with .zip");
      try {
        zipInputStream = new ZipInputStream(new FileInputStream(new File(zipFilename)));

        if (new File(mDestination + "/" + title + ".anki").exists()) {
          title += System.currentTimeMillis();
        }

        String partialDeckPath = mDestination + "/tmp/" + title;
        String deckFilename = partialDeckPath + ".anki.updating";

        ZipEntry zipEntry = null;
        while ((zipEntry = zipInputStream.getNextEntry()) != null) {
          Log.i(AnkiDroidApp.TAG, "zipEntry = " + zipEntry.getName());

          if ("shared.anki".equalsIgnoreCase(zipEntry.getName())) {
            Utils.writeToFile(zipInputStream, deckFilename);
          } else if (zipEntry.getName().startsWith("shared.media/", 0)) {
            Log.i(
                AnkiDroidApp.TAG,
                "Folder created = " + new File(partialDeckPath + ".media/").mkdir());
            Log.i(
                AnkiDroidApp.TAG,
                "Destination = "
                    + AnkiDroidApp.getStorageDirectory()
                    + "/"
                    + title
                    + ".media/"
                    + zipEntry.getName().replace("shared.media/", ""));
            Utils.writeToFile(
                zipInputStream,
                partialDeckPath + ".media/" + zipEntry.getName().replace("shared.media/", ""));
          }
        }
        zipInputStream.close();

        // Delete zip file
        new File(zipFilename).delete();
      } catch (FileNotFoundException e) {
        Log.e(AnkiDroidApp.TAG, "FileNotFoundException = " + e.getMessage());
        e.printStackTrace();
      } catch (IOException e) {
        Log.e(AnkiDroidApp.TAG, "IOException = " + e.getMessage());
        e.printStackTrace();
      }
    }

    return title;
  }
예제 #3
0
  /**
   * Posting feedback or error info to the server. This is called from the AsyncTask.
   *
   * @param url The url to post the feedback to.
   * @param type The type of the info, eg Feedback.TYPE_CRASH_STACKTRACE.
   * @param feedback For feedback types this is the message. For error/crash types this is the path
   *     to the error file.
   * @param groupId A single time generated ID, so that errors/feedback send together can be grouped
   *     together.
   * @param index The index of the error in the list
   * @return A Payload file showing success, response code and response message.
   */
  public static Payload postFeedback(
      String url, String type, String feedback, String groupId, int index, Application app) {
    Payload result = new Payload(null);

    List<NameValuePair> pairs = null;
    if (!isErrorType(type)) {
      pairs = new ArrayList<NameValuePair>();
      pairs.add(new BasicNameValuePair("type", type));
      pairs.add(new BasicNameValuePair("groupid", groupId));
      pairs.add(new BasicNameValuePair("index", "0"));
      pairs.add(new BasicNameValuePair("message", feedback));
      addTimestamp(pairs);
    } else {
      pairs = Feedback.extractPairsFromError(type, feedback, groupId, index, app);
      if (pairs == null) {
        result.success = false;
        result.result = null;
      }
    }

    HttpClient httpClient = new DefaultHttpClient();
    HttpPost httpPost = new HttpPost(url);
    httpPost.addHeader("User-Agent", "AnkiDroid");
    try {
      httpPost.setEntity(new UrlEncodedFormEntity(pairs));
      HttpResponse response = httpClient.execute(httpPost);
      Log.e(AnkiDroidApp.TAG, String.format("Bug report posted to %s", url));

      int respCode = response.getStatusLine().getStatusCode();
      switch (respCode) {
        case 200:
          result.success = true;
          result.returnType = respCode;
          result.result = Utils.convertStreamToString(response.getEntity().getContent());
          Log.i(AnkiDroidApp.TAG, String.format("postFeedback OK: %s", result.result));
          break;

        default:
          Log.e(
              AnkiDroidApp.TAG,
              String.format(
                  "postFeedback failure: %d - %s",
                  response.getStatusLine().getStatusCode(),
                  response.getStatusLine().getReasonPhrase()));
          result.success = false;
          result.returnType = respCode;
          result.result = response.getStatusLine().getReasonPhrase();
          break;
      }
    } catch (ClientProtocolException ex) {
      Log.e(AnkiDroidApp.TAG, "ClientProtocolException: " + ex.toString());
      result.success = false;
      result.result = ex.toString();
    } catch (IOException ex) {
      Log.e(AnkiDroidApp.TAG, "IOException: " + ex.toString());
      result.success = false;
      result.result = ex.toString();
    }
    return result;
  }
예제 #4
0
  public static int restoreBackup(String path, String backupPath) {
    // rename old file and move it to subdirectory
    if (!(new File(path)).exists() || !moveDatabaseToBrokenFolder(path, false)) {
      return RETURN_ERROR;
    }

    // copy backup to new position and rename it
    File backupFile = new File(backupPath);
    File colFile = new File(path);
    if (getFreeDiscSpace(colFile) < colFile.length() + (MIN_FREE_SPACE * 1024 * 1024)) {
      Log.e(AnkiDroidApp.TAG, "Not enough space on sd card to restore " + colFile.getName() + ".");
      return RETURN_NOT_ENOUGH_SPACE;
    }
    try {
      InputStream stream = new FileInputStream(backupFile);
      Utils.writeToFile(stream, colFile.getAbsolutePath());
      stream.close();

      // set timestamp of file in order to avoid creating a new backup unless its changed
      colFile.setLastModified(backupFile.lastModified());
    } catch (IOException e) {
      Log.e(AnkiDroidApp.TAG, Log.getStackTraceString(e));
      Log.e(AnkiDroidApp.TAG, "Restore of file " + colFile.getName() + " failed.");
      return RETURN_ERROR;
    }
    return RETURN_DECK_RESTORED;
  }
예제 #5
0
  /**
   * Factory for AnkiFont creation. Creates a typeface wrapper from a font file representing.
   *
   * @param ctx Activity context, needed to access assets
   * @param path Path to typeface file, needed when this is a custom font.
   * @param fromAssets True if the font is to be found in assets of application
   * @return A new AnkiFont object or null if the file can't be interpreted as typeface.
   */
  public static AnkiFont createAnkiFont(Context ctx, String path, boolean fromAssets) {
    File fontfile = new File(path);
    String name = Utils.splitFilename(fontfile.getName())[0];
    String family = name;
    List<String> attributes = new ArrayList<>();

    if (fromAssets) {
      path = fAssetPathPrefix.concat(fontfile.getName());
    }
    Typeface tf = getTypeface(ctx, path);
    if (tf == null) {
      // unable to create typeface
      return null;
    }

    if (tf.isBold() || name.toLowerCase(Locale.US).contains("bold")) {
      attributes.add("font-weight: bolder;");
      family = family.replaceFirst("(?i)-?Bold", "");
    } else if (name.toLowerCase(Locale.US).contains("light")) {
      attributes.add("font-weight: lighter;");
      family = family.replaceFirst("(?i)-?Light", "");
    } else {
      attributes.add("font-weight: normal;");
    }
    if (tf.isItalic() || name.toLowerCase(Locale.US).contains("italic")) {
      attributes.add("font-style: italic;");
      family = family.replaceFirst("(?i)-?Italic", "");
    } else if (name.toLowerCase(Locale.US).contains("oblique")) {
      attributes.add("font-style: oblique;");
      family = family.replaceFirst("(?i)-?Oblique", "");
    } else {
      attributes.add("font-style: normal;");
    }
    if (name.toLowerCase(Locale.US).contains("condensed")
        || name.toLowerCase(Locale.US).contains("narrow")) {
      attributes.add("font-stretch: condensed;");
      family = family.replaceFirst("(?i)-?Condensed", "");
      family = family.replaceFirst("(?i)-?Narrow(er)?", "");
    } else if (name.toLowerCase(Locale.US).contains("expanded")
        || name.toLowerCase(Locale.US).contains("wide")) {
      attributes.add("font-stretch: expanded;");
      family = family.replaceFirst("(?i)-?Expanded", "");
      family = family.replaceFirst("(?i)-?Wide(r)?", "");
    }

    AnkiFont createdFont = new AnkiFont(name, family, attributes, path);

    // determine if override font or default font
    SharedPreferences preferences = AnkiDroidApp.getSharedPrefs(ctx);
    String defaultFont = preferences.getString("defaultFont", "");
    boolean overrideFont = preferences.getString("overrideFontBehavior", "0").equals("1");
    if (defaultFont.equalsIgnoreCase(name)) {
      if (overrideFont) {
        createdFont.setAsOverride();
      } else {
        createdFont.setAsDefault();
      }
    }
    return createdFont;
  }
예제 #6
0
 /** Query .../models URI */
 public void testQueryAllModels() {
   final ContentResolver cr = getContext().getContentResolver();
   // Query all available models
   final Cursor allModels = cr.query(FlashCardsContract.Model.CONTENT_URI, null, null, null, null);
   assertNotNull(allModels);
   try {
     assertTrue("Check that there is at least one result", allModels.getCount() > 0);
     while (allModels.moveToNext()) {
       long modelId = allModels.getLong(allModels.getColumnIndex(FlashCardsContract.Model._ID));
       Uri modelUri =
           Uri.withAppendedPath(FlashCardsContract.Model.CONTENT_URI, Long.toString(modelId));
       final Cursor singleModel = cr.query(modelUri, null, null, null, null);
       assertNotNull(singleModel);
       try {
         assertEquals("Check that there is exactly one result", 1, singleModel.getCount());
         assertTrue("Move to beginning of cursor", singleModel.moveToFirst());
         String nameFromModels =
             allModels.getString(allModels.getColumnIndex(FlashCardsContract.Model.NAME));
         String nameFromModel =
             singleModel.getString(allModels.getColumnIndex(FlashCardsContract.Model.NAME));
         assertEquals("Check that model names are the same", nameFromModel, nameFromModels);
         String flds =
             allModels.getString(allModels.getColumnIndex(FlashCardsContract.Model.FIELD_NAMES));
         assertTrue("Check that valid number of fields", Utils.splitFields(flds).length >= 1);
         Integer numCards =
             allModels.getInt(allModels.getColumnIndex(FlashCardsContract.Model.NUM_CARDS));
         assertTrue("Check that valid number of cards", numCards >= 1);
       } finally {
         singleModel.close();
       }
     }
   } finally {
     allModels.close();
   }
 }
예제 #7
0
 @SmallTest
 public void testGetRawQuery() {
   assertTrue(CardsQuery.getRawQuery(10, null).contains(Utils.join(", ", CardsQuery.PROJECTION)));
   assertTrue(CardsQuery.getRawQuery(10, null).contains(" LIMIT 10"));
   assertTrue(CardsQuery.getRawQuery(20, null).contains(" LIMIT 20"));
   assertFalse(CardsQuery.getRawQuery(10, null).contains(" AND cards.id > "));
   assertTrue(CardsQuery.getRawQuery(10, "1234").contains(" AND cards.id > 1234 "));
 }
예제 #8
0
 public JSONArray meta() {
   JSONArray o = new JSONArray();
   o.put(mCol.getMod());
   o.put(mCol.getScm());
   o.put(mCol.getUsnForSync());
   o.put(Utils.intNow());
   return o;
 }
예제 #9
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;
 }
예제 #10
0
 private void remove(JSONObject graves) {
   // pretend to be the server so we don't set usn = -1
   boolean wasServer = mCol.getServer();
   mCol.setServer(true);
   try {
     // notes first, so we don't end up with duplicate graves
     mCol._remNotes(Utils.jsonArrayToLongArray(graves.getJSONArray("notes")));
     // then cards
     mCol.remCards(Utils.jsonArrayToLongArray(graves.getJSONArray("cards")), false);
     // and decks
     JSONArray decks = graves.getJSONArray("decks");
     for (int i = 0; i < decks.length(); i++) {
       mCol.getDecks().rem(decks.getLong(i), false, false);
     }
   } catch (JSONException e) {
     throw new RuntimeException(e);
   }
   mCol.setServer(wasServer);
 }
예제 #11
0
 /** Check that inserting a new model works as expected */
 public void testInsertAndUpdateModel() throws Exception {
   final ContentResolver cr = getContext().getContentResolver();
   ContentValues cv = new ContentValues();
   // Insert a new model
   cv.put(FlashCardsContract.Model.NAME, TEST_MODEL_NAME);
   cv.put(FlashCardsContract.Model.FIELD_NAMES, Utils.joinFields(TEST_MODEL_FIELDS));
   cv.put(FlashCardsContract.Model.NUM_CARDS, TEST_MODEL_CARDS.length);
   cv.put(FlashCardsContract.Model.CSS, TEST_MODEL_CSS);
   Uri modelUri = cr.insert(FlashCardsContract.Model.CONTENT_URI, cv);
   assertNotNull("Check inserted model isn't null", modelUri);
   long mid = Long.parseLong(modelUri.getLastPathSegment());
   final Collection col = CollectionHelper.getInstance().getCol(getContext());
   try {
     JSONObject model = col.getModels().get(mid);
     assertEquals("Check model name", TEST_MODEL_NAME, model.getString("name"));
     assertEquals("Check css", TEST_MODEL_CSS, model.getString("css"));
     assertEquals(
         "Check templates length", TEST_MODEL_CARDS.length, model.getJSONArray("tmpls").length());
     assertEquals(
         "Check field length", TEST_MODEL_FIELDS.length, model.getJSONArray("flds").length());
     JSONArray flds = model.getJSONArray("flds");
     for (int i = 0; i < flds.length(); i++) {
       assertEquals(
           "Check name of fields", flds.getJSONObject(i).getString("name"), TEST_MODEL_FIELDS[i]);
     }
     // Update each of the templates in the model
     for (int i = 0; i < TEST_MODEL_CARDS.length; i++) {
       cv = new ContentValues();
       cv.put(FlashCardsContract.CardTemplate.NAME, TEST_MODEL_CARDS[i]);
       cv.put(FlashCardsContract.CardTemplate.QUESTION_FORMAT, TEST_MODEL_QFMT[i]);
       cv.put(FlashCardsContract.CardTemplate.ANSWER_FORMAT, TEST_MODEL_AFMT[i]);
       cv.put(FlashCardsContract.CardTemplate.BROWSER_QUESTION_FORMAT, TEST_MODEL_QFMT[i]);
       cv.put(FlashCardsContract.CardTemplate.BROWSER_ANSWER_FORMAT, TEST_MODEL_AFMT[i]);
       Uri tmplUri =
           Uri.withAppendedPath(Uri.withAppendedPath(modelUri, "templates"), Integer.toString(i));
       assertTrue("Update rows", cr.update(tmplUri, cv, null, null) > 0);
       JSONObject template = col.getModels().get(mid).getJSONArray("tmpls").getJSONObject(i);
       assertEquals("Check template name", TEST_MODEL_CARDS[i], template.getString("name"));
       assertEquals("Check qfmt", TEST_MODEL_QFMT[i], template.getString("qfmt"));
       assertEquals("Check afmt", TEST_MODEL_AFMT[i], template.getString("afmt"));
       assertEquals("Check bqfmt", TEST_MODEL_QFMT[i], template.getString("bqfmt"));
       assertEquals("Check bafmt", TEST_MODEL_AFMT[i], template.getString("bafmt"));
     }
   } finally {
     // Delete the model (this will force a full-sync)
     try {
       col.modSchema(false);
       col.getModels().rem(col.getModels().get(mid));
     } catch (ConfirmModSchemaException e) {
       // This will never happen
       throw new IllegalStateException(
           "Unexpected ConfirmModSchemaException trying to remove model");
     }
   }
 }
예제 #12
0
 private long finish(long mod) {
   if (mod == 0) {
     // server side; we decide new mod time
     mod = Utils.intNow(1000);
   }
   mCol.setLs(mod);
   mCol.setUsnAfterSync(mMaxUsn + 1);
   // ensure we save the mod time even if no changes made
   mCol.getDb().setMod(true);
   mCol.save(null, mod);
   return mod;
 }
예제 #13
0
  public static boolean moveDatabaseToBrokenFolder(String colPath, boolean moveConnectedFilesToo) {
    File colFile = new File(colPath);

    // move file
    Date value = Utils.genToday(Utils.utcOffset());
    String movedFilename =
        String.format(
            Utils.ENGLISH_LOCALE,
            colFile.getName().replace(".anki2", "") + "-corrupt-%tF.anki2",
            value);
    File movedFile = new File(getBrokenDirectory().getPath(), movedFilename);
    int i = 1;
    while (movedFile.exists()) {
      movedFile =
          new File(
              getBrokenDirectory().getPath(),
              movedFilename.replace(".anki2", "-" + Integer.toString(i) + ".anki2"));
      i++;
    }
    movedFilename = movedFile.getName();
    if (!colFile.renameTo(movedFile)) {
      return false;
    }

    if (moveConnectedFilesToo) {
      // move all connected files (like journals, directories...) too
      String deckName = colFile.getName();
      File directory = new File(colFile.getParent());
      for (File f : directory.listFiles()) {
        if (f.getName().startsWith(deckName)) {
          if (!f.renameTo(
              new File(
                  getBrokenDirectory().getPath(), f.getName().replace(deckName, movedFilename)))) {
            return false;
          }
        }
      }
    }
    return true;
  }
예제 #14
0
 public Download(String title) {
   mTitle = title;
   this.put(title, true);
   mSize = -1;
   mDownloaded = 0;
   mStatus = STATUS_STARTED;
   // The deck file name should match the deck title, but some characters are invalid in it,
   // so they need to be replaced.
   mFilename = Utils.removeInvalidDeckNameCharacters(mTitle);
   if (mFilename.length() > 40) {
     mFilename = mFilename.substring(0, 40);
   }
 }
예제 #15
0
 private String[] getCustomFonts(String defaultValue, boolean useFullPath) {
   List<AnkiFont> mFonts = Utils.getCustomFonts(this);
   int count = mFonts.size();
   // Log.d(AnkiDroidApp.TAG, "There are " + count + " custom fonts");
   String[] names = new String[count + 1];
   names[0] = defaultValue;
   if (useFullPath) {
     for (int index = 1; index < count + 1; ++index) {
       names[index] = mFonts.get(index - 1).getPath();
       // Log.d(AnkiDroidApp.TAG, "Adding custom font: " + names[index]);
     }
   } else {
     for (int index = 1; index < count + 1; ++index) {
       names[index] = mFonts.get(index - 1).getName();
       // Log.d(AnkiDroidApp.TAG, "Adding custom font: " + names[index]);
     }
   }
   return names;
 }
예제 #16
0
  public AnkiFont(Context ctx, String path, boolean fromAssets) {
    mPath = path;
    File fontfile = new File(mPath);
    mName = Utils.removeExtension(fontfile.getName());
    mFamily = mName;

    if (fromAssets) {
      mPath = fAssetPathPrefix.concat(fontfile.getName());
    }
    Typeface tf = getTypeface(ctx, mPath);
    if (tf.isBold() || mName.toLowerCase().contains("bold")) {
      mWeight = "font-weight: bolder;";
      mFamily = mFamily.replaceFirst("(?i)-?Bold", "");
    } else if (mName.toLowerCase().contains("light")) {
      mWeight = "font-weight: lighter;";
      mFamily = mFamily.replaceFirst("(?i)-?Light", "");
    } else {
      mWeight = "font-weight: normal;";
    }
    if (tf.isItalic() || mName.toLowerCase().contains("italic")) {
      mStyle = "font-style: italic;";
      mFamily = mFamily.replaceFirst("(?i)-?Italic", "");
    } else if (mName.toLowerCase().contains("oblique")) {
      mStyle = "font-style: oblique;";
      mFamily = mFamily.replaceFirst("(?i)-?Oblique", "");
    } else {
      mStyle = "font-style: normal;";
    }
    if (mName.toLowerCase().contains("condensed") || mName.toLowerCase().contains("narrow")) {
      mStretch = "font-stretch: condensed;";
      mFamily = mFamily.replaceFirst("(?i)-?Condensed", "");
      mFamily = mFamily.replaceFirst("(?i)-?Narrow(er)?", "");
    } else if (mName.toLowerCase().contains("expanded") || mName.toLowerCase().contains("wide")) {
      mStretch = "font-stretch: expanded;";
      mFamily = mFamily.replaceFirst("(?i)-?Expanded", "");
      mFamily = mFamily.replaceFirst("(?i)-?Wide(r)?", "");
    } else {
      mStretch = "font-stretch: normal;";
    }
    mFamily = mFamily.replaceFirst("(?i)-?Regular", "");
  }
예제 #17
0
  /**
   * 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;
    }
  }
예제 #18
0
 private ArrayList<Object[]> newerRows(JSONArray data, String table, int modIdx) {
   long[] ids = new long[data.length()];
   try {
     for (int i = 0; i < data.length(); i++) {
       ids[i] = data.getJSONArray(i).getLong(0);
     }
     HashMap<Long, Long> lmods = new HashMap<Long, Long>();
     Cursor cur = null;
     try {
       cur =
           mCol.getDb()
               .getDatabase()
               .rawQuery(
                   "SELECT id, mod FROM "
                       + table
                       + " WHERE id IN "
                       + Utils.ids2str(ids)
                       + " AND "
                       + usnLim(),
                   null);
       while (cur.moveToNext()) {
         lmods.put(cur.getLong(0), cur.getLong(1));
       }
     } finally {
       if (cur != null && !cur.isClosed()) {
         cur.close();
       }
     }
     ArrayList<Object[]> update = new ArrayList<Object[]>();
     for (int i = 0; i < data.length(); i++) {
       JSONArray r = data.getJSONArray(i);
       if (!lmods.containsKey(r.getLong(0)) || lmods.get(r.getLong(0)) < r.getLong(modIdx)) {
         update.add(ConvUtils.jsonArray2Objects(r));
       }
     }
     return update;
   } catch (JSONException e) {
     throw new RuntimeException(e);
   }
 }
예제 #19
0
  public static ArrayList<HashMap<String, String>> getIntentInformation(Context context) {
    openDBIfClosed(context);
    Cursor cursor = null;
    ArrayList<HashMap<String, String>> list = new ArrayList<HashMap<String, String>>();
    try {
      cursor =
          mMetaDb.query(
              "intentInformation", new String[] {"id", "fields"}, null, null, null, null, "id");
      while (cursor.moveToNext()) {
        HashMap<String, String> item = new HashMap<String, String>();
        item.put("id", Integer.toString(cursor.getInt(0)));
        String fields = cursor.getString(1);
        String[] split = Utils.splitFields(fields);
        String source = null;
        String target = null;
        for (int i = 0; i < split.length; i++) {
          if (source == null || source.length() == 0) {
            source = split[i];
          } else if (target == null || target.length() == 0) {
            target = split[i];
          } else {
            break;
          }
        }
        item.put("source", source);
        item.put("target", target);
        item.put("fields", fields);
        list.add(item);
      }
    } catch (SQLiteException e) {
      upgradeDB(mMetaDb, DATABASE_VERSION);

      Timber.e(e, "Error while querying intentInformation");
    } finally {
      if (cursor != null && !cursor.isClosed()) {
        cursor.close();
      }
    }
    return list;
  }
예제 #20
0
  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));
  }