fix #14 Merge branch 'upstream/develop' into develop

# Conflicts:
#	app/src/main/java/org/houxg/leamonax/ui/Navigation.java
#	app/src/main/java/org/houxg/leamonax/ui/SearchActivity.java
#	app/src/main/res/values-zh/strings.xml
#	app/src/main/res/values/strings.xml
This commit is contained in:
houxg
2017-03-10 15:23:51 +08:00
12 changed files with 147 additions and 249 deletions

View File

@@ -59,6 +59,7 @@ public class NoteSyncService extends Service {
public void call(Subscriber<? super Void> subscriber) {
if (!subscriber.isUnsubscribed()) {
NoteService.fetchFromServer();
NoteService.pushToServer();
subscriber.onNext(null);
subscriber.onCompleted();
}

View File

@@ -49,7 +49,6 @@ public class NoteDataStore {
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();
@@ -59,9 +58,7 @@ public class NoteDataStore {
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))
.and(Note_Table.isDirty.eq(true))
.queryList();
}
@@ -75,7 +72,6 @@ public class NoteDataStore {
.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();

View File

@@ -90,7 +90,6 @@ public class Note extends BaseModel implements Serializable {
long publicTime;
@Column(name = "tags")
String tags = "";
boolean uploadSucc = true;
public long getCreatedTimeVal() {
return createdTime;
@@ -168,14 +167,6 @@ public class Note extends BaseModel implements Serializable {
return usn;
}
public boolean isUploadSucc() {
return uploadSucc;
}
public void setUploadSucc(boolean uploadSucc) {
this.uploadSucc = uploadSucc;
}
public void setUsn(int usn) {
this.usn = usn;
}

View File

@@ -38,6 +38,7 @@ import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import rx.Observable;
public class NoteService {
@@ -48,6 +49,15 @@ public class NoteService {
private static final String CONFLICT_SUFFIX = "--conflict";
private static final int MAX_ENTRY = 20;
public static void pushToServer() {
List<Note> notes = NoteDataStore.getAllDirtyNotes(Account.getCurrent().getUserId());
for (Note note : notes) {
if (!note.getTitle().endsWith(CONFLICT_SUFFIX)) {
saveNote(note.getId());
}
}
}
public static void fetchFromServer() {
//sync notebook
int notebookUsn = Account.getCurrent().getNotebookUsn();
@@ -313,6 +323,7 @@ public class NoteService {
requestBodyMap.put("Content", createPartFromString(content));
requestBodyMap.put("IsMarkdown", createPartFromString(getBooleanString(note.isMarkDown())));
requestBodyMap.put("IsBlog", createPartFromString(getBooleanString(note.isPublicBlog())));
requestBodyMap.put("IsTrash", createPartFromString(getBooleanString(note.isTrash())));
requestBodyMap.put("CreatedTime", createPartFromString(TimeUtils.toServerTime(note.getCreatedTimeVal())));
requestBodyMap.put("UpdatedTime", createPartFromString(TimeUtils.toServerTime(note.getUpdatedTimeVal())));
@@ -389,6 +400,18 @@ public class NoteService {
return localIds;
}
public static void trashNotesOnLocal(Note note) {
note.setIsTrash(true);
note.setIsDirty(true);
note.update();
}
public static void trashNote(Note note) {
if (!note.isLocalNote()) {
saveNote(note.getId());
}
}
public static void deleteNote(Note note) {
if (note.isLocalNote()) {
note.delete();

View File

@@ -20,7 +20,6 @@ import org.greenrobot.eventbus.ThreadMode;
import org.houxg.leamonax.R;
import org.houxg.leamonax.background.NoteSyncService;
import org.houxg.leamonax.component.PullToRefresh;
import org.houxg.leamonax.database.NoteDataStore;
import org.houxg.leamonax.database.NotebookDataStore;
import org.houxg.leamonax.model.Account;
import org.houxg.leamonax.model.Note;
@@ -30,7 +29,6 @@ import org.houxg.leamonax.ui.edit.NoteEditActivity;
import org.houxg.leamonax.utils.NetworkUtils;
import org.houxg.leamonax.utils.ToastUtils;
import java.util.List;
import java.util.Locale;
import butterknife.BindView;
@@ -148,8 +146,8 @@ public class MainActivity extends BaseActivity implements Navigation.Callback {
Note newNote = new Note();
newNote.setUserId(account.getUserId());
Notebook notebook;
Navigation.Mode currentMode = mNavigation.getCurrentMode();
if (currentMode == Navigation.Mode.NOTEBOOK) {
NoteFragment.Mode currentMode = mNavigation.getCurrentMode();
if (currentMode == NoteFragment.Mode.NOTEBOOK) {
notebook = NotebookDataStore.getByLocalId(currentMode.notebookId);
} else {
notebook = NotebookDataStore.getRecentNoteBook(Account.getCurrent().getUserId());
@@ -178,22 +176,8 @@ public class MainActivity extends BaseActivity implements Navigation.Callback {
}
@Override
public boolean onShowNotes(Navigation.Mode mode) {
List<Note> notes;
switch (mode) {
case RECENT_NOTES:
notes = NoteDataStore.getAllNotes(Account.getCurrent().getUserId());
break;
case NOTEBOOK:
notes = NoteDataStore.getNotesFromNotebook(Account.getCurrent().getUserId(), mode.notebookId);
break;
case TAG:
notes = NoteDataStore.getByTagText(mode.tagText, Account.getCurrent().getUserId());
break;
default:
return false;
}
mNoteFragment.setNotes(notes);
public boolean onShowNotes(NoteFragment.Mode mode) {
mNoteFragment.setMode(mode);
return true;
}

View File

@@ -97,7 +97,7 @@ public class Navigation {
private TagAdapter mTagAdapter;
private AlphabetDrawable mAlphabetDrawable = new AlphabetDrawable();
private Mode mCurrentMode = Mode.RECENT_NOTES;
private NoteFragment.Mode mCurrentMode = NoteFragment.Mode.RECENT_NOTES;
public Navigation(Callback callback) {
mCallback = callback;
@@ -265,7 +265,7 @@ public class Navigation {
mTagAdapter.setListener(new TagAdapter.TagAdapterListener() {
@Override
public void onClickedTag(Tag tag) {
mCurrentMode = Mode.TAG;
mCurrentMode = NoteFragment.Mode.TAG;
mCurrentMode.setTagText(tag.getText());
if (mCallback != null) {
if (mCallback.onShowNotes(mCurrentMode)) {
@@ -289,7 +289,7 @@ public class Navigation {
mNotebookAdapter.setListener(new NotebookAdapter.NotebookAdapterListener() {
@Override
public void onClickedNotebook(Notebook notebook) {
mCurrentMode = Mode.NOTEBOOK;
mCurrentMode = NoteFragment.Mode.NOTEBOOK;
mCurrentMode.setNotebookId(notebook.getId());
if (mCallback != null) {
if (mCallback.onShowNotes(mCurrentMode)) {
@@ -455,7 +455,7 @@ public class Navigation {
}
}
public Mode getCurrentMode() {
public NoteFragment.Mode getCurrentMode() {
return mCurrentMode;
}
@@ -464,8 +464,8 @@ public class Navigation {
mAccountAdapter.load(AccountService.getAccountList());
mTagAdapter.refresh();
mNotebookAdapter.refresh();
if (mCurrentMode == Mode.NOTEBOOK && TextUtils.isEmpty(mNotebookAdapter.getCurrentParentId())) {
mCurrentMode = Mode.RECENT_NOTES;
if (mCurrentMode == NoteFragment.Mode.NOTEBOOK && TextUtils.isEmpty(mNotebookAdapter.getCurrentParentId())) {
mCurrentMode = NoteFragment.Mode.RECENT_NOTES;
}
if (mCallback != null) {
if (mCallback.onShowNotes(mCurrentMode)) {
@@ -546,7 +546,7 @@ public class Navigation {
@OnClick(R.id.rl_recent_notes)
void clickedRecent() {
mCurrentMode = Mode.RECENT_NOTES;
mCurrentMode = NoteFragment.Mode.RECENT_NOTES;
if (mCallback != null) {
if (mCallback.onShowNotes(mCurrentMode)) {
close();
@@ -561,38 +561,13 @@ public class Navigation {
* @param mode
* @return true if processed
*/
boolean onShowNotes(Mode mode);
boolean onShowNotes(NoteFragment.Mode mode);
void onClickSetting();
void onClickAbout();
}
public enum Mode {
RECENT_NOTES,
NOTEBOOK,
TAG;
long notebookId;
String tagText;
public void setNotebookId(long notebookId) {
this.notebookId = notebookId;
}
public void setTagText(String tagText) {
this.tagText = tagText;
}
@Override
public String toString() {
return name() + "{" +
"notebookId=" + notebookId +
", tagText='" + tagText + '\'' +
'}';
}
}
@OnClick(R.id.rl_blog)
void clickedMyBlog() {
Account current = Account.getCurrent();

View File

@@ -15,12 +15,16 @@ import android.view.View;
import android.view.ViewGroup;
import org.greenrobot.eventbus.EventBus;
import org.houxg.leamonax.Leamonax;
import org.houxg.leamonax.R;
import org.houxg.leamonax.adapter.NoteAdapter;
import org.houxg.leamonax.database.NoteDataStore;
import org.houxg.leamonax.model.Account;
import org.houxg.leamonax.model.Note;
import org.houxg.leamonax.service.NoteService;
import org.houxg.leamonax.utils.ActionModeHandler;
import org.houxg.leamonax.utils.CollectionUtils;
import org.houxg.leamonax.utils.NetworkUtils;
import org.houxg.leamonax.utils.SharedPreferenceUtils;
import org.houxg.leamonax.utils.ToastUtils;
import org.houxg.leamonax.widget.NoteList;
@@ -51,6 +55,7 @@ public class NoteFragment extends Fragment implements NoteAdapter.NoteAdapterLis
List<Note> mNotes;
ActionModeHandler<Note> mActionModeHandler;
NoteList mNoteList;
Mode mCurrentMode;
public NoteFragment() {
}
@@ -117,7 +122,27 @@ public class NoteFragment extends Fragment implements NoteAdapter.NoteAdapterLis
EventBus.getDefault().unregister(this);
}
public void setNotes(List<Note> notes) {
public void setMode(Mode mode) {
mCurrentMode = mode;
List<Note> notes;
mNoteList.setHighlight("");
switch (mode) {
case RECENT_NOTES:
notes = NoteDataStore.getAllNotes(Account.getCurrent().getUserId());
break;
case NOTEBOOK:
notes = NoteDataStore.getNotesFromNotebook(Account.getCurrent().getUserId(), mode.notebookId);
break;
case TAG:
notes = NoteDataStore.getByTagText(mode.tagText, Account.getCurrent().getUserId());
break;
case SEARCH:
notes = NoteDataStore.searchByTitle(mode.keywords);
mNoteList.setHighlight(mode.keywords);
break;
default:
notes = new ArrayList<>();
}
mNotes = notes;
Collections.sort(mNotes, new Note.UpdateTimeComparetor());
mNoteList.render(mNotes);
@@ -139,7 +164,7 @@ public class NoteFragment extends Fragment implements NoteAdapter.NoteAdapterLis
mNoteList.setSelected(note, isSelected);
}
private void deleteNote(final List<Note> notes) {
private void deleteNote(List<Note> notes) {
Observable.from(notes)
.flatMap(new Func1<Note, rx.Observable<Note>>() {
@Override
@@ -148,7 +173,30 @@ public class NoteFragment extends Fragment implements NoteAdapter.NoteAdapterLis
@Override
public void call(Subscriber<? super Note> subscriber) {
if (!subscriber.isUnsubscribed()) {
NoteService.deleteNote(note);
NoteService.trashNotesOnLocal(note);
subscriber.onNext(note);
subscriber.onCompleted();
}
}
});
}
})
.buffer(notes.size())
.flatMap(new Func1<List<Note>, Observable<Note>>() {
@Override
public Observable<Note> call(List<Note> notes) {
NetworkUtils.checkNetwork();
return Observable.from(notes);
}
})
.flatMap(new Func1<Note, Observable<Note>>() {
@Override
public Observable<Note> call(final Note note) {
return Observable.create(new Observable.OnSubscribe<Note>() {
@Override
public void call(Subscriber<? super Note> subscriber) {
if (!subscriber.isUnsubscribed()) {
NoteService.saveNote(note.getId());
subscriber.onNext(note);
subscriber.onCompleted();
}
@@ -166,8 +214,12 @@ public class NoteFragment extends Fragment implements NoteAdapter.NoteAdapterLis
@Override
public void onError(Throwable e) {
ToastUtils.show(getActivity(), R.string.delete_note_failed);
mNoteList.invalidateAllSelected();
if (e instanceof NetworkUtils.NetworkUnavailableException) {
ToastUtils.show(Leamonax.getContext(), R.string.delete_network_error);
} else {
ToastUtils.show(Leamonax.getContext(), R.string.delete_note_failed);
}
refresh();
}
@Override
@@ -177,6 +229,9 @@ public class NoteFragment extends Fragment implements NoteAdapter.NoteAdapterLis
});
}
private void refresh() {
setMode(mCurrentMode);
}
@Override
public boolean onAction(int actionId, List<Note> pendingItems) {
@@ -218,4 +273,35 @@ public class NoteFragment extends Fragment implements NoteAdapter.NoteAdapterLis
}
}
public enum Mode {
RECENT_NOTES,
NOTEBOOK,
TAG,
SEARCH;
long notebookId;
String tagText;
String keywords;
public void setNotebookId(long notebookId) {
this.notebookId = notebookId;
}
public void setTagText(String tagText) {
this.tagText = tagText;
}
public void setKeywords(String keywords) {
this.keywords = keywords;
}
@Override
public String toString() {
return name() + "{" +
"notebookId=" + notebookId +
", tagText='" + tagText + '\'' +
'}';
}
}
}

View File

@@ -1,11 +1,7 @@
package org.houxg.leamonax.ui;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
@@ -13,42 +9,18 @@ import android.view.View;
import android.widget.ImageView;
import org.houxg.leamonax.R;
import org.houxg.leamonax.adapter.NoteAdapter;
import org.houxg.leamonax.database.NoteDataStore;
import org.houxg.leamonax.model.Note;
import org.houxg.leamonax.service.NoteService;
import org.houxg.leamonax.utils.ActionModeHandler;
import org.houxg.leamonax.utils.CollectionUtils;
import org.houxg.leamonax.utils.ToastUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import rx.Observable;
import rx.Observer;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Func1;
import rx.schedulers.Schedulers;
public class SearchActivity extends BaseActivity implements NoteAdapter.NoteAdapterListener, ActionModeHandler.Callback<Note> {
public class SearchActivity extends BaseActivity {
private static final String EXT_SCROLL_POSITION = "ext_scroll_position";
@BindView(R.id.recycler_view)
RecyclerView mNoteListView;
@BindView(R.id.toolbar)
Toolbar mToolbar;
@BindView(R.id.search)
SearchView mSearchView;
List<Note> mNotes = new ArrayList<>();
private NoteAdapter mAdapter;
private ActionModeHandler<Note> mActionModeHandler;
private float mScrollPosition;
private NoteFragment mNoteFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -57,27 +29,6 @@ public class SearchActivity extends BaseActivity implements NoteAdapter.NoteAdap
ButterKnife.bind(this);
initToolBar(mToolbar, true);
setTitle("");
mActionModeHandler = new ActionModeHandler<>(this, this, R.menu.delete);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
mNoteListView.setLayoutManager(layoutManager);
mNoteListView.setItemAnimator(new DefaultItemAnimator());
mAdapter = new NoteAdapter(this);
mNoteListView.setAdapter(mAdapter);
mNoteListView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
mScrollPosition = dy;
}
});
mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
@@ -86,11 +37,18 @@ public class SearchActivity extends BaseActivity implements NoteAdapter.NoteAdap
@Override
public boolean onQueryTextChange(String newText) {
searchTitle(newText);
NoteFragment.Mode mode = NoteFragment.Mode.SEARCH;
mode.setKeywords(newText);
mNoteFragment.setMode(mode);
return true;
}
});
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
mNoteFragment = NoteFragment.newInstance();
transaction.add(R.id.container, mNoteFragment);
transaction.commit();
ImageView searchCloseIcon = (ImageView) mSearchView.findViewById(android.support.v7.appcompat.R.id.search_close_btn);
searchCloseIcon.setImageResource(R.drawable.ic_clear);
final ImageView searchIcon = (ImageView) mSearchView.findViewById(android.support.v7.appcompat.R.id.search_mag_icon);
@@ -107,126 +65,4 @@ public class SearchActivity extends BaseActivity implements NoteAdapter.NoteAdap
mSearchView.setIconified(false);
mSearchView.setIconifiedByDefault(false);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mScrollPosition = savedInstanceState.getFloat(EXT_SCROLL_POSITION, 0);
}
@Override
protected void onResume() {
super.onResume();
mNoteListView.scrollTo(0, (int) mScrollPosition);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putFloat(EXT_SCROLL_POSITION, mScrollPosition);
}
private void searchTitle(String keyword) {
if (TextUtils.isEmpty(keyword)) {
mNotes = new ArrayList<>();
} else {
mNotes = NoteDataStore.searchByTitle(keyword);
Collections.sort(mNotes, new Note.UpdateTimeComparetor());
}
mAdapter.setHighlight(keyword);
mAdapter.load(mNotes);
}
@Override
public void onClickNote(Note note) {
if (mActionModeHandler.isActionMode()) {
boolean isSelected = mActionModeHandler.chooseItem(note);
mAdapter.setSelected(note, isSelected);
} else {
startActivity(NotePreviewActivity.getOpenIntent(this, note.getId()));
}
}
@Override
public void onLongClickNote(final Note note) {
boolean isSelected = mActionModeHandler.chooseItem(note);
mAdapter.setSelected(note, isSelected);
}
private void deleteNote(final List<Note> notes) {
Observable.from(notes)
.flatMap(new Func1<Note, Observable<Note>>() {
@Override
public rx.Observable<Note> call(final Note note) {
return Observable.create(new Observable.OnSubscribe<Note>() {
@Override
public void call(Subscriber<? super Note> subscriber) {
if (!subscriber.isUnsubscribed()) {
NoteService.deleteNote(note);
subscriber.onNext(note);
subscriber.onCompleted();
}
}
});
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Note>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
ToastUtils.show(SearchActivity.this, R.string.delete_note_failed);
}
@Override
public void onNext(Note note) {
mAdapter.delete(note);
}
});
}
@Override
public boolean onAction(int actionId, List<Note> pendingItems) {
if (CollectionUtils.isEmpty(pendingItems)) {
ToastUtils.show(this, R.string.no_note_was_selected);
return false;
}
final List<Note> waitToDelete = new ArrayList<>();
for (int i = 0; i < pendingItems.size(); i++) {
waitToDelete.add(pendingItems.get(i));
}
new AlertDialog.Builder(this)
.setTitle(R.string.delete_note)
.setMessage(R.string.are_you_sure_to_delete_note)
.setCancelable(true)
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
mActionModeHandler.getPendingItems().clear();
mActionModeHandler.dismiss();
deleteNote(waitToDelete);
}
})
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
})
.show();
return true;
}
@Override
public void onDestroy(List<Note> pendingItems) {
if (CollectionUtils.isNotEmpty(pendingItems)) {
mAdapter.invalidateAllSelected();
}
}
}

View File

@@ -66,6 +66,10 @@ public class NoteList {
mAdapter.notifyDataSetChanged();
}
public void setHighlight(String keyword) {
mAdapter.setHighlight(keyword);
}
public void toggleType() {
int newType = mCurrentType == TYPE_SIMPLE ? TYPE_DETAIL : TYPE_SIMPLE;
setType(newType);

View File

@@ -21,9 +21,9 @@
</android.support.v7.widget.Toolbar>
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"/>
</LinearLayout>

View File

@@ -112,4 +112,5 @@
<string name="update_notebook_title">修改笔记本的名称</string>
<string name="notebook_choose_notebook_hint">选择该笔记本所在的上一级笔记本目录</string>
<string name="notebook_default_root_notebook_title">空目录(最顶层目录)</string>
<string name="delete_network_error">网络不可用,将在下次同步时删除</string>
</resources>

View File

@@ -114,4 +114,5 @@
<string name="update_notebook_title">Update notebook title</string>
<string name="notebook_choose_notebook_hint">选择该笔记本所在的上一级笔记本目录 Choose current Notebook \'s parentNotebook directory</string>
<string name="notebook_default_root_notebook_title">Empty directory</string>
<string name="delete_network_error">Network is unavailable, these notes will be deleted in next sync</string>
</resources>