use of com.ichi2.anim.ActivityTransitionAnimation.Direction.START in project AnkiChinaAndroid by ankichinateam.
the class AnkiChinaSyncer method sync.
// 1、获取sessionkey
// 2、查看本地是否有synclog
// 3、如果本地没有synclog,则使用完整的数据库数据和服务端比对
// 4、如果本地有synclog,则将synclog和数据库对比,得出变化的数据,上报数据库
// 5、获得本地需要改动的返回内容,修改数据库
// 6、将修改后的数据库的内容保存到synclog里,供下次同步对比
public void sync() {
if (SYNCING) {
// updateDialogMessage(SYNCING_ERROR, "同步状态异常,请重启APP");
return;
}
SYNCING = true;
Timber.i("start sync china");
showSyncChinaDialog(mContext);
BackupManager.performBackupInBackground(mCol.getPath(), true, CollectionHelper.getInstance().getTimeSafe(mContext));
mCol.getDb().execute("create table if not exists synclog (" + " id integer not null," + " type integer not null," + " mod integer not null" + ")");
mCol.getDb().execute("create index if not exists ix_synclog on synclog (id,type);)");
mCol.getDb().execute("create index if not exists ix_synclog_id on synclog (id);)");
mCol.getDb().execute("create index if not exists ix_cards_id on cards (id);)");
mCol.getDb().execute("create index if not exists ix_notes_id on notes (id);)");
mCurrentSession = mPreferences.getString(Consts.KEY_SYNC_CHINA_SESSION, "");
mPreferences.edit().putLong(Consts.KEY_LAST_STOP_TIME, System.currentTimeMillis()).apply();
String url = Consts.ANKI_CHINA_BASE + Consts.API_VERSION + "napi/sync/getKey";
url += "?terminal_time=" + System.currentTimeMillis() / 1000 + "&terminal_crt=" + mCol.getCrt();
if (!mCurrentSession.isEmpty()) {
url += "&last_session_key=" + mCurrentSession;
} else {
// 全量同步直接上传本地数据
try {
mCol.getDb().execute("delete from synclog");
} catch (Exception e) {
// mCol=CollectionHelper.getInstance().getColSafe(AnkiDroidApp.getInstance());
// mCol.getDb().execute("drop table synclog");
// mCol.getDb().execute("create table if not exists synclog (" + " id integer not null,"
// + " type integer not null," + " mod integer not null" + ")");
e.printStackTrace();
}
}
updateDialogProgress(SYNCING_DATA, "整理数据中", 2);
if (mCancel) {
return;
}
OKHttpUtil.get(url, mToken, "", new OKHttpUtil.MyCallBack() {
@Override
public void onFailure(Call call, IOException e) {
updateDialogMessage(SYNCING_ERROR, ERROR_NETWORK);
}
@Override
public void onResponse(Call call, String token, Object arg1, Response response) {
if (mCancel) {
return;
}
if (response.isSuccessful()) {
try {
final JSONObject object = new JSONObject(response.body().string());
if (object.getInt("status_code") != 0) {
updateDialogMessage(SYNCING_ERROR, object.getString("message"));
return;
}
final JSONObject item = object.getJSONObject("data");
Timber.i("get session key successfully!:%s", object.toString());
mPostPageSize = item.getInt("page_size");
if (!mCurrentSession.isEmpty()) {
mCurrentSession = item.getString("session_key");
JSONObject localChangedData = compareSyncTable();
// Timber.i("ready to push local sync data:%s", localChangedData.toString());
RequestBody formBody = new FormBody.Builder().add("data", localChangedData.toString()).add("session_key", mCurrentSession).build();
updateDialogProgress(SYNCING_DATA, "上传数据中", 10);
mPostDataPerPercent = 20.0 / (mRestNoteList.size() + 1);
OKHttpUtil.post(Consts.ANKI_CHINA_BASE + Consts.API_VERSION + "napi/sync/postData", formBody, token, "", mPostLocalDataCallback);
} else {
// 全量同步
mCurrentSession = item.getString("session_key");
updateDialogProgress(SYNCING_DATA, "上传数据中", 10);
File zip = zipAllCollection();
List<String> zipPath = new ArrayList<>();
zipPath.add(zip.getAbsolutePath());
OKHttpUtils.doPostRequest(Consts.ANKI_CHINA_BASE + Consts.API_VERSION + "napi/sync/postData", mCurrentSession, zipPath, uploadProgress, zip.getAbsolutePath(), token, new OKHttpUtils.MyCallBack() {
@Override
public void onFailure(Call call, final IOException e) {
Timber.i("upload error------>%s %s", zip.getAbsolutePath(), e.getMessage());
updateDialogMessage(SYNCING_ERROR, ERROR_NETWORK);
mUploadResultEndCount++;
}
@Override
public void onResponse(Call call, Object tag, Response response) throws IOException {
mPostLocalDataCallback.onResponse(call, token, tag, response);
// zip.delete();
}
});
}
} catch (Exception e) {
e.printStackTrace();
updateDialogMessage(SYNCING_ERROR, ERROR_DATA);
}
} else {
Timber.e("get session key error, code %d", response.code());
updateDialogMessage(SYNCING_ERROR, ERROR_DATA);
}
}
});
}
use of com.ichi2.anim.ActivityTransitionAnimation.Direction.START in project AnkiChinaAndroid by ankichinateam.
the class Syncer method start.
public JSONObject start(int minUsn, boolean lnewer, JSONObject graves) {
mMaxUsn = mCol.getUsnForSync();
mMinUsn = minUsn;
mLNewer = !lnewer;
JSONObject lgraves = removed();
remove(graves);
return lgraves;
}
use of com.ichi2.anim.ActivityTransitionAnimation.Direction.START in project AnkiChinaAndroid by ankichinateam.
the class ZipFile method exportInto.
/**
* Export source database into new destination database Note: The following python syntax isn't supported in
* Android: for row in mSrc.db.execute("select * from cards where id in "+ids2str(cids)): therefore we use a
* different method for copying tables
*
* @param path String path to destination database
* @throws JSONException
* @throws IOException
*/
public void exportInto(String path, Context context) throws JSONException, IOException, ImportExportException {
// create a new collection at the target
new File(path).delete();
Collection dst = Storage.Collection(context, path);
mSrc = mCol;
// find cards
Long[] cids = cardIds();
// attach dst to src so we can copy data between them. This isn't done in original libanki as Python more
// flexible
dst.close();
Timber.d("Attach DB");
mSrc.getDb().getDatabase().execSQL("ATTACH '" + path + "' AS DST_DB");
// copy cards, noting used nids (as unique set)
Timber.d("Copy cards");
mSrc.getDb().getDatabase().execSQL("INSERT INTO DST_DB.cards select * from cards where id in " + Utils.ids2str(cids));
Set<Long> nids = new HashSet<>(mSrc.getDb().queryLongList("select nid from cards where id in " + Utils.ids2str(cids)));
// notes
Timber.d("Copy notes");
ArrayList<Long> uniqueNids = new ArrayList<>(nids);
String strnids = Utils.ids2str(uniqueNids);
mSrc.getDb().getDatabase().execSQL("INSERT INTO DST_DB.notes select * from notes where id in " + strnids);
// remove system tags if not exporting scheduling info
if (!mIncludeSched) {
Timber.d("Stripping system tags from list");
ArrayList<String> srcTags = mSrc.getDb().queryStringList("select tags from notes where id in " + strnids);
ArrayList<Object[]> args = new ArrayList<>(srcTags.size());
Object[] arg = new Object[2];
for (int row = 0; row < srcTags.size(); row++) {
arg[0] = removeSystemTags(srcTags.get(row));
arg[1] = uniqueNids.get(row);
args.add(row, arg);
}
mSrc.getDb().executeMany("UPDATE DST_DB.notes set tags=? where id=?", args);
}
// models used by the notes
Timber.d("Finding models used by notes");
ArrayList<Long> mids = mSrc.getDb().queryLongList("select distinct mid from DST_DB.notes where id in " + strnids);
// card history and revlog
if (mIncludeSched) {
Timber.d("Copy history and revlog");
mSrc.getDb().getDatabase().execSQL("insert into DST_DB.revlog select * from revlog where cid in " + Utils.ids2str(cids));
// reopen collection to destination database (different from original python code)
mSrc.getDb().getDatabase().execSQL("DETACH DST_DB");
dst.reopen();
} else {
Timber.d("Detaching destination db and reopening");
// first reopen collection to destination database (different from original python code)
mSrc.getDb().getDatabase().execSQL("DETACH DST_DB");
dst.reopen();
// then need to reset card state
Timber.d("Resetting cards");
dst.getSched().resetCards(cids);
}
// models - start with zero
Timber.d("Copy models");
for (Model m : mSrc.getModels().all()) {
if (mids.contains(m.getLong("id"))) {
Timber.d("Copy models:%s", m.getLong("id"));
dst.getModels().update(m);
}
}
for (Model m : dst.getModels().all()) {
Timber.d("check dst model:%s", m.getLong("id"));
}
// decks
Timber.d("Copy decks");
ArrayList<Long> dids = new ArrayList<>();
if (mDid != null) {
dids.add(mDid);
for (Long x : mSrc.getDecks().children(mDid).values()) {
dids.add(x);
}
}
JSONObject dconfs = new JSONObject();
for (Deck d : mSrc.getDecks().all()) {
if ("1".equals(d.getString("id"))) {
continue;
}
if (mDid != null && !dids.contains(d.getLong("id"))) {
continue;
}
if (d.getInt("dyn") != 1 && d.getLong("conf") != 1L) {
if (mIncludeSched) {
dconfs.put(Long.toString(d.getLong("conf")), true);
}
}
Deck destinationDeck = d.deepClone();
if (!mIncludeSched) {
// scheduling not included, so reset deck settings to default
destinationDeck.put("conf", 1);
}
dst.getDecks().update(destinationDeck);
}
// copy used deck confs
Timber.d("Copy deck options");
for (DeckConfig dc : mSrc.getDecks().allConf()) {
if (dconfs.has(dc.getString("id"))) {
dst.getDecks().updateConf(dc);
}
}
// find used media
Timber.d("Find used media");
JSONObject media = new JSONObject();
mMediaDir = mSrc.getMedia().dir();
if (mIncludeMedia) {
ArrayList<Long> mid = mSrc.getDb().queryLongList("select mid from notes where id in " + strnids);
ArrayList<String> flds = mSrc.getDb().queryStringList("select flds from notes where id in " + strnids);
for (int idx = 0; idx < mid.size(); idx++) {
for (String file : mSrc.getMedia().filesInStr(mid.get(idx), flds.get(idx))) {
// skip files in subdirs
if (file.contains(File.separator)) {
continue;
}
media.put(file, true);
}
}
if (mMediaDir != null) {
for (File f : new File(mMediaDir).listFiles()) {
if (f.isDirectory()) {
continue;
}
String fname = f.getName();
if (fname.startsWith("_")) {
// Loop through every model that will be exported, and check if it contains a reference to f
for (JSONObject model : mSrc.getModels().all()) {
if (_modelHasMedia(model, fname)) {
media.put(fname, true);
break;
}
}
}
}
}
}
JSONArray keys = media.names();
if (keys != null) {
for (int i = 0; i < keys.length(); i++) {
mMediaFiles.add(keys.getString(i));
}
}
Timber.d("Cleanup");
dst.setCrt(mSrc.getCrt());
// todo: tags?
mCount = dst.cardCount();
dst.setMod();
postExport();
dst.close();
}
use of com.ichi2.anim.ActivityTransitionAnimation.Direction.START in project Anki-Android by ankidroid.
the class AbstractFlashcardViewerTest method getViewerController.
@CheckResult
private ActivityController<NonAbstractFlashcardViewer> getViewerController(boolean addCard) {
if (addCard) {
@NonNull Note n = getCol().newNote();
n.setField(0, "a");
getCol().addNote(n);
}
ActivityController<NonAbstractFlashcardViewer> multimediaController = Robolectric.buildActivity(NonAbstractFlashcardViewer.class, new Intent()).create().start().resume().visible();
saveControllerForCleanup((multimediaController));
NonAbstractFlashcardViewer viewer = multimediaController.get();
viewer.onCollectionLoaded(getCol());
viewer.loadInitialCard();
// Without this, AbstractFlashcardViewer.mCard is still null, and RobolectricTest.tearDown executes before
// AsyncTasks spawned by by loading the viewer finish. Is there a way to synchronize these things while under test?
advanceRobolectricLooperWithSleep();
advanceRobolectricLooperWithSleep();
return multimediaController;
}
use of com.ichi2.anim.ActivityTransitionAnimation.Direction.START in project Anki-Android by ankidroid.
the class Syncer method start.
public JSONObject start(int minUsn, boolean lnewer, JSONObject graves) {
mMaxUsn = mCol.getUsnForSync();
mMinUsn = minUsn;
mLNewer = !lnewer;
JSONObject lgraves = removed();
remove(graves);
return lgraves;
}
Aggregations