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.
 
 

380 lines
12 KiB

package org.purplei2p.lightning.browser;
import android.app.Activity;
import android.app.Application;
import android.content.DialogInterface;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.webkit.URLUtil;
import com.anthonycr.bonsai.CompletableOnSubscribe;
import com.anthonycr.bonsai.Schedulers;
import javax.inject.Inject;
import org.purplei2p.lightning.BuildConfig;
import org.purplei2p.lightning.R;
import org.purplei2p.lightning.BrowserApp;
import org.purplei2p.lightning.constant.BookmarkPage;
import org.purplei2p.lightning.constant.Constants;
import org.purplei2p.lightning.constant.StartPage;
import org.purplei2p.lightning.controller.UIController;
import org.purplei2p.lightning.preference.PreferenceManager;
import org.purplei2p.lightning.utils.UrlUtils;
import org.purplei2p.lightning.view.LightningView;
/**
* Presenter in charge of keeping track of
* the current tab and setting the current tab
* of the
*/
public class BrowserPresenter {
private static final String TAG = "BrowserPresenter";
@NonNull private final TabsManager mTabsModel;
@Inject Application mApplication;
@Inject PreferenceManager mPreferences;
@NonNull private final BrowserView mView;
@Nullable private LightningView mCurrentTab;
private final boolean mIsIncognito;
private boolean mShouldClose;
public BrowserPresenter(@NonNull BrowserView view, boolean isIncognito) {
BrowserApp.getAppComponent().inject(this);
mTabsModel = ((UIController) view).getTabModel();
mView = view;
mIsIncognito = isIncognito;
mTabsModel.setTabNumberChangedListener(new TabsManager.TabNumberChangedListener() {
@Override
public void tabNumberChanged(int newNumber) {
mView.updateTabNumber(newNumber);
}
});
}
/**
* Initializes the tab manager with the new intent
* that is handed in by the BrowserActivity.
*
* @param intent the intent to handle, may be null.
*/
public void setupTabs(@Nullable Intent intent) {
mTabsModel.initializeTabs((Activity) mView, intent, mIsIncognito)
.subscribeOn(Schedulers.main())
.subscribe(new CompletableOnSubscribe() {
@Override
public void onComplete() {
// At this point we always have at least a tab in the tab manager
mView.notifyTabViewInitialized();
mView.updateTabNumber(mTabsModel.size());
tabChanged(mTabsModel.last());
}
});
}
/**
* 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.setIsForegroundTab(false);
}
newTab.resumeTimers();
newTab.onResume();
newTab.setIsForegroundTab(true);
mView.updateProgress(newTab.getProgress());
mView.setBackButtonEnabled(newTab.canGoBack());
mView.setForwardButtonEnabled(newTab.canGoForward());
mView.updateUrl(newTab.getUrl(), false);
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);
}
}
@NonNull
private String mapHomepageToCurrentUrl() {
String homepage = mPreferences.getHomepage();
switch (homepage) {
case Constants.SCHEME_HOMEPAGE:
return Constants.FILE + StartPage.getStartPageFile(mApplication);
case Constants.SCHEME_BOOKMARKS:
return Constants.FILE + BookmarkPage.getBookmarkPage(mApplication, null);
default:
return homepage;
}
}
/**
* 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 && tabToDelete.isNewTab();
final LightningView currentTab = mTabsModel.getCurrentTab();
if (mTabsModel.size() == 1 && currentTab != null &&
URLUtil.isFileUrl(currentTab.getUrl()) &&
currentTab.getUrl().equals(mapHomepageToCurrentUrl())) {
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 tabHashCode = 0;
if (intent != null && intent.getExtras() != null) {
tabHashCode = intent.getExtras().getInt(Constants.INTENT_ORIGIN);
}
if (tabHashCode != 0) {
LightningView tab = mTabsModel.getTabForHashCode(tabHashCode);
if (tab != null) {
tab.loadUrl(url);
}
} else if (url != null) {
if (URLUtil.isFileUrl(url)) {
mView.showBlockedLocalFileDialog(new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
newTab(url, true);
mShouldClose = true;
LightningView tab = mTabsModel.lastTab();
if (tab != null) {
tab.setIsNewTab(true);
}
}
});
} else {
newTab(url, true);
mShouldClose = true;
LightningView tab = mTabsModel.lastTab();
if (tab != null) {
tab.setIsNewTab(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 (!BuildConfig.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;
}
public void onAutoCompleteItemPressed() {
final LightningView currentTab = mTabsModel.getCurrentTab();
if (currentTab != null) {
currentTab.requestFocus();
}
}
public void findInPage(@NonNull String query) {
final LightningView currentView = mTabsModel.getCurrentTab();
if (currentView != null) {
currentView.find(query);
}
}
public void onAppLowMemory() {
mTabsModel.freeMemory();
}
}