Lightning browser with I2P configuration
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

519 lines
19 KiB

package acr.browser.lightning.database;
import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.os.Environment;
import android.provider.Browser;
import android.support.annotation.NonNull;
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.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import acr.browser.lightning.R;
import acr.browser.lightning.constant.Constants;
import acr.browser.lightning.preference.PreferenceManager;
import acr.browser.lightning.utils.Utils;
public class BookmarkManager {
private static final String TAG = BookmarkManager.class.getSimpleName();
private final Context mContext;
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";
private Set<String> mBookmarkSearchSet = new HashSet<>();
private final List<HistoryItem> mBookmarkList = new ArrayList<>();
private String mCurrentFolder = "";
private static BookmarkManager mInstance;
public static BookmarkManager getInstance(Context context) {
if (mInstance == null) {
mInstance = new BookmarkManager(context);
}
return mInstance;
}
private BookmarkManager(Context context) {
mContext = context;
mBookmarkList.clear();
mBookmarkList.addAll(getAllBookmarks(true));
mBookmarkSearchSet = getBookmarkUrls(mBookmarkList);
}
public boolean isBookmark(String url) {
return mBookmarkSearchSet.contains(url);
}
/**
* This method adds the the HistoryItem item to permanent bookmark storage.
* It returns true if the operation was successful.
*
* @param item the item to add
*/
public synchronized boolean addBookmark(HistoryItem item) {
File bookmarksFile = new File(mContext.getFilesDir(), FILE_BOOKMARKS);
if (item.getUrl() == null || mBookmarkSearchSet.contains(item.getUrl())) {
return false;
}
mBookmarkList.add(item);
BufferedWriter bookmarkWriter = null;
try {
bookmarkWriter = new BufferedWriter(new FileWriter(bookmarksFile, true));
JSONObject object = new JSONObject();
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();
mBookmarkSearchSet.add(item.getUrl());
} catch (IOException | JSONException e) {
e.printStackTrace();
} finally {
Utils.close(bookmarkWriter);
}
return true;
}
/**
* This method adds the list of HistoryItems to permanent bookmark storage
*
* @param list the list of HistoryItems to add to bookmarks
*/
private synchronized void addBookmarkList(List<HistoryItem> list) {
File bookmarksFile = new File(mContext.getFilesDir(), FILE_BOOKMARKS);
BufferedWriter bookmarkWriter = null;
try {
bookmarkWriter = new BufferedWriter(new FileWriter(bookmarksFile, true));
JSONObject object = new JSONObject();
for (HistoryItem item : list) {
if (item.getUrl() != null && !mBookmarkSearchSet.contains(item.getUrl())) {
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();
mBookmarkSearchSet.add(item.getUrl());
mBookmarkList.add(item);
}
}
} catch (IOException | JSONException e) {
e.printStackTrace();
} finally {
Utils.close(bookmarkWriter);
}
}
/**
* 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(HistoryItem deleteItem) {
if (deleteItem == null || deleteItem.isFolder()) {
return false;
}
mBookmarkSearchSet.remove(deleteItem.getUrl());
mBookmarkList.remove(deleteItem);
overwriteBookmarks(mBookmarkList);
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 (int n = 0; n < mBookmarkList.size(); n++) {
if (mBookmarkList.get(n).getFolder().equals(oldName)) {
mBookmarkList.get(n).setFolder(newName);
} else if (mBookmarkList.get(n).isFolder() && mBookmarkList.get(n).getTitle().equals(oldName)) {
mBookmarkList.get(n).setTitle(newName);
mBookmarkList.get(n).setUrl(Constants.FOLDER + newName);
}
}
overwriteBookmarks(mBookmarkList);
}
/**
* 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) {
Iterator<HistoryItem> iterator = mBookmarkList.iterator();
while (iterator.hasNext()) {
HistoryItem item = iterator.next();
if (!item.isFolder() && item.getFolder().equals(name)) {
item.setFolder("");
} else if (item.getTitle().equals(name)) {
iterator.remove();
}
}
overwriteBookmarks(mBookmarkList);
}
/**
* 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(HistoryItem oldItem, HistoryItem newItem) {
if (oldItem == null || newItem == null || oldItem.isFolder()) {
return;
}
mBookmarkList.remove(oldItem);
mBookmarkList.add(newItem);
if (!oldItem.getUrl().equals(newItem.getUrl())) {
// Update the BookmarkMap if the URL has been changed
mBookmarkSearchSet.remove(oldItem.getUrl());
mBookmarkSearchSet.add(newItem.getUrl());
}
if (newItem.getUrl().isEmpty()) {
deleteBookmark(oldItem);
return;
}
if (newItem.getTitle().isEmpty()) {
newItem.setTitle(mContext.getString(R.string.untitled));
}
overwriteBookmarks(mBookmarkList);
}
/**
* This method exports the stored bookmarks to a text file in the device's
* external download directory
*/
public synchronized void exportBookmarks(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 {
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 (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.
*
* @return returns a list of bookmarks that can be sorted
*/
public synchronized List<HistoryItem> getAllBookmarks(boolean sort) {
final List<HistoryItem> bookmarks = new ArrayList<>();
final File bookmarksFile = new File(mContext.getFilesDir(), FILE_BOOKMARKS);
BufferedReader bookmarksReader = null;
try {
final InputStream inputStream;
if (bookmarksFile.exists() && bookmarksFile.isFile()) {
inputStream = new FileInputStream(bookmarksFile);
} else {
inputStream = mContext.getResources().openRawResource(R.raw.default_bookmarks);
}
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.setOrder(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);
}
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
*/
public synchronized List<HistoryItem> getBookmarksFromFolder(String folder, boolean sort) {
List<HistoryItem> bookmarks = new ArrayList<>();
if (folder == null || folder.isEmpty()) {
bookmarks.addAll(getFolders(sort));
folder = "";
}
mCurrentFolder = folder;
for (int n = 0; n < mBookmarkList.size(); n++) {
if (mBookmarkList.get(n).getFolder().equals(folder))
bookmarks.add(mBookmarkList.get(n));
}
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();
}
/**
* Method is used internally for searching the bookmarks
*
* @return a sorted map of all bookmarks, useful for seeing if a bookmark exists
*/
private Set<String> getBookmarkUrls(List<HistoryItem> list) {
Set<String> set = new HashSet<>();
for (int n = 0; n < list.size(); n++) {
if (!mBookmarkList.get(n).isFolder())
set.add(mBookmarkList.get(n).getUrl());
}
return set;
}
/**
* 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
*/
public synchronized List<HistoryItem> getFolders(boolean sort) {
List<HistoryItem> folders = new ArrayList<>();
Set<String> folderMap = new HashSet<>();
File bookmarksFile = new File(mContext.getFilesDir(), FILE_BOOKMARKS);
BufferedReader bookmarksReader = null;
try {
bookmarksReader = new BufferedReader(new FileReader(bookmarksFile));
String line;
while ((line = bookmarksReader.readLine()) != null) {
JSONObject object = new JSONObject(line);
String folderName = object.getString(FOLDER);
if (!folderName.isEmpty() && !folderMap.contains(folderName)) {
HistoryItem item = new HistoryItem();
item.setTitle(folderName);
item.setUrl(Constants.FOLDER + folderName);
item.setIsFolder(true);
folderMap.add(folderName);
folders.add(item);
}
}
} catch (IOException | JSONException e) {
e.printStackTrace();
} finally {
Utils.close(bookmarksReader);
}
if (sort) {
Collections.sort(folders, new SortIgnoreCase());
}
return folders;
}
/**
* returns a list of folder titles that can be used for suggestions in a
* simple list adapter
*
* @return a list of folder title strings
*/
public synchronized List<String> getFolderTitles() {
List<String> folders = new ArrayList<>();
Set<String> folderMap = new HashSet<>();
File bookmarksFile = new File(mContext.getFilesDir(), FILE_BOOKMARKS);
BufferedReader bookmarksReader = null;
try {
bookmarksReader = new BufferedReader(new FileReader(bookmarksFile));
String line;
while ((line = bookmarksReader.readLine()) != null) {
JSONObject object = new JSONObject(line);
String folderName = object.getString(FOLDER);
if (!folderName.isEmpty() && !folderMap.contains(folderName)) {
folders.add(folderName);
folderMap.add(folderName);
}
}
} catch (IOException | JSONException e) {
e.printStackTrace();
} finally {
Utils.close(bookmarksReader);
}
return 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(File file, Activity activity) {
if (file == null) {
return;
}
List<HistoryItem> list = new ArrayList<>();
BufferedReader bookmarksReader = null;
try {
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 (IOException | JSONException e) {
e.printStackTrace();
Utils.createInformativeDialog(activity, R.string.title_error, R.string.import_bookmark_error);
} finally {
Utils.close(bookmarksReader);
}
}
/**
* This method overwrites the entire bookmark file with the list of
* bookmarks. This is useful when an edit has been made to one or more
* bookmarks in the list
*
* @param list the list of bookmarks to overwrite the old ones with
*/
private synchronized void overwriteBookmarks(List<HistoryItem> list) {
File bookmarksFile = new File(mContext.getFilesDir(), FILE_BOOKMARKS);
BufferedWriter bookmarkWriter = null;
try {
bookmarkWriter = new BufferedWriter(new FileWriter(bookmarksFile, false));
JSONObject object = new JSONObject();
for (int n = 0; n < list.size(); n++) {
HistoryItem item = list.get(n);
if (!item.isFolder()) {
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();
}
}
} catch (IOException | JSONException e) {
e.printStackTrace();
} finally {
Utils.close(bookmarkWriter);
}
}
/**
* find the index of a bookmark in a list using only its URL
*
* @param list the list to search
* @param url the url to compare
* @return returns the index of the bookmark or -1 if none was found
*/
public static int getIndexOfBookmark(final List<HistoryItem> list, final String url) {
for (int n = 0; n < list.size(); n++) {
if (list.get(n).getUrl().equals(url)) {
return n;
}
}
return -1;
}
/**
* This class sorts bookmarks alphabetically, with folders coming after bookmarks
*/
public static class SortIgnoreCase implements Comparator<HistoryItem> {
public int compare(HistoryItem o1, HistoryItem o2) {
if (o1 == null || o2 == null || o1.getTitle() == null || o2.getTitle() == 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;
}
}
}
}