Browse Source

Simplified LightningView with externalized XXXClients

master
Stefano Pacifici 9 years ago
parent
commit
6749ca39b8
  1. 196
      app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java
  2. 32
      app/src/main/java/acr/browser/lightning/activity/TabsManager.java
  3. 7
      app/src/main/java/acr/browser/lightning/app/AppComponent.java
  4. 22
      app/src/main/java/acr/browser/lightning/bus/BookmarkEvents.java
  5. 42
      app/src/main/java/acr/browser/lightning/bus/BrowserEvents.java
  6. 2
      app/src/main/java/acr/browser/lightning/constant/HistoryPage.java
  7. 14
      app/src/main/java/acr/browser/lightning/database/HistoryDatabase.java
  8. 124
      app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java
  9. 8
      app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java
  10. 19
      app/src/main/java/acr/browser/lightning/object/SearchAdapter.java
  11. 3
      app/src/main/java/acr/browser/lightning/utils/WebUtils.java
  12. 234
      app/src/main/java/acr/browser/lightning/view/LightningChromeClient.java
  13. 555
      app/src/main/java/acr/browser/lightning/view/LightningView.java
  14. 66
      app/src/main/java/acr/browser/lightning/view/LightningViewTitle.java
  15. 276
      app/src/main/java/acr/browser/lightning/view/LightningWebClient.java

196
app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java

@ -18,14 +18,8 @@ import android.content.IntentFilter;
import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteException;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.media.MediaPlayer; import android.media.MediaPlayer;
@ -38,17 +32,13 @@ import android.provider.MediaStore;
import android.support.annotation.IdRes; import android.support.annotation.IdRes;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v4.view.GravityCompat; import android.support.v4.view.GravityCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.DrawerLayout; import android.support.v4.widget.DrawerLayout;
import android.support.v4.widget.DrawerLayout.DrawerListener; import android.support.v4.widget.DrawerLayout.DrawerListener;
import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.support.v7.graphics.Palette; import android.support.v7.graphics.Palette;
import android.support.v7.graphics.drawable.DrawerArrowDrawable; import android.support.v7.graphics.drawable.DrawerArrowDrawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.util.Log; import android.util.Log;
import android.view.KeyEvent; import android.view.KeyEvent;
@ -76,7 +66,6 @@ import android.webkit.ValueCallback;
import android.webkit.WebChromeClient.CustomViewCallback; import android.webkit.WebChromeClient.CustomViewCallback;
import android.webkit.WebIconDatabase; import android.webkit.WebIconDatabase;
import android.webkit.WebView; import android.webkit.WebView;
import android.webkit.WebView.HitTestResult;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
@ -95,11 +84,7 @@ import com.squareup.otto.Bus;
import com.squareup.otto.Subscribe; import com.squareup.otto.Subscribe;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
@ -115,7 +100,7 @@ import acr.browser.lightning.constant.HistoryPage;
import acr.browser.lightning.controller.BrowserController; import acr.browser.lightning.controller.BrowserController;
import acr.browser.lightning.database.BookmarkManager; import acr.browser.lightning.database.BookmarkManager;
import acr.browser.lightning.database.HistoryDatabase; import acr.browser.lightning.database.HistoryDatabase;
import acr.browser.lightning.dialog.BookmarksDialogBuilder; import acr.browser.lightning.dialog.LightningDialogBuilder;
import acr.browser.lightning.fragment.TabsFragment; import acr.browser.lightning.fragment.TabsFragment;
import acr.browser.lightning.object.SearchAdapter; import acr.browser.lightning.object.SearchAdapter;
import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.preference.PreferenceManager;
@ -166,10 +151,6 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
mCurrentUiColor = Color.BLACK; mCurrentUiColor = Color.BLACK;
private String mSearchText, mUntitledTitle, mHomepage, mCameraPhotoPath; private String mSearchText, mUntitledTitle, mHomepage, mCameraPhotoPath;
// Storage
private HistoryDatabase mHistoryDatabase;
// The singleton BookmarkManager // The singleton BookmarkManager
@Inject @Inject
BookmarkManager bookmarkManager; BookmarkManager bookmarkManager;
@ -182,7 +163,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
BookmarkPage bookmarkPage; BookmarkPage bookmarkPage;
@Inject @Inject
BookmarksDialogBuilder bookmarksDialogBuilder; LightningDialogBuilder bookmarksDialogBuilder;
@Inject @Inject
TabsManager tabsManager; TabsManager tabsManager;
@ -190,6 +171,9 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
@Inject @Inject
PreferenceManager mPreferences; PreferenceManager mPreferences;
@Inject
HistoryDatabase mHistoryDatabase;
// Image // Image
private Bitmap mWebpageBitmap; private Bitmap mWebpageBitmap;
private final ColorDrawable mBackground = new ColorDrawable(); private final ColorDrawable mBackground = new ColorDrawable();
@ -280,8 +264,6 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
mToolbarLayout.removeView(findViewById(R.id.tabs_toolbar_container)); mToolbarLayout.removeView(findViewById(R.id.tabs_toolbar_container));
} }
mHistoryDatabase = HistoryDatabase.getInstance();
if (actionBar == null) if (actionBar == null)
return; return;
@ -356,7 +338,9 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
WebIconDatabase.getInstance().open(getDir("icons", MODE_PRIVATE).getPath()); WebIconDatabase.getInstance().open(getDir("icons", MODE_PRIVATE).getPath());
} }
// initializeTabs(); tabsManager.restoreTabs(this, mDarkTheme, isIncognito());
// At this point we always have at least a tab in the tab manager
showTab(0);
mProxyUtils.checkForProxy(this); mProxyUtils.checkForProxy(this);
} }
@ -565,45 +549,6 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
} }
} }
/* TODO !!!! THIS MUST BY RESTORED ASAP !!!!
void restoreOrNewTab() {
mIdGenerator = 0;
String url = null;
if (getIntent() != null) {
url = getIntent().getDataString();
if (url != null) {
if (url.startsWith(Constants.FILE)) {
Utils.showSnackbar(this, R.string.message_blocked_local);
url = null;
}
}
}
if (mPreferences.getRestoreLostTabsEnabled()) {
String mem = mPreferences.getMemoryUrl();
mPreferences.setMemoryUrl("");
String[] array = Utils.getArray(mem);
int count = 0;
for (String urlString : array) {
if (!urlString.isEmpty()) {
if (url != null && url.compareTo(urlString) == 0) {
url = null;
}
newTab(urlString, true);
count++;
}
}
if (url != null) {
newTab(url, true);
} else if (count == 0) {
newTab(null, true);
}
} else {
newTab(url, true);
}
}
*/
private void initializePreferences() { private void initializePreferences() {
final LightningView currentView = tabsManager.getCurrentTab(); final LightningView currentView = tabsManager.getCurrentTab();
final WebView currentWebView = currentView.getWebView(); final WebView currentWebView = currentView.getWebView();
@ -1001,7 +946,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
return false; return false;
} }
mIsNewIntent = false; mIsNewIntent = false;
LightningView startingTab = tabsManager.newTab(mActivity, url, mDarkTheme, isIncognito(), this); LightningView startingTab = tabsManager.newTab(this, url, mDarkTheme, isIncognito());
if (mIdGenerator == 0) { if (mIdGenerator == 0) {
startingTab.resumeTimers(); startingTab.resumeTimers();
} }
@ -1227,7 +1172,6 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
currentTab.resumeTimers(); currentTab.resumeTimers();
currentTab.onResume(); currentTab.onResume();
} }
mHistoryDatabase = HistoryDatabase.getInstance();
initializePreferences(); initializePreferences();
tabsManager.resume(this); tabsManager.resume(this);
@ -1366,9 +1310,6 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
@Override @Override
public void run() { public void run() {
try { try {
if (mHistoryDatabase == null) {
mHistoryDatabase = HistoryDatabase.getInstance();
}
mHistoryDatabase.visitHistoryItem(url, title); mHistoryDatabase.visitHistoryItem(url, title);
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
Log.e(Constants.TAG, "IllegalStateException in updateHistory", e); Log.e(Constants.TAG, "IllegalStateException in updateHistory", e);
@ -1848,107 +1789,6 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
} }
} }
private void longPressHistoryLink(final String url) {
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
final LightningView currentTab = tabsManager.getCurrentTab();
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
newTab(url, false);
mDrawerLayout.closeDrawers();
break;
case DialogInterface.BUTTON_NEGATIVE:
mHistoryDatabase.deleteHistoryItem(url);
openHistory();
break;
case DialogInterface.BUTTON_NEUTRAL:
if (currentTab != null) {
loadUrlInCurrentView(url);
}
break;
}
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
builder.setTitle(R.string.action_history)
.setMessage(R.string.dialog_history_long_press)
.setCancelable(true)
.setPositiveButton(R.string.action_new_tab, dialogClickListener)
.setNegativeButton(R.string.action_delete, dialogClickListener)
.setNeutralButton(R.string.action_open, dialogClickListener)
.show();
}
private void longPressImage(final String url) {
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
final LightningView currentTab = tabsManager.getCurrentTab();
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
newTab(url, false);
break;
case DialogInterface.BUTTON_NEGATIVE:
loadUrlInCurrentView(url);
break;
case DialogInterface.BUTTON_NEUTRAL:
if (API > 8) {
Utils.downloadFile(mActivity, url,
currentTab.getUserAgent(), "attachment");
}
break;
}
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
builder.setTitle(url.replace(Constants.HTTP, ""))
.setCancelable(true)
.setMessage(R.string.dialog_image)
.setPositiveButton(R.string.action_new_tab, dialogClickListener)
.setNegativeButton(R.string.action_open, dialogClickListener)
.setNeutralButton(R.string.action_download, dialogClickListener)
.show();
}
private void longPressLink(final String url) {
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
newTab(url, false);
break;
case DialogInterface.BUTTON_NEGATIVE:
loadUrlInCurrentView(url);
break;
case DialogInterface.BUTTON_NEUTRAL:
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("label", url);
clipboard.setPrimaryClip(clip);
break;
}
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); // dialog
builder.setTitle(url)
.setCancelable(true)
.setMessage(R.string.dialog_link)
.setPositiveButton(R.string.action_new_tab, dialogClickListener)
.setNegativeButton(R.string.action_open, dialogClickListener)
.setNeutralButton(R.string.action_copy, dialogClickListener)
.show();
}
/** /**
* This method lets the search bar know that the page is currently loading * This method lets the search bar know that the page is currently loading
* and that it should display the stop icon to indicate to the user that * and that it should display the stop icon to indicate to the user that
@ -2058,13 +1898,14 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
private final Object busEventListener = new Object() { private final Object busEventListener = new Object() {
/** /**
* Load the given bookmark in the current tab, used by the the * Load the given url in the current tab, used by the the
* {@link acr.browser.lightning.fragment.BookmarksFragment} * {@link acr.browser.lightning.fragment.BookmarksFragment} and by the
* {@link LightningDialogBuilder}
* @param event The event as it comes from the bus * @param event The event as it comes from the bus
*/ */
@Subscribe @Subscribe
public void loadBookmarkInCurrentTab(final BookmarkEvents.Clicked event) { public void loadUrlInCurrentTab(final BrowserEvents.OpenUrlInCurrentTab event) {
loadUrlInCurrentView(event.bookmark.getUrl()); loadUrlInCurrentView(event.url);
// keep any jank from happening when the drawer is closed after the // keep any jank from happening when the drawer is closed after the
// URL starts to load // URL starts to load
final Handler handler = new Handler(); final Handler handler = new Handler();
@ -2077,13 +1918,14 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements
} }
/** /**
* Load the given bookmark in a new tab, used by the the * Load the given url in a new tab, used by the the
* {@link acr.browser.lightning.fragment.BookmarksFragment} * {@link acr.browser.lightning.fragment.BookmarksFragment} and by the
* {@link LightningDialogBuilder}
* @param event The event as it comes from the bus * @param event The event as it comes from the bus
*/ */
@Subscribe @Subscribe
public void loadBookmarkInNewTab(final BookmarkEvents.AsNewTab event) { public void loadUrlInNewTab(final BrowserEvents.OpenUrlInNewTab event) {
BrowserActivity.this.newTab(event.bookmark.getUrl(), true); BrowserActivity.this.newTab(event.url, true);
mDrawerLayout.closeDrawers(); mDrawerLayout.closeDrawers();
} }

32
app/src/main/java/acr/browser/lightning/activity/TabsManager.java

@ -13,6 +13,7 @@ import javax.inject.Singleton;
import acr.browser.lightning.controller.BrowserController; import acr.browser.lightning.controller.BrowserController;
import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.preference.PreferenceManager;
import acr.browser.lightning.utils.Utils;
import acr.browser.lightning.view.LightningView; import acr.browser.lightning.view.LightningView;
/** /**
@ -26,7 +27,30 @@ public class TabsManager {
private LightningView mCurrentTab; private LightningView mCurrentTab;
@Inject @Inject
public TabsManager(final Context context, final PreferenceManager preferenceManager) { PreferenceManager mPreferenceManager;
@Inject
public TabsManager() {
}
public void restoreTabs(BrowserActivity activity, boolean darkTheme, boolean incognito) {
mWebViewList.clear();
mCurrentTab = null;
if (!incognito && mPreferenceManager.getRestoreLostTabsEnabled()) {
final String mem = mPreferenceManager.getMemoryUrl();
mPreferenceManager.setMemoryUrl("");
String[] array = Utils.getArray(mem);
int count = 0;
for (String urlString : array) {
if (!urlString.isEmpty()) {
newTab(activity, urlString, darkTheme, incognito);
}
}
}
if (mWebViewList.size() == 0) {
newTab(activity, null, darkTheme, incognito);
}
// mCurrentTab = mWebViewList.get(0);
} }
/** /**
@ -112,13 +136,11 @@ public class TabsManager {
* @param url * @param url
* @param darkTheme * @param darkTheme
* @param isIncognito * @param isIncognito
* @param controller
* @return * @return
*/ */
public synchronized LightningView newTab(final Activity activity, public synchronized LightningView newTab(final BrowserActivity activity,
final String url, final boolean darkTheme, final String url, final boolean darkTheme,
final boolean isIncognito, final boolean isIncognito) {
final BrowserController controller) {
final LightningView tab = new LightningView(activity, url, darkTheme, isIncognito); final LightningView tab = new LightningView(activity, url, darkTheme, isIncognito);
mWebViewList.add(tab); mWebViewList.add(tab);
return tab; return tab;

7
app/src/main/java/acr/browser/lightning/app/AppComponent.java

@ -8,7 +8,8 @@ import javax.inject.Singleton;
import acr.browser.lightning.activity.BrowserActivity; import acr.browser.lightning.activity.BrowserActivity;
import acr.browser.lightning.constant.BookmarkPage; import acr.browser.lightning.constant.BookmarkPage;
import acr.browser.lightning.dialog.BookmarksDialogBuilder; import acr.browser.lightning.database.HistoryDatabase;
import acr.browser.lightning.dialog.LightningDialogBuilder;
import acr.browser.lightning.fragment.BookmarkSettingsFragment; import acr.browser.lightning.fragment.BookmarkSettingsFragment;
import acr.browser.lightning.fragment.BookmarksFragment; import acr.browser.lightning.fragment.BookmarksFragment;
import acr.browser.lightning.fragment.LightningPreferenceFragment; import acr.browser.lightning.fragment.LightningPreferenceFragment;
@ -33,7 +34,7 @@ public interface AppComponent {
void inject(SearchAdapter adapter); void inject(SearchAdapter adapter);
void inject(BookmarksDialogBuilder builder); void inject(LightningDialogBuilder builder);
void inject(BookmarkPage bookmarkPage); void inject(BookmarkPage bookmarkPage);
@ -47,6 +48,8 @@ public interface AppComponent {
Bus getBus(); Bus getBus();
HistoryDatabase getHistoryDatabase();
Context getApplicationContext(); Context getApplicationContext();
void inject(LightningView lightningView); void inject(LightningView lightningView);

22
app/src/main/java/acr/browser/lightning/bus/BookmarkEvents.java

@ -11,28 +11,6 @@ public final class BookmarkEvents {
// No instances // 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 * The user ask to delete the selected bookmark
*/ */

42
app/src/main/java/acr/browser/lightning/bus/BrowserEvents.java

@ -1,11 +1,7 @@
package acr.browser.lightning.bus; package acr.browser.lightning.bus;
import android.support.annotation.IdRes;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes; import android.support.annotation.StringRes;
import acr.browser.lightning.R;
/** /**
* Created by Stefano Pacifici on 26/08/15. * Created by Stefano Pacifici on 26/08/15.
*/ */
@ -21,9 +17,9 @@ public final class BrowserEvents {
* result is a new bookmark added. * result is a new bookmark added.
*/ */
public static class AddBookmark { public static class AddBookmark {
public final String title, url; public final java.lang.String title, url;
public AddBookmark(final String title, final String url) { public AddBookmark(final java.lang.String title, final java.lang.String url) {
this.title = title; this.title = title;
this.url = url; this.url = url;
} }
@ -34,9 +30,9 @@ public final class BrowserEvents {
* {@link acr.browser.lightning.fragment.BookmarksFragment} interface. * {@link acr.browser.lightning.fragment.BookmarksFragment} interface.
*/ */
public static class CurrentPageUrl { public static class CurrentPageUrl {
public final String url; public final java.lang.String url;
public CurrentPageUrl(final String url) { public CurrentPageUrl(final java.lang.String url) {
this.url = url; this.url = url;
} }
} }
@ -53,15 +49,19 @@ public final class BrowserEvents {
public static class TabsChanged { public static class TabsChanged {
} }
/**
*
*/
/** /**
* Notify the Browser to display a SnackBar in the main activity * Notify the Browser to display a SnackBar in the main activity
*/ */
public static class ShowSnackBarMessage { public static class ShowSnackBarMessage {
public final String message; public final java.lang.String message;
@StringRes @StringRes
public final int stringRes; public final int stringRes;
public ShowSnackBarMessage(final String message) { public ShowSnackBarMessage(final java.lang.String message) {
this.message = message; this.message = message;
this.stringRes = -1; this.stringRes = -1;
} }
@ -71,4 +71,26 @@ public final class BrowserEvents {
this.stringRes = stringRes; this.stringRes = stringRes;
} }
} }
/**
* 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;
}
}
} }

2
app/src/main/java/acr/browser/lightning/constant/HistoryPage.java

@ -66,7 +66,7 @@ public class HistoryPage {
} }
private static List<HistoryItem> getWebHistory(Context context) { private static List<HistoryItem> getWebHistory(Context context) {
HistoryDatabase databaseHandler = HistoryDatabase.getInstance(); HistoryDatabase databaseHandler = BrowserApp.getAppComponent().getHistoryDatabase();
return databaseHandler.getLastHundredItems(); return databaseHandler.getLastHundredItems();
} }
} }

14
app/src/main/java/acr/browser/lightning/database/HistoryDatabase.java

@ -12,9 +12,13 @@ import android.database.sqlite.SQLiteOpenHelper;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
import acr.browser.lightning.R; import acr.browser.lightning.R;
import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.app.BrowserApp;
@Singleton
public class HistoryDatabase extends SQLiteOpenHelper { public class HistoryDatabase extends SQLiteOpenHelper {
// All Static variables // All Static variables
@ -39,14 +43,8 @@ public class HistoryDatabase extends SQLiteOpenHelper {
private boolean mLock; private boolean mLock;
public static HistoryDatabase getInstance() { @Inject
if (mInstance == null || mInstance.isClosed()) { public HistoryDatabase(Context context) {
mInstance = new HistoryDatabase(BrowserApp.getAppContext());
}
return mInstance;
}
private HistoryDatabase(Context context) {
super(context.getApplicationContext(), DATABASE_NAME, null, DATABASE_VERSION); super(context.getApplicationContext(), DATABASE_NAME, null, DATABASE_VERSION);
mDatabase = this.getWritableDatabase(); mDatabase = this.getWritableDatabase();
} }

124
app/src/main/java/acr/browser/lightning/dialog/BookmarksDialogBuilder.java → app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java

@ -1,8 +1,13 @@
package acr.browser.lightning.dialog; package acr.browser.lightning.dialog;
import android.app.Activity;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.view.View; import android.view.View;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
@ -19,24 +24,32 @@ import javax.inject.Inject;
import acr.browser.lightning.R; import acr.browser.lightning.R;
import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.app.BrowserApp;
import acr.browser.lightning.bus.BookmarkEvents; import acr.browser.lightning.bus.BookmarkEvents;
import acr.browser.lightning.bus.BrowserEvents;
import acr.browser.lightning.constant.Constants; import acr.browser.lightning.constant.Constants;
import acr.browser.lightning.constant.HistoryPage;
import acr.browser.lightning.database.BookmarkManager; import acr.browser.lightning.database.BookmarkManager;
import acr.browser.lightning.database.HistoryDatabase;
import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.database.HistoryItem;
import acr.browser.lightning.utils.Utils; import acr.browser.lightning.utils.Utils;
/** /**
* TODO Rename this class it doesn't build dialogs only for bookmarks
*
* Created by Stefano Pacifici on 02/09/15, based on Anthony C. Restaino's code. * Created by Stefano Pacifici on 02/09/15, based on Anthony C. Restaino's code.
*/ */
public class BookmarksDialogBuilder { public class LightningDialogBuilder {
@Inject @Inject
BookmarkManager bookmarkManager; BookmarkManager bookmarkManager;
@Inject
HistoryDatabase mHistoryDatabase;
@Inject @Inject
Bus eventBus; Bus eventBus;
@Inject @Inject
public BookmarksDialogBuilder() { public LightningDialogBuilder() {
BrowserApp.getAppComponent().inject(this); BrowserApp.getAppComponent().inject(this);
} }
@ -46,7 +59,7 @@ public class BookmarksDialogBuilder {
* @param context used to show the dialog * @param context used to show the dialog
* @param url the long pressed url * @param url the long pressed url
*/ */
public void showLongPressedDialogForUrl(final Context context, final String url) { public void showLongPressedDialogForBookmarkUrl(final Context context, final String url) {
final HistoryItem item; final HistoryItem item;
if (url.startsWith(Constants.FILE) && url.endsWith(Constants.BOOKMARKS_FILENAME)) { if (url.startsWith(Constants.FILE) && url.endsWith(Constants.BOOKMARKS_FILENAME)) {
// TODO hacky, make a better bookmark mechanism in the future // TODO hacky, make a better bookmark mechanism in the future
@ -65,19 +78,19 @@ public class BookmarksDialogBuilder {
if (item.isFolder()) { if (item.isFolder()) {
showBookmarkFolderLongPressedDialog(context, item); showBookmarkFolderLongPressedDialog(context, item);
} else { } else {
showLongPressedDialogForUrl(context, item); showLongPressedDialogForBookmarkUrl(context, item);
} }
} }
} }
public void showLongPressedDialogForUrl(final Context context, final HistoryItem item) { public void showLongPressedDialogForBookmarkUrl(final Context context, final HistoryItem item) {
final DialogInterface.OnClickListener dialogClickListener = final DialogInterface.OnClickListener dialogClickListener =
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
switch (which) { switch (which) {
case DialogInterface.BUTTON_POSITIVE: case DialogInterface.BUTTON_POSITIVE:
eventBus.post(new BookmarkEvents.AsNewTab(item)); eventBus.post(new BrowserEvents.OpenUrlInNewTab(item.getUrl()));
break; break;
case DialogInterface.BUTTON_NEGATIVE: case DialogInterface.BUTTON_NEGATIVE:
if (bookmarkManager.deleteBookmark(item)) { if (bookmarkManager.deleteBookmark(item)) {
@ -197,4 +210,103 @@ public class BookmarksDialogBuilder {
}); });
editFolderDialog.show(); editFolderDialog.show();
} }
public void showLongPressedHistoryLinkDialog(final Context context, final String url) {
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
eventBus.post(new BrowserEvents.OpenUrlInNewTab(url));
break;
case DialogInterface.BUTTON_NEGATIVE:
mHistoryDatabase.deleteHistoryItem(url);
// openHistory();
eventBus.post(new BrowserEvents.OpenUrlInCurrentTab(HistoryPage.getHistoryPage(context)));
break;
case DialogInterface.BUTTON_NEUTRAL:
eventBus.post(new BrowserEvents.OpenUrlInCurrentTab(url));
break;
default:
break;
}
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.action_history)
.setMessage(R.string.dialog_history_long_press)
.setCancelable(true)
.setPositiveButton(R.string.action_new_tab, dialogClickListener)
.setNegativeButton(R.string.action_delete, dialogClickListener)
.setNeutralButton(R.string.action_open, dialogClickListener)
.show();
}
// TODO There should be a way in which we do not need an activity reference to dowload a file
public void showLongPressImageDialog(@NonNull final Activity activity, @NonNull final String url,
@NonNull final String userAgent) {
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
eventBus.post(new BrowserEvents.OpenUrlInNewTab(url));
break;
case DialogInterface.BUTTON_NEGATIVE:
eventBus.post(new BrowserEvents.OpenUrlInCurrentTab(url));
break;
case DialogInterface.BUTTON_NEUTRAL:
if (Build.VERSION.SDK_INT > 8) {
// Should be better to send an event on the bus here
Utils.downloadFile(activity, url,
userAgent, "attachment");
}
break;
}
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle(url.replace(Constants.HTTP, ""))
.setCancelable(true)
.setMessage(R.string.dialog_image)
.setPositiveButton(R.string.action_new_tab, dialogClickListener)
.setNegativeButton(R.string.action_open, dialogClickListener)
.setNeutralButton(R.string.action_download, dialogClickListener)
.show();
}
public void showLongPressLinkDialog(final Context context, final String url) {
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
eventBus.post(new BrowserEvents.OpenUrlInNewTab(url));
break;
case DialogInterface.BUTTON_NEGATIVE:
eventBus.post(new BrowserEvents.OpenUrlInCurrentTab(url));
break;
case DialogInterface.BUTTON_NEUTRAL:
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("label", url);
clipboard.setPrimaryClip(clip);
break;
}
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(context); // dialog
builder.setTitle(url)
.setCancelable(true)
.setMessage(R.string.dialog_link)
.setPositiveButton(R.string.action_new_tab, dialogClickListener)
.setNegativeButton(R.string.action_open, dialogClickListener)
.setNeutralButton(R.string.action_copy, dialogClickListener)
.show();
}
} }

8
app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java

@ -42,7 +42,7 @@ import acr.browser.lightning.bus.BookmarkEvents;
import acr.browser.lightning.bus.BrowserEvents; import acr.browser.lightning.bus.BrowserEvents;
import acr.browser.lightning.database.BookmarkManager; import acr.browser.lightning.database.BookmarkManager;
import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.database.HistoryItem;
import acr.browser.lightning.dialog.BookmarksDialogBuilder; import acr.browser.lightning.dialog.LightningDialogBuilder;
import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.preference.PreferenceManager;
import acr.browser.lightning.utils.DownloadImageTask; import acr.browser.lightning.utils.DownloadImageTask;
import acr.browser.lightning.utils.ThemeUtils; import acr.browser.lightning.utils.ThemeUtils;
@ -62,7 +62,7 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
// Dialog builder // Dialog builder
@Inject @Inject
BookmarksDialogBuilder bookmarksDialogBuilder; LightningDialogBuilder bookmarksDialogBuilder;
@Inject @Inject
PreferenceManager preferenceManager; PreferenceManager preferenceManager;
@ -109,7 +109,7 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(item.getTitle(), true), setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(item.getTitle(), true),
true); true);
} else { } else {
eventBus.post(new BookmarkEvents.Clicked(item)); eventBus.post(new BrowserEvents.OpenUrlInCurrentTab(item.getUrl()));
} }
} }
}; };
@ -293,7 +293,7 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener,
if (item.isFolder()) { if (item.isFolder()) {
bookmarksDialogBuilder.showBookmarkFolderLongPressedDialog(getContext(), item); bookmarksDialogBuilder.showBookmarkFolderLongPressedDialog(getContext(), item);
} else { } else {
bookmarksDialogBuilder.showLongPressedDialogForUrl(getContext(), item); bookmarksDialogBuilder.showLongPressLinkDialog(getContext(), item.getUrl());
} }
} }

19
app/src/main/java/acr/browser/lightning/object/SearchAdapter.java

@ -54,14 +54,11 @@ public class SearchAdapter extends BaseAdapter implements Filterable {
private final List<HistoryItem> mFilteredList = new ArrayList<>(); private final List<HistoryItem> mFilteredList = new ArrayList<>();
private final List<HistoryItem> mAllBookmarks = new ArrayList<>(); private final List<HistoryItem> mAllBookmarks = new ArrayList<>();
private final Object mLock = new Object(); private final Object mLock = new Object();
private HistoryDatabase mDatabaseHandler;
private final Context mContext; private final Context mContext;
private boolean mUseGoogle = true; private boolean mUseGoogle = true;
private boolean mIsExecuting = false; private boolean mIsExecuting = false;
private final boolean mDarkTheme; private final boolean mDarkTheme;
private final boolean mIncognito; private final boolean mIncognito;
@Inject BookmarkManager mBookmarkManager;
@Inject PreferenceManager mPreferenceManager;
private static final String CACHE_FILE_TYPE = ".sgg"; private static final String CACHE_FILE_TYPE = ".sgg";
private static final String ENCODING = "ISO-8859-1"; private static final String ENCODING = "ISO-8859-1";
private static final long INTERVAL_DAY = 86400000; private static final long INTERVAL_DAY = 86400000;
@ -73,9 +70,17 @@ public class SearchAdapter extends BaseAdapter implements Filterable {
private final Drawable mHistoryDrawable; private final Drawable mHistoryDrawable;
private final Drawable mBookmarkDrawable; private final Drawable mBookmarkDrawable;
@Inject
HistoryDatabase mDatabaseHandler;
@Inject
BookmarkManager mBookmarkManager;
@Inject
PreferenceManager mPreferenceManager;
public SearchAdapter(Context context, boolean dark, boolean incognito) { public SearchAdapter(Context context, boolean dark, boolean incognito) {
BrowserApp.getAppComponent().inject(this); BrowserApp.getAppComponent().inject(this);
mDatabaseHandler = HistoryDatabase.getInstance();
mAllBookmarks.addAll(mBookmarkManager.getAllBookmarks(true)); mAllBookmarks.addAll(mBookmarkManager.getAllBookmarks(true));
mUseGoogle = mPreferenceManager.getGoogleSearchSuggestionsEnabled(); mUseGoogle = mPreferenceManager.getGoogleSearchSuggestionsEnabled();
mContext = context; mContext = context;
@ -125,7 +130,6 @@ public class SearchAdapter extends BaseAdapter implements Filterable {
mSuggestions.clear(); mSuggestions.clear();
} }
} }
mDatabaseHandler = HistoryDatabase.getInstance();
} }
public void refreshBookmarks() { public void refreshBookmarks() {
@ -240,13 +244,10 @@ public class SearchAdapter extends BaseAdapter implements Filterable {
mBookmarks.add(mAllBookmarks.get(n)); mBookmarks.add(mAllBookmarks.get(n));
counter++; counter++;
} }
}
} }
} }
if (mDatabaseHandler == null || mDatabaseHandler.isClosed()) {
mDatabaseHandler = HistoryDatabase.getInstance();
} }
List<HistoryItem> historyList = mDatabaseHandler.findItemsContaining(constraint.toString()); List<HistoryItem> historyList = mDatabaseHandler.findItemsContaining(constraint.toString());
synchronized (mHistory) { synchronized (mHistory) {
mHistory.clear(); mHistory.clear();

3
app/src/main/java/acr/browser/lightning/utils/WebUtils.java

@ -10,6 +10,7 @@ import android.webkit.WebStorage;
import android.webkit.WebView; import android.webkit.WebView;
import android.webkit.WebViewDatabase; import android.webkit.WebViewDatabase;
import acr.browser.lightning.app.BrowserApp;
import acr.browser.lightning.database.HistoryDatabase; import acr.browser.lightning.database.HistoryDatabase;
/** /**
@ -32,7 +33,7 @@ public class WebUtils {
} }
public static void clearHistory(@NonNull Context context) { public static void clearHistory(@NonNull Context context) {
HistoryDatabase.getInstance().deleteHistory(); BrowserApp.getAppComponent().getHistoryDatabase().deleteHistory();
WebViewDatabase m = WebViewDatabase.getInstance(context); WebViewDatabase m = WebViewDatabase.getInstance(context);
m.clearFormData(); m.clearFormData();
m.clearHttpAuthUsernamePassword(); m.clearHttpAuthUsernamePassword();

234
app/src/main/java/acr/browser/lightning/view/LightningChromeClient.java

@ -0,0 +1,234 @@
package acr.browser.lightning.view;
import android.Manifest;
import android.app.Activity;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Message;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.view.View;
import android.webkit.GeolocationPermissions;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import com.squareup.otto.Bus;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import acr.browser.lightning.R;
import acr.browser.lightning.activity.BrowserActivity;
import acr.browser.lightning.app.BrowserApp;
import acr.browser.lightning.bus.BrowserEvents;
import acr.browser.lightning.constant.Constants;
import acr.browser.lightning.utils.PermissionsManager;
import acr.browser.lightning.utils.Utils;
/**
* @author Stefano Pacifici based on Anthony C. Restaino code
* @date 2015/09/21
*/
class LightningChromeClient extends WebChromeClient {
private static final String[] PERMISSIONS = new String[]{Manifest.permission.ACCESS_FINE_LOCATION};
private final BrowserActivity mActivity;
private final LightningView mLightningView;
private final Bus eventBus;
LightningChromeClient(BrowserActivity activity, LightningView lightningView) {
mActivity = activity;
mLightningView = lightningView;
eventBus = BrowserApp.getAppComponent().getBus();
}
@Override
public void onProgressChanged(WebView view, int newProgress) {
if (mLightningView.isShown()) {
mActivity.updateProgress(newProgress);
}
}
@Override
public void onReceivedIcon(WebView view, Bitmap icon) {
if (icon == null)
return;
mLightningView.mTitle.setFavicon(icon);
eventBus.post(new BrowserEvents.TabsChanged()); ;
cacheFavicon(view.getUrl(), icon);
}
/**
* Naive caching of the favicon according to the domain name of the URL
* @param icon the icon to cache
*/
private void cacheFavicon(final String url, final Bitmap icon) {
if (icon == null) return;
final Uri uri = Uri.parse(url);
if (uri.getHost() == null) {
return;
}
new Thread(new Runnable() {
@Override
public void run() {
String hash = String.valueOf(uri.getHost().hashCode());
Log.d(Constants.TAG, "Caching icon for " + uri.getHost());
FileOutputStream fos = null;
try {
File image = new File(BrowserApp.getAppContext().getCacheDir(), hash + ".png");
fos = new FileOutputStream(image);
icon.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
Utils.close(fos);
}
}
}).start();
}
@Override
public void onReceivedTitle(WebView view, String title) {
if (title != null && !title.isEmpty()) {
mLightningView.mTitle.setTitle(title);
} else {
mLightningView.mTitle.setTitle(mActivity.getString(R.string.untitled));
}
eventBus.post(new BrowserEvents.TabsChanged());
if (view != null) {
mActivity.updateHistory(title, view.getUrl());
}
}
@Override
public void onGeolocationPermissionsShowPrompt(final String origin,
final GeolocationPermissions.Callback callback) {
PermissionsManager.getInstance().requestPermissionsIfNecessary(mActivity, PERMISSIONS);
final boolean remember = true;
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
builder.setTitle(mActivity.getString(R.string.location));
String org;
if (origin.length() > 50) {
org = origin.subSequence(0, 50) + "...";
} else {
org = origin;
}
builder.setMessage(org + mActivity.getString(R.string.message_location))
.setCancelable(true)
.setPositiveButton(mActivity.getString(R.string.action_allow),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
callback.invoke(origin, true, remember);
}
})
.setNegativeButton(mActivity.getString(R.string.action_dont_allow),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
callback.invoke(origin, false, remember);
}
});
AlertDialog alert = builder.create();
alert.show();
}
@Override
public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture,
Message resultMsg) {
mActivity.onCreateWindow(resultMsg);
return true;
}
@Override
public void onCloseWindow(WebView window) {
mActivity.onCloseWindow(mLightningView);
}
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
mActivity.openFileChooser(uploadMsg);
}
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
mActivity.openFileChooser(uploadMsg);
}
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
mActivity.openFileChooser(uploadMsg);
}
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
WebChromeClient.FileChooserParams fileChooserParams) {
mActivity.showFileChooser(filePathCallback);
return true;
}
@Override
public Bitmap getDefaultVideoPoster() {
// TODO Simplify the method can be moved here
return mActivity.getDefaultVideoPoster();
}
@Override
public View getVideoLoadingProgressView() {
// TODO Simplify the method can be moved here
return mActivity.getVideoLoadingProgressView();
}
@Override
public void onHideCustomView() {
mActivity.onHideCustomView();
super.onHideCustomView();
}
@Override
public void onShowCustomView(View view, CustomViewCallback callback) {
// While these lines might look like they work, in practice,
// Full-screen videos won't work correctly. I may test this out some
// more
// if (view instanceof FrameLayout) {
// FrameLayout frame = (FrameLayout) view;
// if (frame.getFocusedChild() instanceof VideoView) {
// VideoView video = (VideoView) frame.getFocusedChild();
// video.stopPlayback();
// frame.removeView(video);
// video.setVisibility(View.GONE);
// }
// } else {
mActivity.onShowCustomView(view, callback);
// }
super.onShowCustomView(view, callback);
}
@Override
@Deprecated
public void onShowCustomView(View view, int requestedOrientation,
CustomViewCallback callback) {
// While these lines might look like they work, in practice,
// Full-screen videos won't work correctly. I may test this out some
// more
// if (view instanceof FrameLayout) {
// FrameLayout frame = (FrameLayout) view;
// if (frame.getFocusedChild() instanceof VideoView) {
// VideoView video = (VideoView) frame.getFocusedChild();
// video.stopPlayback();
// frame.removeView(video);
// video.setVisibility(View.GONE);
// }
// } else {
mActivity.onShowCustomView(view, callback);
// }
super.onShowCustomView(view, requestedOrientation, callback);
}
}

555
app/src/main/java/acr/browser/lightning/view/LightningView.java

@ -6,27 +6,18 @@ package acr.browser.lightning.view;
import android.Manifest; import android.Manifest;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.ColorMatrix; import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter; import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint; import android.graphics.Paint;
import android.net.MailTo;
import android.net.Uri; import android.net.Uri;
import android.net.http.SslError;
import android.os.Build; import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v7.app.AlertDialog;
import android.text.InputType;
import android.text.method.PasswordTransformationMethod;
import android.util.Log; import android.util.Log;
import android.view.GestureDetector; import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener; import android.view.GestureDetector.SimpleOnGestureListener;
@ -35,45 +26,30 @@ import android.view.View;
import android.view.View.OnTouchListener; import android.view.View.OnTouchListener;
import android.view.ViewConfiguration; import android.view.ViewConfiguration;
import android.webkit.CookieManager; import android.webkit.CookieManager;
import android.webkit.GeolocationPermissions;
import android.webkit.HttpAuthHandler;
import android.webkit.SslErrorHandler;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebSettings; import android.webkit.WebSettings;
import android.webkit.WebSettings.LayoutAlgorithm; import android.webkit.WebSettings.LayoutAlgorithm;
import android.webkit.WebSettings.PluginState; import android.webkit.WebSettings.PluginState;
import android.webkit.WebView; import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.EditText;
import android.widget.LinearLayout;
import com.squareup.otto.Bus; import com.squareup.otto.Bus;
import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.net.URISyntaxException;
import javax.inject.Inject; import javax.inject.Inject;
import acr.browser.lightning.R; import acr.browser.lightning.R;
import acr.browser.lightning.activity.BrowserActivity;
import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.app.BrowserApp;
import acr.browser.lightning.bus.BrowserEvents; import acr.browser.lightning.bus.BrowserEvents;
import acr.browser.lightning.bus.TabEvents;
import acr.browser.lightning.constant.Constants; import acr.browser.lightning.constant.Constants;
import acr.browser.lightning.constant.HistoryPage; import acr.browser.lightning.constant.HistoryPage;
import acr.browser.lightning.constant.StartPage; import acr.browser.lightning.constant.StartPage;
import acr.browser.lightning.controller.BrowserController; import acr.browser.lightning.dialog.LightningDialogBuilder;
import acr.browser.lightning.dialog.BookmarksDialogBuilder;
import acr.browser.lightning.download.LightningDownloadListener; import acr.browser.lightning.download.LightningDownloadListener;
import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.preference.PreferenceManager;
import acr.browser.lightning.utils.AdBlock;
import acr.browser.lightning.utils.IntentUtils;
import acr.browser.lightning.utils.PermissionsManager; import acr.browser.lightning.utils.PermissionsManager;
import acr.browser.lightning.utils.ProxyUtils; import acr.browser.lightning.utils.ProxyUtils;
import acr.browser.lightning.utils.ThemeUtils; import acr.browser.lightning.utils.ThemeUtils;
@ -81,21 +57,15 @@ import acr.browser.lightning.utils.Utils;
public class LightningView { public class LightningView {
private final Title mTitle; final LightningViewTitle mTitle;
private WebView mWebView; private WebView mWebView;
private final boolean mIsIncognitoTab; final boolean mIsIncognitoTab;
private final BrowserController mBrowserController = null; // TODO REMOVE
private final GestureDetector mGestureDetector; private final GestureDetector mGestureDetector;
private final Activity mActivity; private final BrowserActivity mActivity;
private static String mHomepage; private static String mHomepage;
private static String mDefaultUserAgent; private static String mDefaultUserAgent;
// TODO fix so that mWebpageBitmap can be static - static changes the icon when switching from light to dark and then back to light
private final Bitmap mWebpageBitmap;
private final AdBlock mAdBlock;
private final IntentUtils mIntentUtils;
private final Paint mPaint = new Paint(); private final Paint mPaint = new Paint();
private boolean isForegroundTab; private boolean isForegroundTab;
private boolean mTextReflow = false;
private boolean mInvertPage = false; private boolean mInvertPage = false;
private boolean mToggleDesktop = false; private boolean mToggleDesktop = false;
private static float mMaxFling; private static float mMaxFling;
@ -118,23 +88,20 @@ public class LightningView {
PreferenceManager mPreferences; PreferenceManager mPreferences;
@Inject @Inject
BookmarksDialogBuilder bookmarksDialogBuilder; LightningDialogBuilder bookmarksDialogBuilder;
@SuppressLint("NewApi") @SuppressLint("NewApi")
public LightningView(Activity activity, String url, boolean darkTheme, boolean isIncognito) { public LightningView(BrowserActivity activity, String url, boolean darkTheme, boolean isIncognito) {
BrowserApp.getAppComponent().inject(this); BrowserApp.getAppComponent().inject(this);
mActivity = activity; mActivity = activity;
mWebView = new WebView(activity); mWebView = new WebView(activity);
mIsIncognitoTab = isIncognito; mIsIncognitoTab = isIncognito;
mTitle = new Title(activity, darkTheme); mTitle = new LightningViewTitle(activity, darkTheme);
mAdBlock = AdBlock.getInstance(activity.getApplicationContext()); // mAdBlock = AdBlock.getInstance(activity.getApplicationContext());
mPermissionsManager = PermissionsManager.getInstance(); mPermissionsManager = PermissionsManager.getInstance();
mWebpageBitmap = mTitle.mDefaultIcon;
mMaxFling = ViewConfiguration.get(activity).getScaledMaximumFlingVelocity(); mMaxFling = ViewConfiguration.get(activity).getScaledMaximumFlingVelocity();
mIntentUtils = new IntentUtils(activity);
mWebView.setDrawingCacheBackgroundColor(Color.WHITE); mWebView.setDrawingCacheBackgroundColor(Color.WHITE);
mWebView.setFocusableInTouchMode(true); mWebView.setFocusableInTouchMode(true);
mWebView.setFocusable(true); mWebView.setFocusable(true);
@ -149,8 +116,8 @@ public class LightningView {
mWebView.setScrollbarFadingEnabled(true); mWebView.setScrollbarFadingEnabled(true);
mWebView.setSaveEnabled(true); mWebView.setSaveEnabled(true);
mWebView.setNetworkAvailable(true); mWebView.setNetworkAvailable(true);
mWebView.setWebChromeClient(new LightningChromeClient(activity)); mWebView.setWebChromeClient(new LightningChromeClient(activity, this));
mWebView.setWebViewClient(new LightningWebClient(activity)); mWebView.setWebViewClient(new LightningWebClient(activity, this));
mWebView.setDownloadListener(new LightningDownloadListener(activity)); mWebView.setDownloadListener(new LightningDownloadListener(activity));
mGestureDetector = new GestureDetector(activity, new CustomGestureListener()); mGestureDetector = new GestureDetector(activity, new CustomGestureListener());
mWebView.setOnTouchListener(new TouchListener()); mWebView.setOnTouchListener(new TouchListener());
@ -224,7 +191,7 @@ public class LightningView {
settings.setDefaultTextEncodingName(mPreferences.getTextEncoding()); settings.setDefaultTextEncodingName(mPreferences.getTextEncoding());
mHomepage = mPreferences.getHomepage(); mHomepage = mPreferences.getHomepage();
mAdBlock.updatePreference(); // mAdBlock.updatePreference();
setColorMode(mPreferences.getRenderingMode()); setColorMode(mPreferences.getRenderingMode());
@ -275,7 +242,6 @@ public class LightningView {
} }
if (mPreferences.getTextReflowEnabled()) { if (mPreferences.getTextReflowEnabled()) {
mTextReflow = true;
settings.setLayoutAlgorithm(LayoutAlgorithm.NARROW_COLUMNS); settings.setLayoutAlgorithm(LayoutAlgorithm.NARROW_COLUMNS);
if (API >= android.os.Build.VERSION_CODES.KITKAT) { if (API >= android.os.Build.VERSION_CODES.KITKAT) {
try { try {
@ -287,7 +253,6 @@ public class LightningView {
} }
} }
} else { } else {
mTextReflow = false;
settings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL); settings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL);
} }
@ -549,36 +514,6 @@ public class LightningView {
} }
} }
/**
* Naive caching of the favicon according to the domain name of the URL
* @param icon the icon to cache
*/
private void cacheFavicon(final Bitmap icon) {
if (icon == null) return;
final Uri uri = Uri.parse(getUrl());
if (uri.getHost() == null) {
return;
}
new Thread(new Runnable() {
@Override
public void run() {
String hash = String.valueOf(uri.getHost().hashCode());
Log.d(Constants.TAG, "Caching icon for " + uri.getHost());
FileOutputStream fos = null;
try {
File image = new File(BrowserApp.getAppContext().getCacheDir(), hash + ".png");
fos = new FileOutputStream(image);
icon.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
Utils.close(fos);
}
}
}).start();
}
@SuppressLint("NewApi") @SuppressLint("NewApi")
public synchronized void find(String text) { public synchronized void find(String text) {
if (mWebView != null) { if (mWebView != null) {
@ -623,6 +558,14 @@ public class LightningView {
} }
} }
/**
* Used by {@link LightningWebClient}
*
* @return true if the page is in inverted mode, false otherwise
*/
public boolean getInvertePage() {
return mInvertPage;
}
/** /**
* handles a long click on the page, parameter String url * handles a long click on the page, parameter String url
* is the url that should have been obtained from the WebView touch node * is the url that should have been obtained from the WebView touch node
@ -634,36 +577,36 @@ public class LightningView {
if (currentUrl != null && currentUrl.startsWith(Constants.FILE)) { if (currentUrl != null && currentUrl.startsWith(Constants.FILE)) {
if (currentUrl.endsWith(HistoryPage.FILENAME)) { if (currentUrl.endsWith(HistoryPage.FILENAME)) {
if (url != null) { if (url != null) {
// TODO longPressHistoryLink(url); bookmarksDialogBuilder.showLongPressedHistoryLinkDialog(mActivity, url);
} else if (result != null && result.getExtra() != null) { } else if (result != null && result.getExtra() != null) {
final String newUrl = result.getExtra(); final String newUrl = result.getExtra();
// TODO longPressHistoryLink(newUrl); bookmarksDialogBuilder.showLongPressedHistoryLinkDialog(mActivity, newUrl);
} }
} else if (currentUrl.endsWith(Constants.BOOKMARKS_FILENAME)) { } else if (currentUrl.endsWith(Constants.BOOKMARKS_FILENAME)) {
if (url != null) { if (url != null) {
bookmarksDialogBuilder.showLongPressedDialogForUrl(mActivity, url); bookmarksDialogBuilder.showLongPressLinkDialog(mActivity, url);
} else if (result != null && result.getExtra() != null) { } else if (result != null && result.getExtra() != null) {
final String newUrl = result.getExtra(); final String newUrl = result.getExtra();
bookmarksDialogBuilder.showLongPressedDialogForUrl(mActivity, newUrl); bookmarksDialogBuilder.showLongPressLinkDialog(mActivity, newUrl);
} }
} }
} else { } else {
if (url != null) { if (url != null) {
if (result != null) { if (result != null) {
if (result.getType() == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE || result.getType() == WebView.HitTestResult.IMAGE_TYPE) { if (result.getType() == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE || result.getType() == WebView.HitTestResult.IMAGE_TYPE) {
// TODO longPressImage(url); bookmarksDialogBuilder.showLongPressImageDialog(mActivity, url, getUserAgent());
} else { } else {
// TODO longPressLink(url); bookmarksDialogBuilder.showLongPressLinkDialog(mActivity, url);
} }
} else { } else {
// TODO longPressLink(url); bookmarksDialogBuilder.showLongPressLinkDialog(mActivity, url);
} }
} else if (result != null && result.getExtra() != null) { } else if (result != null && result.getExtra() != null) {
final String newUrl = result.getExtra(); final String newUrl = result.getExtra();
if (result.getType() == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE || result.getType() == WebView.HitTestResult.IMAGE_TYPE) { if (result.getType() == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE || result.getType() == WebView.HitTestResult.IMAGE_TYPE) {
// TODO longPressImage(newUrl); bookmarksDialogBuilder.showLongPressImageDialog(mActivity, newUrl, getUserAgent());
} else { } else {
// TODO longPressLink(newUrl); bookmarksDialogBuilder.showLongPressLinkDialog(mActivity, newUrl);
} }
} }
} }
@ -716,438 +659,6 @@ public class LightningView {
} }
} }
public class LightningWebClient extends WebViewClient {
final Activity mActivity;
LightningWebClient(Activity activity) {
mActivity = activity;
}
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (mAdBlock.isAd(request.getUrl().toString())) {
ByteArrayInputStream EMPTY = new ByteArrayInputStream("".getBytes());
return new WebResourceResponse("text/plain", "utf-8", EMPTY);
}
}
return super.shouldInterceptRequest(view, request);
}
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
if (mAdBlock.isAd(url)) {
ByteArrayInputStream EMPTY = new ByteArrayInputStream("".getBytes());
return new WebResourceResponse("text/plain", "utf-8", EMPTY);
}
return null;
}
@SuppressLint("NewApi")
@Override
public void onPageFinished(WebView view, String url) {
if (view.isShown()) {
mBrowserController.updateUrl(url, true);
view.postInvalidate();
}
if (view.getTitle() == null || view.getTitle().isEmpty()) {
mTitle.setTitle(mActivity.getString(R.string.untitled));
} else {
mTitle.setTitle(view.getTitle());
}
if (API >= android.os.Build.VERSION_CODES.KITKAT && mInvertPage) {
view.evaluateJavascript(Constants.JAVASCRIPT_INVERT_PAGE, null);
}
eventBus.post(new BrowserEvents.TabsChanged());
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
if (isShown()) {
mBrowserController.updateUrl(url, false);
mBrowserController.showActionBar();
}
mTitle.setFavicon(mWebpageBitmap);
eventBus.post(new BrowserEvents.TabsChanged());
}
@Override
public void onReceivedHttpAuthRequest(final WebView view, @NonNull final HttpAuthHandler handler,
final String host, final String realm) {
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
final EditText name = new EditText(mActivity);
final EditText password = new EditText(mActivity);
LinearLayout passLayout = new LinearLayout(mActivity);
passLayout.setOrientation(LinearLayout.VERTICAL);
passLayout.addView(name);
passLayout.addView(password);
name.setHint(mActivity.getString(R.string.hint_username));
name.setSingleLine();
password.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
password.setSingleLine();
password.setTransformationMethod(new PasswordTransformationMethod());
password.setHint(mActivity.getString(R.string.hint_password));
builder.setTitle(mActivity.getString(R.string.title_sign_in));
builder.setView(passLayout);
builder.setCancelable(true)
.setPositiveButton(mActivity.getString(R.string.title_sign_in),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
String user = name.getText().toString();
String pass = password.getText().toString();
handler.proceed(user.trim(), pass.trim());
Log.d(Constants.TAG, "Request Login");
}
})
.setNegativeButton(mActivity.getString(R.string.action_cancel),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
handler.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
}
private boolean mIsRunning = false;
private float mZoomScale = 0.0f;
@SuppressLint("NewApi")
@Override
public void onScaleChanged(final WebView view, final float oldScale, final float newScale) {
if (view.isShown() && mTextReflow && API >= android.os.Build.VERSION_CODES.KITKAT) {
if (mIsRunning)
return;
if (Math.abs(mZoomScale - newScale) > 0.01f) {
mIsRunning = view.postDelayed(new Runnable() {
@Override
public void run() {
mZoomScale = newScale;
view.evaluateJavascript(Constants.JAVASCRIPT_TEXT_REFLOW, null);
mIsRunning = false;
}
}, 100);
}
}
}
@Override
public void onReceivedSslError(WebView view, @NonNull final SslErrorHandler handler, SslError error) {
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
builder.setTitle(mActivity.getString(R.string.title_warning));
builder.setMessage(mActivity.getString(R.string.message_untrusted_certificate))
.setCancelable(true)
.setPositiveButton(mActivity.getString(R.string.action_yes),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
handler.proceed();
}
})
.setNegativeButton(mActivity.getString(R.string.action_no),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
handler.cancel();
}
});
AlertDialog alert = builder.create();
if (error.getPrimaryError() == SslError.SSL_UNTRUSTED) {
alert.show();
} else {
handler.proceed();
}
}
@Override
public void onFormResubmission(WebView view, @NonNull final Message dontResend, final Message resend) {
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
builder.setTitle(mActivity.getString(R.string.title_form_resubmission));
builder.setMessage(mActivity.getString(R.string.message_form_resubmission))
.setCancelable(true)
.setPositiveButton(mActivity.getString(R.string.action_yes),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
resend.sendToTarget();
}
})
.setNegativeButton(mActivity.getString(R.string.action_no),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
dontResend.sendToTarget();
}
});
AlertDialog alert = builder.create();
alert.show();
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// Check if configured proxy is available
if (!ProxyUtils.getInstance().isProxyReady()) {
// User has been notified
return true;
}
if (mIsIncognitoTab) {
return super.shouldOverrideUrlLoading(view, url);
}
if (url.startsWith("about:")) {
return super.shouldOverrideUrlLoading(view, url);
}
if (url.contains("mailto:")) {
MailTo mailTo = MailTo.parse(url);
Intent i = Utils.newEmailIntent(mailTo.getTo(), mailTo.getSubject(),
mailTo.getBody(), mailTo.getCc());
mActivity.startActivity(i);
view.reload();
return true;
} else if (url.startsWith("intent://")) {
Intent intent;
try {
intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
} catch (URISyntaxException ex) {
return false;
}
if (intent != null) {
try {
mActivity.startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.e(Constants.TAG, "ActivityNotFoundException");
}
return true;
}
}
return mIntentUtils.startActivityForUrl(mWebView, url);
}
}
public class LightningChromeClient extends WebChromeClient {
final Activity mActivity;
LightningChromeClient(Activity activity) {
mActivity = activity;
}
@Override
public void onProgressChanged(WebView view, int newProgress) {
if (isShown()) {
mBrowserController.updateProgress(newProgress);
}
}
@Override
public void onReceivedIcon(WebView view, Bitmap icon) {
if (icon == null)
return;
mTitle.setFavicon(icon);
eventBus.post(new BrowserEvents.TabsChanged()); ;
cacheFavicon(icon);
}
@Override
public void onReceivedTitle(WebView view, String title) {
if (title != null && !title.isEmpty()) {
mTitle.setTitle(title);
} else {
mTitle.setTitle(mActivity.getString(R.string.untitled));
}
eventBus.post(new BrowserEvents.TabsChanged()); ;
if (view != null)
mBrowserController.updateHistory(title, view.getUrl());
}
@Override
public void onGeolocationPermissionsShowPrompt(final String origin,
final GeolocationPermissions.Callback callback) {
mPermissionsManager.requestPermissionsIfNecessary(mActivity, PERMISSIONS);
final boolean remember = true;
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
builder.setTitle(mActivity.getString(R.string.location));
String org;
if (origin.length() > 50) {
org = origin.subSequence(0, 50) + "...";
} else {
org = origin;
}
builder.setMessage(org + mActivity.getString(R.string.message_location))
.setCancelable(true)
.setPositiveButton(mActivity.getString(R.string.action_allow),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
callback.invoke(origin, true, remember);
}
})
.setNegativeButton(mActivity.getString(R.string.action_dont_allow),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
callback.invoke(origin, false, remember);
}
});
AlertDialog alert = builder.create();
alert.show();
}
@Override
public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture,
Message resultMsg) {
mBrowserController.onCreateWindow(resultMsg);
return true;
}
@Override
public void onCloseWindow(WebView window) {
mBrowserController.onCloseWindow(LightningView.this);
}
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
mBrowserController.openFileChooser(uploadMsg);
}
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
mBrowserController.openFileChooser(uploadMsg);
}
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
mBrowserController.openFileChooser(uploadMsg);
}
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
WebChromeClient.FileChooserParams fileChooserParams) {
mBrowserController.showFileChooser(filePathCallback);
return true;
}
@Override
public Bitmap getDefaultVideoPoster() {
return mBrowserController.getDefaultVideoPoster();
}
@Override
public View getVideoLoadingProgressView() {
return mBrowserController.getVideoLoadingProgressView();
}
@Override
public void onHideCustomView() {
mBrowserController.onHideCustomView();
super.onHideCustomView();
}
@Override
public void onShowCustomView(View view, CustomViewCallback callback) {
// While these lines might look like they work, in practice,
// Full-screen videos won't work correctly. I may test this out some
// more
// if (view instanceof FrameLayout) {
// FrameLayout frame = (FrameLayout) view;
// if (frame.getFocusedChild() instanceof VideoView) {
// VideoView video = (VideoView) frame.getFocusedChild();
// video.stopPlayback();
// frame.removeView(video);
// video.setVisibility(View.GONE);
// }
// } else {
mBrowserController.onShowCustomView(view, callback);
// }
super.onShowCustomView(view, callback);
}
@Override
@Deprecated
public void onShowCustomView(View view, int requestedOrientation,
CustomViewCallback callback) {
// While these lines might look like they work, in practice,
// Full-screen videos won't work correctly. I may test this out some
// more
// if (view instanceof FrameLayout) {
// FrameLayout frame = (FrameLayout) view;
// if (frame.getFocusedChild() instanceof VideoView) {
// VideoView video = (VideoView) frame.getFocusedChild();
// video.stopPlayback();
// frame.removeView(video);
// video.setVisibility(View.GONE);
// }
// } else {
mBrowserController.onShowCustomView(view, callback);
// }
super.onShowCustomView(view, requestedOrientation, callback);
}
}
public class Title {
private Bitmap mFavicon;
private String mTitle;
private final Bitmap mDefaultIcon;
public Title(Context context, boolean darkTheme) {
mDefaultIcon = ThemeUtils.getThemedBitmap(context, R.drawable.ic_webpage, darkTheme);
mFavicon = mDefaultIcon;
mTitle = mActivity.getString(R.string.action_new_tab);
}
public void setFavicon(Bitmap favicon) {
if (favicon == null) {
mFavicon = mDefaultIcon;
} else {
mFavicon = Utils.padFavicon(favicon);
}
}
public void setTitle(String title) {
if (title == null) {
mTitle = "";
} else {
mTitle = title;
}
}
public void setTitleAndFavicon(String title, Bitmap favicon) {
mTitle = title;
if (favicon == null) {
mFavicon = mDefaultIcon;
} else {
mFavicon = Utils.padFavicon(favicon);
}
}
public String getTitle() {
return mTitle;
}
public Bitmap getFavicon() {
return mFavicon;
}
}
private class TouchListener implements OnTouchListener { private class TouchListener implements OnTouchListener {
float mLocation; float mLocation;
@ -1170,9 +681,9 @@ public class LightningView {
} else if (mAction == MotionEvent.ACTION_UP) { } else if (mAction == MotionEvent.ACTION_UP) {
final float distance = (mY - mLocation); final float distance = (mY - mLocation);
if (distance > SCROLL_UP_THRESHOLD && view.getScrollY() < SCROLL_UP_THRESHOLD) { if (distance > SCROLL_UP_THRESHOLD && view.getScrollY() < SCROLL_UP_THRESHOLD) {
mBrowserController.showActionBar(); mActivity.showActionBar();
} else if (distance < -SCROLL_UP_THRESHOLD) { } else if (distance < -SCROLL_UP_THRESHOLD) {
mBrowserController.hideActionBar(); mActivity.hideActionBar();
} }
mLocation = 0; mLocation = 0;
} }
@ -1187,9 +698,9 @@ public class LightningView {
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
int power = (int) (velocityY * 100 / mMaxFling); int power = (int) (velocityY * 100 / mMaxFling);
if (power < -10) { if (power < -10) {
mBrowserController.hideActionBar(); mActivity.hideActionBar();
} else if (power > 15) { } else if (power > 15) {
mBrowserController.showActionBar(); mActivity.showActionBar();
} }
return super.onFling(e1, e2, velocityX, velocityY); return super.onFling(e1, e2, velocityX, velocityY);
} }

66
app/src/main/java/acr/browser/lightning/view/LightningViewTitle.java

@ -0,0 +1,66 @@
package acr.browser.lightning.view;
import android.content.Context;
import android.graphics.Bitmap;
import acr.browser.lightning.R;
import acr.browser.lightning.utils.ThemeUtils;
import acr.browser.lightning.utils.Utils;
/**
* @author Stefano Pacifici base on Anthony C. Restaino's code
* @date 2015/09/21
*/
class LightningViewTitle {
private static Bitmap DEFAULT_ICON = null;
private Bitmap mFavicon;
private String mTitle;
public LightningViewTitle(Context context, boolean darkTheme) {
if (DEFAULT_ICON == null) {
DEFAULT_ICON = ThemeUtils.getThemedBitmap(context, R.drawable.ic_webpage, darkTheme);
}
mFavicon = DEFAULT_ICON;
mTitle = context.getString(R.string.action_new_tab);
}
public void setFavicon(Bitmap favicon) {
if (favicon == null) {
mFavicon = DEFAULT_ICON;
} else {
mFavicon = Utils.padFavicon(favicon);
}
}
public void setTitle(String title) {
if (title == null) {
mTitle = "";
} else {
mTitle = title;
}
}
public void setTitleAndFavicon(String title, Bitmap favicon) {
mTitle = title;
if (favicon == null) {
mFavicon = DEFAULT_ICON;
} else {
mFavicon = Utils.padFavicon(favicon);
}
}
public String getTitle() {
return mTitle;
}
public Bitmap getFavicon() {
return mFavicon;
}
public Bitmap getDefaultIcon() {
return DEFAULT_ICON;
}
}

276
app/src/main/java/acr/browser/lightning/view/LightningWebClient.java

@ -0,0 +1,276 @@
package acr.browser.lightning.view;
import android.annotation.SuppressLint;
import android.content.ActivityNotFoundException;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.MailTo;
import android.net.http.SslError;
import android.os.Build;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.v7.app.AlertDialog;
import android.text.InputType;
import android.text.method.PasswordTransformationMethod;
import android.util.Log;
import android.webkit.HttpAuthHandler;
import android.webkit.SslErrorHandler;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.EditText;
import android.widget.LinearLayout;
import com.squareup.otto.Bus;
import java.io.ByteArrayInputStream;
import java.net.URISyntaxException;
import acr.browser.lightning.R;
import acr.browser.lightning.activity.BrowserActivity;
import acr.browser.lightning.app.BrowserApp;
import acr.browser.lightning.bus.BrowserEvents;
import acr.browser.lightning.constant.Constants;
import acr.browser.lightning.utils.AdBlock;
import acr.browser.lightning.utils.IntentUtils;
import acr.browser.lightning.utils.ProxyUtils;
import acr.browser.lightning.utils.Utils;
/**
* @author Stefano Pacifici based on Anthony C. Restaino's code
* @date 2015/09/22
*/
public class LightningWebClient extends WebViewClient {
private final BrowserActivity mActivity;
private final LightningView mLightningView;
private final AdBlock mAdBlock;
private final Bus mEventBus;
private final IntentUtils mIntentUtils;
LightningWebClient(BrowserActivity activity, LightningView lightningView) {
mActivity = activity;
mLightningView = lightningView;
mAdBlock = AdBlock.getInstance(activity);
mAdBlock.updatePreference();
mEventBus = BrowserApp.getAppComponent().getBus();
mIntentUtils = new IntentUtils(activity);
}
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (mAdBlock.isAd(request.getUrl().toString())) {
ByteArrayInputStream EMPTY = new ByteArrayInputStream("".getBytes());
return new WebResourceResponse("text/plain", "utf-8", EMPTY);
}
}
return super.shouldInterceptRequest(view, request);
}
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
if (mAdBlock.isAd(url)) {
ByteArrayInputStream EMPTY = new ByteArrayInputStream("".getBytes());
return new WebResourceResponse("text/plain", "utf-8", EMPTY);
}
return null;
}
@SuppressLint("NewApi")
@Override
public void onPageFinished(WebView view, String url) {
if (view.isShown()) {
mActivity.updateUrl(url, true);
view.postInvalidate();
}
if (view.getTitle() == null || view.getTitle().isEmpty()) {
mLightningView.mTitle.setTitle(mActivity.getString(R.string.untitled));
} else {
mLightningView.mTitle.setTitle(view.getTitle());
}
if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT &&
mLightningView.getInvertePage()) {
view.evaluateJavascript(Constants.JAVASCRIPT_INVERT_PAGE, null);
}
mEventBus.post(new BrowserEvents.TabsChanged());
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
if (mLightningView.isShown()) {
mActivity.updateUrl(url, false);
mActivity.showActionBar();
}
mEventBus.post(new BrowserEvents.TabsChanged());
}
@Override
public void onReceivedHttpAuthRequest(final WebView view, @NonNull final HttpAuthHandler handler,
final String host, final String realm) {
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
final EditText name = new EditText(mActivity);
final EditText password = new EditText(mActivity);
LinearLayout passLayout = new LinearLayout(mActivity);
passLayout.setOrientation(LinearLayout.VERTICAL);
passLayout.addView(name);
passLayout.addView(password);
name.setHint(mActivity.getString(R.string.hint_username));
name.setSingleLine();
password.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
password.setSingleLine();
password.setTransformationMethod(new PasswordTransformationMethod());
password.setHint(mActivity.getString(R.string.hint_password));
builder.setTitle(mActivity.getString(R.string.title_sign_in));
builder.setView(passLayout);
builder.setCancelable(true)
.setPositiveButton(mActivity.getString(R.string.title_sign_in),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
String user = name.getText().toString();
String pass = password.getText().toString();
handler.proceed(user.trim(), pass.trim());
Log.d(Constants.TAG, "Request Login");
}
})
.setNegativeButton(mActivity.getString(R.string.action_cancel),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
handler.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
}
private boolean mIsRunning = false;
private float mZoomScale = 0.0f;
@SuppressLint("NewApi")
@Override
public void onScaleChanged(final WebView view, final float oldScale, final float newScale) {
if (view.isShown() && mLightningView.mPreferences.getTextReflowEnabled() &&
Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
if (mIsRunning)
return;
if (Math.abs(mZoomScale - newScale) > 0.01f) {
mIsRunning = view.postDelayed(new Runnable() {
@Override
public void run() {
mZoomScale = newScale;
view.evaluateJavascript(Constants.JAVASCRIPT_TEXT_REFLOW, null);
mIsRunning = false;
}
}, 100);
}
}
}
@Override
public void onReceivedSslError(WebView view, @NonNull final SslErrorHandler handler, SslError error) {
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
builder.setTitle(mActivity.getString(R.string.title_warning));
builder.setMessage(mActivity.getString(R.string.message_untrusted_certificate))
.setCancelable(true)
.setPositiveButton(mActivity.getString(R.string.action_yes),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
handler.proceed();
}
})
.setNegativeButton(mActivity.getString(R.string.action_no),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
handler.cancel();
}
});
AlertDialog alert = builder.create();
if (error.getPrimaryError() == SslError.SSL_UNTRUSTED) {
alert.show();
} else {
handler.proceed();
}
}
@Override
public void onFormResubmission(WebView view, @NonNull final Message dontResend, final Message resend) {
AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
builder.setTitle(mActivity.getString(R.string.title_form_resubmission));
builder.setMessage(mActivity.getString(R.string.message_form_resubmission))
.setCancelable(true)
.setPositiveButton(mActivity.getString(R.string.action_yes),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
resend.sendToTarget();
}
})
.setNegativeButton(mActivity.getString(R.string.action_no),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
dontResend.sendToTarget();
}
});
AlertDialog alert = builder.create();
alert.show();
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// Check if configured proxy is available
if (!ProxyUtils.getInstance().isProxyReady()) {
// User has been notified
return true;
}
if (mLightningView.mIsIncognitoTab) {
return super.shouldOverrideUrlLoading(view, url);
}
if (url.startsWith("about:")) {
return super.shouldOverrideUrlLoading(view, url);
}
if (url.contains("mailto:")) {
MailTo mailTo = MailTo.parse(url);
Intent i = Utils.newEmailIntent(mailTo.getTo(), mailTo.getSubject(),
mailTo.getBody(), mailTo.getCc());
mActivity.startActivity(i);
view.reload();
return true;
} else if (url.startsWith("intent://")) {
Intent intent;
try {
intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
} catch (URISyntaxException ex) {
return false;
}
if (intent != null) {
try {
mActivity.startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.e(Constants.TAG, "ActivityNotFoundException");
}
return true;
}
}
return mIntentUtils.startActivityForUrl(view, url);
}
}
Loading…
Cancel
Save