Java tutorial
/* * Copyright (C) 2009-2015 FBReader.ORG Limited <contact@fbreader.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ package org.geometerplus.android.fbreader; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.support.design.widget.TabLayout; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.view.ContextMenu; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.TextView; import com.laichushu.book.R; import com.laichushu.book.db.Idea_Table; import com.laichushu.book.db.Idea_TableDao; import com.laichushu.book.global.BaseApplication; import com.laichushu.book.global.ConstantValue; import com.laichushu.book.utils.ToastUtil; import com.laichushu.book.utils.UIUtil; import org.fbreader.util.FBReaderWindowUtil; import org.geometerplus.android.fbreader.api.FBReaderIntents; import org.geometerplus.android.fbreader.bookmark.BookmarksEditActivity; import org.geometerplus.android.fbreader.libraryService.BookCollectionShadow; import org.geometerplus.android.util.OrientationUtil; import org.geometerplus.android.util.UIMessageUtil; import org.geometerplus.android.util.ViewUtil; import org.geometerplus.fbreader.book.Book; import org.geometerplus.fbreader.book.BookEvent; import org.geometerplus.fbreader.book.Bookmark; import org.geometerplus.fbreader.book.BookmarkQuery; import org.geometerplus.fbreader.book.HighlightingStyle; import org.geometerplus.fbreader.book.IBookCollection; import org.geometerplus.fbreader.bookmodel.TOCTree; import org.geometerplus.fbreader.fbreader.FBReaderApp; import org.geometerplus.zlibrary.core.application.ZLApplication; import org.geometerplus.zlibrary.core.tree.ZLTree; import org.geometerplus.zlibrary.core.util.ZLColor; import org.geometerplus.zlibrary.ui.android.library.UncaughtExceptionHandler; import org.geometerplus.zlibrary.ui.android.util.ZLAndroidColorUtil; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import yuku.ambilwarna.widget.AmbilWarnaPrefWidgetView; /** * */ public class TOCActivity extends Activity implements IBookCollection.Listener<Book> { private TabLayout mTabLayout; private ViewPager mViewPager; private LayoutInflater mInflater; private List<String> mTitleList = new ArrayList<>(); // ?? private View view1, view2, view3; // ? private List<View> mViewList = new ArrayList<>(); // ?? private TOCAdapter myAdapter; private ZLTree<?> mySelectedItem; private RelativeLayout rlLayout; private TextView tvBook; private RelativeLayout rlBookmark; private RelativeLayout rlNote; private static final int OPEN_ITEM_ID = 0; private static final int EDIT_ITEM_ID = 1; private static final int DELETE_ITEM_ID = 2; private final Map<Integer, HighlightingStyle> myStyles = Collections .synchronizedMap(new HashMap<Integer, HighlightingStyle>()); private final BookCollectionShadow myCollection = new BookCollectionShadow(); private volatile Book myBook; private volatile Bookmark myBookmark; private final Comparator<Bookmark> myComparator = new Bookmark.ByTimeComparator(); private volatile BookmarksAdapter myThisBookAdapter;//adapter private ListView thisBookListView; private String bgValue; private RelativeLayout emptyRl; private ListView markLv; private BookmarkAdapter bookmarkAdapter; private FBReaderApp kooreader; @Override protected void onCreate(Bundle bundle) { super.onCreate(bundle); Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler(this)); setContentView(R.layout.listview_toc); rlLayout = (RelativeLayout) findViewById(R.id.rl_shelf); tvBook = (TextView) findViewById(R.id.tv_book); mViewPager = (ViewPager) findViewById(R.id.vp_view); mTabLayout = (TabLayout) findViewById(R.id.tabs); mInflater = LayoutInflater.from(this); view1 = mInflater.inflate(R.layout.item_listview, null); ListView listView = (ListView) view1.findViewById(R.id.listview); kooreader = (FBReaderApp) ZLApplication.Instance(); final TOCTree root = kooreader.Model.TOCTree; tvBook.setText(kooreader.getCurrentBook().getTitle()); myAdapter = new TOCAdapter(listView, root); TOCTree treeToSelect = kooreader.getCurrentTOCElement(); myAdapter.selectItem(treeToSelect); // ?? mySelectedItem = treeToSelect; view2 = mInflater.inflate(R.layout.list_bookmark, null); rlBookmark = (RelativeLayout) view2.findViewById(R.id.rl_bookmark); thisBookListView = (ListView) view2.findViewById(R.id.bookmark_this_book); myBook = FBReaderIntents.getBookExtra(getIntent(), myCollection); myBookmark = FBReaderIntents.getBookmarkExtra(getIntent()); view3 = mInflater.inflate(R.layout.list_note, null); rlNote = (RelativeLayout) view3.findViewById(R.id.rl_note); emptyRl = (RelativeLayout) view3.findViewById(R.id.rl_null);// markLv = (ListView) view3.findViewById(R.id.lv_mark); myCollection.bindToService(this, new Runnable() { public void run() { myThisBookAdapter = new BookmarksAdapter(thisBookListView, myBookmark != null); bookmarkAdapter = new BookmarkAdapter(markLv); myCollection.addListener(TOCActivity.this); updateStyles(); loadBookmarks(); } }); /** * */ bgValue = kooreader.ViewOptions.getColorProfile().WallpaperOption.getValue(); bgValue = "wallpapers/bg_vine_grey.png"; switch (bgValue) { case "wallpapers/bg_green.png": listView.setBackgroundResource(R.drawable.bg_green); rlLayout.setBackgroundResource(R.drawable.bg_green); mTabLayout.setBackgroundResource(R.drawable.bg_green); rlBookmark.setBackgroundResource(R.drawable.bg_green); rlNote.setBackgroundResource(R.drawable.bg_green); break; case "wallpapers/bg_grey.png": listView.setBackgroundResource(R.drawable.bg_grey); rlLayout.setBackgroundResource(R.drawable.bg_grey); mTabLayout.setBackgroundResource(R.drawable.bg_grey); rlBookmark.setBackgroundResource(R.drawable.bg_grey); rlNote.setBackgroundResource(R.drawable.bg_grey); break; case "wallpapers/bg_night.png": listView.setBackgroundResource(R.drawable.bg_white); rlLayout.setBackgroundResource(R.drawable.bg_white); mTabLayout.setBackgroundResource(R.drawable.bg_white); rlBookmark.setBackgroundResource(R.drawable.bg_white); rlNote.setBackgroundResource(R.drawable.bg_white); break; case "wallpapers/bg_vine_grey.png": listView.setBackgroundResource(R.drawable.bg_vine_grey); rlLayout.setBackgroundResource(R.drawable.bg_vine_grey); // mTabLayout.setBackgroundResource(R.drawable.bg_vine_grey); // rlBookmark.setBackgroundResource(R.drawable.bg_vine_grey); // rlNote.setBackgroundResource(R.drawable.bg_vine_grey); break; case "wallpapers/bg_vine_white.png": listView.setBackgroundResource(R.drawable.bg_vine_white); rlLayout.setBackgroundResource(R.drawable.bg_vine_white); mTabLayout.setBackgroundResource(R.drawable.bg_vine_white); rlBookmark.setBackgroundResource(R.drawable.bg_vine_white); rlNote.setBackgroundResource(R.drawable.bg_vine_white); break; case "wallpapers/bg_white.png": listView.setBackgroundResource(R.drawable.bg_white); rlLayout.setBackgroundResource(R.drawable.bg_white); mTabLayout.setBackgroundResource(R.drawable.bg_white); rlBookmark.setBackgroundResource(R.drawable.bg_white); rlNote.setBackgroundResource(R.drawable.bg_white); break; } //? mViewList.add(view1); mViewList.add(view3); mViewList.add(view2); //? mTitleList.add(""); mTitleList.add(""); mTitleList.add(""); mTabLayout.setTabMode(TabLayout.MODE_FIXED);//tab??? mTabLayout.addTab(mTabLayout.newTab().setText(mTitleList.get(0)));//tab? mTabLayout.addTab(mTabLayout.newTab().setText(mTitleList.get(1))); mTabLayout.addTab(mTabLayout.newTab().setText(mTitleList.get(2))); MyPagerAdapter mAdapter = new MyPagerAdapter(mViewList); mViewPager.setAdapter(mAdapter); // ViewPager? mTabLayout.setupWithViewPager(mViewPager); // TabLayoutViewPager?? mTabLayout.setTabsFromPagerAdapter(mAdapter); // Tabs? } @Override protected void onStart() { super.onStart(); OrientationUtil.setOrientation(this, getIntent()); } @Override protected void onNewIntent(Intent intent) { OrientationUtil.setOrientation(this, intent); } private final class TOCAdapter extends ZLTreeAdapter { TOCAdapter(ListView listView, TOCTree root) { super(listView, root); } @Override public View getView(int position, View convertView, ViewGroup parent) { final View view = (convertView != null) ? convertView : LayoutInflater.from(parent.getContext()).inflate(R.layout.toc_tree_item, parent, false); final TOCTree tree = (TOCTree) getItem(position); ViewUtil.findTextView(view, R.id.toc_tree_item_text).setText(tree.getText()); return view; } void openBookText(TOCTree tree) { final TOCTree.Reference reference = tree.getReference(); if (reference != null) { finish(); final FBReaderApp kooreader = (FBReaderApp) ZLApplication.Instance(); kooreader.addInvisibleBookmark(); kooreader.BookTextView.gotoPosition(reference.ParagraphIndex, 0, 0); kooreader.showBookTextView(); kooreader.storePosition(); } } @Override protected boolean runTreeItem(ZLTree<?> tree) { if (super.runTreeItem(tree)) { return true; } if (ConstantValue.ISREADER) { FBReaderApp fbReaderApp = (FBReaderApp) FBReaderApp.Instance(); if (fbReaderApp.getCurrentTOCElement() != null) { TOCTree parent = fbReaderApp.getCurrentTOCElement().Parent; List<TOCTree> subtrees = parent.subtrees();// for (int i = 0; i < subtrees.size(); i++) { if (((TOCTree) tree).getText().equals(subtrees.get(i).getText())) { if (i > ConstantValue.ISREADER_NUMBER - 1) { ToastUtil.showToast("???"); return false; } } } } } openBookText((TOCTree) tree); return true; } } //ViewPager? class MyPagerAdapter extends PagerAdapter { private List<View> mViewList; public MyPagerAdapter(List<View> mViewList) { this.mViewList = mViewList; } @Override public int getCount() { return mViewList.size();//? } @Override public boolean isViewFromObject(View view, Object object) { return view == object;//?? } @Override public Object instantiateItem(ViewGroup container, int position) { container.addView(mViewList.get(position));//? return mViewList.get(position); } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView(mViewList.get(position));//? } @Override public CharSequence getPageTitle(int position) { return mTitleList.get(position);//? } } private void updateStyles() { synchronized (myStyles) { myStyles.clear(); for (HighlightingStyle style : myCollection.highlightingStyles()) { myStyles.put(style.Id, style); } } } private final Object myBookmarksLock = new Object(); private void loadBookmarks() { new Thread(new Runnable() { public void run() { synchronized (myBookmarksLock) { for (BookmarkQuery query = new BookmarkQuery(myBook, 10000);; query = query.next()) { final List<Bookmark> thisBookBookmarks = myCollection.bookmarks(query); if (thisBookBookmarks.isEmpty()) { break; } myThisBookAdapter.addAll(thisBookBookmarks); bookmarkAdapter.addAll(thisBookBookmarks); // myAllBooksAdapter.addAll(thisBookBookmarks); } // for (BookmarkQuery query = new BookmarkQuery(50); ; query = query.next()) { // final List<Bookmark> allBookmarks = myCollection.bookmarks(query); // if (allBookmarks.isEmpty()) { // break; // } // myAllBooksAdapter.addAll(allBookmarks); // } } } }).start(); } private void updateBookmarks(final Book book) { new Thread(new Runnable() { public void run() { synchronized (myBookmarksLock) { final boolean flagThisBookTab = book.getId() == myBook.getId(); final Map<String, Bookmark> oldBookmarks = new HashMap<String, Bookmark>(); final Map<String, Bookmark> oldBookmarks2 = new HashMap<String, Bookmark>(); if (flagThisBookTab) { for (Bookmark b : myThisBookAdapter.bookmarks()) { oldBookmarks.put(b.Uid, b); } for (Bookmark b : bookmarkAdapter.bookmarks()) { oldBookmarks2.put(b.Uid, b); } } for (BookmarkQuery query = new BookmarkQuery(book, 50);; query = query.next()) { final List<Bookmark> loaded = myCollection.bookmarks(query); if (loaded.isEmpty()) { break; } for (Bookmark b : loaded) { final Bookmark old = oldBookmarks.remove(b.Uid); // myAllBooksAdapter.replace(old, b); if (flagThisBookTab) { myThisBookAdapter.replace(old, b); } } for (Bookmark b : loaded) { final Bookmark old = oldBookmarks2.remove(b.Uid); if (flagThisBookTab) { bookmarkAdapter.replace(old, b); } } } if (flagThisBookTab) { myThisBookAdapter.removeAll(oldBookmarks.values()); bookmarkAdapter.removeAll(oldBookmarks.values()); } } } }).start(); } @Override public void onDestroy() { myCollection.unbind(); super.onDestroy(); } private boolean isBookMark; @Override public boolean onContextItemSelected(MenuItem item) { final int position = ((AdapterView.AdapterContextMenuInfo) item.getMenuInfo()).position; BaseAdapter adapter = null; if (isBookMark) {//? adapter = bookmarkAdapter; } else { adapter = myThisBookAdapter; } final Bookmark bookmark = (Bookmark) adapter.getItem(position); switch (item.getItemId()) { case OPEN_ITEM_ID:// gotoBookmark(bookmark); return true; case EDIT_ITEM_ID:// final Intent intent = new Intent(this, BookmarksEditActivity.class); intent.putExtra("bgColor", bgValue); intent.putExtra("type", true); FBReaderIntents.putBookmarkExtra(intent, bookmark); OrientationUtil.startActivity(this, intent); return true; case DELETE_ITEM_ID:// myCollection.deleteBookmark(bookmark); FBReaderWindowUtil.deleteBookmarkForIdea_Table(bookmark); if (isBookMark) { bookmarkAdapter.myBookmarksList.remove(bookmark); bookmarkAdapter.notifyDataSetChanged(); if (bookmarkAdapter.myBookmarksList.size() == 0) { emptyRl.setVisibility(View.VISIBLE); markLv.setVisibility(View.INVISIBLE); } } return true; } return super.onContextItemSelected(item); } private void gotoBookmark(Bookmark bookmark) { bookmark.markAsAccessed(); myCollection.saveBookmark(bookmark); final Book book = myCollection.getBookById(bookmark.BookId); if (book != null) { // int paragraphIndex = bookmark.getParagraphIndex(); // int paragraphIndex1 = kooreader.getCurrentTOCElement().getReference().ParagraphIndex; // int abs = paragraphIndex1 - paragraphIndex; // kooreader.getTextView().gotoPosition(paragraphIndex1+abs,0,0); // FBReader.openBookActivity(this, book, bookmark); kooreader.getTextView().gotoPosition(bookmark); kooreader.getViewWidget().reset(); kooreader.getViewWidget().repaint(); finish(); } else { UIMessageUtil.showErrorMessage(this, "cannotOpenBook"); } } private final class BookmarksAdapter extends BaseAdapter implements AdapterView.OnItemClickListener, View.OnCreateContextMenuListener { private final List<Bookmark> myBookmarksList = Collections.synchronizedList(new LinkedList<Bookmark>()); private volatile boolean myShowAddBookmarkItem; BookmarksAdapter(ListView listView, boolean showAddBookmarkItem) { myShowAddBookmarkItem = showAddBookmarkItem; listView.setAdapter(this); listView.setOnItemClickListener(this); listView.setOnCreateContextMenuListener(this); } public List<Bookmark> bookmarks() { return Collections.unmodifiableList(myBookmarksList); } public void addAll(final List<Bookmark> bookmarks) { runOnUiThread(new Runnable() { public void run() { synchronized (myBookmarksList) { for (Bookmark b : bookmarks) { final int position = Collections.binarySearch(myBookmarksList, b, myComparator); if (position < 0 && b.getStyleId() != 5) { myBookmarksList.add(-position - 1, b); } } } notifyDataSetChanged(); } }); } private boolean areEqualsForView(Bookmark b0, Bookmark b1) { return b0.getStyleId() == b1.getStyleId() && b0.getText().equals(b1.getText()) && b0.getTimestamp(Bookmark.DateType.Latest).equals(b1.getTimestamp(Bookmark.DateType.Latest)); } public void replace(final Bookmark old, final Bookmark b) { if (old != null && areEqualsForView(old, b)) { return; } if (b.getStyleId() == 5) { return; } runOnUiThread(new Runnable() { public void run() { synchronized (myBookmarksList) { if (old != null) { myBookmarksList.remove(old); } final int position = Collections.binarySearch(myBookmarksList, b, myComparator); if (position < 0) { myBookmarksList.add(-position - 1, b); } } notifyDataSetChanged(); } }); } public void removeAll(final Collection<Bookmark> bookmarks) { if (bookmarks.isEmpty()) { return; } runOnUiThread(new Runnable() { public void run() { myBookmarksList.removeAll(bookmarks); notifyDataSetChanged(); } }); } public void clear() { runOnUiThread(new Runnable() { public void run() { myBookmarksList.clear(); notifyDataSetChanged(); } }); } public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) { final int position = ((AdapterView.AdapterContextMenuInfo) menuInfo).position; if (getItem(position) != null) { isBookMark = false; menu.add(0, OPEN_ITEM_ID, 0, ""); menu.add(0, EDIT_ITEM_ID, 0, ""); menu.add(0, DELETE_ITEM_ID, 0, ""); } } @Override public View getView(int position, View convertView, ViewGroup parent) { final View view = (convertView != null) ? convertView : LayoutInflater.from(parent.getContext()).inflate(R.layout.bookmark_item, parent, false); final ImageView imageView = ViewUtil.findImageView(view, R.id.bookmark_item_icon); final View colorContainer = ViewUtil.findView(view, R.id.bookmark_item_color_container); final AmbilWarnaPrefWidgetView colorView = (AmbilWarnaPrefWidgetView) ViewUtil.findView(view, R.id.bookmark_item_color); final TextView textView = ViewUtil.findTextView(view, R.id.bookmark_item_text); final TextView bookTitleView = ViewUtil.findTextView(view, R.id.bookmark_item_booktitle); final Bookmark bookmark = getItem(position); if (bookmark == null) { // imageView.setVisibility(View.VISIBLE); // imageView.setImageResource(R.drawable.ic_list_plus); colorContainer.setVisibility(View.GONE); // textView.setText(""); bookTitleView.setVisibility(View.GONE); } else { imageView.setVisibility(View.GONE); // colorContainer.setVisibility(View.VISIBLE); setupColorView(colorView, myStyles.get(bookmark.getStyleId())); textView.setText(bookmark.getText()); if (myShowAddBookmarkItem) { bookTitleView.setVisibility(View.GONE); } else { bookTitleView.setVisibility(View.VISIBLE); bookTitleView.setText(bookmark.BookTitle); } } return view; } @Override public final boolean areAllItemsEnabled() { return true; } @Override public final boolean isEnabled(int position) { return true; } @Override public final long getItemId(int position) { final Bookmark item = getItem(position); return item != null ? item.getId() : 0; } @Override public final Bookmark getItem(int position) { if (myShowAddBookmarkItem) { // --position; } return position >= 0 ? myBookmarksList.get(position) : null; } @Override public final int getCount() { return myShowAddBookmarkItem ? myBookmarksList.size() : myBookmarksList.size(); } public final void onItemClick(AdapterView<?> parent, View view, int position, long id) { final Bookmark bookmark = getItem(position); if (bookmark != null) { gotoBookmark(bookmark); } else if (myShowAddBookmarkItem) { myShowAddBookmarkItem = false; myCollection.saveBookmark(myBookmark); } } } // method from IBookCollection.Listener public void onBookEvent(BookEvent event, Book book) { switch (event) { default: break; case BookmarkStyleChanged: runOnUiThread(new Runnable() { public void run() { updateStyles(); myThisBookAdapter.notifyDataSetChanged(); } }); break; case BookmarksUpdated: updateBookmarks(book); break; } } // method from IBookCollection.Listener public void onBuildEvent(IBookCollection.Status status) { } static void setupColorView(AmbilWarnaPrefWidgetView colorView, HighlightingStyle style) { Integer rgb = null; if (style != null) { final ZLColor color = style.getBackgroundColor(); if (color != null) { rgb = ZLAndroidColorUtil.rgb(color); } } if (rgb != null) { colorView.setBackgroundColor(rgb); } else { colorView.setBackgroundColor(0); } } private final class BookmarkAdapter extends BaseAdapter implements AdapterView.OnItemClickListener, View.OnCreateContextMenuListener { private ListView mListView; private final List<Bookmark> myBookmarksList = Collections.synchronizedList(new LinkedList<Bookmark>()); public BookmarkAdapter(ListView markLv) { mListView = markLv; mListView.setAdapter(this); mListView.setOnItemClickListener(this); mListView.setOnCreateContextMenuListener(this); } @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { final int position = ((AdapterView.AdapterContextMenuInfo) menuInfo).position; if (getItem(position) != null) { isBookMark = true; menu.add(0, OPEN_ITEM_ID, 0, ""); menu.add(0, DELETE_ITEM_ID, 0, ""); } } @Override public int getCount() { return myBookmarksList.size(); } @Override public final Bookmark getItem(int position) { return myBookmarksList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { Viewholder holder = null; if (convertView != null) { holder = (Viewholder) convertView.getTag(); } else { convertView = UIUtil.inflate(R.layout.item_bookmark); holder = new Viewholder(convertView); convertView.setTag(holder); } if (myBookmarksList.size() > 0) {//??? markLv.setVisibility(View.VISIBLE); emptyRl.setVisibility(View.INVISIBLE); } else {//? emptyRl.setVisibility(View.VISIBLE); markLv.setVisibility(View.INVISIBLE); } TOCTree currentTOCElement = kooreader.getCurrentTOCElement(getItem(position).getParagraphIndex()); int paragraphIndex = currentTOCElement.getReference().ParagraphIndex; String text = currentTOCElement.getText().toString(); String content = getItem(position).getText().toString(); // holder.tv.setText(""+text+" "+""+paragraphIndex+""); if (content.length() > 5) { content = content.substring(0, 4); } holder.tv.setText("" + text + " " + content + "......"); return convertView; } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { final Bookmark bookmark = getItem(position); if (bookmark != null) { gotoBookmark(bookmark); } } public void addAll(final List<Bookmark> bookmarks) { runOnUiThread(new Runnable() { public void run() { synchronized (myBookmarksList) { for (Bookmark b : bookmarks) { final int position = Collections.binarySearch(myBookmarksList, b, myComparator); if (position < 0 && b.getStyleId() == 5) { myBookmarksList.add(-position - 1, b); } } } notifyDataSetChanged(); } }); } public void replace(final Bookmark old, final Bookmark b) { if (old != null && areEqualsForView(old, b)) { return; } if (b.getStyleId() != 5) { return; } runOnUiThread(new Runnable() { public void run() { synchronized (myBookmarksList) { if (old != null) { myBookmarksList.remove(old); } final int position = Collections.binarySearch(myBookmarksList, b, myComparator); if (position < 0) { myBookmarksList.add(-position - 1, b); } } notifyDataSetChanged(); } }); } private boolean areEqualsForView(Bookmark b0, Bookmark b1) { return b0.getStyleId() == b1.getStyleId() && b0.getText().equals(b1.getText()) && b0.getTimestamp(Bookmark.DateType.Latest).equals(b1.getTimestamp(Bookmark.DateType.Latest)); } class Viewholder { TextView tv; public Viewholder(View itemView) { tv = (TextView) itemView.findViewById(R.id.tv_item); } } public void removeAll(final Collection<Bookmark> bookmarks) { if (bookmarks.isEmpty()) { return; } runOnUiThread(new Runnable() { public void run() { myBookmarksList.removeAll(bookmarks); notifyDataSetChanged(); } }); } public List<Bookmark> bookmarks() { return Collections.unmodifiableList(myBookmarksList); } } }