Search in sources :

Example 41 with START

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);
            }
        }
    });
}
Also used : Call(okhttp3.Call) OKHttpUtil(com.ichi2.utils.OKHttpUtil) IOException(java.io.IOException) FileNotFoundException(java.io.FileNotFoundException) SQLiteConstraintException(android.database.sqlite.SQLiteConstraintException) IOException(java.io.IOException) Response(okhttp3.Response) JSONObject(com.ichi2.utils.JSONObject) JSONObject(com.ichi2.utils.JSONObject) List(java.util.List) ArrayList(java.util.ArrayList) LinkedList(java.util.LinkedList) ZipFile(java.util.zip.ZipFile) File(java.io.File) RequestBody(okhttp3.RequestBody)

Example 42 with START

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;
}
Also used : JSONObject(com.ichi2.utils.JSONObject)

Example 43 with START

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();
}
Also used : ArrayList(java.util.ArrayList) JSONArray(com.ichi2.utils.JSONArray) JSONObject(com.ichi2.utils.JSONObject) JSONObject(com.ichi2.utils.JSONObject) File(java.io.File) HashSet(java.util.HashSet)

Example 44 with START

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;
}
Also used : NonNull(androidx.annotation.NonNull) Note(com.ichi2.libanki.Note) Intent(android.content.Intent) CheckResult(androidx.annotation.CheckResult)

Example 45 with START

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;
}
Also used : JSONObject(com.ichi2.utils.JSONObject)

Aggregations

Test (org.junit.Test)23 Intent (android.content.Intent)21 JSONObject (com.ichi2.utils.JSONObject)17 Model (com.ichi2.libanki.Model)14 View (android.view.View)13 ArrayList (java.util.ArrayList)13 Bundle (android.os.Bundle)12 Collection (com.ichi2.libanki.Collection)11 Note (com.ichi2.libanki.Note)9 File (java.io.File)9 JSONArray (com.ichi2.utils.JSONArray)8 SharedPreferences (android.content.SharedPreferences)7 TextView (android.widget.TextView)7 EditText (android.widget.EditText)6 Deck (com.ichi2.libanki.Deck)6 Dialog (android.app.Dialog)5 NonNull (androidx.annotation.NonNull)5 MaterialDialog (com.afollestad.materialdialogs.MaterialDialog)5 RobolectricTest (com.ichi2.anki.RobolectricTest)5 TaskData (com.ichi2.async.TaskData)5