Merge pull request #567 from anthonycr/bookmarks-refactor
Moving bookmarks from flat file to database
This commit is contained in:
commit
5ca24e7d11
@ -0,0 +1,48 @@
|
||||
package acr.browser.lightning.activity;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import acr.browser.lightning.browser.BookmarksView;
|
||||
|
||||
/**
|
||||
* The UI model representing the current folder shown
|
||||
* by the {@link BookmarksView}.
|
||||
* <p>
|
||||
* Created by anthonycr on 5/7/17.
|
||||
*/
|
||||
public class BookmarkUiModel {
|
||||
|
||||
@Nullable private String mCurrentFolder;
|
||||
|
||||
/**
|
||||
* Sets the current folder that is being shown.
|
||||
* Use null as the root folder.
|
||||
*
|
||||
* @param folder the current folder, null for root.
|
||||
*/
|
||||
public void setCurrentFolder(@Nullable String folder) {
|
||||
mCurrentFolder = folder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the current folder is
|
||||
* the root folder.
|
||||
*
|
||||
* @return true if the current folder is
|
||||
* the root, false otherwise.
|
||||
*/
|
||||
public boolean isRootFolder() {
|
||||
return mCurrentFolder == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current folder that is being shown.
|
||||
*
|
||||
* @return the current folder, null for root.
|
||||
*/
|
||||
@Nullable
|
||||
public String getCurrentFolder() {
|
||||
return mCurrentFolder;
|
||||
}
|
||||
|
||||
}
|
@ -99,9 +99,9 @@ import acr.browser.lightning.constant.BookmarkPage;
|
||||
import acr.browser.lightning.constant.Constants;
|
||||
import acr.browser.lightning.constant.HistoryPage;
|
||||
import acr.browser.lightning.controller.UIController;
|
||||
import acr.browser.lightning.database.BookmarkManager;
|
||||
import acr.browser.lightning.database.HistoryItem;
|
||||
import acr.browser.lightning.database.HistoryModel;
|
||||
import acr.browser.lightning.database.bookmark.BookmarkModel;
|
||||
import acr.browser.lightning.database.history.HistoryModel;
|
||||
import acr.browser.lightning.dialog.BrowserDialog;
|
||||
import acr.browser.lightning.dialog.LightningDialogBuilder;
|
||||
import acr.browser.lightning.fragment.BookmarksFragment;
|
||||
@ -181,7 +181,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
|
||||
private String mCameraPhotoPath;
|
||||
|
||||
// The singleton BookmarkManager
|
||||
@Inject BookmarkManager mBookmarkManager;
|
||||
@Inject BookmarkModel mBookmarkManager;
|
||||
|
||||
@Inject LightningDialogBuilder mBookmarksDialogBuilder;
|
||||
|
||||
@ -827,23 +827,37 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
|
||||
|
||||
// By using a manager, adds a bookmark and notifies third parties about that
|
||||
private void addBookmark(final String title, final String url) {
|
||||
final HistoryItem item = !mBookmarkManager.isBookmark(url)
|
||||
? new HistoryItem(url, title)
|
||||
: null;
|
||||
if (item != null && mBookmarkManager.addBookmark(item)) {
|
||||
mSuggestionsAdapter.refreshBookmarks();
|
||||
mBookmarksView.handleUpdatedUrl(url);
|
||||
}
|
||||
|
||||
final HistoryItem item = new HistoryItem(url, title);
|
||||
mBookmarkManager.addBookmarkIfNotExists(item)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.main())
|
||||
.subscribe(new SingleOnSubscribe<Boolean>() {
|
||||
@Override
|
||||
public void onItem(@Nullable Boolean item) {
|
||||
if (Boolean.TRUE.equals(item)) {
|
||||
mSuggestionsAdapter.refreshBookmarks();
|
||||
mBookmarksView.handleUpdatedUrl(url);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void deleteBookmark(final String title, final String url) {
|
||||
final HistoryItem item = mBookmarkManager.isBookmark(url)
|
||||
? new HistoryItem(url, title)
|
||||
: null;
|
||||
if (item != null && mBookmarkManager.deleteBookmark(item)) {
|
||||
mSuggestionsAdapter.refreshBookmarks();
|
||||
mBookmarksView.handleUpdatedUrl(url);
|
||||
}
|
||||
final HistoryItem item = new HistoryItem(url, title);
|
||||
|
||||
mBookmarkManager.deleteBookmark(item)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.main())
|
||||
.subscribe(new SingleOnSubscribe<Boolean>() {
|
||||
@Override
|
||||
public void onItem(@Nullable Boolean item) {
|
||||
if (Boolean.TRUE.equals(item)) {
|
||||
mSuggestionsAdapter.refreshBookmarks();
|
||||
mBookmarksView.handleUpdatedUrl(url);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void putToolbarInRoot() {
|
||||
@ -1098,11 +1112,19 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
|
||||
}
|
||||
|
||||
if (!UrlUtils.isSpecialUrl(url)) {
|
||||
if (!mBookmarkManager.isBookmark(url)) {
|
||||
addBookmark(title, url);
|
||||
} else {
|
||||
deleteBookmark(title, url);
|
||||
}
|
||||
mBookmarkManager.isBookmark(url)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.main())
|
||||
.subscribe(new SingleOnSubscribe<Boolean>() {
|
||||
@Override
|
||||
public void onItem(@Nullable Boolean item) {
|
||||
if (Boolean.TRUE.equals(item)) {
|
||||
addBookmark(title, url);
|
||||
} else {
|
||||
deleteBookmark(title, url);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,6 @@ import acr.browser.lightning.constant.BookmarkPage;
|
||||
import acr.browser.lightning.constant.Constants;
|
||||
import acr.browser.lightning.constant.HistoryPage;
|
||||
import acr.browser.lightning.constant.StartPage;
|
||||
import acr.browser.lightning.database.BookmarkManager;
|
||||
import acr.browser.lightning.dialog.BrowserDialog;
|
||||
import acr.browser.lightning.preference.PreferenceManager;
|
||||
import acr.browser.lightning.utils.FileUtils;
|
||||
@ -64,7 +63,6 @@ public class TabsManager {
|
||||
private final List<Runnable> mPostInitializationWorkList = new ArrayList<>();
|
||||
|
||||
@Inject PreferenceManager mPreferenceManager;
|
||||
@Inject BookmarkManager mBookmarkManager;
|
||||
@Inject Application mApp;
|
||||
|
||||
public TabsManager() {
|
||||
|
@ -11,7 +11,7 @@ import acr.browser.lightning.browser.BrowserPresenter;
|
||||
import acr.browser.lightning.constant.BookmarkPage;
|
||||
import acr.browser.lightning.constant.HistoryPage;
|
||||
import acr.browser.lightning.constant.StartPage;
|
||||
import acr.browser.lightning.database.HistoryDatabase;
|
||||
import acr.browser.lightning.database.history.HistoryDatabase;
|
||||
import acr.browser.lightning.dialog.LightningDialogBuilder;
|
||||
import acr.browser.lightning.download.LightningDownloadListener;
|
||||
import acr.browser.lightning.fragment.BookmarkSettingsFragment;
|
||||
|
@ -8,6 +8,8 @@ import net.i2p.android.ui.I2PAndroidHelper;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import acr.browser.lightning.database.bookmark.BookmarkDatabase;
|
||||
import acr.browser.lightning.database.bookmark.BookmarkModel;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@ -29,6 +31,13 @@ public class AppModule {
|
||||
return mApp.getApplicationContext();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Provides
|
||||
@Singleton
|
||||
public BookmarkModel provideBookmarkMode() {
|
||||
return new BookmarkDatabase(mApp);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Provides
|
||||
@Singleton
|
||||
|
@ -12,14 +12,20 @@ import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import com.anthonycr.bonsai.Schedulers;
|
||||
import com.squareup.leakcanary.LeakCanary;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import acr.browser.lightning.BuildConfig;
|
||||
import acr.browser.lightning.database.HistoryItem;
|
||||
import acr.browser.lightning.database.bookmark.BookmarkExporter;
|
||||
import acr.browser.lightning.database.bookmark.legacy.LegacyBookmarkManager;
|
||||
import acr.browser.lightning.database.bookmark.BookmarkModel;
|
||||
import acr.browser.lightning.preference.PreferenceManager;
|
||||
import acr.browser.lightning.utils.FileUtils;
|
||||
import acr.browser.lightning.utils.MemoryLeakUtils;
|
||||
@ -33,6 +39,7 @@ public class BrowserApp extends Application {
|
||||
private static final Executor mIOThread = Executors.newSingleThreadExecutor();
|
||||
|
||||
@Inject PreferenceManager mPreferenceManager;
|
||||
@Inject BookmarkModel mBookmarkModel;
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
@ -74,6 +81,22 @@ public class BrowserApp extends Application {
|
||||
sAppComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
|
||||
sAppComponent.inject(this);
|
||||
|
||||
Schedulers.worker().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
List<HistoryItem> oldBookmarks = LegacyBookmarkManager.destructiveGetBookmarks(BrowserApp.this);
|
||||
|
||||
if (!oldBookmarks.isEmpty()) {
|
||||
// If there are old bookmarks, import them
|
||||
mBookmarkModel.addBookmarkList(oldBookmarks).subscribeOn(Schedulers.io()).subscribe();
|
||||
} else if (mBookmarkModel.count() == 0) {
|
||||
// If the database is empty, fill it from the assets list
|
||||
List<HistoryItem> assetsBookmarks = BookmarkExporter.importBookmarksFromAssets(BrowserApp.this);
|
||||
mBookmarkModel.addBookmarkList(assetsBookmarks).subscribeOn(Schedulers.io()).subscribe();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (mPreferenceManager.getUseLeakCanary() && !isRelease()) {
|
||||
LeakCanary.install(this);
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import android.support.annotation.Nullable;
|
||||
|
||||
import com.anthonycr.bonsai.Single;
|
||||
import com.anthonycr.bonsai.SingleAction;
|
||||
import com.anthonycr.bonsai.SingleOnSubscribe;
|
||||
import com.anthonycr.bonsai.SingleSubscriber;
|
||||
|
||||
import java.io.File;
|
||||
@ -24,8 +25,9 @@ import javax.inject.Inject;
|
||||
|
||||
import acr.browser.lightning.R;
|
||||
import acr.browser.lightning.app.BrowserApp;
|
||||
import acr.browser.lightning.database.BookmarkManager;
|
||||
import acr.browser.lightning.database.HistoryItem;
|
||||
import acr.browser.lightning.database.bookmark.BookmarkModel;
|
||||
import acr.browser.lightning.utils.Preconditions;
|
||||
import acr.browser.lightning.utils.ThemeUtils;
|
||||
import acr.browser.lightning.utils.Utils;
|
||||
|
||||
@ -37,25 +39,25 @@ public final class BookmarkPage {
|
||||
public static final String FILENAME = "bookmarks.html";
|
||||
|
||||
private static final String HEADING_1 = "<!DOCTYPE html><html xmlns=http://www.w3.org/1999/xhtml>\n" +
|
||||
"<head>\n" +
|
||||
"<meta content=en-us http-equiv=Content-Language />\n" +
|
||||
"<meta content='text/html; charset=utf-8' http-equiv=Content-Type />\n" +
|
||||
"<meta name=viewport content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'>\n" +
|
||||
"<title>";
|
||||
"<head>\n" +
|
||||
"<meta content=en-us http-equiv=Content-Language />\n" +
|
||||
"<meta content='text/html; charset=utf-8' http-equiv=Content-Type />\n" +
|
||||
"<meta name=viewport content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'>\n" +
|
||||
"<title>";
|
||||
|
||||
private static final String HEADING_2 = "</title>\n" +
|
||||
"</head>\n" +
|
||||
"<style>body{background:#f5f5f5;max-width:100%;min-height:100%}#content{width:100%;max-width:800px;margin:0 auto;text-align:center}.box{vertical-align:middle;text-align:center;position:relative;display:inline-block;height:45px;width:150px;margin:10px;background-color:#fff;box-shadow:0 3px 6px rgba(0,0,0,0.25);font-family:Arial;color:#444;font-size:12px;-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px}.box-content{height:25px;width:100%;vertical-align:middle;text-align:center;display:table-cell}p.ellipses{" +
|
||||
"width:130px;font-size: small;font-family: Arial, Helvetica, 'sans-serif';white-space:nowrap;overflow:hidden;text-align:left;vertical-align:middle;margin:auto;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.box a{width:100%;height:100%;position:absolute;left:0;top:0}img{vertical-align:middle;margin-right:10px;width:20px;height:20px;}.margin{margin:10px}</style>\n" +
|
||||
"<body><div id=content>";
|
||||
"</head>\n" +
|
||||
"<style>body{background:#f5f5f5;max-width:100%;min-height:100%}#content{width:100%;max-width:800px;margin:0 auto;text-align:center}.box{vertical-align:middle;text-align:center;position:relative;display:inline-block;height:45px;width:150px;margin:10px;background-color:#fff;box-shadow:0 3px 6px rgba(0,0,0,0.25);font-family:Arial;color:#444;font-size:12px;-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px}.box-content{height:25px;width:100%;vertical-align:middle;text-align:center;display:table-cell}p.ellipses{" +
|
||||
"width:130px;font-size: small;font-family: Arial, Helvetica, 'sans-serif';white-space:nowrap;overflow:hidden;text-align:left;vertical-align:middle;margin:auto;text-overflow:ellipsis;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis}.box a{width:100%;height:100%;position:absolute;left:0;top:0}img{vertical-align:middle;margin-right:10px;width:20px;height:20px;}.margin{margin:10px}</style>\n" +
|
||||
"<body><div id=content>";
|
||||
|
||||
private static final String PART1 = "<div class=box><a href='";
|
||||
|
||||
private static final String PART2 = "'></a>\n" +
|
||||
"<div class=margin>\n" +
|
||||
"<div class=box-content>\n" +
|
||||
"<p class=ellipses>\n" +
|
||||
"<img src='";
|
||||
"<div class=margin>\n" +
|
||||
"<div class=box-content>\n" +
|
||||
"<p class=ellipses>\n" +
|
||||
"<img src='";
|
||||
|
||||
private static final String PART3 = "https://www.google.com/s2/favicons?domain=";
|
||||
|
||||
@ -71,7 +73,7 @@ public final class BookmarkPage {
|
||||
private File mCacheDir;
|
||||
|
||||
@Inject Application mApp;
|
||||
@Inject BookmarkManager mManager;
|
||||
@Inject BookmarkModel mManager;
|
||||
|
||||
@NonNull private final Bitmap mFolderIcon;
|
||||
@NonNull private final String mTitle;
|
||||
@ -115,45 +117,52 @@ public final class BookmarkPage {
|
||||
}
|
||||
|
||||
private void buildBookmarkPage(@Nullable final String folder) {
|
||||
final List<HistoryItem> list = mManager.getBookmarksCopyFromFolder(folder, true);
|
||||
final File bookmarkWebPage;
|
||||
if (folder == null || folder.isEmpty()) {
|
||||
bookmarkWebPage = new File(mFilesDir, FILENAME);
|
||||
} else {
|
||||
bookmarkWebPage = new File(mFilesDir, folder + '-' + FILENAME);
|
||||
}
|
||||
final StringBuilder bookmarkBuilder = new StringBuilder(HEADING_1 + mTitle + HEADING_2);
|
||||
mManager.getBookmarksFromFolder(folder)
|
||||
.subscribe(new SingleOnSubscribe<List<HistoryItem>>() {
|
||||
@Override
|
||||
public void onItem(@Nullable List<HistoryItem> list) {
|
||||
Preconditions.checkNonNull(list);
|
||||
|
||||
final String folderIconPath = Constants.FILE + mCacheDir + '/' + FOLDER_ICON;
|
||||
for (int n = 0, size = list.size(); n < size; n++) {
|
||||
final HistoryItem item = list.get(n);
|
||||
bookmarkBuilder.append(PART1);
|
||||
if (item.isFolder()) {
|
||||
final File folderPage = new File(mFilesDir, item.getTitle() + '-' + FILENAME);
|
||||
bookmarkBuilder.append(Constants.FILE).append(folderPage);
|
||||
bookmarkBuilder.append(PART2);
|
||||
bookmarkBuilder.append(folderIconPath);
|
||||
buildBookmarkPage(item.getTitle());
|
||||
} else {
|
||||
bookmarkBuilder.append(item.getUrl());
|
||||
bookmarkBuilder.append(PART2).append(PART3);
|
||||
bookmarkBuilder.append(item.getUrl());
|
||||
}
|
||||
bookmarkBuilder.append(PART4);
|
||||
bookmarkBuilder.append(item.getTitle());
|
||||
bookmarkBuilder.append(PART5);
|
||||
}
|
||||
bookmarkBuilder.append(END);
|
||||
FileWriter bookWriter = null;
|
||||
try {
|
||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||
bookWriter = new FileWriter(bookmarkWebPage, false);
|
||||
bookWriter.write(bookmarkBuilder.toString());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
Utils.close(bookWriter);
|
||||
}
|
||||
final File bookmarkWebPage;
|
||||
if (folder == null || folder.isEmpty()) {
|
||||
bookmarkWebPage = new File(mFilesDir, FILENAME);
|
||||
} else {
|
||||
bookmarkWebPage = new File(mFilesDir, folder + '-' + FILENAME);
|
||||
}
|
||||
final StringBuilder bookmarkBuilder = new StringBuilder(HEADING_1 + mTitle + HEADING_2);
|
||||
|
||||
final String folderIconPath = Constants.FILE + mCacheDir + '/' + FOLDER_ICON;
|
||||
for (int n = 0, size = list.size(); n < size; n++) {
|
||||
final HistoryItem item = list.get(n);
|
||||
bookmarkBuilder.append(PART1);
|
||||
if (item.isFolder()) {
|
||||
final File folderPage = new File(mFilesDir, item.getTitle() + '-' + FILENAME);
|
||||
bookmarkBuilder.append(Constants.FILE).append(folderPage);
|
||||
bookmarkBuilder.append(PART2);
|
||||
bookmarkBuilder.append(folderIconPath);
|
||||
buildBookmarkPage(item.getTitle());
|
||||
} else {
|
||||
bookmarkBuilder.append(item.getUrl());
|
||||
bookmarkBuilder.append(PART2).append(PART3);
|
||||
bookmarkBuilder.append(item.getUrl());
|
||||
}
|
||||
bookmarkBuilder.append(PART4);
|
||||
bookmarkBuilder.append(item.getTitle());
|
||||
bookmarkBuilder.append(PART5);
|
||||
}
|
||||
bookmarkBuilder.append(END);
|
||||
FileWriter bookWriter = null;
|
||||
try {
|
||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||
bookWriter = new FileWriter(bookmarkWebPage, false);
|
||||
bookWriter.write(bookmarkBuilder.toString());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
Utils.close(bookWriter);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ import javax.inject.Inject;
|
||||
import acr.browser.lightning.R;
|
||||
import acr.browser.lightning.app.BrowserApp;
|
||||
import acr.browser.lightning.database.HistoryItem;
|
||||
import acr.browser.lightning.database.HistoryModel;
|
||||
import acr.browser.lightning.database.history.HistoryModel;
|
||||
import acr.browser.lightning.utils.Preconditions;
|
||||
import acr.browser.lightning.utils.Utils;
|
||||
|
||||
|
@ -1,552 +0,0 @@
|
||||
package acr.browser.lightning.database;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import acr.browser.lightning.R;
|
||||
import acr.browser.lightning.app.BrowserApp;
|
||||
import acr.browser.lightning.constant.Constants;
|
||||
import acr.browser.lightning.utils.Utils;
|
||||
|
||||
@Singleton
|
||||
public class BookmarkManager {
|
||||
|
||||
private static final String TAG = "BookmarkManager";
|
||||
|
||||
private static final String TITLE = "title";
|
||||
private static final String URL = "url";
|
||||
private static final String FOLDER = "folder";
|
||||
private static final String ORDER = "order";
|
||||
private static final String FILE_BOOKMARKS = "bookmarks.dat";
|
||||
|
||||
@NonNull private final String DEFAULT_BOOKMARK_TITLE;
|
||||
|
||||
private Map<String, HistoryItem> mBookmarksMap;
|
||||
@NonNull private String mCurrentFolder = "";
|
||||
@NonNull private final Executor mExecutor;
|
||||
private File mFilesDir;
|
||||
|
||||
@Inject
|
||||
public BookmarkManager(@NonNull Context context) {
|
||||
mExecutor = BrowserApp.getIOThread();
|
||||
DEFAULT_BOOKMARK_TITLE = context.getString(R.string.untitled);
|
||||
mExecutor.execute(new BookmarkInitializer(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the BookmarkManager, it's a one-time operation and will be executed asynchronously.
|
||||
* When done, mReady flag will been set to true.
|
||||
*/
|
||||
private class BookmarkInitializer implements Runnable {
|
||||
private final Context mContext;
|
||||
|
||||
BookmarkInitializer(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (BookmarkManager.this) {
|
||||
mFilesDir = mContext.getFilesDir();
|
||||
final Map<String, HistoryItem> bookmarks = new HashMap<>();
|
||||
final File bookmarksFile = new File(mFilesDir, FILE_BOOKMARKS);
|
||||
|
||||
BufferedReader bookmarksReader = null;
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
if (bookmarksFile.exists() && bookmarksFile.isFile()) {
|
||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||
inputStream = new FileInputStream(bookmarksFile);
|
||||
} else {
|
||||
inputStream = mContext.getResources().openRawResource(R.raw.default_bookmarks);
|
||||
}
|
||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||
bookmarksReader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
String line;
|
||||
while ((line = bookmarksReader.readLine()) != null) {
|
||||
try {
|
||||
JSONObject object = new JSONObject(line);
|
||||
HistoryItem item = new HistoryItem();
|
||||
item.setTitle(object.getString(TITLE));
|
||||
final String url = object.getString(URL);
|
||||
item.setUrl(url);
|
||||
item.setFolder(object.getString(FOLDER));
|
||||
item.setOrder(object.getInt(ORDER));
|
||||
item.setImageId(R.drawable.ic_bookmark);
|
||||
bookmarks.put(url, item);
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Can't parse line " + line, e);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error reading the bookmarks file", e);
|
||||
} finally {
|
||||
Utils.close(bookmarksReader);
|
||||
Utils.close(inputStream);
|
||||
}
|
||||
mBookmarksMap = bookmarks;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump all the given bookmarks to the bookmark file using a temporary file
|
||||
*/
|
||||
private class BookmarksWriter implements Runnable {
|
||||
|
||||
private final List<HistoryItem> mBookmarks;
|
||||
|
||||
BookmarksWriter(List<HistoryItem> bookmarks) {
|
||||
mBookmarks = bookmarks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final File tempFile = new File(mFilesDir,
|
||||
String.format(Locale.US, "bm_%d.dat", System.currentTimeMillis()));
|
||||
final File bookmarksFile = new File(mFilesDir, FILE_BOOKMARKS);
|
||||
boolean success = false;
|
||||
BufferedWriter bookmarkWriter = null;
|
||||
try {
|
||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||
bookmarkWriter = new BufferedWriter(new FileWriter(tempFile, false));
|
||||
JSONObject object = new JSONObject();
|
||||
for (HistoryItem item : mBookmarks) {
|
||||
object.put(TITLE, item.getTitle());
|
||||
object.put(URL, item.getUrl());
|
||||
object.put(FOLDER, item.getFolder());
|
||||
object.put(ORDER, item.getOrder());
|
||||
bookmarkWriter.write(object.toString());
|
||||
bookmarkWriter.newLine();
|
||||
}
|
||||
success = true;
|
||||
} catch (@NonNull IOException | JSONException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
Utils.close(bookmarkWriter);
|
||||
}
|
||||
|
||||
if (success) {
|
||||
// Overwrite the bookmarks file by renaming the temp file
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
tempFile.renameTo(bookmarksFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for bookmark using the url
|
||||
*
|
||||
* @param url the lookup url
|
||||
* @return the bookmark as an {@link HistoryItem} or null
|
||||
*/
|
||||
@Nullable
|
||||
public HistoryItem findBookmarkForUrl(final String url) {
|
||||
return mBookmarksMap.get(url);
|
||||
}
|
||||
|
||||
public boolean isBookmark(String url) {
|
||||
return mBookmarksMap.containsKey(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method adds the the HistoryItem item to permanent bookmark storage.<br>
|
||||
* This operation is blocking if the manager is still not ready.
|
||||
*
|
||||
* @param item the item to add
|
||||
* @return It returns true if the operation was successful.
|
||||
*/
|
||||
public synchronized boolean addBookmark(@NonNull HistoryItem item) {
|
||||
final String url = item.getUrl();
|
||||
if (mBookmarksMap.containsKey(url)) {
|
||||
return false;
|
||||
}
|
||||
mBookmarksMap.put(url, item);
|
||||
mExecutor.execute(new BookmarksWriter(new LinkedList<>(mBookmarksMap.values())));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method adds the list of HistoryItems to permanent bookmark storage
|
||||
*
|
||||
* @param list the list of HistoryItems to add to bookmarks
|
||||
*/
|
||||
public synchronized void addBookmarkList(@Nullable List<HistoryItem> list) {
|
||||
if (list == null || list.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (HistoryItem item : list) {
|
||||
final String url = item.getUrl();
|
||||
if (!mBookmarksMap.containsKey(url)) {
|
||||
mBookmarksMap.put(url, item);
|
||||
}
|
||||
}
|
||||
mExecutor.execute(new BookmarksWriter(new LinkedList<>(mBookmarksMap.values())));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method deletes the bookmark with the given url. It returns
|
||||
* true if the deletion was successful.
|
||||
*
|
||||
* @param deleteItem the bookmark item to delete
|
||||
*/
|
||||
public synchronized boolean deleteBookmark(@Nullable HistoryItem deleteItem) {
|
||||
if (deleteItem == null || deleteItem.isFolder()) {
|
||||
return false;
|
||||
}
|
||||
mBookmarksMap.remove(deleteItem.getUrl());
|
||||
mExecutor.execute(new BookmarksWriter(new LinkedList<>(mBookmarksMap.values())));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* renames a folder and moves all it's contents to that folder
|
||||
*
|
||||
* @param oldName the folder to be renamed
|
||||
* @param newName the new name of the folder
|
||||
*/
|
||||
public synchronized void renameFolder(@NonNull String oldName, @NonNull String newName) {
|
||||
if (newName.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (HistoryItem item : mBookmarksMap.values()) {
|
||||
if (item.getFolder().equals(oldName)) {
|
||||
item.setFolder(newName);
|
||||
} else if (item.isFolder() && item.getTitle().equals(oldName)) {
|
||||
item.setTitle(newName);
|
||||
item.setUrl(Constants.FOLDER + newName);
|
||||
}
|
||||
}
|
||||
mExecutor.execute(new BookmarksWriter(new LinkedList<>(mBookmarksMap.values())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the folder and move all bookmarks to the top level
|
||||
*
|
||||
* @param name the name of the folder to be deleted
|
||||
*/
|
||||
public synchronized void deleteFolder(@NonNull String name) {
|
||||
final Map<String, HistoryItem> bookmarks = new HashMap<>();
|
||||
for (HistoryItem item : mBookmarksMap.values()) {
|
||||
final String url = item.getUrl();
|
||||
if (item.isFolder()) {
|
||||
if (!item.getTitle().equals(name)) {
|
||||
bookmarks.put(url, item);
|
||||
}
|
||||
} else {
|
||||
if (item.getFolder().equals(name)) {
|
||||
item.setFolder("");
|
||||
}
|
||||
bookmarks.put(url, item);
|
||||
}
|
||||
}
|
||||
mBookmarksMap = bookmarks;
|
||||
mExecutor.execute(new BookmarksWriter(new LinkedList<>(mBookmarksMap.values())));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method deletes ALL bookmarks created
|
||||
* by the user. Use this method carefully and
|
||||
* do not use it without explicit user consent.
|
||||
*/
|
||||
public synchronized void deleteAllBookmarks() {
|
||||
mBookmarksMap = new HashMap<>();
|
||||
mExecutor.execute(new BookmarksWriter(new LinkedList<>(mBookmarksMap.values())));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method edits a particular bookmark in the bookmark database
|
||||
*
|
||||
* @param oldItem This is the old item that you wish to edit
|
||||
* @param newItem This is the new item that will overwrite the old item
|
||||
*/
|
||||
public synchronized void editBookmark(@Nullable HistoryItem oldItem, @Nullable HistoryItem newItem) {
|
||||
if (oldItem == null || newItem == null || oldItem.isFolder()) {
|
||||
return;
|
||||
}
|
||||
if (newItem.getUrl().isEmpty()) {
|
||||
deleteBookmark(oldItem);
|
||||
return;
|
||||
}
|
||||
if (newItem.getTitle().isEmpty()) {
|
||||
newItem.setTitle(DEFAULT_BOOKMARK_TITLE);
|
||||
}
|
||||
final String oldUrl = oldItem.getUrl();
|
||||
final String newUrl = newItem.getUrl();
|
||||
if (!oldUrl.equals(newUrl)) {
|
||||
// The url has been changed, remove the old one
|
||||
mBookmarksMap.remove(oldUrl);
|
||||
}
|
||||
mBookmarksMap.put(newUrl, newItem);
|
||||
mExecutor.execute(new BookmarksWriter(new LinkedList<>(mBookmarksMap.values())));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method exports the stored bookmarks to a text file in the device's
|
||||
* external download directory
|
||||
*/
|
||||
public synchronized void exportBookmarks(@NonNull Activity activity) {
|
||||
List<HistoryItem> bookmarkList = getAllBookmarks(true);
|
||||
File bookmarksExport = new File(
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
|
||||
"BookmarksExport.txt");
|
||||
int counter = 0;
|
||||
while (bookmarksExport.exists()) {
|
||||
counter++;
|
||||
bookmarksExport = new File(
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
|
||||
"BookmarksExport-" + counter + ".txt");
|
||||
}
|
||||
BufferedWriter bookmarkWriter = null;
|
||||
try {
|
||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||
bookmarkWriter = new BufferedWriter(new FileWriter(bookmarksExport,
|
||||
false));
|
||||
JSONObject object = new JSONObject();
|
||||
for (HistoryItem item : bookmarkList) {
|
||||
object.put(TITLE, item.getTitle());
|
||||
object.put(URL, item.getUrl());
|
||||
object.put(FOLDER, item.getFolder());
|
||||
object.put(ORDER, item.getOrder());
|
||||
bookmarkWriter.write(object.toString());
|
||||
bookmarkWriter.newLine();
|
||||
}
|
||||
Utils.showSnackbar(activity, activity.getString(R.string.bookmark_export_path)
|
||||
+ ' ' + bookmarksExport.getPath());
|
||||
} catch (@NonNull IOException | JSONException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
Utils.close(bookmarkWriter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns a list of ALL stored bookmarks.
|
||||
* This is a disk-bound operation and should not be
|
||||
* done very frequently.
|
||||
*
|
||||
* @param sort force to sort the returned bookmarkList
|
||||
* @return returns a list of bookmarks that can be sorted
|
||||
*/
|
||||
@NonNull
|
||||
public synchronized List<HistoryItem> getAllBookmarks(boolean sort) {
|
||||
final List<HistoryItem> bookmarks = new ArrayList<>(mBookmarksMap.values());
|
||||
if (sort) {
|
||||
Collections.sort(bookmarks, new SortIgnoreCase());
|
||||
}
|
||||
return bookmarks;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns a list of bookmarks and folders located in the specified folder.
|
||||
* This method should generally be used by the UI when it needs a list to display to the
|
||||
* user as it returns a subset of all bookmarks and includes folders as well which are
|
||||
* really 'fake' bookmarks.
|
||||
*
|
||||
* @param folder the name of the folder to retrieve bookmarks from
|
||||
* @return a list of bookmarks found in that folder
|
||||
*/
|
||||
@NonNull
|
||||
public synchronized List<HistoryItem> getBookmarksFromFolder(@Nullable String folder, boolean sort) {
|
||||
List<HistoryItem> bookmarks = new ArrayList<>(1);
|
||||
if (folder == null || folder.isEmpty()) {
|
||||
bookmarks.addAll(getFolders(sort));
|
||||
folder = "";
|
||||
}
|
||||
mCurrentFolder = folder;
|
||||
for (HistoryItem item : mBookmarksMap.values()) {
|
||||
if (item.getFolder().equals(folder))
|
||||
bookmarks.add(item);
|
||||
}
|
||||
if (sort) {
|
||||
Collections.sort(bookmarks, new SortIgnoreCase());
|
||||
}
|
||||
return bookmarks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Different from {@link #getBookmarksFromFolder(String, boolean)} only in
|
||||
* that it doesn't affect the internal state of the bookmark manager which
|
||||
* tracks the current folder used by the bookmark drawer.
|
||||
* <p/>
|
||||
* This method returns a list of bookmarks and folders located in the specified folder.
|
||||
* This method should generally be used by the UI when it needs a list to display to the
|
||||
* user as it returns a subset of all bookmarks and includes folders as well which are
|
||||
* really 'fake' bookmarks.
|
||||
*
|
||||
* @param folder the name of the folder to retrieve bookmarks from
|
||||
* @return a list of bookmarks found in that folder
|
||||
*/
|
||||
@NonNull
|
||||
public synchronized List<HistoryItem> getBookmarksCopyFromFolder(@Nullable String folder, boolean sort) {
|
||||
List<HistoryItem> bookmarks = new ArrayList<>(1);
|
||||
if (folder == null || folder.isEmpty()) {
|
||||
bookmarks.addAll(getFolders(sort));
|
||||
folder = "";
|
||||
}
|
||||
for (HistoryItem item : mBookmarksMap.values()) {
|
||||
if (item.getFolder().equals(folder))
|
||||
bookmarks.add(item);
|
||||
}
|
||||
if (sort) {
|
||||
Collections.sort(bookmarks, new SortIgnoreCase());
|
||||
}
|
||||
return bookmarks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells you if you are at the root folder or in a subfolder
|
||||
*
|
||||
* @return returns true if you are in the root folder
|
||||
*/
|
||||
public boolean isRootFolder() {
|
||||
return mCurrentFolder.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current folder
|
||||
*
|
||||
* @return the current folder
|
||||
*/
|
||||
@NonNull
|
||||
public String getCurrentFolder() {
|
||||
return mCurrentFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns a list of all folders.
|
||||
* Folders cannot be empty as they are generated from
|
||||
* the list of bookmarks that have non-empty folder fields.
|
||||
*
|
||||
* @return a list of all folders
|
||||
*/
|
||||
@NonNull
|
||||
private synchronized List<HistoryItem> getFolders(boolean sort) {
|
||||
final HashMap<String, HistoryItem> folders = new HashMap<>();
|
||||
for (HistoryItem item : mBookmarksMap.values()) {
|
||||
final String folderName = item.getFolder();
|
||||
if (!folderName.isEmpty() && !folders.containsKey(folderName)) {
|
||||
final HistoryItem folder = new HistoryItem();
|
||||
folder.setIsFolder(true);
|
||||
folder.setTitle(folderName);
|
||||
folder.setImageId(R.drawable.ic_folder);
|
||||
folder.setUrl(Constants.FOLDER + folderName);
|
||||
folders.put(folderName, folder);
|
||||
}
|
||||
}
|
||||
final List<HistoryItem> result = new ArrayList<>(folders.values());
|
||||
if (sort) {
|
||||
Collections.sort(result, new SortIgnoreCase());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a list of folder titles that can be used for suggestions in a
|
||||
* simple list adapter
|
||||
*
|
||||
* @return a list of folder title strings
|
||||
*/
|
||||
@NonNull
|
||||
public synchronized List<String> getFolderTitles() {
|
||||
final Set<String> folders = new HashSet<>();
|
||||
for (HistoryItem item : mBookmarksMap.values()) {
|
||||
final String folderName = item.getFolder();
|
||||
if (!folderName.isEmpty()) {
|
||||
folders.add(folderName);
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(folders);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method imports the bookmarks from a backup file that is located on
|
||||
* external storage
|
||||
*
|
||||
* @param file the file to attempt to import bookmarks from
|
||||
*/
|
||||
public synchronized void importBookmarksFromFile(@Nullable File file, @NonNull Activity activity) {
|
||||
if (file == null) {
|
||||
return;
|
||||
}
|
||||
List<HistoryItem> list = new ArrayList<>();
|
||||
BufferedReader bookmarksReader = null;
|
||||
try {
|
||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||
bookmarksReader = new BufferedReader(new FileReader(file));
|
||||
String line;
|
||||
int number = 0;
|
||||
while ((line = bookmarksReader.readLine()) != null) {
|
||||
JSONObject object = new JSONObject(line);
|
||||
HistoryItem item = new HistoryItem();
|
||||
item.setTitle(object.getString(TITLE));
|
||||
item.setUrl(object.getString(URL));
|
||||
item.setFolder(object.getString(FOLDER));
|
||||
item.setOrder(object.getInt(ORDER));
|
||||
list.add(item);
|
||||
number++;
|
||||
}
|
||||
addBookmarkList(list);
|
||||
String message = activity.getResources().getString(R.string.message_import);
|
||||
Utils.showSnackbar(activity, number + " " + message);
|
||||
} catch (@NonNull IOException | JSONException e) {
|
||||
e.printStackTrace();
|
||||
Utils.createInformativeDialog(activity, R.string.title_error, R.string.import_bookmark_error);
|
||||
} finally {
|
||||
Utils.close(bookmarksReader);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class sorts bookmarks alphabetically, with folders coming after bookmarks
|
||||
*/
|
||||
private static class SortIgnoreCase implements Comparator<HistoryItem> {
|
||||
|
||||
public int compare(@Nullable HistoryItem o1, @Nullable HistoryItem o2) {
|
||||
if (o1 == null || o2 == null) {
|
||||
return 0;
|
||||
}
|
||||
if (o1.isFolder() == o2.isFolder()) {
|
||||
return o1.getTitle().toLowerCase(Locale.getDefault())
|
||||
.compareTo(o2.getTitle().toLowerCase(Locale.getDefault()));
|
||||
|
||||
} else {
|
||||
return o1.isFolder() ? 1 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -25,7 +25,7 @@ public class HistoryItem implements Comparable<HistoryItem> {
|
||||
private Bitmap mBitmap = null;
|
||||
|
||||
private int mImageId = 0;
|
||||
private int mOrder = 0;
|
||||
private int mPosition = 0;
|
||||
private boolean mIsFolder = false;
|
||||
|
||||
public HistoryItem() {}
|
||||
@ -63,12 +63,12 @@ public class HistoryItem implements Comparable<HistoryItem> {
|
||||
mFolder = (folder == null) ? "" : folder;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
mOrder = order;
|
||||
public void setPosition(int order) {
|
||||
mPosition = order;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return mOrder;
|
||||
public int getPosition() {
|
||||
return mPosition;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@ -132,8 +132,8 @@ public class HistoryItem implements Comparable<HistoryItem> {
|
||||
HistoryItem that = (HistoryItem) object;
|
||||
|
||||
return mImageId == that.mImageId &&
|
||||
this.mTitle.equals(that.mTitle) && this.mUrl.equals(that.mUrl) &&
|
||||
this.mFolder.equals(that.mFolder);
|
||||
this.mTitle.equals(that.mTitle) && this.mUrl.equals(that.mUrl) &&
|
||||
this.mFolder.equals(that.mFolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,390 @@
|
||||
package acr.browser.lightning.database.bookmark;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.anthonycr.bonsai.Completable;
|
||||
import com.anthonycr.bonsai.CompletableAction;
|
||||
import com.anthonycr.bonsai.CompletableSubscriber;
|
||||
import com.anthonycr.bonsai.Single;
|
||||
import com.anthonycr.bonsai.SingleAction;
|
||||
import com.anthonycr.bonsai.SingleSubscriber;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import acr.browser.lightning.R;
|
||||
import acr.browser.lightning.constant.Constants;
|
||||
import acr.browser.lightning.database.HistoryItem;
|
||||
|
||||
/**
|
||||
* The disk backed bookmark database.
|
||||
* See {@link BookmarkModel} for method
|
||||
* documentation.
|
||||
* <p>
|
||||
* Created by anthonycr on 5/6/17.
|
||||
*/
|
||||
@Singleton
|
||||
public class BookmarkDatabase extends SQLiteOpenHelper implements BookmarkModel {
|
||||
|
||||
private static final String TAG = "BookmarkDatabase";
|
||||
|
||||
// Database Version
|
||||
private static final int DATABASE_VERSION = 1;
|
||||
|
||||
// Database Name
|
||||
private static final String DATABASE_NAME = "bookmarkManager";
|
||||
|
||||
// HistoryItems table name
|
||||
private static final String TABLE_BOOKMARK = "bookmark";
|
||||
|
||||
// HistoryItems Table Columns names
|
||||
private static final String KEY_ID = "id";
|
||||
private static final String KEY_URL = "url";
|
||||
private static final String KEY_TITLE = "title";
|
||||
private static final String KEY_FOLDER = "folder";
|
||||
private static final String KEY_POSITION = "position";
|
||||
|
||||
|
||||
@NonNull private final String DEFAULT_BOOKMARK_TITLE;
|
||||
|
||||
@Nullable private SQLiteDatabase mDatabase;
|
||||
|
||||
@Inject
|
||||
public BookmarkDatabase(@NonNull Application application) {
|
||||
super(application, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
DEFAULT_BOOKMARK_TITLE = application.getString(R.string.untitled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily initializes the database
|
||||
* field when called.
|
||||
*
|
||||
* @return a non null writable database.
|
||||
*/
|
||||
@NonNull
|
||||
private SQLiteDatabase lazyDatabase() {
|
||||
if (mDatabase == null) {
|
||||
mDatabase = getWritableDatabase();
|
||||
}
|
||||
|
||||
return mDatabase;
|
||||
}
|
||||
|
||||
// Creating Tables
|
||||
@Override
|
||||
public void onCreate(@NonNull SQLiteDatabase db) {
|
||||
String CREATE_BOOKMARK_TABLE = "CREATE TABLE " +
|
||||
DatabaseUtils.sqlEscapeString(TABLE_BOOKMARK) + '(' +
|
||||
DatabaseUtils.sqlEscapeString(KEY_ID) + " INTEGER PRIMARY KEY," +
|
||||
DatabaseUtils.sqlEscapeString(KEY_URL) + " TEXT," +
|
||||
DatabaseUtils.sqlEscapeString(KEY_TITLE) + " TEXT," +
|
||||
DatabaseUtils.sqlEscapeString(KEY_FOLDER) + " TEXT," +
|
||||
DatabaseUtils.sqlEscapeString(KEY_POSITION) + " INTEGER" + ')';
|
||||
db.execSQL(CREATE_BOOKMARK_TABLE);
|
||||
}
|
||||
|
||||
// Upgrading database
|
||||
@Override
|
||||
public void onUpgrade(@NonNull SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
// Drop older table if it exists
|
||||
db.execSQL("DROP TABLE IF EXISTS " + DatabaseUtils.sqlEscapeString(TABLE_BOOKMARK));
|
||||
// Create tables again
|
||||
onCreate(db);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static ContentValues bindBookmarkToContentValues(@NonNull HistoryItem bookmarkItem) {
|
||||
ContentValues contentValues = new ContentValues(4);
|
||||
contentValues.put(KEY_TITLE, bookmarkItem.getTitle());
|
||||
contentValues.put(KEY_URL, bookmarkItem.getUrl());
|
||||
contentValues.put(KEY_FOLDER, bookmarkItem.getFolder());
|
||||
contentValues.put(KEY_POSITION, bookmarkItem.getPosition());
|
||||
|
||||
return contentValues;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static HistoryItem bindCursorToHistoryItem(@NonNull Cursor cursor) {
|
||||
HistoryItem bookmark = new HistoryItem();
|
||||
|
||||
bookmark.setImageId(R.drawable.ic_bookmark);
|
||||
bookmark.setUrl(cursor.getString(cursor.getColumnIndex(KEY_URL)));
|
||||
bookmark.setTitle(cursor.getString(cursor.getColumnIndex(KEY_TITLE)));
|
||||
bookmark.setFolder(cursor.getString(cursor.getColumnIndex(KEY_FOLDER)));
|
||||
bookmark.setPosition(cursor.getInt(cursor.getColumnIndex(KEY_POSITION)));
|
||||
|
||||
return bookmark;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static List<HistoryItem> bindCursorToHistoryItemList(@NonNull Cursor cursor) {
|
||||
List<HistoryItem> bookmarks = new ArrayList<>();
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
bookmarks.add(bindCursorToHistoryItem(cursor));
|
||||
}
|
||||
|
||||
cursor.close();
|
||||
|
||||
return bookmarks;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Single<HistoryItem> findBookmarkForUrl(@NonNull final String url) {
|
||||
return Single.create(new SingleAction<HistoryItem>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull SingleSubscriber<HistoryItem> subscriber) {
|
||||
Cursor cursor = lazyDatabase().query(TABLE_BOOKMARK, null, KEY_URL + "=?", new String[]{url}, null, null, null, "1");
|
||||
|
||||
if (cursor.moveToFirst()) {
|
||||
subscriber.onItem(bindCursorToHistoryItem(cursor));
|
||||
} else {
|
||||
subscriber.onItem(null);
|
||||
}
|
||||
|
||||
cursor.close();
|
||||
subscriber.onComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Single<Boolean> isBookmark(@NonNull final String url) {
|
||||
return Single.create(new SingleAction<Boolean>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull SingleSubscriber<Boolean> subscriber) {
|
||||
Cursor cursor = lazyDatabase().query(TABLE_BOOKMARK, null, KEY_URL + "=?", new String[]{url}, null, null, null, "1");
|
||||
|
||||
subscriber.onItem(cursor.moveToFirst());
|
||||
|
||||
cursor.close();
|
||||
subscriber.onComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Single<Boolean> addBookmarkIfNotExists(@NonNull final HistoryItem item) {
|
||||
return Single.create(new SingleAction<Boolean>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull SingleSubscriber<Boolean> subscriber) {
|
||||
Cursor cursor = lazyDatabase().query(TABLE_BOOKMARK, null, KEY_URL + "=?", new String[]{item.getUrl()}, null, null, null, "1");
|
||||
|
||||
if (cursor.moveToFirst()) {
|
||||
cursor.close();
|
||||
subscriber.onItem(false);
|
||||
subscriber.onComplete();
|
||||
return;
|
||||
}
|
||||
|
||||
cursor.close();
|
||||
|
||||
long id = lazyDatabase().insert(TABLE_BOOKMARK, null, bindBookmarkToContentValues(item));
|
||||
|
||||
subscriber.onItem(id != -1);
|
||||
subscriber.onComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Completable addBookmarkList(@NonNull final List<HistoryItem> bookmarkItems) {
|
||||
return Completable.create(new CompletableAction() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull CompletableSubscriber subscriber) {
|
||||
lazyDatabase().beginTransaction();
|
||||
|
||||
for (HistoryItem item : bookmarkItems) {
|
||||
addBookmarkIfNotExists(item).subscribe();
|
||||
}
|
||||
|
||||
lazyDatabase().setTransactionSuccessful();
|
||||
lazyDatabase().endTransaction();
|
||||
|
||||
subscriber.onComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Single<Boolean> deleteBookmark(@NonNull final HistoryItem bookmark) {
|
||||
return Single.create(new SingleAction<Boolean>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull SingleSubscriber<Boolean> subscriber) {
|
||||
int rows = lazyDatabase().delete(TABLE_BOOKMARK, KEY_URL + "=?", new String[]{bookmark.getUrl()});
|
||||
|
||||
subscriber.onItem(rows > 0);
|
||||
subscriber.onComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Completable renameFolder(@NonNull final String oldName, @NonNull final String newName) {
|
||||
return Completable.create(new CompletableAction() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull CompletableSubscriber subscriber) {
|
||||
ContentValues contentValues = new ContentValues(1);
|
||||
contentValues.put(KEY_FOLDER, newName);
|
||||
|
||||
lazyDatabase().update(TABLE_BOOKMARK, contentValues, KEY_FOLDER + "=?", new String[]{oldName});
|
||||
|
||||
subscriber.onComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Completable deleteFolder(@NonNull final String folderToDelete) {
|
||||
return Completable.create(new CompletableAction() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull CompletableSubscriber subscriber) {
|
||||
renameFolder(folderToDelete, "").subscribe();
|
||||
|
||||
subscriber.onComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Completable deleteAllBookmarks() {
|
||||
return Completable.create(new CompletableAction() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull CompletableSubscriber subscriber) {
|
||||
lazyDatabase().delete(TABLE_BOOKMARK, null, null);
|
||||
|
||||
subscriber.onComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Completable editBookmark(@NonNull final HistoryItem oldBookmark, @NonNull final HistoryItem newBookmark) {
|
||||
return Completable.create(new CompletableAction() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull CompletableSubscriber subscriber) {
|
||||
if (newBookmark.getTitle().isEmpty()) {
|
||||
newBookmark.setTitle(DEFAULT_BOOKMARK_TITLE);
|
||||
}
|
||||
ContentValues contentValues = bindBookmarkToContentValues(newBookmark);
|
||||
|
||||
lazyDatabase().update(TABLE_BOOKMARK, contentValues, KEY_URL + "=?", new String[]{oldBookmark.getUrl()});
|
||||
|
||||
subscriber.onComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Single<List<HistoryItem>> getAllBookmarks() {
|
||||
return Single.create(new SingleAction<List<HistoryItem>>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull SingleSubscriber<List<HistoryItem>> subscriber) {
|
||||
Cursor cursor = lazyDatabase().query(TABLE_BOOKMARK, null, null, null, null, null, null);
|
||||
|
||||
subscriber.onItem(bindCursorToHistoryItemList(cursor));
|
||||
subscriber.onComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Single<List<HistoryItem>> getBookmarksFromFolder(@Nullable final String folder) {
|
||||
return Single.create(new SingleAction<List<HistoryItem>>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull SingleSubscriber<List<HistoryItem>> subscriber) {
|
||||
String finalFolder = folder != null ? folder : "";
|
||||
Cursor cursor = lazyDatabase().query(TABLE_BOOKMARK, null, KEY_FOLDER + "=?", new String[]{finalFolder}, null, null, null);
|
||||
|
||||
subscriber.onItem(bindCursorToHistoryItemList(cursor));
|
||||
subscriber.onComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Single<List<HistoryItem>> getFolders() {
|
||||
return Single.create(new SingleAction<List<HistoryItem>>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull SingleSubscriber<List<HistoryItem>> subscriber) {
|
||||
Cursor cursor = lazyDatabase().query(true, TABLE_BOOKMARK, new String[]{KEY_FOLDER}, null, null, null, null, null, null);
|
||||
|
||||
List<HistoryItem> folders = new ArrayList<>();
|
||||
while (cursor.moveToNext()) {
|
||||
String folderName = cursor.getString(cursor.getColumnIndex(KEY_FOLDER));
|
||||
if (TextUtils.isEmpty(folderName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final HistoryItem folder = new HistoryItem();
|
||||
folder.setIsFolder(true);
|
||||
folder.setTitle(folderName);
|
||||
folder.setImageId(R.drawable.ic_folder);
|
||||
folder.setUrl(Constants.FOLDER + folderName);
|
||||
|
||||
folders.add(folder);
|
||||
}
|
||||
|
||||
cursor.close();
|
||||
|
||||
subscriber.onItem(folders);
|
||||
subscriber.onComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Single<List<String>> getFolderNames() {
|
||||
return Single.create(new SingleAction<List<String>>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull SingleSubscriber<List<String>> subscriber) {
|
||||
Cursor cursor = lazyDatabase().query(true, TABLE_BOOKMARK, new String[]{KEY_FOLDER}, null, null, null, null, null, null);
|
||||
|
||||
List<String> folders = new ArrayList<>();
|
||||
while (cursor.moveToNext()) {
|
||||
String folderName = cursor.getString(cursor.getColumnIndex(KEY_FOLDER));
|
||||
if (TextUtils.isEmpty(folderName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
folders.add(folderName);
|
||||
}
|
||||
|
||||
cursor.close();
|
||||
|
||||
subscriber.onItem(folders);
|
||||
subscriber.onComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return DatabaseUtils.queryNumEntries(lazyDatabase(), TABLE_BOOKMARK);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,201 @@
|
||||
package acr.browser.lightning.database.bookmark;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.WorkerThread;
|
||||
import android.util.Log;
|
||||
|
||||
import com.anthonycr.bonsai.Completable;
|
||||
import com.anthonycr.bonsai.CompletableAction;
|
||||
import com.anthonycr.bonsai.CompletableSubscriber;
|
||||
import com.anthonycr.bonsai.Single;
|
||||
import com.anthonycr.bonsai.SingleAction;
|
||||
import com.anthonycr.bonsai.SingleSubscriber;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import acr.browser.lightning.R;
|
||||
import acr.browser.lightning.database.HistoryItem;
|
||||
import acr.browser.lightning.utils.Preconditions;
|
||||
import acr.browser.lightning.utils.Utils;
|
||||
|
||||
/**
|
||||
* The class responsible for importing and exporting
|
||||
* bookmarks in the JSON format.
|
||||
* <p>
|
||||
* Created by anthonycr on 5/7/17.
|
||||
*/
|
||||
public class BookmarkExporter {
|
||||
|
||||
private static final String TAG = "BookmarkExporter";
|
||||
|
||||
private static final String KEY_URL = "url";
|
||||
private static final String KEY_TITLE = "title";
|
||||
private static final String KEY_FOLDER = "folder";
|
||||
private static final String KEY_ORDER = "order";
|
||||
|
||||
/**
|
||||
* Retrieves all the default bookmarks stored
|
||||
* in the raw file within assets.
|
||||
*
|
||||
* @param context the context necessary to open assets.
|
||||
* @return a non null list of the bookmarks stored in assets.
|
||||
*/
|
||||
@NonNull
|
||||
public static List<HistoryItem> importBookmarksFromAssets(@NonNull Context context) {
|
||||
List<HistoryItem> bookmarks = new ArrayList<>();
|
||||
BufferedReader bookmarksReader = null;
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
inputStream = context.getResources().openRawResource(R.raw.default_bookmarks);
|
||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||
bookmarksReader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
String line;
|
||||
while ((line = bookmarksReader.readLine()) != null) {
|
||||
try {
|
||||
JSONObject object = new JSONObject(line);
|
||||
HistoryItem item = new HistoryItem();
|
||||
item.setTitle(object.getString(KEY_TITLE));
|
||||
final String url = object.getString(KEY_URL);
|
||||
item.setUrl(url);
|
||||
item.setFolder(object.getString(KEY_FOLDER));
|
||||
item.setPosition(object.getInt(KEY_ORDER));
|
||||
item.setImageId(R.drawable.ic_bookmark);
|
||||
bookmarks.add(item);
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Can't parse line " + line, e);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error reading the bookmarks file", e);
|
||||
} finally {
|
||||
Utils.close(bookmarksReader);
|
||||
Utils.close(inputStream);
|
||||
}
|
||||
|
||||
return bookmarks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the list of bookmarks to a file.
|
||||
*
|
||||
* @param bookmarkList the bookmarks to export.
|
||||
* @param file the file to export to.
|
||||
* @return an observable that emits a completion
|
||||
* event when the export is complete, or an error
|
||||
* event if there is a problem.
|
||||
*/
|
||||
@NonNull
|
||||
public static Completable exportBookmarksToFile(@NonNull final List<HistoryItem> bookmarkList,
|
||||
@NonNull final File file) {
|
||||
return Completable.create(new CompletableAction() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull final CompletableSubscriber subscriber) {
|
||||
Preconditions.checkNonNull(bookmarkList);
|
||||
BufferedWriter bookmarkWriter = null;
|
||||
try {
|
||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||
bookmarkWriter = new BufferedWriter(new FileWriter(file, false));
|
||||
|
||||
JSONObject object = new JSONObject();
|
||||
for (HistoryItem item : bookmarkList) {
|
||||
object.put(KEY_TITLE, item.getTitle());
|
||||
object.put(KEY_URL, item.getUrl());
|
||||
object.put(KEY_FOLDER, item.getFolder());
|
||||
object.put(KEY_ORDER, item.getPosition());
|
||||
bookmarkWriter.write(object.toString());
|
||||
bookmarkWriter.newLine();
|
||||
}
|
||||
subscriber.onComplete();
|
||||
} catch (@NonNull IOException | JSONException e) {
|
||||
subscriber.onError(e);
|
||||
} finally {
|
||||
Utils.close(bookmarkWriter);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to import bookmarks from the
|
||||
* given file. If the file is not in a
|
||||
* supported format, it will fail.
|
||||
*
|
||||
* @param file the file to import from.
|
||||
* @return an observable that emits the
|
||||
* imported bookmarks, or an error if the
|
||||
* file cannot be imported.
|
||||
*/
|
||||
@NonNull
|
||||
public static Single<List<HistoryItem>> importBookmarksFromFile(@NonNull final File file) {
|
||||
return Single.create(new SingleAction<List<HistoryItem>>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull SingleSubscriber<List<HistoryItem>> subscriber) {
|
||||
BufferedReader bookmarksReader = null;
|
||||
try {
|
||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||
bookmarksReader = new BufferedReader(new FileReader(file));
|
||||
String line;
|
||||
|
||||
List<HistoryItem> bookmarks = new ArrayList<>();
|
||||
while ((line = bookmarksReader.readLine()) != null) {
|
||||
JSONObject object = new JSONObject(line);
|
||||
HistoryItem item = new HistoryItem();
|
||||
item.setTitle(object.getString(KEY_TITLE));
|
||||
item.setUrl(object.getString(KEY_URL));
|
||||
item.setFolder(object.getString(KEY_FOLDER));
|
||||
item.setPosition(object.getInt(KEY_ORDER));
|
||||
bookmarks.add(item);
|
||||
}
|
||||
|
||||
subscriber.onItem(bookmarks);
|
||||
subscriber.onComplete();
|
||||
} catch (IOException | JSONException e) {
|
||||
subscriber.onError(e);
|
||||
} finally {
|
||||
Utils.close(bookmarksReader);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A blocking call that creates a new export file with
|
||||
* the name "BookmarkExport.txt" and an appropriate
|
||||
* numerical appendage if a file already exists with
|
||||
* that name.
|
||||
*
|
||||
* @return a non null empty file that can be used
|
||||
* to export bookmarks to.
|
||||
*/
|
||||
@WorkerThread
|
||||
@NonNull
|
||||
public static File createNewExportFile() {
|
||||
File bookmarksExport = new File(
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
|
||||
"BookmarksExport.txt");
|
||||
int counter = 0;
|
||||
while (bookmarksExport.exists()) {
|
||||
counter++;
|
||||
bookmarksExport = new File(
|
||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
|
||||
"BookmarksExport-" + counter + ".txt");
|
||||
}
|
||||
|
||||
return bookmarksExport;
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package acr.browser.lightning.database;
|
||||
package acr.browser.lightning.database.bookmark;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
@ -15,6 +15,7 @@ import com.anthonycr.bonsai.SingleSubscriber;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import acr.browser.lightning.database.HistoryItem;
|
||||
import acr.browser.lightning.utils.Utils;
|
||||
|
||||
public class BookmarkLocalSync {
|
@ -0,0 +1,164 @@
|
||||
package acr.browser.lightning.database.bookmark;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.WorkerThread;
|
||||
|
||||
import com.anthonycr.bonsai.Completable;
|
||||
import com.anthonycr.bonsai.Single;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import acr.browser.lightning.database.HistoryItem;
|
||||
|
||||
/**
|
||||
* The interface that should be used to
|
||||
* communicate with the bookmark database.
|
||||
* <p>
|
||||
* Created by anthonycr on 5/6/17.
|
||||
*/
|
||||
public interface BookmarkModel {
|
||||
|
||||
/**
|
||||
* Gets the bookmark associated with the URL.
|
||||
*
|
||||
* @param url the URL to look for.
|
||||
* @return an observable that will emit either
|
||||
* the bookmark associated with the URL or null.
|
||||
*/
|
||||
@NonNull
|
||||
Single<HistoryItem> findBookmarkForUrl(@NonNull String url);
|
||||
|
||||
/**
|
||||
* Determines if a URL is associated with a bookmark.
|
||||
*
|
||||
* @param url the URL to check.
|
||||
* @return an observable that will emit true if
|
||||
* the URL is a bookmark, false otherwise.
|
||||
*/
|
||||
@NonNull
|
||||
Single<Boolean> isBookmark(@NonNull String url);
|
||||
|
||||
/**
|
||||
* Adds a bookmark if one does not already exist with
|
||||
* the same URL.
|
||||
*
|
||||
* @param item the bookmark to add.
|
||||
* @return an observable that emits true if the bookmark
|
||||
* was added, false otherwise.
|
||||
*/
|
||||
@NonNull
|
||||
Single<Boolean> addBookmarkIfNotExists(@NonNull HistoryItem item);
|
||||
|
||||
/**
|
||||
* Adds a list of bookmarks to the database.
|
||||
*
|
||||
* @param bookmarkItems the bookmarks to add.
|
||||
* @return an observable that emits a complete event
|
||||
* when all the bookmarks have been added.
|
||||
*/
|
||||
@NonNull
|
||||
Completable addBookmarkList(@NonNull List<HistoryItem> bookmarkItems);
|
||||
|
||||
/**
|
||||
* Deletes a bookmark from the database.
|
||||
*
|
||||
* @param bookmark the bookmark to delete.
|
||||
* @return an observable that emits true when
|
||||
* the bookmark is deleted, false otherwise.
|
||||
*/
|
||||
@NonNull
|
||||
Single<Boolean> deleteBookmark(@NonNull HistoryItem bookmark);
|
||||
|
||||
/**
|
||||
* Moves all bookmarks in the old folder to the new folder.
|
||||
*
|
||||
* @param oldName the name of the old folder.
|
||||
* @param newName the name of the new folder.
|
||||
* @return an observable that emits a completion
|
||||
* event when the folder is renamed.
|
||||
*/
|
||||
@NonNull
|
||||
Completable renameFolder(@NonNull String oldName, @NonNull String newName);
|
||||
|
||||
/**
|
||||
* Deletes a folder from the database, all bookmarks
|
||||
* in that folder will be moved to the root level.
|
||||
*
|
||||
* @param folderToDelete the folder to delete.
|
||||
* @return an observable that emits a completion
|
||||
* event when the folder has been deleted.
|
||||
*/
|
||||
@NonNull
|
||||
Completable deleteFolder(@NonNull String folderToDelete);
|
||||
|
||||
/**
|
||||
* Deletes all bookmarks in the database.
|
||||
*
|
||||
* @return an observable that emits a completion
|
||||
* event when all bookmarks have been deleted.
|
||||
*/
|
||||
@NonNull
|
||||
Completable deleteAllBookmarks();
|
||||
|
||||
/**
|
||||
* Changes the bookmark with the original URL
|
||||
* with all the data from the new bookmark.
|
||||
*
|
||||
* @param oldBookmark the old bookmark to replace.
|
||||
* @param newBookmark the new bookmark.
|
||||
* @return an observable that emits a completion event
|
||||
* when the bookmark edit is done.
|
||||
*/
|
||||
@NonNull
|
||||
Completable editBookmark(@NonNull HistoryItem oldBookmark, @NonNull HistoryItem newBookmark);
|
||||
|
||||
/**
|
||||
* Emits a list of all bookmarks
|
||||
*
|
||||
* @return an observable that emits a list
|
||||
* of all bookmarks.
|
||||
*/
|
||||
@NonNull
|
||||
Single<List<HistoryItem>> getAllBookmarks();
|
||||
|
||||
/**
|
||||
* Emits all bookmarks in a certain folder.
|
||||
* If the folder chosen is null, then all bookmarks
|
||||
* without a specified folder will be returned.
|
||||
*
|
||||
* @param folder gets the bookmarks from this folder, may be null.
|
||||
* @return an observable that emits a list of bookmarks
|
||||
* in the given folder.
|
||||
*/
|
||||
@NonNull
|
||||
Single<List<HistoryItem>> getBookmarksFromFolder(@Nullable String folder);
|
||||
|
||||
/**
|
||||
* Returns all folders as {@link HistoryItem}.
|
||||
* The root folder is omitted.
|
||||
*
|
||||
* @return an observable that emits a list of folders.
|
||||
*/
|
||||
@NonNull
|
||||
Single<List<HistoryItem>> getFolders();
|
||||
|
||||
/**
|
||||
* Returns the names of all folders.
|
||||
* The root folder is omitted.
|
||||
*
|
||||
* @return an observable that emits a list of folder names.
|
||||
*/
|
||||
@NonNull
|
||||
Single<List<String>> getFolderNames();
|
||||
|
||||
/**
|
||||
* A synchronous call to the model
|
||||
* that returns the number of bookmarks.
|
||||
* Should be called from a background thread.
|
||||
*
|
||||
* @return the number of bookmarks in the database.
|
||||
*/
|
||||
@WorkerThread
|
||||
long count();
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
package acr.browser.lightning.database.bookmark.legacy;
|
||||
|
||||
import android.app.Application;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.WorkerThread;
|
||||
import android.util.Log;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import acr.browser.lightning.R;
|
||||
import acr.browser.lightning.database.HistoryItem;
|
||||
import acr.browser.lightning.utils.Utils;
|
||||
|
||||
@Deprecated
|
||||
public class LegacyBookmarkManager {
|
||||
|
||||
private static final String TAG = "LegacyBookmarkManager";
|
||||
|
||||
private static final String TITLE = "title";
|
||||
private static final String URL = "url";
|
||||
private static final String FOLDER = "folder";
|
||||
private static final String ORDER = "order";
|
||||
private static final String FILE_BOOKMARKS = "bookmarks.dat";
|
||||
|
||||
/**
|
||||
* Gets all bookmarks from the old bookmark file
|
||||
* and then deletes the file.
|
||||
*
|
||||
* @param application the context needed to open the file.
|
||||
* @return a list of bookmarks from the old bookmark file.
|
||||
*/
|
||||
@WorkerThread
|
||||
@NonNull
|
||||
public static List<HistoryItem> destructiveGetBookmarks(@NonNull Application application) {
|
||||
File filesDir = application.getFilesDir();
|
||||
List<HistoryItem> bookmarks = new ArrayList<>();
|
||||
final File bookmarksFile = new File(filesDir, FILE_BOOKMARKS);
|
||||
|
||||
BufferedReader bookmarksReader = null;
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
if (bookmarksFile.exists() && bookmarksFile.isFile()) {
|
||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||
inputStream = new FileInputStream(bookmarksFile);
|
||||
} else {
|
||||
return bookmarks;
|
||||
}
|
||||
//noinspection IOResourceOpenedButNotSafelyClosed
|
||||
bookmarksReader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
String line;
|
||||
while ((line = bookmarksReader.readLine()) != null) {
|
||||
try {
|
||||
JSONObject object = new JSONObject(line);
|
||||
|
||||
HistoryItem item = new HistoryItem();
|
||||
|
||||
item.setTitle(object.getString(TITLE));
|
||||
item.setUrl(object.getString(URL));
|
||||
item.setFolder(object.getString(FOLDER));
|
||||
item.setPosition(object.getInt(ORDER));
|
||||
item.setImageId(R.drawable.ic_bookmark);
|
||||
|
||||
bookmarks.add(item);
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Can't parse line " + line, e);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error reading the bookmarks file", e);
|
||||
} finally {
|
||||
Utils.close(bookmarksReader);
|
||||
Utils.close(inputStream);
|
||||
}
|
||||
|
||||
bookmarksFile.delete();
|
||||
|
||||
return bookmarks;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class sorts bookmarks alphabetically, with folders coming after bookmarks
|
||||
*/
|
||||
private static class SortIgnoreCase implements Comparator<HistoryItem> {
|
||||
|
||||
public int compare(@Nullable HistoryItem o1, @Nullable HistoryItem o2) {
|
||||
if (o1 == null || o2 == null) {
|
||||
return 0;
|
||||
}
|
||||
if (o1.isFolder() == o2.isFolder()) {
|
||||
return o1.getTitle().toLowerCase(Locale.getDefault())
|
||||
.compareTo(o2.getTitle().toLowerCase(Locale.getDefault()));
|
||||
|
||||
} else {
|
||||
return o1.isFolder() ? 1 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright 2014 A.C.R. Development
|
||||
*/
|
||||
package acr.browser.lightning.database;
|
||||
package acr.browser.lightning.database.history;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.ContentValues;
|
||||
@ -20,6 +20,7 @@ import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import acr.browser.lightning.R;
|
||||
import acr.browser.lightning.database.HistoryItem;
|
||||
|
||||
@Singleton
|
||||
@WorkerThread
|
@ -1,4 +1,4 @@
|
||||
package acr.browser.lightning.database;
|
||||
package acr.browser.lightning.database.history;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
@ -13,6 +13,7 @@ import com.anthonycr.bonsai.SingleSubscriber;
|
||||
import java.util.List;
|
||||
|
||||
import acr.browser.lightning.app.BrowserApp;
|
||||
import acr.browser.lightning.database.HistoryItem;
|
||||
|
||||
/**
|
||||
* A model class providing reactive bindings
|
@ -5,6 +5,7 @@ import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
@ -14,6 +15,7 @@ import android.widget.EditText;
|
||||
|
||||
import com.anthonycr.bonsai.CompletableOnSubscribe;
|
||||
import com.anthonycr.bonsai.Schedulers;
|
||||
import com.anthonycr.bonsai.SingleOnSubscribe;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -25,10 +27,11 @@ import acr.browser.lightning.app.BrowserApp;
|
||||
import acr.browser.lightning.constant.BookmarkPage;
|
||||
import acr.browser.lightning.constant.Constants;
|
||||
import acr.browser.lightning.controller.UIController;
|
||||
import acr.browser.lightning.database.BookmarkManager;
|
||||
import acr.browser.lightning.database.HistoryItem;
|
||||
import acr.browser.lightning.database.HistoryModel;
|
||||
import acr.browser.lightning.database.bookmark.BookmarkModel;
|
||||
import acr.browser.lightning.database.history.HistoryModel;
|
||||
import acr.browser.lightning.preference.PreferenceManager;
|
||||
import acr.browser.lightning.utils.Preconditions;
|
||||
import acr.browser.lightning.utils.Utils;
|
||||
|
||||
/**
|
||||
@ -44,7 +47,7 @@ public class LightningDialogBuilder {
|
||||
INCOGNITO
|
||||
}
|
||||
|
||||
@Inject BookmarkManager mBookmarkManager;
|
||||
@Inject BookmarkModel mBookmarkManager;
|
||||
@Inject PreferenceManager mPreferenceManager;
|
||||
|
||||
@Inject
|
||||
@ -60,7 +63,7 @@ public class LightningDialogBuilder {
|
||||
* @param url the long pressed url
|
||||
*/
|
||||
public void showLongPressedDialogForBookmarkUrl(@NonNull final Activity activity,
|
||||
@NonNull UIController uiController,
|
||||
@NonNull final UIController uiController,
|
||||
@NonNull final String url) {
|
||||
final HistoryItem item;
|
||||
if (url.startsWith(Constants.FILE) && url.endsWith(BookmarkPage.FILENAME)) {
|
||||
@ -73,15 +76,19 @@ public class LightningDialogBuilder {
|
||||
item.setTitle(folderTitle);
|
||||
item.setImageId(R.drawable.ic_folder);
|
||||
item.setUrl(Constants.FOLDER + folderTitle);
|
||||
showBookmarkFolderLongPressedDialog(activity, uiController, item);
|
||||
} else {
|
||||
item = mBookmarkManager.findBookmarkForUrl(url);
|
||||
}
|
||||
if (item != null) {
|
||||
if (item.isFolder()) {
|
||||
showBookmarkFolderLongPressedDialog(activity, uiController, item);
|
||||
} else {
|
||||
showLongPressedDialogForBookmarkUrl(activity, uiController, item);
|
||||
}
|
||||
mBookmarkManager.findBookmarkForUrl(url)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.main())
|
||||
.subscribe(new SingleOnSubscribe<HistoryItem>() {
|
||||
@Override
|
||||
public void onItem(@Nullable HistoryItem historyItem) {
|
||||
if (historyItem != null) {
|
||||
showLongPressedDialogForBookmarkUrl(activity, uiController, historyItem);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,9 +123,18 @@ public class LightningDialogBuilder {
|
||||
new BrowserDialog.Item(R.string.dialog_remove_bookmark) {
|
||||
@Override
|
||||
public void onClick() {
|
||||
if (mBookmarkManager.deleteBookmark(item)) {
|
||||
uiController.handleBookmarkDeleted(item);
|
||||
}
|
||||
mBookmarkManager.deleteBookmark(item)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.main())
|
||||
.subscribe(new SingleOnSubscribe<Boolean>() {
|
||||
@Override
|
||||
public void onItem(@Nullable Boolean success) {
|
||||
Preconditions.checkNonNull(success);
|
||||
if (success) {
|
||||
uiController.handleBookmarkDeleted(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
new BrowserDialog.Item(R.string.dialog_edit_bookmark) {
|
||||
@ -143,28 +159,44 @@ public class LightningDialogBuilder {
|
||||
(AutoCompleteTextView) dialogLayout.findViewById(R.id.bookmark_folder);
|
||||
getFolder.setHint(R.string.folder);
|
||||
getFolder.setText(item.getFolder());
|
||||
final List<String> folders = mBookmarkManager.getFolderTitles();
|
||||
final ArrayAdapter<String> suggestionsAdapter = new ArrayAdapter<>(activity,
|
||||
android.R.layout.simple_dropdown_item_1line, folders);
|
||||
getFolder.setThreshold(1);
|
||||
getFolder.setAdapter(suggestionsAdapter);
|
||||
editBookmarkDialog.setView(dialogLayout);
|
||||
editBookmarkDialog.setPositiveButton(activity.getString(R.string.action_ok),
|
||||
new DialogInterface.OnClickListener() {
|
||||
|
||||
mBookmarkManager.getFolderNames()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.main())
|
||||
.subscribe(new SingleOnSubscribe<List<String>>() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
HistoryItem editedItem = new HistoryItem();
|
||||
editedItem.setTitle(getTitle.getText().toString());
|
||||
editedItem.setUrl(getUrl.getText().toString());
|
||||
editedItem.setUrl(getUrl.getText().toString());
|
||||
editedItem.setFolder(getFolder.getText().toString());
|
||||
mBookmarkManager.editBookmark(item, editedItem);
|
||||
uiController.handleBookmarksChange();
|
||||
public void onItem(@Nullable List<String> folders) {
|
||||
Preconditions.checkNonNull(folders);
|
||||
final ArrayAdapter<String> suggestionsAdapter = new ArrayAdapter<>(activity,
|
||||
android.R.layout.simple_dropdown_item_1line, folders);
|
||||
getFolder.setThreshold(1);
|
||||
getFolder.setAdapter(suggestionsAdapter);
|
||||
editBookmarkDialog.setView(dialogLayout);
|
||||
editBookmarkDialog.setPositiveButton(activity.getString(R.string.action_ok),
|
||||
new DialogInterface.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
HistoryItem editedItem = new HistoryItem();
|
||||
editedItem.setTitle(getTitle.getText().toString());
|
||||
editedItem.setUrl(getUrl.getText().toString());
|
||||
editedItem.setUrl(getUrl.getText().toString());
|
||||
editedItem.setFolder(getFolder.getText().toString());
|
||||
mBookmarkManager.editBookmark(item, editedItem)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.main())
|
||||
.subscribe(new CompletableOnSubscribe() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
uiController.handleBookmarksChange();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
Dialog dialog = editBookmarkDialog.show();
|
||||
BrowserDialog.setDialogSize(activity, dialog);
|
||||
}
|
||||
});
|
||||
Dialog dialog = editBookmarkDialog.show();
|
||||
BrowserDialog.setDialogSize(activity, dialog);
|
||||
}
|
||||
|
||||
public void showBookmarkFolderLongPressedDialog(@NonNull final Activity activity,
|
||||
@ -181,8 +213,15 @@ public class LightningDialogBuilder {
|
||||
new BrowserDialog.Item(R.string.dialog_remove_folder) {
|
||||
@Override
|
||||
public void onClick() {
|
||||
mBookmarkManager.deleteFolder(item.getTitle());
|
||||
uiController.handleBookmarkDeleted(item);
|
||||
mBookmarkManager.deleteFolder(item.getTitle())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.main())
|
||||
.subscribe(new CompletableOnSubscribe() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
uiController.handleBookmarkDeleted(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -202,8 +241,15 @@ public class LightningDialogBuilder {
|
||||
editedItem.setUrl(Constants.FOLDER + text);
|
||||
editedItem.setFolder(item.getFolder());
|
||||
editedItem.setIsFolder(true);
|
||||
mBookmarkManager.renameFolder(oldTitle, text);
|
||||
uiController.handleBookmarksChange();
|
||||
mBookmarkManager.renameFolder(oldTitle, text)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.main())
|
||||
.subscribe(new CompletableOnSubscribe() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
uiController.handleBookmarksChange();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -21,6 +21,7 @@ import android.support.v7.app.AlertDialog;
|
||||
import android.util.Log;
|
||||
import android.widget.ArrayAdapter;
|
||||
|
||||
import com.anthonycr.bonsai.CompletableOnSubscribe;
|
||||
import com.anthonycr.bonsai.SingleOnSubscribe;
|
||||
import com.anthonycr.grant.PermissionsManager;
|
||||
import com.anthonycr.grant.PermissionsResultAction;
|
||||
@ -36,13 +37,14 @@ import javax.inject.Inject;
|
||||
|
||||
import acr.browser.lightning.R;
|
||||
import acr.browser.lightning.app.BrowserApp;
|
||||
import acr.browser.lightning.database.BookmarkLocalSync;
|
||||
import acr.browser.lightning.database.BookmarkLocalSync.Source;
|
||||
import acr.browser.lightning.database.BookmarkManager;
|
||||
import acr.browser.lightning.database.bookmark.BookmarkExporter;
|
||||
import acr.browser.lightning.database.bookmark.BookmarkLocalSync;
|
||||
import acr.browser.lightning.database.bookmark.BookmarkLocalSync.Source;
|
||||
import acr.browser.lightning.database.HistoryItem;
|
||||
|
||||
import com.anthonycr.bonsai.Schedulers;
|
||||
|
||||
import acr.browser.lightning.database.bookmark.BookmarkModel;
|
||||
import acr.browser.lightning.dialog.BrowserDialog;
|
||||
import acr.browser.lightning.utils.Preconditions;
|
||||
import acr.browser.lightning.utils.Utils;
|
||||
@ -58,7 +60,7 @@ public class BookmarkSettingsFragment extends PreferenceFragment implements Pref
|
||||
|
||||
@Nullable private Activity mActivity;
|
||||
|
||||
@Inject BookmarkManager mBookmarkManager;
|
||||
@Inject BookmarkModel mBookmarkManager;
|
||||
private File[] mFileList;
|
||||
private String[] mFileNameList;
|
||||
@Nullable private BookmarkLocalSync mSync;
|
||||
@ -187,7 +189,28 @@ public class BookmarkSettingsFragment extends PreferenceFragment implements Pref
|
||||
new PermissionsResultAction() {
|
||||
@Override
|
||||
public void onGranted() {
|
||||
mBookmarkManager.exportBookmarks(getActivity());
|
||||
mBookmarkManager.getAllBookmarks()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(new SingleOnSubscribe<List<HistoryItem>>() {
|
||||
@Override
|
||||
public void onItem(@Nullable List<HistoryItem> item) {
|
||||
Preconditions.checkNonNull(item);
|
||||
final File exportFile = BookmarkExporter.createNewExportFile();
|
||||
BookmarkExporter.exportBookmarksToFile(item, exportFile)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.main())
|
||||
.subscribe(new CompletableOnSubscribe() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Activity activity = getActivity();
|
||||
if (activity != null) {
|
||||
Utils.showSnackbar(activity, activity.getString(R.string.bookmark_export_path)
|
||||
+ ' ' + exportFile.getPath());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -245,7 +268,7 @@ public class BookmarkSettingsFragment extends PreferenceFragment implements Pref
|
||||
builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
mBookmarkManager.deleteAllBookmarks();
|
||||
mBookmarkManager.deleteAllBookmarks().subscribeOn(Schedulers.io()).subscribe();
|
||||
}
|
||||
});
|
||||
Dialog dialog = builder.show();
|
||||
@ -408,7 +431,32 @@ public class BookmarkSettingsFragment extends PreferenceFragment implements Pref
|
||||
Dialog dialog1 = builder.show();
|
||||
BrowserDialog.setDialogSize(mActivity, dialog1);
|
||||
} else {
|
||||
mBookmarkManager.importBookmarksFromFile(mFileList[which], getActivity());
|
||||
BookmarkExporter.importBookmarksFromFile(mFileList[which])
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(new SingleOnSubscribe<List<HistoryItem>>() {
|
||||
@Override
|
||||
public void onItem(@Nullable final List<HistoryItem> importList) {
|
||||
Preconditions.checkNonNull(importList);
|
||||
mBookmarkManager.addBookmarkList(importList)
|
||||
.observeOn(Schedulers.main())
|
||||
.subscribe(new CompletableOnSubscribe() {
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Activity activity = getActivity();
|
||||
if (activity != null) {
|
||||
String message = activity.getResources().getString(R.string.message_import);
|
||||
Utils.showSnackbar(activity, importList.size() + " " + message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable throwable) {
|
||||
Log.e(TAG, "onError: importing bookmarks", throwable);
|
||||
Utils.createInformativeDialog(getActivity(), R.string.title_error, R.string.import_bookmark_error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,10 +22,7 @@ import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.anthonycr.bonsai.Schedulers;
|
||||
import com.anthonycr.bonsai.Single;
|
||||
import com.anthonycr.bonsai.SingleAction;
|
||||
import com.anthonycr.bonsai.SingleOnSubscribe;
|
||||
import com.anthonycr.bonsai.SingleSubscriber;
|
||||
import com.anthonycr.bonsai.Subscription;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
@ -35,6 +32,7 @@ import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import acr.browser.lightning.R;
|
||||
import acr.browser.lightning.activity.BookmarkUiModel;
|
||||
import acr.browser.lightning.activity.ReadingActivity;
|
||||
import acr.browser.lightning.activity.TabsManager;
|
||||
import acr.browser.lightning.animation.AnimationUtils;
|
||||
@ -42,8 +40,8 @@ import acr.browser.lightning.app.BrowserApp;
|
||||
import acr.browser.lightning.browser.BookmarksView;
|
||||
import acr.browser.lightning.constant.Constants;
|
||||
import acr.browser.lightning.controller.UIController;
|
||||
import acr.browser.lightning.database.BookmarkManager;
|
||||
import acr.browser.lightning.database.HistoryItem;
|
||||
import acr.browser.lightning.database.bookmark.BookmarkModel;
|
||||
import acr.browser.lightning.dialog.LightningDialogBuilder;
|
||||
import acr.browser.lightning.favicon.FaviconModel;
|
||||
import acr.browser.lightning.preference.PreferenceManager;
|
||||
@ -71,7 +69,7 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
|
||||
public final static String INCOGNITO_MODE = TAG + ".INCOGNITO_MODE";
|
||||
|
||||
// Managers
|
||||
@Inject BookmarkManager mBookmarkManager;
|
||||
@Inject BookmarkModel mBookmarkManager;
|
||||
|
||||
// Dialog builder
|
||||
@Inject LightningDialogBuilder mBookmarksDialogBuilder;
|
||||
@ -109,16 +107,8 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
|
||||
@Nullable
|
||||
private Subscription mBookmarksSubscription;
|
||||
|
||||
private static Single<List<HistoryItem>> initBookmarks(@NonNull final BookmarkManager bookmarkManager) {
|
||||
return Single.create(new SingleAction<List<HistoryItem>>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull SingleSubscriber<List<HistoryItem>> subscriber) {
|
||||
subscriber.onItem(bookmarkManager.getBookmarksFromFolder(null, true));
|
||||
subscriber.onComplete();
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
@NonNull
|
||||
private final BookmarkUiModel mUiModel = new BookmarkUiModel();
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
@ -150,7 +140,7 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
|
||||
final HistoryItem item = mBookmarks.get(position);
|
||||
if (item.isFolder()) {
|
||||
mScrollIndex = ((LinearLayoutManager) mBookmarksListView.getLayoutManager()).findFirstVisibleItemPosition();
|
||||
setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(item.getTitle(), true), true);
|
||||
setBookmarksShown(item.getTitle(), true);
|
||||
} else {
|
||||
mUiController.bookmarkItemClicked(item);
|
||||
}
|
||||
@ -170,7 +160,7 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (mBookmarkAdapter != null) {
|
||||
setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(null, true), false);
|
||||
setBookmarksShown(null, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,9 +174,8 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
|
||||
backView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mBookmarkManager == null) return;
|
||||
if (!mBookmarkManager.isRootFolder()) {
|
||||
setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(null, true), true);
|
||||
if (!mUiModel.isRootFolder()) {
|
||||
setBookmarksShown(null, true);
|
||||
mBookmarksListView.getLayoutManager().scrollToPosition(mScrollIndex);
|
||||
}
|
||||
}
|
||||
@ -201,17 +190,7 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
|
||||
mBookmarksListView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
mBookmarksListView.setAdapter(mBookmarkAdapter);
|
||||
|
||||
mBookmarksSubscription = initBookmarks(mBookmarkManager)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.main())
|
||||
.subscribe(new SingleOnSubscribe<List<HistoryItem>>() {
|
||||
@Override
|
||||
public void onItem(@Nullable List<HistoryItem> item) {
|
||||
mBookmarksSubscription = null;
|
||||
Preconditions.checkNonNull(item);
|
||||
setBookmarkDataSet(item, true);
|
||||
}
|
||||
});
|
||||
setBookmarksShown(null, true);
|
||||
|
||||
return view;
|
||||
}
|
||||
@ -251,31 +230,70 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
|
||||
}
|
||||
|
||||
private void updateBookmarkIndicator(final String url) {
|
||||
if (!mBookmarkManager.isBookmark(url)) {
|
||||
mBookmarkImage.setImageResource(R.drawable.ic_action_star);
|
||||
mBookmarkImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN);
|
||||
} else {
|
||||
mBookmarkImage.setImageResource(R.drawable.ic_bookmark);
|
||||
mBookmarkImage.setColorFilter(ThemeUtils.getAccentColor(getContext()), PorterDuff.Mode.SRC_IN);
|
||||
}
|
||||
mBookmarkManager.isBookmark(url)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.main())
|
||||
.subscribe(new SingleOnSubscribe<Boolean>() {
|
||||
@Override
|
||||
public void onItem(@Nullable Boolean item) {
|
||||
Preconditions.checkNonNull(item);
|
||||
if (!item) {
|
||||
mBookmarkImage.setImageResource(R.drawable.ic_action_star);
|
||||
mBookmarkImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN);
|
||||
} else {
|
||||
mBookmarkImage.setImageResource(R.drawable.ic_bookmark);
|
||||
mBookmarkImage.setColorFilter(ThemeUtils.getAccentColor(getContext()), PorterDuff.Mode.SRC_IN);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleBookmarkDeleted(@NonNull HistoryItem item) {
|
||||
mBookmarks.remove(item);
|
||||
if (item.isFolder()) {
|
||||
setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(null, true), false);
|
||||
setBookmarksShown(null, false);
|
||||
} else {
|
||||
mBookmarkAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void setBookmarksShown(@Nullable final String folder, final boolean animate) {
|
||||
mBookmarksSubscription = mBookmarkManager.getBookmarksFromFolder(folder)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.main())
|
||||
.subscribe(new SingleOnSubscribe<List<HistoryItem>>() {
|
||||
@Override
|
||||
public void onItem(@Nullable final List<HistoryItem> item) {
|
||||
mBookmarksSubscription = null;
|
||||
Preconditions.checkNonNull(item);
|
||||
|
||||
mUiModel.setCurrentFolder(folder);
|
||||
if (folder == null) {
|
||||
mBookmarkManager.getFolders()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.main())
|
||||
.subscribe(new SingleOnSubscribe<List<HistoryItem>>() {
|
||||
@Override
|
||||
public void onItem(@Nullable List<HistoryItem> folders) {
|
||||
Preconditions.checkNonNull(folders);
|
||||
item.addAll(folders);
|
||||
setBookmarkDataSet(item, animate);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
setBookmarkDataSet(item, animate);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setBookmarkDataSet(@NonNull List<HistoryItem> items, boolean animate) {
|
||||
mBookmarks.clear();
|
||||
mBookmarks.addAll(items);
|
||||
mBookmarkAdapter.notifyDataSetChanged();
|
||||
final int resource;
|
||||
if (mBookmarkManager.isRootFolder()) {
|
||||
if (mUiModel.isRootFolder()) {
|
||||
resource = R.drawable.ic_action_star;
|
||||
} else {
|
||||
resource = R.drawable.ic_action_back;
|
||||
@ -339,10 +357,10 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
|
||||
|
||||
@Override
|
||||
public void navigateBack() {
|
||||
if (mBookmarkManager.isRootFolder()) {
|
||||
if (mUiModel.isRootFolder()) {
|
||||
mUiController.closeBookmarksDrawer();
|
||||
} else {
|
||||
setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(null, true), true);
|
||||
setBookmarksShown(null, true);
|
||||
mBookmarksListView.getLayoutManager().scrollToPosition(mScrollIndex);
|
||||
}
|
||||
}
|
||||
@ -350,8 +368,8 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
|
||||
@Override
|
||||
public void handleUpdatedUrl(@NonNull String url) {
|
||||
updateBookmarkIndicator(url);
|
||||
String folder = mBookmarkManager.getCurrentFolder();
|
||||
setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(folder, true), false);
|
||||
String folder = mUiModel.getCurrentFolder();
|
||||
setBookmarksShown(folder, false);
|
||||
}
|
||||
|
||||
static class BookmarkViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
||||
|
@ -38,9 +38,9 @@ import javax.inject.Inject;
|
||||
|
||||
import acr.browser.lightning.R;
|
||||
import acr.browser.lightning.app.BrowserApp;
|
||||
import acr.browser.lightning.database.BookmarkManager;
|
||||
import acr.browser.lightning.database.HistoryItem;
|
||||
import acr.browser.lightning.database.HistoryModel;
|
||||
import acr.browser.lightning.database.bookmark.BookmarkModel;
|
||||
import acr.browser.lightning.database.history.HistoryModel;
|
||||
import acr.browser.lightning.preference.PreferenceManager;
|
||||
import acr.browser.lightning.utils.Preconditions;
|
||||
import acr.browser.lightning.utils.ThemeUtils;
|
||||
@ -65,7 +65,7 @@ public class SuggestionsAdapter extends BaseAdapter implements Filterable {
|
||||
|
||||
private final Comparator<HistoryItem> mFilterComparator = new SuggestionsComparator();
|
||||
|
||||
@Inject BookmarkManager mBookmarkManager;
|
||||
@Inject BookmarkModel mBookmarkManager;
|
||||
@Inject PreferenceManager mPreferenceManager;
|
||||
@Inject Application mApplication;
|
||||
|
||||
@ -102,15 +102,16 @@ public class SuggestionsAdapter extends BaseAdapter implements Filterable {
|
||||
}
|
||||
|
||||
public void refreshBookmarks() {
|
||||
Completable.create(new CompletableAction() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull CompletableSubscriber subscriber) {
|
||||
mAllBookmarks.clear();
|
||||
mAllBookmarks.addAll(mBookmarkManager.getAllBookmarks(true));
|
||||
|
||||
subscriber.onComplete();
|
||||
}
|
||||
}).subscribeOn(Schedulers.io()).subscribe();
|
||||
mBookmarkManager.getAllBookmarks()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(new SingleOnSubscribe<List<HistoryItem>>() {
|
||||
@Override
|
||||
public void onItem(@Nullable List<HistoryItem> item) {
|
||||
Preconditions.checkNonNull(item);
|
||||
mAllBookmarks.clear();
|
||||
mAllBookmarks.addAll(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -9,7 +9,6 @@ import android.graphics.ColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.AttrRes;
|
||||
@ -19,7 +18,6 @@ import android.support.annotation.NonNull;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.graphics.drawable.DrawableCompat;
|
||||
import android.util.TypedValue;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import acr.browser.lightning.R;
|
||||
|
||||
|
@ -13,7 +13,7 @@ import android.webkit.WebViewDatabase;
|
||||
|
||||
import com.anthonycr.bonsai.Schedulers;
|
||||
|
||||
import acr.browser.lightning.database.HistoryModel;
|
||||
import acr.browser.lightning.database.history.HistoryModel;
|
||||
|
||||
/**
|
||||
* Copyright 8/4/2015 Anthony Restaino
|
||||
|
@ -32,6 +32,7 @@ import android.webkit.WebSettings.LayoutAlgorithm;
|
||||
import android.webkit.WebSettings.PluginState;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import com.anthonycr.bonsai.Schedulers;
|
||||
import com.anthonycr.bonsai.Single;
|
||||
import com.anthonycr.bonsai.SingleAction;
|
||||
import com.anthonycr.bonsai.SingleOnSubscribe;
|
||||
@ -49,13 +50,9 @@ import acr.browser.lightning.constant.Constants;
|
||||
import acr.browser.lightning.constant.HistoryPage;
|
||||
import acr.browser.lightning.constant.StartPage;
|
||||
import acr.browser.lightning.controller.UIController;
|
||||
import acr.browser.lightning.database.BookmarkManager;
|
||||
import acr.browser.lightning.dialog.LightningDialogBuilder;
|
||||
import acr.browser.lightning.download.LightningDownloadListener;
|
||||
import acr.browser.lightning.preference.PreferenceManager;
|
||||
|
||||
import com.anthonycr.bonsai.Schedulers;
|
||||
|
||||
import acr.browser.lightning.utils.Preconditions;
|
||||
import acr.browser.lightning.utils.ProxyUtils;
|
||||
import acr.browser.lightning.utils.UrlUtils;
|
||||
@ -110,7 +107,6 @@ public class LightningView {
|
||||
@Inject PreferenceManager mPreferences;
|
||||
@Inject LightningDialogBuilder mBookmarksDialogBuilder;
|
||||
@Inject ProxyUtils mProxyUtils;
|
||||
@Inject BookmarkManager mBookmarkManager;
|
||||
|
||||
public LightningView(@NonNull Activity activity, @Nullable String url, boolean isIncognito) {
|
||||
BrowserApp.getAppComponent().inject(this);
|
||||
|
Loading…
Reference in New Issue
Block a user