() {
+ @Override
+ public void onComplete() {
+ // At this point we always have at least a tab in the tab manager
+ tabChanged(mTabsModel.last());
+ mView.updateTabNumber(mTabsModel.size());
+ }
+ });
+ }
+
+ /**
+ * Notify the presenter that a change occurred to
+ * the current tab. Currently doesn't do anything
+ * other than tell the view to notify the adapter
+ * about the change.
+ *
+ * @param tab the tab that changed, may be null.
+ */
+ public void tabChangeOccurred(@Nullable LightningView tab) {
+ mView.notifyTabViewChanged(mTabsModel.indexOfTab(tab));
+ }
+
+ private void onTabChanged(@Nullable LightningView newTab) {
+ Log.d(TAG, "On tab changed");
+ if (newTab == null) {
+ mView.removeTabView();
+ if (mCurrentTab != null) {
+ mCurrentTab.pauseTimers();
+ mCurrentTab.onDestroy();
+ }
+ } else {
+ if (newTab.getWebView() == null) {
+ mView.removeTabView();
+ if (mCurrentTab != null) {
+ mCurrentTab.pauseTimers();
+ mCurrentTab.onDestroy();
+ }
+ } else {
+ if (mCurrentTab != null) {
+ // TODO: Restore this when Google fixes the bug where the WebView is
+ // blank after calling onPause followed by onResume.
+ // mCurrentTab.onPause();
+ mCurrentTab.setForegroundTab(false);
+ }
+
+ newTab.resumeTimers();
+ newTab.onResume();
+ newTab.setForegroundTab(true);
+
+ mView.updateProgress(newTab.getProgress());
+ mView.setBackButtonEnabled(newTab.canGoBack());
+ mView.setForwardButtonEnabled(newTab.canGoForward());
+ mView.updateUrl(newTab.getUrl(), true);
+ mView.setTabView(newTab.getWebView());
+ int index = mTabsModel.indexOfTab(newTab);
+ if (index >= 0) {
+ mView.notifyTabViewChanged(mTabsModel.indexOfTab(newTab));
+ }
+ }
+ }
+
+ mCurrentTab = newTab;
+ }
+
+ /**
+ * Closes all tabs but the current tab.
+ */
+ public void closeAllOtherTabs() {
+
+ while (mTabsModel.last() != mTabsModel.indexOfCurrentTab()) {
+ deleteTab(mTabsModel.last());
+ }
+
+ while (0 != mTabsModel.indexOfCurrentTab()) {
+ deleteTab(0);
+ }
+
+ }
+
+ /**
+ * Deletes the tab at the specified position.
+ *
+ * @param position the position at which to
+ * delete the tab.
+ */
+ public void deleteTab(int position) {
+ Log.d(TAG, "delete Tab");
+ final LightningView tabToDelete = mTabsModel.getTabAtPosition(position);
+
+ if (tabToDelete == null) {
+ return;
+ }
+
+ if (!UrlUtils.isSpecialUrl(tabToDelete.getUrl()) && !mIsIncognito) {
+ mPreferences.setSavedUrl(tabToDelete.getUrl());
+ }
+
+ final boolean isShown = tabToDelete.isShown();
+ boolean shouldClose = mShouldClose && isShown && Boolean.TRUE.equals(tabToDelete.getTag());
+ final LightningView currentTab = mTabsModel.getCurrentTab();
+ if (mTabsModel.size() == 1 && currentTab != null &&
+ (UrlUtils.isSpecialUrl(currentTab.getUrl()) ||
+ currentTab.getUrl().equals(mPreferences.getHomepage()))) {
+ mView.closeActivity();
+ return;
+ } else {
+ if (isShown) {
+ mView.removeTabView();
+ }
+ boolean currentDeleted = mTabsModel.deleteTab(position);
+ if (currentDeleted) {
+ tabChanged(mTabsModel.indexOfCurrentTab());
+ }
+ }
+
+ final LightningView afterTab = mTabsModel.getCurrentTab();
+ mView.notifyTabViewRemoved(position);
+
+ if (afterTab == null) {
+ mView.closeBrowser();
+ return;
+ } else if (afterTab != currentTab) {
+ //TODO remove this?
+// switchTabs(currentTab, afterTab);
+// if (currentTab != null) {
+// currentTab.pauseTimers();
+// }
+ mView.notifyTabViewChanged(mTabsModel.indexOfCurrentTab());
+ }
+
+ if (shouldClose) {
+ mShouldClose = false;
+ mView.closeActivity();
+ }
+
+ mView.updateTabNumber(mTabsModel.size());
+
+ Log.d(TAG, "deleted tab");
+ }
+
+ /**
+ * Handle a new intent from the the main
+ * BrowserActivity.
+ *
+ * @param intent the intent to handle,
+ * may be null.
+ */
+ public void onNewIntent(@Nullable final Intent intent) {
+ mTabsModel.doAfterInitialization(new Runnable() {
+ @Override
+ public void run() {
+ final String url;
+ if (intent != null) {
+ url = intent.getDataString();
+ } else {
+ url = null;
+ }
+ int num = 0;
+ if (intent != null && intent.getExtras() != null) {
+ num = intent.getExtras().getInt(Constants.INTENT_ORIGIN);
+ }
+
+ if (num == 1) {
+ loadUrlInCurrentView(url);
+ } else if (url != null) {
+ if (url.startsWith(Constants.FILE)) {
+ mView.showBlockedLocalFileDialog(new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ newTab(url, true);
+ }
+ });
+ } else {
+ newTab(url, true);
+ }
+ mShouldClose = true;
+ LightningView tab = mTabsModel.lastTab();
+ if (tab != null) {
+ tab.setTag(true);
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Loads a URL in the current tab.
+ *
+ * @param url the URL to load, must
+ * not be null.
+ */
+ public void loadUrlInCurrentView(@NonNull final String url) {
+ final LightningView currentTab = mTabsModel.getCurrentTab();
+ if (currentTab == null) {
+ // This is a problem, probably an assert will be better than a return
+ return;
+ }
+
+ currentTab.loadUrl(url);
+ }
+
+ /**
+ * Notifies the presenter that it should
+ * shut down. This should be called when
+ * the BrowserActivity is destroyed so that
+ * we don't leak any memory.
+ */
+ public void shutdown() {
+ onTabChanged(null);
+ mTabsModel.setTabNumberChangedListener(null);
+ mTabsModel.cancelPendingWork();
+ }
+
+ /**
+ * Notifies the presenter that we wish
+ * to switch to a different tab at the
+ * specified position. If the position
+ * is not in the model, this method will
+ * do nothing.
+ *
+ * @param position the position of the
+ * tab to switch to.
+ */
+ public synchronized void tabChanged(int position) {
+ Log.d(TAG, "tabChanged: " + position);
+ if (position < 0 || position >= mTabsModel.size()) {
+ return;
+ }
+ LightningView tab = mTabsModel.switchToTab(position);
+ onTabChanged(tab);
+ }
+
+ /**
+ * Open a new tab with the specified URL. You
+ * can choose to show the tab or load it in the
+ * background.
+ *
+ * @param url the URL to load, may be null if you
+ * don't wish to load anything.
+ * @param show whether or not to switch to this
+ * tab after opening it.
+ * @return true if we successfully created the tab,
+ * false if we have hit max tabs.
+ */
+ public synchronized boolean newTab(@Nullable String url, boolean show) {
+ // Limit number of tabs for limited version of app
+ if (!Constants.FULL_VERSION && mTabsModel.size() >= 10) {
+ mView.showSnackbar(R.string.max_tabs);
+ return false;
+ }
+
+ Log.d(TAG, "New tab, show: " + show);
+
+ LightningView startingTab = mTabsModel.newTab((Activity) mView, url, mIsIncognito);
+ if (mTabsModel.size() == 1) {
+ startingTab.resumeTimers();
+ }
+
+ mView.notifyTabViewAdded();
+
+ if (show) {
+ LightningView tab = mTabsModel.switchToTab(mTabsModel.last());
+ onTabChanged(tab);
+ }
+
+ mView.updateTabNumber(mTabsModel.size());
+
+ return true;
+ }
+
+}
diff --git a/app/src/main/java/acr/browser/lightning/browser/BrowserView.java b/app/src/main/java/acr/browser/lightning/browser/BrowserView.java
new file mode 100644
index 000000000..4ef45f59f
--- /dev/null
+++ b/app/src/main/java/acr/browser/lightning/browser/BrowserView.java
@@ -0,0 +1,38 @@
+package acr.browser.lightning.browser;
+
+import android.content.DialogInterface;
+import android.support.annotation.NonNull;
+import android.support.annotation.StringRes;
+import android.view.View;
+
+public interface BrowserView {
+
+ void setTabView(@NonNull View view);
+
+ void removeTabView();
+
+ void updateUrl(String url, boolean shortUrl);
+
+ void updateProgress(int progress);
+
+ void updateTabNumber(int number);
+
+ void closeBrowser();
+
+ void closeActivity();
+
+ void showBlockedLocalFileDialog(DialogInterface.OnClickListener listener);
+
+ void showSnackbar(@StringRes int resource);
+
+ void setForwardButtonEnabled(boolean enabled);
+
+ void setBackButtonEnabled(boolean enabled);
+
+ void notifyTabViewRemoved(int position);
+
+ void notifyTabViewAdded();
+
+ void notifyTabViewChanged(int position);
+
+}
diff --git a/app/src/main/java/acr/browser/lightning/browser/TabsView.java b/app/src/main/java/acr/browser/lightning/browser/TabsView.java
new file mode 100644
index 000000000..8c4e64ee2
--- /dev/null
+++ b/app/src/main/java/acr/browser/lightning/browser/TabsView.java
@@ -0,0 +1,11 @@
+package acr.browser.lightning.browser;
+
+public interface TabsView {
+
+ void tabAdded();
+
+ void tabRemoved(int position);
+
+ void tabChanged(int position);
+
+}
diff --git a/app/src/main/java/acr/browser/lightning/bus/BookmarkEvents.java b/app/src/main/java/acr/browser/lightning/bus/BookmarkEvents.java
index 3331eb7e7..20c10e595 100644
--- a/app/src/main/java/acr/browser/lightning/bus/BookmarkEvents.java
+++ b/app/src/main/java/acr/browser/lightning/bus/BookmarkEvents.java
@@ -2,37 +2,12 @@
import acr.browser.lightning.database.HistoryItem;
-/**
- * Created by Stefano Pacifici on 26/08/15.
- */
public final class BookmarkEvents {
private BookmarkEvents() {
// No instances
}
- /**
- * A bookmark was clicked
- */
- public final static class Clicked {
- public final HistoryItem bookmark;
-
- public Clicked(final HistoryItem bookmark) {
- this.bookmark = bookmark;
- }
- }
-
- /**
- * The user ask to open the bookmark as new tab
- */
- public final static class AsNewTab {
- public final HistoryItem bookmark;
-
- public AsNewTab(final HistoryItem bookmark) {
- this.bookmark = bookmark;
- }
- }
-
/**
* The user ask to delete the selected bookmark
*/
@@ -45,29 +20,11 @@ public Deleted(final HistoryItem item) {
}
/**
- * The user ask to bookmark the currently displayed page
+ * The user ask to add/del a bookmark to the currently displayed page
*/
- public static class WantToBookmarkCurrentPage {
+ public static class ToggleBookmarkForCurrentPage {
}
- /**
- * The bookmark was added
- */
- public static class Added {
- public final HistoryItem item;
-
- public Added(final HistoryItem item) {
- this.item = item;
- }
- }
-
- /**
- * The {@link acr.browser.lightning.fragment.BookmarksFragment} want to know the url (and title)
- * of the currently shown web page.
- */
- // public static class WantInfoAboutCurrentPage {
- // }
-
/**
* Sended by the {@link acr.browser.lightning.fragment.BookmarksFragment} when it wants to close
* itself (generally in reply to a {@link acr.browser.lightning.bus.BrowserEvents.UserPressedBack}
diff --git a/app/src/main/java/acr/browser/lightning/bus/BrowserEvents.java b/app/src/main/java/acr/browser/lightning/bus/BrowserEvents.java
index 15ff37369..17dbe6664 100644
--- a/app/src/main/java/acr/browser/lightning/bus/BrowserEvents.java
+++ b/app/src/main/java/acr/browser/lightning/bus/BrowserEvents.java
@@ -1,8 +1,8 @@
package acr.browser.lightning.bus;
-/**
- * Created by Stefano Pacifici on 26/08/15.
- */
+import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
+
public final class BrowserEvents {
private BrowserEvents() {
@@ -10,23 +10,21 @@ private BrowserEvents() {
}
/**
- * Used to reply to the {@link acr.browser.lightning.fragment.BookmarksFragment} message
- * {@link acr.browser.lightning.bus.BookmarkEvents.WantToBookmarkCurrentPage}. The interaction
- * result is a new bookmark added.
+ * The {@link acr.browser.lightning.activity.BrowserActivity} signal a new bookmark was added
+ * (mainly to the {@link acr.browser.lightning.fragment.BookmarksFragment}).
*/
- public static class AddBookmark {
+ public static class BookmarkAdded {
public final String title, url;
- public AddBookmark(final String title, final String url) {
+ public BookmarkAdded(final String title, final String url) {
this.title = title;
this.url = url;
}
}
/**
- * Used to reply to {@link acr.browser.lightning.fragment.BookmarksFragment} message
- * {@link acr.browser.lightning.bus.BookmarkEvents.WantInfoAboutCurrentPage}. This is generally
- * used to update the {@link acr.browser.lightning.fragment.BookmarksFragment} interface.
+ * Notify the current page has a new url. This is generally used to update the
+ * {@link acr.browser.lightning.fragment.BookmarksFragment} interface.
*/
public static class CurrentPageUrl {
public final String url;
@@ -41,4 +39,52 @@ public CurrentPageUrl(final String url) {
*/
public static class UserPressedBack {
}
+
+ /**
+ *
+ */
+
+ /**
+ * Notify the Browser to display a SnackBar in the main activity
+ */
+ public static class ShowSnackBarMessage {
+ @Nullable public final String message;
+ @StringRes
+ public final int stringRes;
+
+ public ShowSnackBarMessage(@Nullable final String message) {
+ this.message = message;
+ this.stringRes = -1;
+ }
+
+ public ShowSnackBarMessage(@StringRes final int stringRes) {
+ this.message = null;
+ this.stringRes = stringRes;
+ }
+ }
+
+ public final static class OpenHistoryInCurrentTab {
+ }
+
+ /**
+ * The user want to open the given url in the current tab
+ */
+ public final static class OpenUrlInCurrentTab {
+ public final String url;
+
+ public OpenUrlInCurrentTab(final String url) {
+ this.url = url;
+ }
+ }
+
+ /**
+ * The user ask to open the given url as new tab
+ */
+ public final static class OpenUrlInNewTab {
+ public final String url;
+
+ public OpenUrlInNewTab(final String url) {
+ this.url = url;
+ }
+ }
}
diff --git a/app/src/main/java/acr/browser/lightning/bus/NavigationEvents.java b/app/src/main/java/acr/browser/lightning/bus/NavigationEvents.java
new file mode 100644
index 000000000..5f7519fa3
--- /dev/null
+++ b/app/src/main/java/acr/browser/lightning/bus/NavigationEvents.java
@@ -0,0 +1,34 @@
+package acr.browser.lightning.bus;
+
+/**
+ * Collections of navigation events, like go back or go forward
+ *
+ * @author Stefano Pacifici
+ * @date 2015/09/15
+ */
+public class NavigationEvents {
+ private NavigationEvents() {
+ // No instances please
+ }
+
+ /**
+ * Fired by {@link acr.browser.lightning.fragment.TabsFragment} when the user presses back
+ * button.
+ */
+ public static class GoBack {
+ }
+
+ /**
+ * Fired by {@link acr.browser.lightning.fragment.TabsFragment} when the user presses forward
+ * button.
+ */
+ public static class GoForward {
+ }
+
+ /**
+ * Fired by {@link acr.browser.lightning.fragment.TabsFragment} when the user presses the home
+ * button.
+ */
+ public static class GoHome {
+ }
+}
diff --git a/app/src/main/java/acr/browser/lightning/bus/TabEvents.java b/app/src/main/java/acr/browser/lightning/bus/TabEvents.java
new file mode 100644
index 000000000..a2bac5c9d
--- /dev/null
+++ b/app/src/main/java/acr/browser/lightning/bus/TabEvents.java
@@ -0,0 +1,65 @@
+package acr.browser.lightning.bus;
+
+/**
+ * A collection of events been sent by {@link acr.browser.lightning.fragment.TabsFragment}
+ *
+ * @author Stefano Pacifici
+ * @date 2015/09/14
+ */
+public final class TabEvents {
+
+ private TabEvents() {
+ // No instances
+ }
+
+
+ /**
+ * Sended by {@link acr.browser.lightning.fragment.TabsFragment} when the user click on the
+ * tab exit button
+ */
+ public static class CloseTab {
+ public final int position;
+
+ public CloseTab(int position) {
+ this.position = position;
+ }
+ }
+
+ /**
+ * Sended by {@link acr.browser.lightning.fragment.TabsFragment} when the user click on the
+ * tab itself.
+ */
+ public static class ShowTab {
+ public final int position;
+
+ public ShowTab(int position) {
+ this.position = position;
+ }
+ }
+
+ /**
+ * Sended by {@link acr.browser.lightning.fragment.TabsFragment} when the user long press on the
+ * tab itself.
+ */
+ public static class ShowCloseDialog {
+ public final int position;
+
+ public ShowCloseDialog(int position) {
+ this.position = position;
+ }
+ }
+
+ /**
+ * Sended by {@link acr.browser.lightning.fragment.TabsFragment} when the user want to create a
+ * new tab.
+ */
+ public static class NewTab {
+ }
+
+ /**
+ * Sended by {@link acr.browser.lightning.fragment.TabsFragment} when the user long presses on
+ * new tab button.
+ */
+ public static class NewTabLongPress {
+ }
+}
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 67c176586..6e4a02ae1 100644
--- a/app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java
+++ b/app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java
@@ -3,31 +3,44 @@
*/
package acr.browser.lightning.constant;
-import android.content.Context;
+import android.app.Activity;
+import android.app.Application;
+import android.graphics.Bitmap;
+import android.os.AsyncTask;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
+import java.lang.ref.WeakReference;
import java.util.List;
-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.utils.ThemeUtils;
import acr.browser.lightning.utils.Utils;
+import acr.browser.lightning.view.LightningView;
+
+public final class BookmarkPage extends AsyncTask {
-public final class BookmarkPage {
+ /**
+ * The bookmark page standard suffix
+ */
+ public static final String FILENAME = "bookmarks.html";
- private static final String HEADING = "\n" +
+ private static final String HEADING_1 = "\n" +
"\n" +
"\n" +
"\n" +
- "\n" +
- "" +
- BrowserApp.getAppContext().getString(R.string.action_bookmarks) +
- "\n" +
+ "\n" +
+ "";
+
+ private static final String HEADING_2 = "\n" +
"\n" +
"\n" +
@@ -41,7 +54,7 @@ public final class BookmarkPage {
"\n" +
"";
@@ -49,55 +62,90 @@ public final class BookmarkPage {
private static final String END = "";
- @Inject
- BookmarkManager manager;
+ private File mFilesDir;
+ private File mCacheDir;
+
+ private final Application mApp;
+ private final BookmarkManager mManager;
+ @NonNull private final WeakReference mTabReference;
+ private final Bitmap mFolderIcon;
+ @NonNull private final String mTitle;
+
+ public BookmarkPage(LightningView tab, @NonNull Activity activity, BookmarkManager manager) {
+ mApp = BrowserApp.get(activity);
+ final Bitmap folderIcon = ThemeUtils.getThemedBitmap(activity, R.drawable.ic_folder, false);
+ mTitle = mApp.getString(R.string.action_bookmarks);
+ mManager = manager;
+ mTabReference = new WeakReference<>(tab);
+ mFolderIcon = folderIcon;
+ }
- private final File FILES_DIR;
- private final File CACHE_DIR;
+ @Override
+ protected Void doInBackground(Void... params) {
+ mCacheDir = mApp.getCacheDir();
+ mFilesDir = mApp.getFilesDir();
+ cacheDefaultFolderIcon();
+ buildBookmarkPage(null, mManager);
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void aVoid) {
+ super.onPostExecute(aVoid);
+ LightningView tab = mTabReference.get();
+ if (tab != null) {
+ File bookmarkWebPage = new File(mFilesDir, FILENAME);
+ tab.loadUrl(Constants.FILE + bookmarkWebPage);
+ }
+ }
- @Inject
- public BookmarkPage(Context context) {
- BrowserApp.getAppComponent().inject(this);
- FILES_DIR = context.getFilesDir();
- CACHE_DIR = context.getCacheDir();
+ private void cacheDefaultFolderIcon() {
+ FileOutputStream outputStream = null;
+ File image = new File(mCacheDir, "folder.png");
+ try {
+ outputStream = new FileOutputStream(image);
+ mFolderIcon.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
+ mFolderIcon.recycle();
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } finally {
+ Utils.close(outputStream);
+ }
}
- public void buildBookmarkPage(final String folder, final List list) {
+ private void buildBookmarkPage(@Nullable final String folder, @NonNull final BookmarkManager manager) {
+ final List list = manager.getBookmarksCopyFromFolder(folder, true);
final File bookmarkWebPage;
if (folder == null || folder.isEmpty()) {
- bookmarkWebPage = new File(FILES_DIR, Constants.BOOKMARKS_FILENAME);
+ bookmarkWebPage = new File(mFilesDir, FILENAME);
} else {
- bookmarkWebPage = new File(FILES_DIR, folder + '-' + Constants.BOOKMARKS_FILENAME);
+ bookmarkWebPage = new File(mFilesDir, folder + '-' + FILENAME);
}
- final StringBuilder bookmarkBuilder = new StringBuilder(BookmarkPage.HEADING);
+ final StringBuilder bookmarkBuilder = new StringBuilder(HEADING_1 + mTitle + HEADING_2);
- final String folderIconPath = Constants.FILE + CACHE_DIR + "/folder.png";
+ final String folderIconPath = Constants.FILE + mCacheDir + "/folder.png";
for (int n = 0, size = list.size(); n < size; n++) {
final HistoryItem item = list.get(n);
- bookmarkBuilder.append(BookmarkPage.PART1);
+ bookmarkBuilder.append(PART1);
if (item.isFolder()) {
- final File folderPage = new File(FILES_DIR, item.getTitle() + '-' + Constants.BOOKMARKS_FILENAME);
+ final File folderPage = new File(mFilesDir, item.getTitle() + '-' + FILENAME);
bookmarkBuilder.append(Constants.FILE).append(folderPage);
- bookmarkBuilder.append(BookmarkPage.PART2);
+ bookmarkBuilder.append(PART2);
bookmarkBuilder.append(folderIconPath);
- new Thread(new Runnable() {
- @Override
- public void run() {
- buildBookmarkPage(item.getTitle(), manager.getBookmarksFromFolder(item.getTitle(), true));
- }
- }).run();
+ buildBookmarkPage(item.getTitle(), manager);
} else {
bookmarkBuilder.append(item.getUrl());
- bookmarkBuilder.append(BookmarkPage.PART2).append(BookmarkPage.PART3);
+ bookmarkBuilder.append(PART2).append(PART3);
bookmarkBuilder.append(item.getUrl());
}
- bookmarkBuilder.append(BookmarkPage.PART4);
+ bookmarkBuilder.append(PART4);
bookmarkBuilder.append(item.getTitle());
- bookmarkBuilder.append(BookmarkPage.PART5);
+ bookmarkBuilder.append(PART5);
}
- bookmarkBuilder.append(BookmarkPage.END);
+ bookmarkBuilder.append(END);
FileWriter bookWriter = null;
try {
+ //noinspection IOResourceOpenedButNotSafelyClosed
bookWriter = new FileWriter(bookmarkWebPage, false);
bookWriter.write(bookmarkBuilder.toString());
} catch (IOException e) {
@@ -107,4 +155,8 @@ public void run() {
}
}
+ public void load() {
+ executeOnExecutor(BrowserApp.getIOThread());
+ }
+
}
diff --git a/app/src/main/java/acr/browser/lightning/constant/Constants.java b/app/src/main/java/acr/browser/lightning/constant/Constants.java
index 33bc97335..3861a28e8 100644
--- a/app/src/main/java/acr/browser/lightning/constant/Constants.java
+++ b/app/src/main/java/acr/browser/lightning/constant/Constants.java
@@ -27,6 +27,22 @@ private Constants() {
public static final String YANDEX_SEARCH = "https://yandex.ru/yandsearch?lr=21411&text=";
public static final String JAVASCRIPT_INVERT_PAGE = "javascript:(function(){var e='img {-webkit-filter: invert(100%);'+'-moz-filter: invert(100%);'+'-o-filter: invert(100%);'+'-ms-filter: invert(100%); }',t=document.getElementsByTagName('head')[0],n=document.createElement('style');if(!window.counter){window.counter=1}else{window.counter++;if(window.counter%2==0){var e='html {-webkit-filter: invert(0%); -moz-filter: invert(0%); -o-filter: invert(0%); -ms-filter: invert(0%); }'}}n.type='text/css';if(n.styleSheet){n.styleSheet.cssText=e}else{n.appendChild(document.createTextNode(e))}t.appendChild(n)})();";
public static final String JAVASCRIPT_TEXT_REFLOW = "javascript:document.getElementsByTagName('body')[0].style.width=window.innerWidth+'px';";
+ public static final String JAVASCRIPT_THEME_COLOR = "(function () {\n" +
+ " \"use strict\";\n" +
+ " var metas, i, tag;\n" +
+ " metas = document.getElementsByTagName('meta');\n" +
+ " if (metas !== null) {\n" +
+ " for (i = 0; i < metas.length; i++) {\n" +
+ " tag = metas[i].getAttribute('name');\n" +
+ " if (tag !== null && tag.toLowerCase() === 'theme-color') {\n" +
+ " return metas[i].getAttribute('content');\n" +
+ " }\n" +
+ " console.log(tag);\n" +
+ " }\n" +
+ " }\n" +
+ '\n' +
+ " return '';\n" +
+ "}());";
public static final String LOAD_READING_URL = "ReadingUrl";
@@ -43,12 +59,9 @@ private Constants() {
public static final int PROXY_I2P = 2;
public static final int PROXY_MANUAL = 3;
- /**
- * The bookmark page standard suffix
- */
- public static final String BOOKMARKS_FILENAME = "bookmarks.html";
-
public static final String DEFAULT_ENCODING = "UTF-8";
public static final String[] TEXT_ENCODINGS = {"ISO-8859-1", "UTF-8", "GBK", "Big5", "ISO-2022-JP", "SHIFT_JS", "EUC-JP", "EUC-KR"};
+
+ public static final String INTENT_ORIGIN = "URL_INTENT_ORIGIN";
}
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 41dc907ad..e628e8ac6 100644
--- a/app/src/main/java/acr/browser/lightning/constant/HistoryPage.java
+++ b/app/src/main/java/acr/browser/lightning/constant/HistoryPage.java
@@ -3,27 +3,32 @@
*/
package acr.browser.lightning.constant;
+import android.app.Application;
+import android.os.AsyncTask;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
+import java.lang.ref.WeakReference;
import java.util.Iterator;
import java.util.List;
-import android.content.Context;
-
-import acr.browser.lightning.app.BrowserApp;
-import acr.browser.lightning.database.HistoryItem;
import acr.browser.lightning.R;
+import acr.browser.lightning.app.BrowserApp;
import acr.browser.lightning.database.HistoryDatabase;
+import acr.browser.lightning.database.HistoryItem;
import acr.browser.lightning.utils.Utils;
+import acr.browser.lightning.view.LightningView;
-public class HistoryPage {
+public class HistoryPage extends AsyncTask {
public static final String FILENAME = "history.html";
- private static final String HEADING = ""
- + BrowserApp.getAppContext().getString(R.string.action_history)
- + "";
+ private static final String HEADING_1 = "
";
+
+ private static final String HEADING_2 = "";
private static final String PART1 = "
historyList = getWebHistory(context);
+ @NonNull private final WeakReference mTabReference;
+ @NonNull private final Application mApp;
+ @NonNull private final String mTitle;
+ private final HistoryDatabase mHistoryDatabase;
+
+ @Nullable private String mHistoryUrl = null;
+
+ public HistoryPage(LightningView tab, @NonNull Application app, HistoryDatabase database) {
+ mTabReference = new WeakReference<>(tab);
+ mApp = app;
+ mTitle = app.getString(R.string.action_history);
+ mHistoryDatabase = database;
+ }
+
+ @Nullable
+ @Override
+ protected Void doInBackground(Void... params) {
+ mHistoryUrl = getHistoryPage();
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void aVoid) {
+ super.onPostExecute(aVoid);
+ LightningView tab = mTabReference.get();
+ if (tab != null && mHistoryUrl != null) {
+ tab.loadUrl(mHistoryUrl);
+ }
+ }
+
+ @NonNull
+ private String getHistoryPage() {
+ StringBuilder historyBuilder = new StringBuilder(HEADING_1 + mTitle + HEADING_2);
+ List historyList = mHistoryDatabase.getLastHundredItems();
Iterator it = historyList.iterator();
HistoryItem helper;
while (it.hasNext()) {
helper = it.next();
- historyBuilder.append(HistoryPage.PART1);
+ historyBuilder.append(PART1);
historyBuilder.append(helper.getUrl());
- historyBuilder.append(HistoryPage.PART2);
+ historyBuilder.append(PART2);
historyBuilder.append(helper.getTitle());
- historyBuilder.append(HistoryPage.PART3);
+ historyBuilder.append(PART3);
historyBuilder.append(helper.getUrl());
- historyBuilder.append(HistoryPage.PART4);
+ historyBuilder.append(PART4);
}
- historyBuilder.append(HistoryPage.END);
- File historyWebPage = new File(context.getFilesDir(), FILENAME);
+ historyBuilder.append(END);
+ File historyWebPage = new File(mApp.getFilesDir(), FILENAME);
FileWriter historyWriter = null;
try {
+ //noinspection IOResourceOpenedButNotSafelyClosed
historyWriter = new FileWriter(historyWebPage, false);
historyWriter.write(historyBuilder.toString());
} catch (IOException e) {
@@ -65,8 +102,22 @@ public static String getHistoryPage(Context context) {
return Constants.FILE + historyWebPage;
}
- private static List getWebHistory(Context context) {
- HistoryDatabase databaseHandler = HistoryDatabase.getInstance();
- return databaseHandler.getLastHundredItems();
+ public void load() {
+ executeOnExecutor(BrowserApp.getIOThread());
}
-}
+
+ /**
+ * Use this method to immediately delete the history
+ * page on the current thread. This will clear the
+ * cached history page that was stored on file.
+ *
+ * @param application the application object needed to get the file.
+ */
+ public static void deleteHistoryPage(@NonNull Application application) {
+ File historyWebPage = new File(application.getFilesDir(), FILENAME);
+ if (historyWebPage.exists()) {
+ historyWebPage.delete();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/acr/browser/lightning/constant/StartPage.java b/app/src/main/java/acr/browser/lightning/constant/StartPage.java
index 35b5453af..15de36510 100644
--- a/app/src/main/java/acr/browser/lightning/constant/StartPage.java
+++ b/app/src/main/java/acr/browser/lightning/constant/StartPage.java
@@ -3,29 +3,36 @@
*/
package acr.browser.lightning.constant;
-import android.app.Activity;
+import android.app.Application;
+import android.os.AsyncTask;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
+import java.lang.ref.WeakReference;
+
+import javax.inject.Inject;
-import acr.browser.lightning.app.BrowserApp;
import acr.browser.lightning.R;
+import acr.browser.lightning.app.BrowserApp;
import acr.browser.lightning.preference.PreferenceManager;
import acr.browser.lightning.utils.Utils;
+import acr.browser.lightning.view.LightningView;
-public class StartPage {
+public class StartPage extends AsyncTask {
- private static final String FILENAME = "homepage.html";
+ public static final String FILENAME = "homepage.html";
- private static final String HEAD = ""
+ private static final String HEAD_1 = ""
+ ""
+ ""
+ ""
+ ""
- + ""
- + BrowserApp.getAppContext().getString(R.string.home)
- + ""
+ + "";
+
+ private static final String HEAD_2 = ""
+ ""
+ " ";
+ @NonNull private final String mTitle;
+ @NonNull private final Application mApp;
+ @NonNull private final WeakReference mTabReference;
+
+ @Inject PreferenceManager mPreferenceManager;
+
+ private String mStartpageUrl;
+
+ public StartPage(LightningView tab, @NonNull Application app) {
+ BrowserApp.getAppComponent().inject(this);
+ mTitle = app.getString(R.string.home);
+ mApp = app;
+ mTabReference = new WeakReference<>(tab);
+ }
+
+ @Nullable
+ @Override
+ protected Void doInBackground(Void... params) {
+ mStartpageUrl = getHomepage();
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void aVoid) {
+ super.onPostExecute(aVoid);
+ LightningView tab = mTabReference.get();
+ if (tab != null) {
+ tab.loadUrl(mStartpageUrl);
+ }
+ }
+
/**
* This method builds the homepage and returns the local URL to be loaded
* when it finishes building.
*
* @return the URL to load
*/
- public static String getHomepage(Activity activity) {
- StringBuilder homepageBuilder = new StringBuilder(StartPage.HEAD);
+ @NonNull
+ private String getHomepage() {
+ StringBuilder homepageBuilder = new StringBuilder(HEAD_1 + mTitle + HEAD_2);
String icon;
String searchUrl;
- switch (PreferenceManager.getInstance().getSearchChoice()) {
+ switch (mPreferenceManager.getSearchChoice()) {
case 0:
// CUSTOM SEARCH
icon = "file:///android_asset/lightning.png";
- searchUrl = PreferenceManager.getInstance().getSearchUrl();
+ searchUrl = mPreferenceManager.getSearchUrl();
break;
case 1:
// GOOGLE_SEARCH;
@@ -88,14 +127,14 @@ public static String getHomepage(Activity activity) {
break;
case 5:
// STARTPAGE_SEARCH;
- icon = "file:///android_asset/startpage.png";
- // "https://startpage.com/graphics/startp_logo.gif";
+ icon = "file:///android_asset/png";
+ // "https://com/graphics/startp_logo.gif";
searchUrl = Constants.STARTPAGE_SEARCH;
break;
case 6:
// STARTPAGE_MOBILE
- icon = "file:///android_asset/startpage.png";
- // "https://startpage.com/graphics/startp_logo.gif";
+ icon = "file:///android_asset/png";
+ // "https://com/graphics/startp_logo.gif";
searchUrl = Constants.STARTPAGE_MOBILE_SEARCH;
break;
case 7:
@@ -131,13 +170,14 @@ public static String getHomepage(Activity activity) {
}
homepageBuilder.append(icon);
- homepageBuilder.append(StartPage.MIDDLE);
+ homepageBuilder.append(MIDDLE);
homepageBuilder.append(searchUrl);
- homepageBuilder.append(StartPage.END);
+ homepageBuilder.append(END);
- File homepage = new File(activity.getFilesDir(), StartPage.FILENAME);
+ File homepage = new File(mApp.getFilesDir(), FILENAME);
FileWriter hWriter = null;
try {
+ //noinspection IOResourceOpenedButNotSafelyClosed
hWriter = new FileWriter(homepage, false);
hWriter.write(homepageBuilder.toString());
} catch (IOException e) {
@@ -148,4 +188,9 @@ public static String getHomepage(Activity activity) {
return Constants.FILE + homepage;
}
+
+ public void load() {
+ executeOnExecutor(BrowserApp.getIOThread());
+ }
+
}
diff --git a/app/src/main/java/acr/browser/lightning/controller/BrowserController.java b/app/src/main/java/acr/browser/lightning/controller/BrowserController.java
deleted file mode 100644
index 73c8e7fde..000000000
--- a/app/src/main/java/acr/browser/lightning/controller/BrowserController.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2014 A.C.R. Development
- */
-package acr.browser.lightning.controller;
-
-import android.graphics.Bitmap;
-import android.net.Uri;
-import android.os.Message;
-import android.view.View;
-import android.webkit.ValueCallback;
-import android.webkit.WebChromeClient.CustomViewCallback;
-import android.webkit.WebView;
-
-import acr.browser.lightning.view.LightningView;
-
-public interface BrowserController {
-
- void updateUrl(String title, boolean shortUrl);
-
- void updateProgress(int n);
-
- void updateHistory(String title, String url);
-
- void openFileChooser(ValueCallback uploadMsg);
-
- void updateTabs();
-
- void onLongPress();
-
- void onShowCustomView(View view, CustomViewCallback callback);
-
- void onHideCustomView();
-
- Bitmap getDefaultVideoPoster();
-
- View getVideoLoadingProgressView();
-
- void onCreateWindow(Message resultMsg);
-
- void onCloseWindow(LightningView view);
-
- void hideActionBar();
-
- void showActionBar();
-
- void longClickPage(String url);
-
- void openBookmarkPage(WebView view);
-
- void showFileChooser(ValueCallback filePathCallback);
-
- void closeEmptyTab();
-
- boolean proxyIsNotReady();
-
- // void updateBookmarkIndicator(String url);
-
-}
diff --git a/app/src/main/java/acr/browser/lightning/controller/UIController.java b/app/src/main/java/acr/browser/lightning/controller/UIController.java
new file mode 100644
index 000000000..e28ca136b
--- /dev/null
+++ b/app/src/main/java/acr/browser/lightning/controller/UIController.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2014 A.C.R. Development
+ */
+package acr.browser.lightning.controller;
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Message;
+import android.support.annotation.ColorInt;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.webkit.ValueCallback;
+import android.webkit.WebChromeClient.CustomViewCallback;
+
+import acr.browser.lightning.activity.TabsManager;
+import acr.browser.lightning.view.LightningView;
+
+public interface UIController {
+
+ void changeToolbarBackground(@NonNull Bitmap favicon, @Nullable Drawable drawable);
+
+ @ColorInt
+ int getUiColor();
+
+ boolean getUseDarkTheme();
+
+ void updateUrl(@Nullable String title, boolean shortUrl);
+
+ void updateProgress(int n);
+
+ void updateHistory(@Nullable String title, @NonNull String url);
+
+ void openFileChooser(ValueCallback uploadMsg);
+
+ void onShowCustomView(View view, CustomViewCallback callback);
+
+ void onShowCustomView(View view, CustomViewCallback callback, int requestedOrienation);
+
+ void onHideCustomView();
+
+ void onCreateWindow(Message resultMsg);
+
+ void onCloseWindow(LightningView view);
+
+ void hideActionBar();
+
+ void showActionBar();
+
+ void showFileChooser(ValueCallback filePathCallback);
+
+ void closeEmptyTab();
+
+ void showCloseDialog(int position);
+
+ void newTabClicked();
+
+ void setForwardButtonEnabled(boolean enabled);
+
+ void setBackButtonEnabled(boolean enabled);
+
+ void tabChanged(LightningView tab);
+
+ TabsManager getTabModel();
+
+}
diff --git a/app/src/main/java/acr/browser/lightning/database/BookmarkLocalSync.java b/app/src/main/java/acr/browser/lightning/database/BookmarkLocalSync.java
index 1c1b8c110..827c099b7 100644
--- a/app/src/main/java/acr/browser/lightning/database/BookmarkLocalSync.java
+++ b/app/src/main/java/acr/browser/lightning/database/BookmarkLocalSync.java
@@ -11,6 +11,9 @@
import java.util.ArrayList;
import java.util.List;
+import acr.browser.lightning.react.Action;
+import acr.browser.lightning.react.Observable;
+import acr.browser.lightning.react.Subscriber;
import acr.browser.lightning.utils.Utils;
public class BookmarkLocalSync {
@@ -19,25 +22,29 @@ public class BookmarkLocalSync {
private static final String STOCK_BOOKMARKS_CONTENT = "content://browser/bookmarks";
private static final String CHROME_BOOKMARKS_CONTENT = "content://com.android.chrome.browser/bookmarks";
+ private static final String CHROME_BETA_BOOKMARKS_CONTENT = "content://com.chrome.beta.browser/bookmarks";
+ private static final String CHROME_DEV_BOOKMARKS_CONTENT = "content://com.chrome.dev.browser/bookmarks";
private static final String COLUMN_TITLE = "title";
private static final String COLUMN_URL = "url";
private static final String COLUMN_BOOKMARK = "bookmark";
- private final Context mContext;
+ @NonNull private final Context mContext;
- public BookmarkLocalSync(Context context) {
+ public enum Source {
+ STOCK,
+ CHROME_STABLE,
+ CHROME_BETA,
+ CHROME_DEV
+ }
+
+ public BookmarkLocalSync(@NonNull Context context) {
mContext = context;
}
- @NonNull
- @WorkerThread
- public List getBookmarksFromStockBrowser() {
+ public List getBookmarksFromContentUri(String contentUri) {
List list = new ArrayList<>();
- if (!isStockSupported()) {
- return list;
- }
- Cursor cursor = getStockCursor();
+ Cursor cursor = getBrowserCursor(contentUri);
try {
if (cursor != null) {
for (int n = 0; n < cursor.getColumnCount(); n++) {
@@ -54,7 +61,9 @@ public List getBookmarksFromStockBrowser() {
if (title == null || title.isEmpty()) {
title = Utils.getDomainName(url);
}
- list.add(new HistoryItem(url, title));
+ if (title != null) {
+ list.add(new HistoryItem(url, title));
+ }
}
}
}
@@ -65,81 +74,133 @@ public List getBookmarksFromStockBrowser() {
return list;
}
- @NonNull
+ @Nullable
@WorkerThread
- public List getBookmarksFromChrome() {
- List list = new ArrayList<>();
- if (!isChromeSupported()) {
- return list;
- }
- Cursor cursor = getStockCursor();
+ private Cursor getBrowserCursor(String contentUri) {
+ Cursor cursor;
+ Uri uri = Uri.parse(contentUri);
try {
- if (cursor != null) {
- for (int n = 0; n < cursor.getColumnCount(); n++) {
- Log.d(TAG, cursor.getColumnName(n));
- }
+ cursor = mContext.getContentResolver().query(uri,
+ new String[]{COLUMN_URL, COLUMN_TITLE, COLUMN_BOOKMARK}, null, null, null);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ return cursor;
+ }
- while (cursor.moveToNext()) {
- if (cursor.getInt(2) == 1) {
- String url = cursor.getString(0);
- String title = cursor.getString(1);
- if (url.isEmpty()) {
- continue;
- }
- if (title == null || title.isEmpty()) {
- title = Utils.getDomainName(url);
- }
- list.add(new HistoryItem(url, title));
- }
+ @NonNull
+ public Observable> getSupportedBrowsers() {
+ return Observable.create(new Action>() {
+ @Override
+ public void onSubscribe(@NonNull Subscriber> subscriber) {
+ List