diff --git a/app/src/main/java/acr/browser/lightning/activity/BookmarkUiModel.java b/app/src/main/java/acr/browser/lightning/activity/BookmarkUiModel.java
new file mode 100644
index 0000000..457a984
--- /dev/null
+++ b/app/src/main/java/acr/browser/lightning/activity/BookmarkUiModel.java
@@ -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}.
+ *
+ * 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;
+ }
+
+}
diff --git a/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java b/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java
index 15373f0..a542889 100644
--- a/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java
+++ b/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java
@@ -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() {
+ @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() {
+ @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() {
+ @Override
+ public void onItem(@Nullable Boolean item) {
+ if (Boolean.TRUE.equals(item)) {
+ addBookmark(title, url);
+ } else {
+ deleteBookmark(title, url);
+ }
+ }
+ });
}
}
diff --git a/app/src/main/java/acr/browser/lightning/activity/TabsManager.java b/app/src/main/java/acr/browser/lightning/activity/TabsManager.java
index 02f825d..e2bf713 100644
--- a/app/src/main/java/acr/browser/lightning/activity/TabsManager.java
+++ b/app/src/main/java/acr/browser/lightning/activity/TabsManager.java
@@ -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;
@@ -51,7 +50,7 @@ import acr.browser.lightning.view.LightningView;
public class TabsManager {
private static final String TAG = "TabsManager";
-
+
private static final String BUNDLE_KEY = "WEBVIEW_";
private static final String URL_KEY = "URL_KEY";
private static final String BUNDLE_STORAGE = "SAVED_TABS.parcel";
@@ -64,7 +63,6 @@ public class TabsManager {
private final List mPostInitializationWorkList = new ArrayList<>();
@Inject PreferenceManager mPreferenceManager;
- @Inject BookmarkManager mBookmarkManager;
@Inject Application mApp;
public TabsManager() {
diff --git a/app/src/main/java/acr/browser/lightning/app/AppComponent.java b/app/src/main/java/acr/browser/lightning/app/AppComponent.java
index 12ed5ef..6605da1 100644
--- a/app/src/main/java/acr/browser/lightning/app/AppComponent.java
+++ b/app/src/main/java/acr/browser/lightning/app/AppComponent.java
@@ -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;
diff --git a/app/src/main/java/acr/browser/lightning/app/AppModule.java b/app/src/main/java/acr/browser/lightning/app/AppModule.java
index 605baf2..d4c68f8 100644
--- a/app/src/main/java/acr/browser/lightning/app/AppModule.java
+++ b/app/src/main/java/acr/browser/lightning/app/AppModule.java
@@ -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
diff --git a/app/src/main/java/acr/browser/lightning/app/BrowserApp.java b/app/src/main/java/acr/browser/lightning/app/BrowserApp.java
index 1fd4962..e51ee5f 100644
--- a/app/src/main/java/acr/browser/lightning/app/BrowserApp.java
+++ b/app/src/main/java/acr/browser/lightning/app/BrowserApp.java
@@ -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 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 assetsBookmarks = BookmarkExporter.importBookmarksFromAssets(BrowserApp.this);
+ mBookmarkModel.addBookmarkList(assetsBookmarks).subscribeOn(Schedulers.io()).subscribe();
+ }
+ }
+ });
+
if (mPreferenceManager.getUseLeakCanary() && !isRelease()) {
LeakCanary.install(this);
}
diff --git a/app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java b/app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java
index bdf878d..208dc81 100644
--- a/app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java
+++ b/app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java
@@ -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 = "\n" +
- "\n" +
- "\n" +
- "\n" +
- "\n" +
- "";
+ "\n" +
+ "\n" +
+ "\n" +
+ "\n" +
+ "";
private static final String HEADING_2 = "\n" +
- "\n" +
- "\n" +
- "";
+ "\n" +
+ "\n" +
+ "
";
private static final String PART1 = "
\n" +
- "
\n" +
- "
\n" +
- "
\n" +
- " 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);
-
- 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);
- }
+ mManager.getBookmarksFromFolder(folder)
+ .subscribe(new SingleOnSubscribe>() {
+ @Override
+ public void onItem(@Nullable List list) {
+ Preconditions.checkNonNull(list);
+
+ 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);
+ }
+ }
+ });
}
}
diff --git a/app/src/main/java/acr/browser/lightning/constant/HistoryPage.java b/app/src/main/java/acr/browser/lightning/constant/HistoryPage.java
index 323c190..b175082 100644
--- a/app/src/main/java/acr/browser/lightning/constant/HistoryPage.java
+++ b/app/src/main/java/acr/browser/lightning/constant/HistoryPage.java
@@ -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;
diff --git a/app/src/main/java/acr/browser/lightning/database/BookmarkManager.java b/app/src/main/java/acr/browser/lightning/database/BookmarkManager.java
deleted file mode 100644
index cf6fac4..0000000
--- a/app/src/main/java/acr/browser/lightning/database/BookmarkManager.java
+++ /dev/null
@@ -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 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 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 mBookmarks;
-
- BookmarksWriter(List 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.
- * 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 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 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 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 getAllBookmarks(boolean sort) {
- final List 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 getBookmarksFromFolder(@Nullable String folder, boolean sort) {
- List 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.
- *
- * 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 getBookmarksCopyFromFolder(@Nullable String folder, boolean sort) {
- List 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 getFolders(boolean sort) {
- final HashMap 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 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 getFolderTitles() {
- final Set 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 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 {
-
- 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;
- }
- }
-
- }
-}
diff --git a/app/src/main/java/acr/browser/lightning/database/HistoryItem.java b/app/src/main/java/acr/browser/lightning/database/HistoryItem.java
index b439b1b..9021e73 100644
--- a/app/src/main/java/acr/browser/lightning/database/HistoryItem.java
+++ b/app/src/main/java/acr/browser/lightning/database/HistoryItem.java
@@ -25,7 +25,7 @@ public class HistoryItem implements Comparable {
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 {
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 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
diff --git a/app/src/main/java/acr/browser/lightning/database/bookmark/BookmarkDatabase.java b/app/src/main/java/acr/browser/lightning/database/bookmark/BookmarkDatabase.java
new file mode 100644
index 0000000..e82904b
--- /dev/null
+++ b/app/src/main/java/acr/browser/lightning/database/bookmark/BookmarkDatabase.java
@@ -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.
+ *
+ * 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 bindCursorToHistoryItemList(@NonNull Cursor cursor) {
+ List bookmarks = new ArrayList<>();
+
+ while (cursor.moveToNext()) {
+ bookmarks.add(bindCursorToHistoryItem(cursor));
+ }
+
+ cursor.close();
+
+ return bookmarks;
+ }
+
+ @NonNull
+ @Override
+ public Single findBookmarkForUrl(@NonNull final String url) {
+ return Single.create(new SingleAction() {
+ @Override
+ public void onSubscribe(@NonNull SingleSubscriber 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 isBookmark(@NonNull final String url) {
+ return Single.create(new SingleAction() {
+ @Override
+ public void onSubscribe(@NonNull SingleSubscriber 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 addBookmarkIfNotExists(@NonNull final HistoryItem item) {
+ return Single.create(new SingleAction() {
+ @Override
+ public void onSubscribe(@NonNull SingleSubscriber 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 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 deleteBookmark(@NonNull final HistoryItem bookmark) {
+ return Single.create(new SingleAction() {
+ @Override
+ public void onSubscribe(@NonNull SingleSubscriber 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> getAllBookmarks() {
+ return Single.create(new SingleAction>() {
+ @Override
+ public void onSubscribe(@NonNull SingleSubscriber> subscriber) {
+ Cursor cursor = lazyDatabase().query(TABLE_BOOKMARK, null, null, null, null, null, null);
+
+ subscriber.onItem(bindCursorToHistoryItemList(cursor));
+ subscriber.onComplete();
+ }
+ });
+ }
+
+ @NonNull
+ @Override
+ public Single> getBookmarksFromFolder(@Nullable final String folder) {
+ return Single.create(new SingleAction>() {
+ @Override
+ public void onSubscribe(@NonNull SingleSubscriber> 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> getFolders() {
+ return Single.create(new SingleAction>() {
+ @Override
+ public void onSubscribe(@NonNull SingleSubscriber> subscriber) {
+ Cursor cursor = lazyDatabase().query(true, TABLE_BOOKMARK, new String[]{KEY_FOLDER}, null, null, null, null, null, null);
+
+ List 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> getFolderNames() {
+ return Single.create(new SingleAction>() {
+ @Override
+ public void onSubscribe(@NonNull SingleSubscriber> subscriber) {
+ Cursor cursor = lazyDatabase().query(true, TABLE_BOOKMARK, new String[]{KEY_FOLDER}, null, null, null, null, null, null);
+
+ List 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);
+ }
+
+}
diff --git a/app/src/main/java/acr/browser/lightning/database/bookmark/BookmarkExporter.java b/app/src/main/java/acr/browser/lightning/database/bookmark/BookmarkExporter.java
new file mode 100644
index 0000000..005f395
--- /dev/null
+++ b/app/src/main/java/acr/browser/lightning/database/bookmark/BookmarkExporter.java
@@ -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.
+ *
+ * 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 importBookmarksFromAssets(@NonNull Context context) {
+ List 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 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> importBookmarksFromFile(@NonNull final File file) {
+ return Single.create(new SingleAction>() {
+ @Override
+ public void onSubscribe(@NonNull SingleSubscriber> subscriber) {
+ BufferedReader bookmarksReader = null;
+ try {
+ //noinspection IOResourceOpenedButNotSafelyClosed
+ bookmarksReader = new BufferedReader(new FileReader(file));
+ String line;
+
+ List 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;
+ }
+
+}
diff --git a/app/src/main/java/acr/browser/lightning/database/BookmarkLocalSync.java b/app/src/main/java/acr/browser/lightning/database/bookmark/BookmarkLocalSync.java
similarity index 98%
rename from app/src/main/java/acr/browser/lightning/database/BookmarkLocalSync.java
rename to app/src/main/java/acr/browser/lightning/database/bookmark/BookmarkLocalSync.java
index ce329a9..d7bd6d7 100644
--- a/app/src/main/java/acr/browser/lightning/database/BookmarkLocalSync.java
+++ b/app/src/main/java/acr/browser/lightning/database/bookmark/BookmarkLocalSync.java
@@ -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 {
diff --git a/app/src/main/java/acr/browser/lightning/database/bookmark/BookmarkModel.java b/app/src/main/java/acr/browser/lightning/database/bookmark/BookmarkModel.java
new file mode 100644
index 0000000..04d8f00
--- /dev/null
+++ b/app/src/main/java/acr/browser/lightning/database/bookmark/BookmarkModel.java
@@ -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.
+ *
+ * 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 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 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 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 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 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> 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> 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> getFolders();
+
+ /**
+ * Returns the names of all folders.
+ * The root folder is omitted.
+ *
+ * @return an observable that emits a list of folder names.
+ */
+ @NonNull
+ Single> 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();
+}
diff --git a/app/src/main/java/acr/browser/lightning/database/bookmark/legacy/LegacyBookmarkManager.java b/app/src/main/java/acr/browser/lightning/database/bookmark/legacy/LegacyBookmarkManager.java
new file mode 100644
index 0000000..50de3e6
--- /dev/null
+++ b/app/src/main/java/acr/browser/lightning/database/bookmark/legacy/LegacyBookmarkManager.java
@@ -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 destructiveGetBookmarks(@NonNull Application application) {
+ File filesDir = application.getFilesDir();
+ List 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 {
+
+ 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;
+ }
+ }
+
+ }
+}
diff --git a/app/src/main/java/acr/browser/lightning/database/HistoryDatabase.java b/app/src/main/java/acr/browser/lightning/database/history/HistoryDatabase.java
similarity index 98%
rename from app/src/main/java/acr/browser/lightning/database/HistoryDatabase.java
rename to app/src/main/java/acr/browser/lightning/database/history/HistoryDatabase.java
index 07ae7cc..7bbc07e 100644
--- a/app/src/main/java/acr/browser/lightning/database/HistoryDatabase.java
+++ b/app/src/main/java/acr/browser/lightning/database/history/HistoryDatabase.java
@@ -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
diff --git a/app/src/main/java/acr/browser/lightning/database/HistoryModel.java b/app/src/main/java/acr/browser/lightning/database/history/HistoryModel.java
similarity index 96%
rename from app/src/main/java/acr/browser/lightning/database/HistoryModel.java
rename to app/src/main/java/acr/browser/lightning/database/history/HistoryModel.java
index a577c09..3660584 100644
--- a/app/src/main/java/acr/browser/lightning/database/HistoryModel.java
+++ b/app/src/main/java/acr/browser/lightning/database/history/HistoryModel.java
@@ -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
diff --git a/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java b/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java
index b07cb88..1202299 100644
--- a/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java
+++ b/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java
@@ -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() {
+ @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() {
+ @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 folders = mBookmarkManager.getFolderTitles();
- final ArrayAdapter 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>() {
@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 folders) {
+ Preconditions.checkNonNull(folders);
+ final ArrayAdapter 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();
+ }
+ });
}
}
});
diff --git a/app/src/main/java/acr/browser/lightning/fragment/BookmarkSettingsFragment.java b/app/src/main/java/acr/browser/lightning/fragment/BookmarkSettingsFragment.java
index 3bf2590..5a7e1af 100644
--- a/app/src/main/java/acr/browser/lightning/fragment/BookmarkSettingsFragment.java
+++ b/app/src/main/java/acr/browser/lightning/fragment/BookmarkSettingsFragment.java
@@ -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>() {
+ @Override
+ public void onItem(@Nullable List 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>() {
+ @Override
+ public void onItem(@Nullable final List 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);
+ }
+ });
}
}
diff --git a/app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java b/app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java
index c48cffe..8c4964f 100644
--- a/app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java
+++ b/app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java
@@ -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> initBookmarks(@NonNull final BookmarkManager bookmarkManager) {
- return Single.create(new SingleAction>() {
- @Override
- public void onSubscribe(@NonNull SingleSubscriber> 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>() {
- @Override
- public void onItem(@Nullable List 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() {
+ @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>() {
+ @Override
+ public void onItem(@Nullable final List item) {
+ mBookmarksSubscription = null;
+ Preconditions.checkNonNull(item);
+
+ mUiModel.setCurrentFolder(folder);
+ if (folder == null) {
+ mBookmarkManager.getFolders()
+ .subscribeOn(Schedulers.io())
+ .observeOn(Schedulers.main())
+ .subscribe(new SingleOnSubscribe>() {
+ @Override
+ public void onItem(@Nullable List folders) {
+ Preconditions.checkNonNull(folders);
+ item.addAll(folders);
+ setBookmarkDataSet(item, animate);
+ }
+ });
+ } else {
+ setBookmarkDataSet(item, animate);
+ }
+ }
+ });
+ }
+
private void setBookmarkDataSet(@NonNull List 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 {
diff --git a/app/src/main/java/acr/browser/lightning/search/SuggestionsAdapter.java b/app/src/main/java/acr/browser/lightning/search/SuggestionsAdapter.java
index a29f83e..2937f70 100644
--- a/app/src/main/java/acr/browser/lightning/search/SuggestionsAdapter.java
+++ b/app/src/main/java/acr/browser/lightning/search/SuggestionsAdapter.java
@@ -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 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>() {
+ @Override
+ public void onItem(@Nullable List item) {
+ Preconditions.checkNonNull(item);
+ mAllBookmarks.clear();
+ mAllBookmarks.addAll(item);
+ }
+ });
}
@Override
diff --git a/app/src/main/java/acr/browser/lightning/utils/ThemeUtils.java b/app/src/main/java/acr/browser/lightning/utils/ThemeUtils.java
index 165f8d8..0660f39 100644
--- a/app/src/main/java/acr/browser/lightning/utils/ThemeUtils.java
+++ b/app/src/main/java/acr/browser/lightning/utils/ThemeUtils.java
@@ -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;
diff --git a/app/src/main/java/acr/browser/lightning/utils/WebUtils.java b/app/src/main/java/acr/browser/lightning/utils/WebUtils.java
index 7905664..2ae62c6 100644
--- a/app/src/main/java/acr/browser/lightning/utils/WebUtils.java
+++ b/app/src/main/java/acr/browser/lightning/utils/WebUtils.java
@@ -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
diff --git a/app/src/main/java/acr/browser/lightning/view/LightningView.java b/app/src/main/java/acr/browser/lightning/view/LightningView.java
index 4bf514e..4148fe5 100644
--- a/app/src/main/java/acr/browser/lightning/view/LightningView.java
+++ b/app/src/main/java/acr/browser/lightning/view/LightningView.java
@@ -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);