public class NotebookAdapter extends RecyclerView.Adapter<NotebookAdapter.NotebookHolder> {

    private static final int TYPE_NOTEBOOK = 46;
    private static final int TYPE_ADD = 735;

    private Stack<String> mStack = new Stack<>();
    private List<Notebook> mData;
    private NotebookAdapterListener mListener;
    private boolean mHasAddButton = true;
    private boolean mCanOpenEmpty = true;
    private int currentSelection = -1;

    public NotebookAdapter setListener(NotebookAdapterListener listener) {
        mListener = listener;
        return this;
    }

    public NotebookAdapter setHasAddButton(boolean hasAddButton) {
        mHasAddButton = hasAddButton;
        return this;
    }

    public NotebookAdapter setCanOpenEmpty(boolean canOpenEmpty) {
        mCanOpenEmpty = canOpenEmpty;
        return this;
    }

    public void refresh() {
        getSafeNotebook(mStack);
        notifyDataSetChanged();
    }

    private void getSafeNotebook(Stack<String> stack) {
        if (stack.isEmpty()) {
           mData = AppDataBase.getRootNotebooks(AccountService.getCurrent().getUserId());
        } else {
            Notebook parent = AppDataBase.getNotebookByServerId(stack.peek());
            if (parent.isDeleted()) {
                stack.pop();
                getSafeNotebook(stack);
            } else {
                mData = AppDataBase.getChildNotebook(mStack.peek(), AccountService.getCurrent().getUserId());
                mData.add(0, parent);
            }
        }
    }

    public String getCurrentParentId() {
        return mStack.size() == 0 ? "" : mStack.peek();
    }

    @Override
    public int getItemViewType(int position) {
        if (mHasAddButton && position == getItemCount() - 1) {
            return TYPE_ADD;
        } else {
            return TYPE_NOTEBOOK;
        }
    }

    @Override
    public NotebookAdapter.NotebookHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view;
        if (viewType == TYPE_ADD) {
            view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_add_notebook, parent, false);
        } else {
            view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_notebook, parent, false);
        }
        return new NotebookHolder(view);
    }

    @Override
    public void onBindViewHolder(NotebookAdapter.NotebookHolder holder, int position) {
        if (getItemViewType(position) == TYPE_ADD) {
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (mListener != null) {
                        mListener.onClickedAddNotebook(getCurrentParentId());
                    }
                }
            });
            return;
        }

        final Notebook notebook = mData.get(position);
        holder.titleTv.setText(notebook.getTitle());

        String notebookId = notebook.getNotebookId();
        boolean isSuper = isSuper(notebookId);
        boolean isSuperOrRoot = isSuper | mStack.isEmpty();
        boolean hasChild = hasChild(notebookId);
        holder.placeholder.setVisibility(isSuperOrRoot ? View.GONE : View.VISIBLE);
        holder.navigator.setVisibility(mCanOpenEmpty | hasChild ? View.VISIBLE : View.INVISIBLE);
        holder.navigator.setImageResource(isSuper ? R.drawable.ic_expanding : R.drawable.ic_expandable);
        holder.navigator.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //TODO: arrow animation
                if (isSuper(notebook.getNotebookId())) {
                    listUpper();
                } else {
                    listChild(notebook);
                }
            }
        });
        holder.titleTv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mListener != null) {
                    listChild(notebook);
                    mListener.onClickedNotebook(notebook);
                }
            }
        });
    }

    private boolean isSuper(String notebookId) {
        if (mStack.isEmpty()) {
            return false;
        } else {
            return mStack.peek().equals(notebookId);
        }
    }

    private boolean hasChild(String notebookId) {
        return CollectionUtils.isNotEmpty(AppDataBase.getChildNotebook(notebookId, AccountService.getCurrent().getUserId()));
    }

    private void listUpper() {
        int childrenSize = mData.size();
        mData = new ArrayList<>();
        notifyItemRangeRemoved(0, childrenSize);

        mStack.pop();
        if (mStack.isEmpty()) {
            mData = AppDataBase.getRootNotebooks(AccountService.getCurrent().getUserId());
        } else {
            String parentId = mStack.peek();
            mData.add(AppDataBase.getNotebookByServerId(parentId));
            mData.addAll(AppDataBase.getChildNotebook(parentId, AccountService.getCurrent().getUserId()));
        }
        notifyItemRangeInserted(0, mData.size());
    }

    private void listChild(Notebook notebook) {
        int index = mData.indexOf(notebook);
        for (int i = 0; i < index; i++) {
            mData.remove(0);
        }
        notifyItemRangeRemoved(0, index);
        int size = mData.size() - 1;
        for (int i = 0; i < size; i++) {
            mData.remove(1);
        }
        notifyItemRangeRemoved(1, size);
        notifyItemChanged(0);

        mStack.push(notebook.getNotebookId());
        List<Notebook> children = AppDataBase.getChildNotebook(notebook.getNotebookId(), AccountService.getCurrent().getUserId());
        int childrenSize = children.size();
        mData.addAll(children);
        notifyItemRangeInserted(1, childrenSize);
    }

    @Override
    public int getItemCount() {
        int fixed = mHasAddButton ? 1 : 0;
        return mData == null ? fixed : mData.size() + fixed;
    }

    public interface NotebookAdapterListener {
        void onClickedNotebook(Notebook notebook);

        void onClickedAddNotebook(String parentNotebookId);
    }

    static class NotebookHolder extends RecyclerView.ViewHolder {
        View itemView;
        @BindView(R.id.navigator)
        ImageView navigator;
        @BindView(R.id.tv_title)
        TextView titleTv;
        @Nullable
        @BindView(R.id.placeholder)
        View placeholder;

        public NotebookHolder(View itemView) {
            super(itemView);
            this.itemView = itemView;
            ButterKnife.bind(this, itemView);
        }
    }
} public class NoteSyncService extends Service {

    private static final String TAG = "NoteSyncService:";

    public static void startServiceForNote(Context context) {
        if (!AccountService.isSignedIn()) {
            XLog.w(TAG + "Trying to sync but not login");
            return;
        }

        Intent intent = new Intent(context, NoteSyncService.class);
        context.startService(intent);
    }


    @Override
    public void onCreate() {
        super.onCreate();
    }


    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


    @Override
    public int onStartCommand(final Intent intent, int flags, int startId) {
        if (intent == null) return START_NOT_STICKY;

        Observable.create(
                new Observable.OnSubscribe<Void>() {
                    @Override
                    public void call(Subscriber<? super Void> subscriber) {
                        if (!subscriber.isUnsubscribed()) {
                            NoteService.fetchFromServer();
                            subscriber.onNext(null);
                            subscriber.onCompleted();
                        }
                    }
                })
                .subscribeOn(Schedulers.io())
                .observeOn(Schedulers.io())
                .subscribe(new Observer<Void>() {
                    @Override
                    public void onCompleted() {
                        stopSelf();
                    }

                    @Override
                    public void onError(Throwable e) {
                        e.printStackTrace();
                        EventBus.getDefault().post(new SyncEvent(false));
                        stopSelf();
                    }

                    @Override
                    public void onNext(Void val) {
                        EventBus.getDefault().post(new SyncEvent(true));
                    }
                });

        return START_NOT_STICKY;
    }
} @Database(name = "leanote_db", version = 4)
public class AppDataBase {

    private static final String TAG = "AppDataBase:";

    @Migration(version = 2, database = AppDataBase.class)
    public static class UpdateTag extends BaseMigration {

        @Override
        public void migrate(DatabaseWrapper database) {
            Cursor cursor = SQLite.select()
                    .from(Note.class)
                    .where(Note_Table.tags.notEq(""))
                    .query(database);
            if (cursor == null) {
                return;
            }
            int idIndex = cursor.getColumnIndex("id");
            int tagIndex = cursor.getColumnIndex("tags");
            int uidIndex = cursor.getColumnIndex("userId");
            while (cursor.moveToNext()) {
                String originalTagText = cursor.getString(tagIndex);
                String uid = cursor.getString(uidIndex);
                long noteLocalId = cursor.getLong(idIndex);
                String[] tagTexts = originalTagText.split(",");
                for (String tagText : tagTexts) {
                    tagText = tagText.trim();
                    if (TextUtils.isEmpty(tagText)) {
                        continue;
                    }
                    Tag tagModel = SQLite.select()
                            .from(Tag.class)
                            .where(Tag_Table.userId.eq(uid))
                            .and(Tag_Table.text.eq(tagText))
                            .querySingle(database);
                    long tagId;
                    if (tagModel == null) {
                        tagModel = new Tag(uid, tagText);
                        tagModel.insert(database);
                    }
                    tagId = tagModel.getId();

                    RelationshipOfNoteTag relationship = new RelationshipOfNoteTag(noteLocalId, tagId, uid);
                    relationship.insert(database);
                }
            }
            cursor.close();
        }
    }

    @Migration(version = 3, priority = 1, database = AppDataBase.class)
    public static class AddUsnColumn extends AlterTableMigration<Account> {

        public AddUsnColumn(Class<Account> table) {
            super(table);
        }

        @Override
        public void onPreMigrate() {
            super.onPreMigrate();
            addColumn(SQLiteType.INTEGER, "noteUsn");
            addColumn(SQLiteType.INTEGER, "notebookUsn");
        }
    }

    @Migration(version = 3, priority =  0, database = AppDataBase.class)
    public static class UpdateUsn extends BaseMigration {

        @Override
        public void migrate(DatabaseWrapper database) {
            Cursor cursor = SQLite.select()
                    .from(Account.class)
                    .query(database);
            if (cursor == null) {
                return;
            }
            int idIndex = cursor.getColumnIndex("id");
            int usnIndex = cursor.getColumnIndex("lastUsn");
            while (cursor.moveToNext()) {
                int lastUsn = cursor.getInt(usnIndex);
                int id = cursor.getInt(idIndex);
                Account account = SQLite.select()
                        .from(Account.class)
                        .where(Account_Table.id.eq(id))
                        .querySingle(database);
                if (account != null) {
                    SQLite.update(Account.class)
                            .set(Account_Table.notebookUsn.eq(lastUsn), Account_Table.noteUsn.eq(lastUsn))
                            .where(Account_Table.id.eq(account.getLocalUserId()))
                            .execute(database);
                }
            }
            cursor.close();
        }
    }

    @Migration(version = 4, priority = 1, database = AppDataBase.class)
    public static class AddColLastUseTime extends AlterTableMigration<Account> {

        public AddColLastUseTime(Class<Account> table) {
            super(table);
        }

        @Override
        public void onPreMigrate() {
            super.onPreMigrate();
            addColumn(SQLiteType.INTEGER, "lastUseTime");
        }
    }

    @Migration(version = 4, priority =  0, database = AppDataBase.class)
    public static class UpdateLastUseTime extends BaseMigration {

        @Override
        public void migrate(DatabaseWrapper database) {
            Cursor cursor = SQLite.select()
                    .from(Account.class)
                    .where(Account_Table.token.notEq(""))
                    .query(database);
            if (cursor == null) {
                return;
            }
            int idIndex = cursor.getColumnIndex("id");
            while (cursor.moveToNext()) {
                int id = cursor.getInt(idIndex);
                Account account = SQLite.select()
                        .from(Account.class)
                        .where(Account_Table.id.eq(id))
                        .querySingle(database);
                if (account != null) {
                    account.updateLastUseTime();
                    SQLite.update(Account.class)
                            .set(Account_Table.lastUseTime.eq(account.getLastUseTime()))
                            .where(Account_Table.id.eq(account.getLocalUserId()))
                            .execute(database);
                }
            }
            cursor.close();
        }
    }

    public static void deleteNoteByLocalId(long localId) {
        SQLite.delete().from(Note.class)
                .where(Note_Table.id.eq(localId))
                .async()
                .execute();
    }

    public static Note getNoteByServerId(String serverId) {
        return SQLite.select()
                .from(Note.class)
                .where(Note_Table.noteId.eq(serverId))
                .querySingle();
    }

    public static Note getNoteByLocalId(long localId) {
        return SQLite.select()
                .from(Note.class)
                .where(Note_Table.id.eq(localId))
                .querySingle();
    }

    public static List<Note> getNotesFromNotebook(String userId, long localNotebookId) {
        Notebook notebook = getNotebookByLocalId(localNotebookId);
        if (notebook == null) {
            return new ArrayList<>();
        }
        return SQLite.select()
                .from(Note.class)
                .where(Note_Table.notebookId.eq(notebook.getNotebookId()))
                .and(Note_Table.userId.eq(userId))
                .and(Note_Table.isTrash.eq(false))
                .and(Note_Table.isDeleted.eq(false))
                .and(Note_Table.isTrash.eq(false))
                .queryList();
    }

    public static List<Note> getNotesByTagText(String tagText, String userId) {
        Tag tag = getTagByText(tagText, userId);
        if (tag == null) {
            return new ArrayList<>();
        }
        return getNotesByTagId(tag.getId());
    }

    public static List<Note> getAllNotes(String userId) {
        return SQLite.select()
                .from(Note.class)
                .where(Note_Table.userId.eq(userId))
                .and(Note_Table.isTrash.eq(false))
                .and(Note_Table.isDeleted.eq(false))
                .and(Note_Table.isTrash.eq(false))
                .queryList();
    }

    public static Notebook getNotebookByServerId(String serverId) {
        return SQLite.select()
                .from(Notebook.class)
                .where(Notebook_Table.notebookId.eq(serverId))
                .querySingle();
    }

    public static Notebook getNotebookByLocalId(long localId) {
        return SQLite.select()
                .from(Notebook.class)
                .where(Notebook_Table.id.eq(localId))
                .querySingle();
    }

    public static Notebook getRecentNoteBook(String userId) {
        Note recentNotes = SQLite.select()
                .from(Note.class)
                .where(Note_Table.userId.eq(userId))
                .and(Note_Table.notebookId.notEq(""))
                .orderBy(Note_Table.updatedTime, false)
                .querySingle();
        if (recentNotes != null) {
            Notebook notebook = getNotebookByServerId(recentNotes.getNoteBookId());
            if (notebook != null && !notebook.isDeleted()) {
                return notebook;
            }
        }

        return SQLite.select()
                .from(Notebook.class)
                .where(Notebook_Table.userId.eq(userId))
                .and(Notebook_Table.isDeletedOnServer.eq(false))
                .querySingle();
    }

    public static List<Notebook> getAllNotebook(String userId) {
        return SQLite.select()
                .from(Notebook.class)
                .where(Notebook_Table.userId.eq(userId))
                .and(Notebook_Table.isDeletedOnServer.eq(false))
                .queryList();
    }

    public static List<Notebook> getRootNotebooks(String userId) {
        return SQLite.select()
                .from(Notebook.class)
                .where(Notebook_Table.userId.eq(userId))
                .and(Notebook_Table.parentNotebookId.eq(""))
                .and(Notebook_Table.isDeletedOnServer.eq(false))
                .queryList();
    }

    public static List<Notebook> getChildNotebook(String notebookId, String userId) {
        return SQLite.select()
                .from(Notebook.class)
                .where(Notebook_Table.userId.eq(userId))
                .and(Notebook_Table.parentNotebookId.eq(notebookId))
                .and(Notebook_Table.isDeletedOnServer.eq(false))
                .queryList();
    }

    public static List<NoteFile> getAllRelatedFile(long noteLocalId) {
        return SQLite.select()
                .from(NoteFile.class)
                .where(NoteFile_Table.noteLocalId.eq(noteLocalId))
                .queryList();
    }

    public static NoteFile getNoteFileByLocalId(String localId) {
        return SQLite.select()
                .from(NoteFile.class)
                .where(NoteFile_Table.localId.eq(localId))
                .querySingle();
    }

    public static NoteFile getNoteFileByServerId(String serverId) {
        return SQLite.select()
                .from(NoteFile.class)
                .where(NoteFile_Table.serverId.eq(serverId))
                .querySingle();
    }

    public static void deleteFileExcept(long noteLocalId, Collection<String> excepts) {
        SQLite.delete()
                .from(NoteFile.class)
                .where(NoteFile_Table.noteLocalId.eq(noteLocalId))
                .and(NoteFile_Table.localId.notIn(excepts))
                .async()
                .execute();
    }

    public static Account getAccount(String email, String host) {
        return SQLite.select()
                .from(Account.class)
                .where(Account_Table.email.eq(email))
                .and(Account_Table.host.eq(host))
                .querySingle();
    }

    public static Account getAccountWithToken() {
        return SQLite.select()
                .from(Account.class)
                .where(Account_Table.token.notEq(""))
                .orderBy(Account_Table.lastUseTime, false)
                .querySingle();
    }

    public static List<Account> getAccountListWithToken() {
        return SQLite.select()
                .from(Account.class)
                .where(Account_Table.token.notEq(""))
                .orderBy(Account_Table.lastUseTime, false)
                .queryList();
    }

    public static List<Tag> getTagByNoteLocalId(long noteLocalId) {
        return SQLite.select()
                .from(Tag.class).as("T")
                .join(RelationshipOfNoteTag.class, Join.JoinType.INNER).as("R")
                .on(Tag_Table.id.withTable(NameAlias.builder("T").build())
                        .eq(RelationshipOfNoteTag_Table.tagLocalId.withTable(NameAlias.builder("R").build())))
                .where(RelationshipOfNoteTag_Table.noteLocalId.withTable(NameAlias.builder("R").build()).eq(noteLocalId))
                .queryList();
    }

    public static List<Note> getNotesByTagId(long tagId) {
        IProperty[] properties = Note_Table.ALL_COLUMN_PROPERTIES;
        NameAlias nameAlias = NameAlias.builder("N").build();
        for (int i = 0; i < properties.length; i++) {
            properties[i] = properties[i].withTable(nameAlias);
        }
        return SQLite.select(properties)
                .from(Note.class).as("N")
                .join(RelationshipOfNoteTag.class, Join.JoinType.INNER).as("R")
                .on(Tag_Table.id.withTable(NameAlias.builder("N").build())
                        .eq(RelationshipOfNoteTag_Table.noteLocalId.withTable(NameAlias.builder("R").build())))
                .where(RelationshipOfNoteTag_Table.tagLocalId.withTable(NameAlias.builder("R").build()).eq(tagId))
                .queryList();
    }

    public static RelationshipOfNoteTag getRelationShip(long noteLocalId, long tagId, String userId) {
        return SQLite.select()
                .from(RelationshipOfNoteTag.class)
                .where(RelationshipOfNoteTag_Table.userId.eq(userId))
                .and(RelationshipOfNoteTag_Table.tagLocalId.eq(tagId))
                .and(RelationshipOfNoteTag_Table.noteLocalId.eq(noteLocalId))
                .querySingle();
    }

    public static Tag getTagByText(String text, String userId) {
        return SQLite.select()
                .from(Tag.class)
                .where(Tag_Table.userId.eq(userId))
                .and(Tag_Table.text.eq(text))
                .querySingle();
    }

    public static void deleteAllRelatedTags(long noteLocalId, String userId) {
        SQLite.delete()
                .from(RelationshipOfNoteTag.class)
                .where(RelationshipOfNoteTag_Table.userId.eq(userId))
                .and(RelationshipOfNoteTag_Table.noteLocalId.eq(noteLocalId))
                .async()
                .execute();
    }

    public static void deleteRelatedTags(long noteLocalId, String userId, long firstReservedId, long... reservedIds) {
        SQLite.delete()
                .from(RelationshipOfNoteTag.class)
                .where(RelationshipOfNoteTag_Table.userId.eq(userId))
                .and(RelationshipOfNoteTag_Table.noteLocalId.eq(noteLocalId))
                .and(RelationshipOfNoteTag_Table.id.notIn(firstReservedId, reservedIds))
                .async()
                .execute();
    }

    public static List<Tag> getAllTags(String userId) {
        return SQLite.select()
                .from(Tag.class)
                .where(Tag_Table.userId.eq(userId))
                .queryList();
    }
} @Table(name = "Account", database = AppDataBase.class)
public class Account extends BaseModel {

    public static final int EDITOR_RICH_TEXT = 0;
    public static final int EDITOR_MARKDOWN = 1;

    @SerializedName("Ok")
    boolean isOk;
    @SerializedName("Msg")
    String msg;

    @Column(name = "id")
    @PrimaryKey(autoincrement = true)
    long localUserId;
    @Column(name = "userId")
    @SerializedName("UserId")
    String userId = "";
    @Column(name = "userName")
    @SerializedName("Username")
    String userName = "";
    @Column(name = "email")
    @SerializedName("Email")
    String email = "";
    @Column(name = "verified")
    @SerializedName("Verified")
    boolean verified;
    @Column(name = "avatar")
    @SerializedName("Avatar")
    String avatar = "";
    @Column(name = "token")
    @SerializedName("Token")
    String accessToken = "";
    @Column(name = "defaultEditor")
    int defaultEditor = EDITOR_MARKDOWN;
    @Column(name = "host")
    @SerializedName("Host")
    String host = "";
    @Column(name = "noteUsn")
    int noteUsn;
    @Column(name = "notebookUsn")
    int notebookUsn;
    @Column(name = "lastUseTime")
    long lastUseTime;

    @Deprecated
    @Column(name = "lastUsn")
    @SerializedName("LastSyncUsn")
    int lastSyncUsn;

    public Account() {
    }

    public boolean isOk() {
        return isOk;
    }

    public long getLocalUserId() {
        return localUserId;
    }

    public String getUserId() {
        return userId;
    }

    public String getUserName() {
        return userName;
    }

    public String getEmail() {
        return email;
    }

    public boolean isVerified() {
        return verified;
    }

    public String getAvatar() {
        return avatar;
    }

    public String getAccessToken() {
        return accessToken;
    }

    @Deprecated
    public int getLastSyncUsn() {
        return lastSyncUsn;
    }

    public int getDefaultEditor() {
        return defaultEditor;
    }

    public String getHost() {
        return host;
    }

    public String getMsg() {
        return msg;
    }

    public void setAccessToken(String accessToken) {
        this.accessToken = accessToken;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public void setAvatar(String avatar) {
        this.avatar = avatar;
    }

    public void setHost(String host) {
        this.host = host;
    }

    @Deprecated
    public void setLastUsn(int lastUsn) {
        this.lastSyncUsn = lastUsn;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public void setVerified(boolean verified) {
        this.verified = verified;
    }

    public void setDefaultEditor(int defaultEditor) {
        this.defaultEditor = defaultEditor;
    }

    public int getNoteUsn() {
        return noteUsn;
    }

    public int getNotebookUsn() {
        return notebookUsn;
    }

    public void setNoteUsn(int noteUsn) {
        this.noteUsn = noteUsn;
    }

    public void setNotebookUsn(int notebookUsn) {
        this.notebookUsn = notebookUsn;
    }

    public void updateLastUseTime() {
        lastUseTime = System.currentTimeMillis() / 1000;
    }

    public long getLastUseTime() {
        return lastUseTime;
    }

    @Override
    public String toString() {
        return "Account{" +
                "isOk=" + isOk +
                ", msg='" + msg + '\'' +
                ", localUserId=" + localUserId +
                ", userId='" + userId + '\'' +
                ", userName='" + userName + '\'' +
                ", email='" + email + '\'' +
                ", verified=" + verified +
                ", avatar='" + avatar + '\'' +
                ", accessToken='" + accessToken + '\'' +
                ", defaultEditor=" + defaultEditor +
                ", lastSyncUsn=" + lastSyncUsn +
                ", host='" + host + '\'' +
                '}';
    }
} @Table(name = "Note", database = AppDataBase.class)
public class Note extends BaseModel implements Serializable {

    public static final String TAG = "Note:";

    @SerializedName("Ok")
    boolean isOk = true;
    @SerializedName("Msg")
    String msg;

    @Column(name = "noteId")
    @SerializedName("NoteId")
    String noteId = "";
    @Column(name = "notebookId")
    @SerializedName("NotebookId")
    String noteBookId = "";
    @Column(name = "userId")
    @SerializedName("UserId")
    String userId = "";
    @Column(name = "title")
    @SerializedName("Title")
    String title = "";
    @Column(name = "content")
    @SerializedName("Content")
    String content = "";
    @Column(name = "isMarkDown")
    @SerializedName("IsMarkdown")
    boolean isMarkDown;
    @Column(name = "isTrash")
    @SerializedName("IsTrash")
    boolean isTrash;
    @Column(name = "isDeleted")
    @SerializedName("IsDeleted")
    boolean isDeleted;
    @Column(name = "isBlog")
    @SerializedName("IsBlog")
    boolean isPublicBlog;
    @Column(name = "usn")
    @SerializedName("Usn")
    int usn;

    @SerializedName("Tags")
    List<String> tagData;
    @SerializedName("Files")
    List<NoteFile> noteFiles;
    @SerializedName("UpdatedTime")
    String updatedTimeData = "";
    @SerializedName("CreatedTime")
    String createdTimeData = "";
    @SerializedName("PublicTime")
    String publicTimeData = "";

    @Column(name = "id")
    @PrimaryKey(autoincrement = true)
    Long id;
    Long localNotebookId;
    @Column(name = "desc")
    String desc = "";
    @Column(name = "noteAbstract")
    String noteAbstract = "";
    String fileIds;
    @Column(name = "isDirty")
    boolean isDirty;
    @Column(name = "isUploading")
    boolean isUploading;
    @Column(name = "createdTime")
    long createdTime;
    @Column(name = "updatedTime")
    long updatedTime;
    @Column(name = "publicTime")
    long publicTime;
    @Column(name = "tags")
    String tags = "";
    boolean uploadSucc = true;

    public long getCreatedTimeVal() {
        return createdTime;
    }

    public void setCreatedTimeVal(long createdTime) {
        this.createdTime = createdTime;
    }

    public long getUpdatedTimeVal() {
        return updatedTime;
    }

    public void setUpdatedTimeVal(long updatedTime) {
        this.updatedTime = updatedTime;
    }

    public long getPublicTimeVal() {
        return publicTime;
    }

    public void setPublicTimeVal(long publicTime) {
        this.publicTime = publicTime;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getNoteBookId() {
        return noteBookId;
    }

    public void setNoteBookId(String noteBookId) {
        this.noteBookId = noteBookId;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public boolean isMarkDown() {
        return isMarkDown;
    }

    public void setIsMarkDown(boolean isMarkDown) {
        this.isMarkDown = isMarkDown;
    }

    public boolean isTrash() {
        return isTrash;
    }

    public void setIsTrash(boolean isTrash) {
        this.isTrash = isTrash;
    }

    public int getUsn() {
        return usn;
    }

    public boolean isUploadSucc() {
        return uploadSucc;
    }

    public void setUploadSucc(boolean uploadSucc) {
        this.uploadSucc = uploadSucc;
    }

    public void setUsn(int usn) {
        this.usn = usn;
    }

    public String getNoteId() {
        return noteId;
    }

    public void setNoteId(String noteId) {
        this.noteId = noteId;
    }

    public List<String> getTagData() {
        return tagData;
    }

    public void updateTags() {
        if (CollectionUtils.isEmpty(tagData)) {
            tags = "";
            return;
        }
        StringBuilder tagBuilder = new StringBuilder();
        int size = tagData.size();
        int lastIndex = size - 1;
        for (int i = 0; i < size; i++) {
            tagBuilder.append(tagData.get(i));
            if (i < lastIndex) {
                tagBuilder.append(",");
            }
        }
        tags = tagBuilder.toString();
    }

    public void updateTime() {
        createdTime = TimeUtils.toTimestamp(createdTimeData);
        updatedTime = TimeUtils.toTimestamp(updatedTimeData);
        publicTime = TimeUtils.toTimestamp(publicTimeData);
    }

    public List<NoteFile> getNoteFiles() {
        return noteFiles;
    }

    //TODO:delete this
    public String getUpdatedTime() {
        return updatedTimeData;
    }

    //TODO:delete this
    public String getCreatedTime() {
        return updatedTimeData;
    }

    //TODO:delete this
    public String getPublicTime() {
        return publicTimeData;
    }

    //TODO:delete this
    public void setUpdatedTime(String v) {
    }

    //TODO:delete this
    public void setCreatedTime(String v) {
    }

    //TODO:delete this
    public void setPublicTime(String publicTime) {
    }

    @Override
    public String toString() {
        return "Note{" +
                "id=" + id +
                ", noteId='" + noteId + '\'' +
                ", noteBookId='" + noteBookId + '\'' +
                ", userId='" + userId + '\'' +
                ", title='" + title + '\'' +
                ", desc='" + desc + '\'' +
                ", tags='" + tags + '\'' +
                ", noteAbstract='" + noteAbstract + '\'' +
                ", content='" + content + '\'' +
                ", fileIds='" + fileIds + '\'' +
                ", isMarkDown=" + isMarkDown +
                ", isTrash=" + isTrash +
                ", isDeleted=" + isDeleted +
                ", isDirty=" + isDirty +
                ", isPublicBlog=" + isPublicBlog +
                ", createdTime='" + createdTime + '\'' +
                ", updatedTime='" + updatedTime + '\'' +
                ", publicTime='" + publicTime + '\'' +
                ", usn=" + usn +
                '}';
    }

    public boolean hasChanges(Note otherNote) {
        return otherNote == null
                || isChanged("title", title, otherNote.title)
                || isChanged("content", content, otherNote.content)
                || isChanged("notebookId", noteBookId, otherNote.noteBookId)
                || isChanged("isMarkDown", isMarkDown, otherNote.isMarkDown)
                || isChanged("tags", tags, otherNote.tags)
                || isChanged("isBlog", isPublicBlog, otherNote.isPublicBlog);
    }

    private boolean isChanged(String message, Object l, Object r) {
        boolean isEqual = l.equals(r);
        if (!isEqual) {
            XLog.i(TAG + message + " changed, origin  =" + l);
            XLog.i(TAG + message + " changed, modified=" + r);
        }
        return !isEqual;
    }

    public boolean isPublicBlog() {
        return isPublicBlog;
    }

    public void setIsPublicBlog(boolean isPublicBlog) {
        this.isPublicBlog = isPublicBlog;
    }

    public boolean isDeleted() {
        return isDeleted;
    }

    public void setIsDeleted(boolean isDeleted) {
        this.isDeleted = isDeleted;
    }

    public boolean isDirty() {
        return isDirty;
    }

    public void setIsDirty(boolean isDirty) {
        this.isDirty = isDirty;
    }

    public String getNoteAbstract() {
        return noteAbstract;
    }

    public void setNoteAbstract(String noteAbstract) {
        this.noteAbstract = noteAbstract;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getFileIds() {
        return fileIds;
    }

    public void setFileIds(String fileIds) {
        this.fileIds = fileIds;
    }

    public boolean isUploading() {
        return isUploading;
    }

    public void setIsUploading(boolean isUploading) {
        this.isUploading = isUploading;
    }

    public Long getLocalNotebookId() {
        return localNotebookId;
    }

    public void setLocalNotebookId(Long localNotebookId) {
        this.localNotebookId = localNotebookId;
    }

    public boolean isOk() {
        return isOk;
    }

    public String getMsg() {
        return msg;
    }

    public static class UpdateTimeComparetor implements Comparator<Note> {
        @Override
        public int compare(Note lhs, Note rhs) {
            long lTime = lhs.getUpdatedTimeVal();
            long rTime = rhs.getUpdatedTimeVal();
            if (lTime > rTime) {
                return -1;
            } else if (lTime < rTime) {
                return 1;
            } else {
                return 0;
            }
        }
    }
} @Table(name = "Notebook", database = AppDataBase.class)
public class Notebook extends BaseModel {

    @SerializedName("Ok")
    boolean isOk = true;
    @SerializedName("Msg")
    String msg;

    @Column(name = "id")
    @PrimaryKey(autoincrement = true)
    long id;
    @Column(name = "notebookId")
    @SerializedName("NotebookId")
    String notebookId;
    @Column(name = "parentNotebookId")
    @SerializedName("ParentNotebookId")
    String parentNotebookId;
    @Column(name = "userId")
    @SerializedName("UserId")
    String userId;
    @Column(name = "title")
    @SerializedName("Title")
    String title;
    String urlTitle;
    @Column(name = "seq")
    @SerializedName("Seq")
    int seq;
    @SerializedName("IsBlog")
    boolean isBlog;
    @Column(name = "createdTime")
    @SerializedName("CreatedTime")
    String createTime;
    @Column(name = "updatedTime")
    @SerializedName("UpdatedTime")
    String updateTime;
    @Column(name = "isDirty")
    boolean isDirty;
    @Column(name = "isDeletedOnServer")
    @SerializedName("IsDeleted")
    boolean isDeleted;
    @Column(name = "isTrash")
    boolean isTrash;
    @Column(name = "usn")
    @SerializedName("Usn")
    int usn;

    public String getCreateTime() {
        return createTime;
    }

    public void setCreateTime(String createTime) {
        this.createTime = createTime;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public boolean isBlog() {
        return isBlog;
    }

    public void setIsBlog(boolean isBlog) {
        this.isBlog = isBlog;
    }

    public boolean isDeleted() {
        return isDeleted;
    }

    public void setIsDeleted(boolean isDeleted) {
        this.isDeleted = isDeleted;
    }

    public boolean isDirty() {
        return isDirty;
    }

    public void setIsDirty(boolean isDirty) {
        this.isDirty = isDirty;
    }

    public String getNotebookId() {
        return notebookId;
    }

    public void setNotebookId(String notebookId) {
        this.notebookId = notebookId;
    }

    public int getSeq() {
        return seq;
    }

    public void setSeq(int seq) {
        this.seq = seq;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(String updateTime) {
        this.updateTime = updateTime;
    }

    public String getUrlTitle() {
        return urlTitle;
    }

    public void setUrlTitle(String urlTitle) {
        this.urlTitle = urlTitle;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public int getUsn() {
        return usn;
    }

    public void setUsn(int usn) {
        this.usn = usn;
    }

    public String getParentNotebookId() {
        return parentNotebookId;
    }

    public void setParentNotebookId(String parentNotebookId) {
        this.parentNotebookId = parentNotebookId;
    }

    public boolean isTrash() {
        return isTrash;
    }

    public void setIsTrash(boolean isTrash) {
        this.isTrash = isTrash;
    }


    public boolean isOk() {
        return isOk;
    }

    public String getMsg() {
        return msg;
    }
} public class SyncEvent {
    private boolean isSucceed;

    public SyncEvent(boolean isSucceed) {
        this.isSucceed = isSucceed;
    }

    public boolean isSucceed() {
        return isSucceed;
    }
} @Table(name = "Tag", database = AppDataBase.class)
public class Tag extends BaseModel{

    @SerializedName("TagId")
    @Column(name = "tagId")
    String tagId;
    @SerializedName("UserId")
    @Column(name = "userId")
    String UserId;
    @SerializedName("Tag")
    @Column(name = "text")
    String text;
    @SerializedName("IsDeleted")
    @Column(name = "isDeleted")
    boolean isDeleted;
    @SerializedName("Usn")
    @Column(name = "usn")
    int usn;
    @SerializedName("CreatedTime")
    String createTimeData;
    @SerializedName("UpdatedTime")
    String updatedTimeData;

    @Column(name = "id")
    @PrimaryKey(autoincrement = true)
    long id;
    @Column(name = "createdTime")
    long createdTime;
    @Column(name = "updatedTime")
    long updatedTime;

    Tag() {}

    public Tag(String userId, String text) {
        UserId = userId;
        this.text = text;
    }

    public String getTagId() {
        return tagId;
    }

    public String getUserId() {
        return UserId;
    }

    public String getText() {
        return text;
    }

    public boolean isDeleted() {
        return isDeleted;
    }

    public int getUsn() {
        return usn;
    }

    public long getId() {
        return id;
    }

    public long getCreatedTime() {
        return createdTime;
    }

    public long getUpdatedTime() {
        return updatedTime;
    }

    public void setTagId(String tagId) {
        this.tagId = tagId;
    }

    public void setUserId(String userId) {
        UserId = userId;
    }

    public void setText(String text) {
        this.text = text;
    }

    public void setDeleted(boolean deleted) {
        isDeleted = deleted;
    }

    public void setUsn(int usn) {
        this.usn = usn;
    }

    public void setCreatedTime(long createdTime) {
        this.createdTime = createdTime;
    }

    public void setUpdatedTime(long updatedTime) {
        this.updatedTime = updatedTime;
    }

    public void setId(long id) {
        this.id = id;
    }

    public void updateTime() {
        createdTime = TimeUtils.toTimestamp(createTimeData);
        updatedTime = TimeUtils.toTimestamp(updatedTimeData);
    }
} public class AccountService {

    public static Observable<BaseResponse> register(String email, String password) {
        return RetrofitUtils.create(ApiProvider.getInstance().getAuthApi().register(email, password));
    }

    public static Observable<Authentication> login(String email, String password) {
        return RetrofitUtils.create(ApiProvider.getInstance().getAuthApi().login(email, password));
    }

    public static Observable<User> getInfo(String userId) {
        return RetrofitUtils.create(ApiProvider.getInstance().getUserApi().getInfo(userId));
    }

    public static long saveToAccount(Authentication authentication, String host) {
        Account localAccount = AppDataBase.getAccount(authentication.getEmail(), host);
        if (localAccount == null) {
            localAccount = new Account();
        }
        localAccount.setHost(host);
        localAccount.setEmail(authentication.getEmail());
        localAccount.setAccessToken(authentication.getAccessToken());
        localAccount.setUserId(authentication.getUserId());
        localAccount.setUserName(authentication.getUserName());
        localAccount.save();
        return localAccount.getLocalUserId();
    }

    public static void saveToAccount(User user, String host) {
        Account localAccount = AppDataBase.getAccount(user.getEmail(), host);
        if (localAccount == null) {
            localAccount = new Account();
        }
        localAccount.setHost(host);
        localAccount.setEmail(user.getEmail());
        localAccount.setUserId(user.getUserId());
        localAccount.setUserName(user.getUserName());
        localAccount.setAvatar(user.getAvatar());
        localAccount.setVerified(user.isVerified());
        localAccount.save();
    }

    public static void logout() {
        Account account = getCurrent();
        account.setAccessToken("");
        account.update();
    }

    public static Observable<BaseResponse> changePassword(String oldPassword, String newPassword) {
        return RetrofitUtils.create(ApiProvider.getInstance().getUserApi().updatePassword(oldPassword, newPassword));
    }

    public static Observable<BaseResponse> changeUserName(String userName) {
        return RetrofitUtils.create(ApiProvider.getInstance().getUserApi().updateUsername(userName));
    }

    public static Account getCurrent() {
        return AppDataBase.getAccountWithToken();
    }

    public static List<Account> getAccountList() {
        return AppDataBase.getAccountListWithToken();
    }

    public static Account getAccountById(long id) {
        return new Select()
                .from(Account.class)
                .where(Account_Table.id.eq(id))
                .querySingle();
    }

    public static boolean isSignedIn() {
        return getCurrent() != null;
    }
} public class NoteEditActivity extends BaseActivity implements EditorFragment.EditorFragmentListener, SettingFragment.SettingFragmentListener {

    private static final String TAG = "NoteEditActivity:";
    public static final int RESULT_CONFLICT = 525;
    public static final String EXT_NOTE_LOCAL_ID = "ext_note_local_id";
    public static final String EXT_IS_NEW_NOTE = "ext_is_new_note";
    public static final String TAG_EDITOR = "tag_editor_tag";
    public static final String TAG_SETTING = "tag_setting_tag";
    public static final int FRAG_EDITOR = 0;
    public static final int FRAG_SETTINGS = 1;

    private EditorFragment mEditorFragment;
    private SettingFragment mSettingsFragment;
    private Wrapper mOriginal;
    private Wrapper mModified;
    private boolean mIsNewNote;

    private LeaViewPager mPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_edit_note);
        initToolBar((Toolbar) findViewById(R.id.toolbar), true);
        mPager = (LeaViewPager) findViewById(R.id.pager);
        mPager.setPagingEnabled(false);
        mPager.setAdapter(new SectionAdapter(getSupportFragmentManager()));
        mPager.setOffscreenPageLimit(2);

        if (savedInstanceState != null) {
            mEditorFragment = (EditorFragment) getSupportFragmentManager().findFragmentByTag(savedInstanceState.getString(TAG_EDITOR));
            mSettingsFragment = (SettingFragment) getSupportFragmentManager().findFragmentByTag(savedInstanceState.getString(TAG_SETTING));
        }

        long noteLocalId = getIntent().getLongExtra(EXT_NOTE_LOCAL_ID, -1);
        if (noteLocalId == -1) {
            finish();
            return;
        }
        mIsNewNote = getIntent().getBooleanExtra(EXT_IS_NEW_NOTE, false);
        mOriginal = new Wrapper(AppDataBase.getNoteByLocalId(noteLocalId));
        mModified = new Wrapper(AppDataBase.getNoteByLocalId(noteLocalId));
        setResult(RESULT_CANCELED);
    }

    public static Intent getOpenIntent(Context context, long noteLocalId, boolean isNewNote) {
        Intent intent = new Intent(context, NoteEditActivity.class);
        intent.putExtra(EXT_NOTE_LOCAL_ID, noteLocalId);
        intent.putExtra(EXT_IS_NEW_NOTE, isNewNote);
        return intent;
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        //TODO:save note state
        outState.putString(TAG_EDITOR, mEditorFragment.getTag());
        outState.putString(TAG_SETTING, mSettingsFragment.getTag());
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.edit_note, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(final MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_save:
                filterUnchanged()
                        .doOnNext(new Action1<Wrapper>() {
                            @Override
                            public void call(Wrapper wrapper) {
                                saveAsDraft(wrapper);
                                setResult(RESULT_OK);
                                NetworkUtils.checkNetwork();
                            }
                        })
                        .flatMap(new Func1<Wrapper, Observable<Long>>() {
                            @Override
                            public Observable<Long> call(Wrapper wrapper) {
                                return uploadToServer(wrapper.note.getId());
                            }
                        })
                        .subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread())
                        .doOnSubscribe(new Action0() {
                            @Override
                            public void call() {
                                DialogDisplayer.showProgress(NoteEditActivity.this, R.string.saving_note);
                            }
                        })
                        .subscribe(new Observer<Long>() {
                            @Override
                            public void onCompleted() {
                                DialogDisplayer.dismissProgress();
                                finish();
                            }

                            @Override
                            public void onError(Throwable e) {
                                DialogDisplayer.dismissProgress();
                                ToastUtils.show(Leamonax.getContext(), e.getMessage());
                                if (e instanceof NetworkUtils.NetworkUnavailableException) {
                                    finish();
                                } else if (e instanceof ReadableException) {
                                    setResult(RESULT_CONFLICT);
                                    finish();
                                }
                            }

                            @Override
                            public void onNext(Long noteLocalId) {
                                Note localNote = AppDataBase.getNoteByLocalId(noteLocalId);
                                localNote.setIsDirty(false);
                                localNote.save();
                            }
                        });
                return true;
            case R.id.action_settings:
                mPager.setCurrentItem(FRAG_SETTINGS);
                return true;
            case android.R.id.home:
                if (mPager.getCurrentItem() > FRAG_EDITOR) {
                    mPager.setCurrentItem(FRAG_EDITOR);
                } else {
                    filterUnchanged()
                            .observeOn(AndroidSchedulers.mainThread())
                            .doOnCompleted(new Action0() {
                                @Override
                                public void call() {
                                    NoteEditActivity.super.onOptionsItemSelected(item);
                                }
                            })
                            .subscribe(new Action1<Wrapper>() {
                                @Override
                                public void call(Wrapper wrapper) {
                                    setResult(RESULT_OK);
                                    XLog.i(TAG + wrapper.toString());

                                    if (mIsNewNote && isTitleContentEmpty(wrapper.note)) {
                                        XLog.i(TAG + "remove empty note, id=" + wrapper.note.getId());
                                        AppDataBase.deleteNoteByLocalId(wrapper.note.getId());
                                    } else {
                                        saveAsDraft(wrapper);
                                    }
                                }
                            });
                }
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private Observable<Long> uploadToServer(final long noteLocalId) {
        return Observable.create(
                new Observable.OnSubscribe<Long>() {
                    @Override
                    public void call(Subscriber<? super Long> subscriber) {
                        if (!subscriber.isUnsubscribed()) {
                            NoteService.saveNote(noteLocalId);
                            subscriber.onNext(noteLocalId);
                            subscriber.onCompleted();
                        }
                    }
                });
    }

    @Override
    public void onBackPressed() {
        if (mPager.getCurrentItem() > FRAG_EDITOR) {
            mPager.setCurrentItem(FRAG_EDITOR);
        } else {
            filterUnchanged()
                    .observeOn(AndroidSchedulers.mainThread())
                    .doOnCompleted(new Action0() {
                        @Override
                        public void call() {
                            NoteEditActivity.super.onBackPressed();
                        }
                    })
                    .subscribe(new Action1<Wrapper>() {
                        @Override
                        public void call(Wrapper wrapper) {
                            setResult(RESULT_OK);
                            XLog.i(TAG + wrapper.toString());

                            if (mIsNewNote && isTitleContentEmpty(wrapper.note)) {
                                XLog.i(TAG + "remove empty note, id=" + wrapper.note.getId());
                                AppDataBase.deleteNoteByLocalId(wrapper.note.getId());
                            } else {
                                saveAsDraft(wrapper);
                            }
                        }
                    });
        }
    }

    private Observable<Wrapper> filterUnchanged() {
        return Observable.create(
                new Observable.OnSubscribe<Wrapper>() {
                    @Override
                    public void call(Subscriber<? super Wrapper> subscriber) {
                        if (!subscriber.isUnsubscribed()) {
                            updateNote();
                            if (mOriginal.note.hasChanges(mModified.note)
                                    || isTitleContentEmpty(mModified.note)
                                    || !CollectionUtils.isTheSame(mOriginal.tags, mModified.tags)
                                    || mModified.note.isDirty()
                                    || isLocalNote(mModified.note)) {
                                subscriber.onNext(mModified);
                            }
                            subscriber.onCompleted();
                        }
                    }
                })
                .subscribeOn(Schedulers.io());
    }

    private boolean isTitleContentEmpty(Note note) {
        return TextUtils.isEmpty(note.getTitle()) && TextUtils.isEmpty(note.getContent());
    }

    private boolean isLocalNote(Note note) {
        return TextUtils.isEmpty(note.getNoteId());
    }

    private void updateNote() {
        String title = mEditorFragment.getTitle();
        String content = mEditorFragment.getContent();
        mModified.note.setTitle(title);
        mModified.note.setContent(content);
        mModified.note.setNoteBookId(mSettingsFragment.getNotebookId());
        List<String> tagTexts = mSettingsFragment.getTags();
        mModified.tags = new ArrayList<>();
        for (String tagText : tagTexts) {
            mModified.tags.add(tagText);
        }
        mModified.note.setIsPublicBlog(mSettingsFragment.shouldPublic());
    }

    private void saveAsDraft(Wrapper wrapper) {
        Note modifiedNote = wrapper.note;
        XLog.i(TAG + "saveAsDraft(), local id=" + modifiedNote.getId());
        Note noteFromDb = AppDataBase.getNoteByLocalId(modifiedNote.getId());
        noteFromDb.setContent(modifiedNote.getContent());
        noteFromDb.setTitle(modifiedNote.getTitle());
        noteFromDb.setNoteBookId(modifiedNote.getNoteBookId());
        noteFromDb.setIsPublicBlog(modifiedNote.isPublicBlog());
        noteFromDb.setIsDirty(true);
        long updateTime = System.currentTimeMillis();
        noteFromDb.setUpdatedTimeVal(updateTime);
        if (mIsNewNote) {
            noteFromDb.setCreatedTimeVal(updateTime);
        }
        noteFromDb.update();

        NoteService.updateTagsToLocal(modifiedNote.getId(), wrapper.tags);
    }

    @Override
    public Uri createImage(String filePath) {
        return NoteFileService.createImageFile(mModified.note.getId(), filePath);
    }

    @Override
    public Uri createAttach(String filePath) {
        return null;
    }

    @Override
    public void onInitialized() {
        mEditorFragment.setTitle(mModified.note.getTitle());
        mEditorFragment.setContent(mModified.note.getContent());
    }

    @Override
    public void onFragmentInitialized() {
        mSettingsFragment.setNotebookId(mModified.note.getNoteBookId());
        mSettingsFragment.setShouldPublic(mModified.note.isPublicBlog());
        List<String> tagTexts = new ArrayList<>();
        for (String tag : mModified.tags) {
            tagTexts.add(tag);
        }
        mSettingsFragment.setTags(tagTexts);
    }

    private class SectionAdapter extends FragmentPagerAdapter {


        public SectionAdapter(android.support.v4.app.FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            switch (position) {
                case 0:
                    return EditorFragment.getNewInstance(mModified.note.isMarkDown(), true);
                case 1:
                    return SettingFragment.getNewInstance();
                default:
                    return null;
            }
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            Fragment fragment = (Fragment) super.instantiateItem(container, position);
            switch (position) {
                case 0:
                    mEditorFragment = (EditorFragment) fragment;
                    break;
                case 1:
                    mSettingsFragment = (SettingFragment) fragment;
                    break;
            }

            return fragment;
        }

        @Override
        public int getCount() {
            return 2;
        }
    }

    private static class Wrapper {
        Note note;
        List<String> tags;

        public Wrapper(Note note) {
            this.note = note;
            List<Tag> tags = AppDataBase.getTagByNoteLocalId(note.getId());
            this.tags = new ArrayList<>();
            for (Tag tag : tags) {
                this.tags.add(tag.getText());
            }
        }
    }
} public class NetworkUtils {

    private static NetworkInfo getActiveNetworkInfo(Context context) {
        if (context == null) {
            return null;
        }
        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (cm == null) {
            return null;
        }
        // note that this may return null if no network is currently active
        return cm.getActiveNetworkInfo();
    }

    public static boolean isNetworkAvailable() {
        NetworkInfo info = getActiveNetworkInfo(Leamonax.getContext());
        return (info != null && info.isConnected());
    }

    public static void checkNetwork() throws NetworkUnavailableException {
        if (!isNetworkAvailable()) {
            throw new NetworkUnavailableException();
        }
    }

    public static class NetworkUnavailableException extends IllegalStateException {
        public NetworkUnavailableException() {
            super(Leamonax.getContext().getResources().getString(R.string.network_is_unavailable));
        }
    }
}
import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import com.elvishew.xlog.XLog;
import com.tencent.bugly.crashreport.CrashReport;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import org.houxg.leamonax.R;
import org.houxg.leamonax.adapter.NotebookAdapter;
import org.houxg.leamonax.background.NoteSyncService;
import org.houxg.leamonax.database.AppDataBase;
import org.houxg.leamonax.model.Account;
import org.houxg.leamonax.model.Note;
import org.houxg.leamonax.model.Notebook;
import org.houxg.leamonax.model.SyncEvent;
import org.houxg.leamonax.model.Tag;
import org.houxg.leamonax.service.AccountService;
import org.houxg.leamonax.ui.edit.NoteEditActivity;
import org.houxg.leamonax.utils.NetworkUtils;
import org.houxg.leamonax.utils.ToastUtils;
import java.util.Collections;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
        EventBus.getDefault().register(this);

        if (shouldReload) {
            mSwipeRefresh.postDelayed(new Runnable() {
                @Override
                public void run() {
                    XLog.i("fetching notes");
                    mSwipeRefresh.setRefreshing(true);
                    syncNotes();
                }
            }, 200);
        }
    }

    private void syncNotes() {
        if (!NetworkUtils.isNetworkAvailable()) {
            ToastUtils.showNetworkUnavailable(MainActivity.this);
            mSwipeRefresh.setRefreshing(false);
            return;
        }
        NoteSyncService.startServiceForNote(MainActivity.this);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mNavigation.onResume();
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == android.R.id.home) {
            mNavigation.toggle();
            return true;
        } else if (item.getItemId() == R.id.action_search) {
            Intent intent = new Intent(this, SearchActivity.class);
            startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        mNavigation.onActivityResult(requestCode, resultCode, data);
    }

    @Override
    public void onBackPressed() {
        if (mNavigation.isOpen()) {
            mNavigation.close();
        } else {
            super.onBackPressed();
        }
    }

    @OnClick(R.id.fab)
    void clickedFab() {
        Account account = AccountService.getCurrent();
        Note newNote = new Note();
        newNote.setUserId(account.getUserId());
        Notebook recentNotebook = AppDataBase.getRecentNoteBook(AccountService.getCurrent().getUserId());
        if (recentNotebook != null) {
            newNote.setNoteBookId(recentNotebook.getNotebookId());
        }
        newNote.setIsMarkDown(account.getDefaultEditor() == Account.EDITOR_MARKDOWN);
        newNote.save();
        Intent intent = NoteEditActivity.getOpenIntent(this, newNote.getId(), true);
        startActivity(intent);
    }

    @Override
    public boolean onChangeAccount(Account account) {
        account.updateLastUseTime();
        account.update();
        mNavigation.refresh();
        mSwipeRefresh.setRefreshing(true);
        syncNotes();
        return true;
    }

    @Override
    public boolean onShowNotes(Navigation.Mode mode) {
        List<Note> notes;
        switch (mode) {
            case RECENT_NOTES:
                notes = AppDataBase.getAllNotes(AccountService.getCurrent().getUserId());
                break;
            case NOTEBOOK:
                notes = AppDataBase.getNotesFromNotebook(AccountService.getCurrent().getUserId(), mode.notebookId);
                break;
            case TAG:
                notes = AppDataBase.getNotesByTagText(mode.tagText, AccountService.getCurrent().getUserId());
                break;
            default:
                return false;
        }
        mNoteFragment.setNotes(notes);
        return true;
    }

    @Override
    public void onClickSetting() {
        Intent intent = new Intent(this, SettingsActivity.class);
        startActivity(intent);
    }

    @Override
    public void onClickAbout() {
        Intent intent = new Intent(this, AboutActivity.class);
        startActivity(intent);
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(SyncEvent event) {