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.
 
 

2319 lines
85 KiB

/*
* Copyright 2015 Anthony Restaino
*/
package org.purplei2p.lightning.browser.activity;
import android.app.Activity;
import android.app.Dialog;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.provider.MediaStore;
import android.support.annotation.ColorInt;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v4.widget.DrawerLayout.DrawerListener;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.graphics.Palette;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnKeyListener;
import android.view.View.OnTouchListener;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewParent;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient.CustomViewCallback;
import android.webkit.WebIconDatabase;
import android.webkit.WebView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AutoCompleteTextView;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import android.widget.VideoView;
import com.anthonycr.bonsai.Completable;
import com.anthonycr.bonsai.CompletableOnSubscribe;
import com.anthonycr.bonsai.Schedulers;
import com.anthonycr.bonsai.SingleOnSubscribe;
import com.anthonycr.grant.PermissionsManager;
import com.anthonycr.progress.AnimatedProgressBar;
import java.io.File;
import java.io.IOException;
import javax.inject.Inject;
import org.purplei2p.lightning.R;
import org.purplei2p.lightning.reading.activity.ReadingActivity;
import org.purplei2p.lightning.browser.BookmarksView;
import org.purplei2p.lightning.browser.BrowserPresenter;
import org.purplei2p.lightning.browser.BrowserView;
import org.purplei2p.lightning.IncognitoActivity;
import org.purplei2p.lightning.browser.SearchBoxModel;
import org.purplei2p.lightning.browser.TabsManager;
import org.purplei2p.lightning.browser.TabsView;
import org.purplei2p.lightning.BrowserApp;
import org.purplei2p.lightning.constant.Constants;
import org.purplei2p.lightning.constant.DownloadsPage;
import org.purplei2p.lightning.constant.HistoryPage;
import org.purplei2p.lightning.controller.UIController;
import org.purplei2p.lightning.database.HistoryItem;
import org.purplei2p.lightning.database.bookmark.BookmarkModel;
import org.purplei2p.lightning.database.history.HistoryModel;
import org.purplei2p.lightning.dialog.BrowserDialog;
import org.purplei2p.lightning.dialog.LightningDialogBuilder;
import org.purplei2p.lightning.browser.fragment.BookmarksFragment;
import org.purplei2p.lightning.browser.fragment.TabsFragment;
import org.purplei2p.lightning.interpolator.BezierDecelerateInterpolator;
import org.purplei2p.lightning.receiver.NetworkReceiver;
import org.purplei2p.lightning.search.SearchEngineProvider;
import org.purplei2p.lightning.search.SuggestionsAdapter;
import org.purplei2p.lightning.search.engine.BaseSearchEngine;
import org.purplei2p.lightning.settings.activity.SettingsActivity;
import org.purplei2p.lightning.utils.DrawableUtils;
import org.purplei2p.lightning.utils.IntentUtils;
import org.purplei2p.lightning.utils.Preconditions;
import org.purplei2p.lightning.utils.ProxyUtils;
import org.purplei2p.lightning.utils.ThemeUtils;
import org.purplei2p.lightning.utils.UrlUtils;
import org.purplei2p.lightning.utils.Utils;
import org.purplei2p.lightning.utils.WebUtils;
import org.purplei2p.lightning.view.Handlers;
import org.purplei2p.lightning.view.LightningView;
import org.purplei2p.lightning.view.SearchView;
import butterknife.BindView;
import butterknife.ButterKnife;
public abstract class BrowserActivity extends ThemableBrowserActivity implements BrowserView, UIController, OnClickListener {
private static final String TAG = "BrowserActivity";
private static final String INTENT_PANIC_TRIGGER = "info.guardianproject.panic.action.TRIGGER";
private static final String TAG_BOOKMARK_FRAGMENT = "TAG_BOOKMARK_FRAGMENT";
private static final String TAG_TABS_FRAGMENT = "TAG_TABS_FRAGMENT";
// Static Layout
@BindView(R.id.drawer_layout) DrawerLayout mDrawerLayout;
@BindView(R.id.content_frame) FrameLayout mBrowserFrame;
@BindView(R.id.left_drawer) ViewGroup mDrawerLeft;
@BindView(R.id.right_drawer) ViewGroup mDrawerRight;
@BindView(R.id.ui_layout) ViewGroup mUiLayout;
@BindView(R.id.toolbar_layout) ViewGroup mToolbarLayout;
@BindView(R.id.progress_view) AnimatedProgressBar mProgressBar;
@BindView(R.id.search_bar) RelativeLayout mSearchBar;
// Toolbar Views
@BindView(R.id.toolbar) Toolbar mToolbar;
private View mSearchBackground;
private SearchView mSearch;
private ImageView mArrowImage;
// Current tab view being displayed
@Nullable private View mCurrentView;
// Full Screen Video Views
private FrameLayout mFullscreenContainer;
private VideoView mVideoView;
private View mCustomView;
// Adapter
private SuggestionsAdapter mSuggestionsAdapter;
// Callback
private CustomViewCallback mCustomViewCallback;
private ValueCallback<Uri> mUploadMessage;
private ValueCallback<Uri[]> mFilePathCallback;
// Primitives
private boolean mFullScreen;
private boolean mDarkTheme;
private boolean mIsFullScreen = false;
private boolean mIsImmersive = false;
private boolean mShowTabsInDrawer;
private boolean mSwapBookmarksAndTabs;
private int mOriginalOrientation;
private int mBackgroundColor;
private int mIconColor;
private int mDisabledIconColor;
private int mCurrentUiColor = Color.BLACK;
private long mKeyDownStartTime;
private String mSearchText;
private String mUntitledTitle;
private String mCameraPhotoPath;
// The singleton BookmarkManager
@Inject BookmarkModel mBookmarkManager;
@Inject HistoryModel mHistoryModel;
@Inject LightningDialogBuilder mBookmarksDialogBuilder;
@Inject SearchBoxModel mSearchBoxModel;
@Inject SearchEngineProvider mSearchEngineProvider;
private TabsManager mTabsManager;
// Image
private Bitmap mWebpageBitmap;
private final ColorDrawable mBackground = new ColorDrawable();
private Drawable mDeleteIcon, mRefreshIcon, mClearIcon, mIcon;
private BrowserPresenter mPresenter;
private TabsView mTabsView;
private BookmarksView mBookmarksView;
// Proxy
@Inject ProxyUtils mProxyUtils;
// Constant
private static final int API = android.os.Build.VERSION.SDK_INT;
private static final String NETWORK_BROADCAST_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
private static final LayoutParams MATCH_PARENT = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
private static final FrameLayout.LayoutParams COVER_SCREEN_PARAMS = new FrameLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
protected abstract boolean isIncognito();
public abstract void closeActivity();
public abstract void updateHistory(@Nullable final String title, @NonNull final String url);
@NonNull
protected abstract Completable updateCookiePreference();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BrowserApp.getAppComponent().inject(this);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
mTabsManager = new TabsManager();
mPresenter = new BrowserPresenter(this, isIncognito());
initialize(savedInstanceState);
}
private synchronized void initialize(Bundle savedInstanceState) {
initializeToolbarHeight(getResources().getConfiguration());
setSupportActionBar(mToolbar);
ActionBar actionBar = getSupportActionBar();
//TODO make sure dark theme flag gets set correctly
mDarkTheme = mPreferences.getUseTheme() != 0 || isIncognito();
mIconColor = mDarkTheme ? ThemeUtils.getIconDarkThemeColor(this) : ThemeUtils.getIconLightThemeColor(this);
mDisabledIconColor = mDarkTheme ? ContextCompat.getColor(this, R.color.icon_dark_theme_disabled) :
ContextCompat.getColor(this, R.color.icon_light_theme_disabled);
mShowTabsInDrawer = mPreferences.getShowTabsInDrawer(!isTablet());
mSwapBookmarksAndTabs = mPreferences.getBookmarksAndTabsSwapped();
// initialize background ColorDrawable
int primaryColor = ThemeUtils.getPrimaryColor(this);
mBackground.setColor(primaryColor);
// Drawer stutters otherwise
mDrawerLeft.setLayerType(View.LAYER_TYPE_NONE, null);
mDrawerRight.setLayerType(View.LAYER_TYPE_NONE, null);
mDrawerLayout.addDrawerListener(new DrawerListener() {
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {}
@Override
public void onDrawerOpened(View drawerView) {}
@Override
public void onDrawerClosed(View drawerView) {}
@Override
public void onDrawerStateChanged(int newState) {
if (newState == DrawerLayout.STATE_DRAGGING) {
mDrawerLeft.setLayerType(View.LAYER_TYPE_HARDWARE, null);
mDrawerRight.setLayerType(View.LAYER_TYPE_HARDWARE, null);
} else if (newState == DrawerLayout.STATE_IDLE) {
mDrawerLeft.setLayerType(View.LAYER_TYPE_NONE, null);
mDrawerRight.setLayerType(View.LAYER_TYPE_NONE, null);
}
}
});
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !mShowTabsInDrawer) {
getWindow().setStatusBarColor(Color.BLACK);
}
setNavigationDrawerWidth();
mDrawerLayout.addDrawerListener(new DrawerLocker());
mWebpageBitmap = ThemeUtils.getThemedBitmap(this, R.drawable.ic_webpage, mDarkTheme);
final FragmentManager fragmentManager = getSupportFragmentManager();
TabsFragment tabsFragment = (TabsFragment) fragmentManager.findFragmentByTag(TAG_TABS_FRAGMENT);
BookmarksFragment bookmarksFragment = (BookmarksFragment) fragmentManager.findFragmentByTag(TAG_BOOKMARK_FRAGMENT);
if (tabsFragment != null) {
fragmentManager.beginTransaction().remove(tabsFragment).commit();
}
tabsFragment = TabsFragment.createTabsFragment(isIncognito(), mShowTabsInDrawer);
mTabsView = tabsFragment;
if (bookmarksFragment != null) {
fragmentManager.beginTransaction().remove(bookmarksFragment).commit();
}
bookmarksFragment = BookmarksFragment.createFragment(isIncognito());
mBookmarksView = bookmarksFragment;
fragmentManager.executePendingTransactions();
fragmentManager
.beginTransaction()
.replace(getTabsFragmentViewId(), tabsFragment, TAG_TABS_FRAGMENT)
.replace(getBookmarksFragmentViewId(), bookmarksFragment, TAG_BOOKMARK_FRAGMENT)
.commit();
if (mShowTabsInDrawer) {
mToolbarLayout.removeView(findViewById(R.id.tabs_toolbar_container));
}
Preconditions.checkNonNull(actionBar);
// set display options of the ActionBar
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setDisplayShowHomeEnabled(false);
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setCustomView(R.layout.toolbar_content);
View customView = actionBar.getCustomView();
LayoutParams lp = customView.getLayoutParams();
lp.width = LayoutParams.MATCH_PARENT;
lp.height = LayoutParams.MATCH_PARENT;
customView.setLayoutParams(lp);
mArrowImage = customView.findViewById(R.id.arrow);
FrameLayout arrowButton = customView.findViewById(R.id.arrow_button);
if (mShowTabsInDrawer) {
if (mArrowImage.getWidth() <= 0) {
mArrowImage.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
}
updateTabNumber(0);
// Post drawer locking in case the activity is being recreated
Handlers.MAIN.post(new Runnable() {
@Override
public void run() {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, getTabDrawer());
}
});
} else {
// Post drawer locking in case the activity is being recreated
Handlers.MAIN.post(new Runnable() {
@Override
public void run() {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, getTabDrawer());
}
});
mArrowImage.setImageResource(R.drawable.ic_action_home);
mArrowImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN);
}
// Post drawer locking in case the activity is being recreated
Handlers.MAIN.post(new Runnable() {
@Override
public void run() {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, getBookmarkDrawer());
}
});
arrowButton.setOnClickListener(this);
// create the search EditText in the ToolBar
mSearch = customView.findViewById(R.id.search);
mSearchBackground = customView.findViewById(R.id.search_container);
// initialize search background color
mSearchBackground.getBackground().setColorFilter(getSearchBarColor(primaryColor, primaryColor), PorterDuff.Mode.SRC_IN);
mSearch.setHintTextColor(ThemeUtils.getThemedTextHintColor(mDarkTheme));
mSearch.setTextColor(mDarkTheme ? Color.WHITE : Color.BLACK);
mUntitledTitle = getString(R.string.untitled);
mBackgroundColor = ThemeUtils.getPrimaryColor(this);
mDeleteIcon = ThemeUtils.getThemedDrawable(this, R.drawable.ic_action_delete, mDarkTheme);
mRefreshIcon = ThemeUtils.getThemedDrawable(this, R.drawable.ic_action_refresh, mDarkTheme);
mClearIcon = ThemeUtils.getThemedDrawable(this, R.drawable.ic_action_delete, mDarkTheme);
int iconBounds = Utils.dpToPx(24);
mDeleteIcon.setBounds(0, 0, iconBounds, iconBounds);
mRefreshIcon.setBounds(0, 0, iconBounds, iconBounds);
mClearIcon.setBounds(0, 0, iconBounds, iconBounds);
mIcon = mRefreshIcon;
SearchListenerClass search = new SearchListenerClass();
mSearch.setCompoundDrawablePadding(Utils.dpToPx(3));
mSearch.setCompoundDrawables(null, null, mRefreshIcon, null);
mSearch.setOnKeyListener(search);
mSearch.setOnFocusChangeListener(search);
mSearch.setOnEditorActionListener(search);
mSearch.setOnTouchListener(search);
mSearch.setOnPreFocusListener(search);
initializeSearchSuggestions(mSearch);
mDrawerLayout.setDrawerShadow(R.drawable.drawer_right_shadow, GravityCompat.END);
mDrawerLayout.setDrawerShadow(R.drawable.drawer_left_shadow, GravityCompat.START);
if (API <= Build.VERSION_CODES.JELLY_BEAN_MR2) {
//noinspection deprecation
WebIconDatabase.getInstance().open(getDir("icons", MODE_PRIVATE).getPath());
}
@SuppressWarnings("VariableNotUsedInsideIf")
Intent intent = savedInstanceState == null ? getIntent() : null;
boolean launchedFromHistory = intent != null && (intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0;
if (isPanicTrigger(intent)) {
setIntent(null);
panicClean();
} else {
if (launchedFromHistory) {
intent = null;
}
mPresenter.setupTabs(intent);
setIntent(null);
mProxyUtils.checkForProxy(this);
}
}
@IdRes
private int getBookmarksFragmentViewId() {
return mSwapBookmarksAndTabs ? R.id.left_drawer : R.id.right_drawer;
}
private int getTabsFragmentViewId() {
if (mShowTabsInDrawer) {
return mSwapBookmarksAndTabs ? R.id.right_drawer : R.id.left_drawer;
} else {
return R.id.tabs_toolbar_container;
}
}
/**
* Determines if an intent is originating
* from a panic trigger.
*
* @param intent the intent to check.
* @return true if the panic trigger sent
* the intent, false otherwise.
*/
protected static boolean isPanicTrigger(@Nullable Intent intent) {
return intent != null && INTENT_PANIC_TRIGGER.equals(intent.getAction());
}
protected void panicClean() {
Log.d(TAG, "Closing browser");
mTabsManager.newTab(this, "", false);
mTabsManager.switchToTab(0);
mTabsManager.clearSavedState();
HistoryPage.deleteHistoryPage(getApplication()).subscribe();
closeBrowser();
// System exit needed in the case of receiving
// the panic intent since finish() isn't completely
// closing the browser
System.exit(1);
}
private class SearchListenerClass implements OnKeyListener, OnEditorActionListener,
OnFocusChangeListener, OnTouchListener, SearchView.PreFocusListener {
@Override
public boolean onKey(View searchView, int keyCode, KeyEvent keyEvent) {
switch (keyCode) {
case KeyEvent.KEYCODE_ENTER:
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mSearch.getWindowToken(), 0);
searchTheWeb(mSearch.getText().toString());
final LightningView currentView = mTabsManager.getCurrentTab();
if (currentView != null) {
currentView.requestFocus();
}
return true;
default:
break;
}
return false;
}
@Override
public boolean onEditorAction(TextView arg0, int actionId, KeyEvent arg2) {
// hide the keyboard and search the web when the enter key
// button is pressed
if (actionId == EditorInfo.IME_ACTION_GO || actionId == EditorInfo.IME_ACTION_DONE
|| actionId == EditorInfo.IME_ACTION_NEXT
|| actionId == EditorInfo.IME_ACTION_SEND
|| actionId == EditorInfo.IME_ACTION_SEARCH
|| (arg2.getAction() == KeyEvent.KEYCODE_ENTER)) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mSearch.getWindowToken(), 0);
searchTheWeb(mSearch.getText().toString());
final LightningView currentView = mTabsManager.getCurrentTab();
if (currentView != null) {
currentView.requestFocus();
}
return true;
}
return false;
}
@Override
public void onFocusChange(final View v, final boolean hasFocus) {
final LightningView currentView = mTabsManager.getCurrentTab();
if (!hasFocus && currentView != null) {
setIsLoading(currentView.getProgress() < 100);
updateUrl(currentView.getUrl(), false);
} else if (hasFocus && currentView != null) {
// Hack to make sure the text gets selected
((SearchView) v).selectAll();
mIcon = mClearIcon;
mSearch.setCompoundDrawables(null, null, mClearIcon, null);
}
if (!hasFocus) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mSearch.getWindowToken(), 0);
}
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if (mSearch.getCompoundDrawables()[2] != null) {
boolean tappedX = event.getX() > (mSearch.getWidth()
- mSearch.getPaddingRight() - mIcon.getIntrinsicWidth());
if (tappedX) {
if (event.getAction() == MotionEvent.ACTION_UP) {
if (mSearch.hasFocus()) {
mSearch.setText("");
} else {
refreshOrStop();
}
}
return true;
}
}
return false;
}
@Override
public void onPreFocus() {
final LightningView currentView = mTabsManager.getCurrentTab();
if (currentView == null) {
return;
}
String url = currentView.getUrl();
if (!UrlUtils.isSpecialUrl(url)) {
if (!mSearch.hasFocus()) {
mSearch.setText(url);
}
}
}
}
private class DrawerLocker implements DrawerListener {
@Override
public void onDrawerClosed(View v) {
View tabsDrawer = getTabDrawer();
View bookmarksDrawer = getBookmarkDrawer();
if (v == tabsDrawer) {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, bookmarksDrawer);
} else if (mShowTabsInDrawer) {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, tabsDrawer);
}
}
@Override
public void onDrawerOpened(View v) {
View tabsDrawer = getTabDrawer();
View bookmarksDrawer = getBookmarkDrawer();
if (v == tabsDrawer) {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, bookmarksDrawer);
} else {
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, tabsDrawer);
}
}
@Override
public void onDrawerSlide(View v, float arg) {}
@Override
public void onDrawerStateChanged(int arg) {}
}
private void setNavigationDrawerWidth() {
int width = getResources().getDisplayMetrics().widthPixels - Utils.dpToPx(56);
int maxWidth;
if (isTablet()) {
maxWidth = Utils.dpToPx(320);
} else {
maxWidth = Utils.dpToPx(300);
}
if (width > maxWidth) {
DrawerLayout.LayoutParams params = (android.support.v4.widget.DrawerLayout.LayoutParams) mDrawerLeft
.getLayoutParams();
params.width = maxWidth;
mDrawerLeft.setLayoutParams(params);
mDrawerLeft.requestLayout();
DrawerLayout.LayoutParams paramsRight = (android.support.v4.widget.DrawerLayout.LayoutParams) mDrawerRight
.getLayoutParams();
paramsRight.width = maxWidth;
mDrawerRight.setLayoutParams(paramsRight);
mDrawerRight.requestLayout();
} else {
DrawerLayout.LayoutParams params = (android.support.v4.widget.DrawerLayout.LayoutParams) mDrawerLeft
.getLayoutParams();
params.width = width;
mDrawerLeft.setLayoutParams(params);
mDrawerLeft.requestLayout();
DrawerLayout.LayoutParams paramsRight = (android.support.v4.widget.DrawerLayout.LayoutParams) mDrawerRight
.getLayoutParams();
paramsRight.width = width;
mDrawerRight.setLayoutParams(paramsRight);
mDrawerRight.requestLayout();
}
}
private void initializePreferences() {
final LightningView currentView = mTabsManager.getCurrentTab();
mFullScreen = mPreferences.getFullScreenEnabled();
boolean colorMode = mPreferences.getColorModeEnabled();
colorMode &= !mDarkTheme;
if (!isIncognito() && !colorMode && !mDarkTheme && mWebpageBitmap != null) {
changeToolbarBackground(mWebpageBitmap, null);
} else if (!isIncognito() && currentView != null && !mDarkTheme) {
changeToolbarBackground(currentView.getFavicon(), null);
} else if (!isIncognito() && !mDarkTheme && mWebpageBitmap != null) {
changeToolbarBackground(mWebpageBitmap, null);
}
FragmentManager manager = getSupportFragmentManager();
Fragment tabsFragment = manager.findFragmentByTag(TAG_TABS_FRAGMENT);
if (tabsFragment instanceof TabsFragment) {
((TabsFragment) tabsFragment).reinitializePreferences();
}
Fragment bookmarksFragment = manager.findFragmentByTag(TAG_BOOKMARK_FRAGMENT);
if (bookmarksFragment instanceof BookmarksFragment) {
((BookmarksFragment) bookmarksFragment).reinitializePreferences();
}
// TODO layout transition causing memory leak
// mBrowserFrame.setLayoutTransition(new LayoutTransition());
setFullscreen(mPreferences.getHideStatusBarEnabled(), false);
BaseSearchEngine currentSearchEngine = mSearchEngineProvider.getCurrentSearchEngine();
mSearchText = currentSearchEngine.getQueryUrl();
updateCookiePreference().subscribeOn(Schedulers.worker()).subscribe();
mProxyUtils.updateProxySettings(this);
}
@Override
public void onWindowVisibleToUserAfterResume() {
super.onWindowVisibleToUserAfterResume();
mToolbarLayout.setTranslationY(0);
setWebViewTranslation(mToolbarLayout.getHeight());
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_ENTER) {
if (mSearch.hasFocus()) {
searchTheWeb(mSearch.getText().toString());
}
} else if ((keyCode == KeyEvent.KEYCODE_MENU)
&& (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN)
&& (Build.MANUFACTURER.compareTo("LGE") == 0)) {
// Workaround for stupid LG devices that crash
return true;
} else if (keyCode == KeyEvent.KEYCODE_BACK) {
mKeyDownStartTime = System.currentTimeMillis();
Handlers.MAIN.postDelayed(mLongPressBackRunnable, ViewConfiguration.getLongPressTimeout());
}
return super.onKeyDown(keyCode, event);
}
private final Runnable mLongPressBackRunnable = new Runnable() {
@Override
public void run() {
final LightningView currentTab = mTabsManager.getCurrentTab();
showCloseDialog(mTabsManager.positionOf(currentTab));
}
};
@Override
public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_MENU)
&& (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN)
&& (Build.MANUFACTURER.compareTo("LGE") == 0)) {
// Workaround for stupid LG devices that crash
openOptionsMenu();
return true;
} else if (keyCode == KeyEvent.KEYCODE_BACK) {
Handlers.MAIN.removeCallbacks(mLongPressBackRunnable);
if ((System.currentTimeMillis() - mKeyDownStartTime) > ViewConfiguration.getLongPressTimeout()) {
return true;
}
}
return super.onKeyUp(keyCode, event);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
// Keyboard shortcuts
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (event.isCtrlPressed()) {
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_F:
// Search in page
findInPage();
return true;
case KeyEvent.KEYCODE_T:
// Open new tab
newTab(null, true);
return true;
case KeyEvent.KEYCODE_W:
// Close current tab
mPresenter.deleteTab(mTabsManager.indexOfCurrentTab());
return true;
case KeyEvent.KEYCODE_Q:
// Close browser
closeBrowser();
return true;
case KeyEvent.KEYCODE_R:
// Refresh current tab
LightningView currentTab = mTabsManager.getCurrentTab();
if (currentTab != null) {
currentTab.reload();
}
return true;
case KeyEvent.KEYCODE_TAB:
int nextIndex;
if (event.isShiftPressed()) {
// Go back one tab
if (mTabsManager.indexOfCurrentTab() > 0) {
nextIndex = mTabsManager.indexOfCurrentTab() - 1;
} else {
nextIndex = mTabsManager.last();
}
} else {
// Go forward one tab
if (mTabsManager.indexOfCurrentTab() < mTabsManager.last()) {
nextIndex = mTabsManager.indexOfCurrentTab() + 1;
} else {
nextIndex = 0;
}
}
mPresenter.tabChanged(nextIndex);
return true;
}
} else if (event.getKeyCode() == KeyEvent.KEYCODE_SEARCH) {
// Highlight search field
mSearch.requestFocus();
mSearch.selectAll();
return true;
} else if (event.isAltPressed()) {
// Alt + tab number
if (KeyEvent.KEYCODE_0 <= event.getKeyCode() && event.getKeyCode() <= KeyEvent.KEYCODE_9) {
int nextIndex;
if (event.getKeyCode() > mTabsManager.last() + KeyEvent.KEYCODE_1 || event.getKeyCode() == KeyEvent.KEYCODE_0) {
nextIndex = mTabsManager.last();
} else {
nextIndex = event.getKeyCode() - KeyEvent.KEYCODE_1;
}
mPresenter.tabChanged(nextIndex);
return true;
}
}
}
return super.dispatchKeyEvent(event);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
final LightningView currentView = mTabsManager.getCurrentTab();
final String currentUrl = currentView != null ? currentView.getUrl() : null;
// Handle action buttons
switch (item.getItemId()) {
case android.R.id.home:
if (mDrawerLayout.isDrawerOpen(getBookmarkDrawer())) {
mDrawerLayout.closeDrawer(getBookmarkDrawer());
}
return true;
case R.id.action_back:
if (currentView != null && currentView.canGoBack()) {
currentView.goBack();
}
return true;
case R.id.action_forward:
if (currentView != null && currentView.canGoForward()) {
currentView.goForward();
}
return true;
case R.id.action_add_to_homescreen:
if (currentView != null) {
HistoryItem shortcut = new HistoryItem(currentView.getUrl(), currentView.getTitle());
shortcut.setBitmap(currentView.getFavicon());
Utils.createShortcut(this, shortcut);
}
return true;
case R.id.action_new_tab:
newTab(null, true);
return true;
case R.id.action_incognito:
startActivity(new Intent(this, IncognitoActivity.class));
overridePendingTransition(R.anim.slide_up_in, R.anim.fade_out_scale);
return true;
case R.id.action_share:
new IntentUtils(this).shareUrl(currentUrl, currentView != null ? currentView.getTitle() : null);
return true;
case R.id.action_bookmarks:
openBookmarks();
return true;
case R.id.action_copy:
if (currentUrl != null && !UrlUtils.isSpecialUrl(currentUrl)) {
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("label", currentUrl);
clipboard.setPrimaryClip(clip);
Utils.showSnackbar(this, R.string.message_link_copied);
}
return true;
case R.id.action_settings:
startActivity(new Intent(this, SettingsActivity.class));
return true;
case R.id.action_history:
openHistory();
return true;
case R.id.action_downloads:
openDownloads();
return true;
case R.id.action_add_bookmark:
if (currentUrl != null && !UrlUtils.isSpecialUrl(currentUrl)) {
addBookmark(currentView.getTitle(), currentUrl);
}
return true;
case R.id.action_find:
findInPage();
return true;
case R.id.action_reading_mode:
if (currentUrl != null) {
Intent read = new Intent(this, ReadingActivity.class);
read.putExtra(Constants.LOAD_READING_URL, currentUrl);
startActivity(read);
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
// By using a manager, adds a bookmark and notifies third parties about that
private void addBookmark(final String title, final String url) {
final HistoryItem item = new HistoryItem(url, title);
mBookmarkManager.addBookmarkIfNotExists(item)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.main())
.subscribe(new SingleOnSubscribe<Boolean>() {
@Override
public void onItem(@Nullable Boolean item) {
if (Boolean.TRUE.equals(item)) {
mSuggestionsAdapter.refreshBookmarks();
mBookmarksView.handleUpdatedUrl(url);
}
}
});
}
private void deleteBookmark(final String title, final String url) {
final HistoryItem item = new HistoryItem(url, title);
mBookmarkManager.deleteBookmark(item)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.main())
.subscribe(new SingleOnSubscribe<Boolean>() {
@Override
public void onItem(@Nullable Boolean item) {
if (Boolean.TRUE.equals(item)) {
mSuggestionsAdapter.refreshBookmarks();
mBookmarksView.handleUpdatedUrl(url);
}
}
});
}
private void putToolbarInRoot() {
if (mToolbarLayout.getParent() != mUiLayout) {
if (mToolbarLayout.getParent() != null) {
((ViewGroup) mToolbarLayout.getParent()).removeView(mToolbarLayout);
}
mUiLayout.addView(mToolbarLayout, 0);
mUiLayout.requestLayout();
}
setWebViewTranslation(0);
}
private void overlayToolbarOnWebView() {
if (mToolbarLayout.getParent() != mBrowserFrame) {
if (mToolbarLayout.getParent() != null) {
((ViewGroup) mToolbarLayout.getParent()).removeView(mToolbarLayout);
}
mBrowserFrame.addView(mToolbarLayout);
mBrowserFrame.requestLayout();
}
setWebViewTranslation(mToolbarLayout.getHeight());
}
private void setWebViewTranslation(float translation) {
if (mFullScreen && mCurrentView != null) {
mCurrentView.setTranslationY(translation);
} else if (mCurrentView != null) {
mCurrentView.setTranslationY(0);
}
}
/**
* method that shows a dialog asking what string the user wishes to search
* for. It highlights the text entered.
*/
private void findInPage() {
BrowserDialog.showEditText(this,
R.string.action_find,
R.string.search_hint,
R.string.search_hint, new BrowserDialog.EditorListener() {
@Override
public void onClick(String text) {
if (!TextUtils.isEmpty(text)) {
mPresenter.findInPage(text);
showFindInPageControls(text);
}
}
});
}
private void showFindInPageControls(@NonNull String text) {
mSearchBar.setVisibility(View.VISIBLE);
TextView tw = (TextView) findViewById(R.id.search_query);
tw.setText('\'' + text + '\'');
ImageButton up = (ImageButton) findViewById(R.id.button_next);
up.setOnClickListener(this);
ImageButton down = (ImageButton) findViewById(R.id.button_back);
down.setOnClickListener(this);
ImageButton quit = (ImageButton) findViewById(R.id.button_quit);
quit.setOnClickListener(this);
}
@Override
public TabsManager getTabModel() {
return mTabsManager;
}
@Override
public void showCloseDialog(final int position) {
if (position < 0) {
return;
}
BrowserDialog.show(this, R.string.dialog_title_close_browser,
new BrowserDialog.Item(R.string.close_tab) {
@Override
public void onClick() {
mPresenter.deleteTab(position);
}
},
new BrowserDialog.Item(R.string.close_other_tabs) {
@Override
public void onClick() {
mPresenter.closeAllOtherTabs();
}
},
new BrowserDialog.Item(R.string.close_all_tabs) {
@Override
public void onClick() {
closeBrowser();
}
});
}
@Override
public void notifyTabViewRemoved(int position) {
Log.d(TAG, "Notify Tab Removed: " + position);
mTabsView.tabRemoved(position);
}
@Override
public void notifyTabViewAdded() {
Log.d(TAG, "Notify Tab Added");
mTabsView.tabAdded();
}
@Override
public void notifyTabViewChanged(int position) {
Log.d(TAG, "Notify Tab Changed: " + position);
mTabsView.tabChanged(position);
}
@Override
public void notifyTabViewInitialized() {
Log.d(TAG, "Notify Tabs Initialized");
mTabsView.tabsInitialized();
}
@Override
public void tabChanged(LightningView tab) {
mPresenter.tabChangeOccurred(tab);
}
@Override
public void removeTabView() {
Log.d(TAG, "Remove the tab view");
// Set the background color so the color mode color doesn't show through
mBrowserFrame.setBackgroundColor(mBackgroundColor);
removeViewFromParent(mCurrentView);
mCurrentView = null;
// Use a delayed handler to make the transition smooth
// otherwise it will get caught up with the showTab code
// and cause a janky motion
Handlers.MAIN.postDelayed(new Runnable() {
@Override
public void run() {
mDrawerLayout.closeDrawers();
}
}, 200);
}
@Override
public void setTabView(@NonNull final View view) {
if (mCurrentView == view) {
return;
}
Log.d(TAG, "Setting the tab view");
// Set the background color so the color mode color doesn't show through
mBrowserFrame.setBackgroundColor(mBackgroundColor);
removeViewFromParent(view);
removeViewFromParent(mCurrentView);
mBrowserFrame.addView(view, 0, MATCH_PARENT);
if (mFullScreen) {
view.setTranslationY(mToolbarLayout.getHeight() + mToolbarLayout.getTranslationY());
} else {
view.setTranslationY(0);
}
view.requestFocus();
mCurrentView = view;
showActionBar();
// Use a delayed handler to make the transition smooth
// otherwise it will get caught up with the showTab code
// and cause a janky motion
Handlers.MAIN.postDelayed(new Runnable() {
@Override
public void run() {
mDrawerLayout.closeDrawers();
}
}, 200);
// Handlers.MAIN.postDelayed(new Runnable() {
// @Override
// public void run() {
// Remove browser frame background to reduce overdraw
//TODO evaluate performance
// mBrowserFrame.setBackgroundColor(Color.TRANSPARENT);
// }
// }, 300);
}
@Override
public void showBlockedLocalFileDialog(@NonNull DialogInterface.OnClickListener listener) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
Dialog dialog = builder.setCancelable(true)
.setTitle(R.string.title_warning)
.setMessage(R.string.message_blocked_local)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.action_open, listener)
.show();
BrowserDialog.setDialogSize(this, dialog);
}
@Override
public void showSnackbar(@StringRes int resource) {
Utils.showSnackbar(this, resource);
}
@Override
public void tabCloseClicked(int position) {
mPresenter.deleteTab(position);
}
@Override
public void tabClicked(int position) {
showTab(position);
}
@Override
public void newTabButtonClicked() {
mPresenter.newTab(null, true);
}
@Override
public void newTabButtonLongClicked() {
String url = mPreferences.getSavedUrl();
if (url != null) {
newTab(url, true);
Utils.showSnackbar(this, R.string.deleted_tab);
}
mPreferences.setSavedUrl(null);
}
@Override
public void bookmarkButtonClicked() {
final LightningView currentTab = mTabsManager.getCurrentTab();
final String url = currentTab != null ? currentTab.getUrl() : null;
final String title = currentTab != null ? currentTab.getTitle() : null;
if (url == null) {
return;
}
if (!UrlUtils.isSpecialUrl(url)) {
mBookmarkManager.isBookmark(url)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.main())
.subscribe(new SingleOnSubscribe<Boolean>() {
@Override
public void onItem(@Nullable Boolean item) {
if (Boolean.TRUE.equals(item)) {
deleteBookmark(title, url);
} else {
addBookmark(title, url);
}
}
});
}
}
@Override
public void bookmarkItemClicked(@NonNull HistoryItem item) {
mPresenter.loadUrlInCurrentView(item.getUrl());
// keep any jank from happening when the drawer is closed after the
// URL starts to load
Handlers.MAIN.postDelayed(new Runnable() {
@Override
public void run() {
closeDrawers(null);
}
}, 150);
}
@Override
public void handleHistoryChange() {
openHistory();
}
/**
* displays the WebView contained in the LightningView Also handles the
* removal of previous views
*
* @param position the poition of the tab to display
*/
// TODO move to presenter
private synchronized void showTab(final int position) {
mPresenter.tabChanged(position);
}
private static void removeViewFromParent(@Nullable View view) {
if (view == null) {
return;
}
ViewParent parent = view.getParent();
if (parent instanceof ViewGroup) {
((ViewGroup) parent).removeView(view);
}
}
protected void handleNewIntent(Intent intent) {
mPresenter.onNewIntent(intent);
}
@Override
public void closeEmptyTab() {
// Currently do nothing
// Possibly closing the current tab might close the browser
// and mess stuff up
}
@Override
public void onTrimMemory(int level) {
if (level > TRIM_MEMORY_MODERATE && Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
Log.d(TAG, "Low Memory, Free Memory");
mPresenter.onAppLowMemory();
}
}
// TODO move to presenter
private synchronized boolean newTab(String url, boolean show) {
return mPresenter.newTab(url, show);
}
protected void performExitCleanUp() {
final LightningView currentTab = mTabsManager.getCurrentTab();
if (mPreferences.getClearCacheExit() && currentTab != null && !isIncognito()) {
WebUtils.clearCache(currentTab.getWebView());
Log.d(TAG, "Cache Cleared");
}
if (mPreferences.getClearHistoryExitEnabled() && !isIncognito()) {
WebUtils.clearHistory(this, mHistoryModel);
Log.d(TAG, "History Cleared");
}
if (mPreferences.getClearCookiesExitEnabled() && !isIncognito()) {
WebUtils.clearCookies(this);
Log.d(TAG, "Cookies Cleared");
}
if (mPreferences.getClearWebStorageExitEnabled() && !isIncognito()) {
WebUtils.clearWebStorage();
Log.d(TAG, "WebStorage Cleared");
} else if (isIncognito()) {
WebUtils.clearWebStorage(); // We want to make sure incognito mode is secure
}
mSuggestionsAdapter.clearCache();
}
@Override
public void onConfigurationChanged(final Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Log.d(TAG, "onConfigurationChanged");
if (mFullScreen) {
showActionBar();
mToolbarLayout.setTranslationY(0);
setWebViewTranslation(mToolbarLayout.getHeight());
}
supportInvalidateOptionsMenu();
initializeToolbarHeight(newConfig);
}
private void initializeToolbarHeight(@NonNull final Configuration configuration) {
// TODO externalize the dimensions
doOnLayout(mUiLayout, new Runnable() {
@Override
public void run() {
int toolbarSize;
if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
// In portrait toolbar should be 56 dp tall
toolbarSize = Utils.dpToPx(56);
} else {
// In landscape toolbar should be 48 dp tall
toolbarSize = Utils.dpToPx(52);
}
mToolbar.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, toolbarSize));
mToolbar.setMinimumHeight(toolbarSize);
doOnLayout(mToolbar, new Runnable() {
@Override
public void run() {
setWebViewTranslation(mToolbarLayout.getHeight());
}
});
mToolbar.requestLayout();
}
});
}
public void closeBrowser() {
mBrowserFrame.setBackgroundColor(mBackgroundColor);
removeViewFromParent(mCurrentView);
performExitCleanUp();
int size = mTabsManager.size();
mTabsManager.shutdown();
mCurrentView = null;
for (int n = 0; n < size; n++) {
mTabsView.tabRemoved(0);
}
finish();
}
@Override
public synchronized void onBackPressed() {
final LightningView currentTab = mTabsManager.getCurrentTab();
if (mDrawerLayout.isDrawerOpen(getTabDrawer())) {
mDrawerLayout.closeDrawer(getTabDrawer());
} else if (mDrawerLayout.isDrawerOpen(getBookmarkDrawer())) {
mBookmarksView.navigateBack();
} else {
if (currentTab != null) {
Log.d(TAG, "onBackPressed");
if (mSearch.hasFocus()) {
currentTab.requestFocus();
} else if (currentTab.canGoBack()) {
if (!currentTab.isShown()) {
onHideCustomView();
} else {
currentTab.goBack();
}
} else {
if (mCustomView != null || mCustomViewCallback != null) {
onHideCustomView();
} else {
mPresenter.deleteTab(mTabsManager.positionOf(currentTab));
}
}
} else {
Log.e(TAG, "This shouldn't happen ever");
super.onBackPressed();
}
}
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause");
mTabsManager.pauseAll();
try {
getApplication().unregisterReceiver(mNetworkReceiver);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Receiver was not registered", e);
}
if (isIncognito() && isFinishing()) {
overridePendingTransition(R.anim.fade_in_scale, R.anim.slide_down_out);
}
}
protected void saveOpenTabs() {
if (mPreferences.getRestoreLostTabsEnabled()) {
mTabsManager.saveState();
}
}
@Override
protected void onStop() {
super.onStop();
}
@Override
protected void onDestroy() {
Log.d(TAG, "onDestroy");
Handlers.MAIN.removeCallbacksAndMessages(null);
mPresenter.shutdown();
super.onDestroy();
}
@Override
protected void onStart() {
super.onStart();
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mTabsManager.shutdown();
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume");
if (mSwapBookmarksAndTabs != mPreferences.getBookmarksAndTabsSwapped()) {
restart();
}
if (mSuggestionsAdapter != null) {
mSuggestionsAdapter.refreshPreferences();
mSuggestionsAdapter.refreshBookmarks();
}
mTabsManager.resumeAll(this);
initializePreferences();
supportInvalidateOptionsMenu();
IntentFilter filter = new IntentFilter();
filter.addAction(NETWORK_BROADCAST_ACTION);
getApplication().registerReceiver(mNetworkReceiver, filter);
if (mFullScreen) {
overlayToolbarOnWebView();
} else {
putToolbarInRoot();
}
}
/**
* searches the web for the query fixing any and all problems with the input
* checks if it is a search, url, etc.
*/
private void searchTheWeb(@NonNull String query) {
final LightningView currentTab = mTabsManager.getCurrentTab();
if (query.isEmpty()) {
return;
}
String searchUrl = mSearchText + UrlUtils.QUERY_PLACE_HOLDER;
query = query.trim();
if (currentTab != null) {
currentTab.stopLoading();
mPresenter.loadUrlInCurrentView(UrlUtils.smartUrlFilter(query, true, searchUrl));
}
}
/**
* Animates the color of the toolbar from one color to another. Optionally animates
* the color of the tab background, for use when the tabs are displayed on the top
* of the screen.
*
* @param favicon the Bitmap to extract the color from
* @param tabBackground the optional LinearLayout to color
*/
@Override
public void changeToolbarBackground(@NonNull Bitmap favicon, @Nullable final Drawable tabBackground) {
final int defaultColor = ContextCompat.getColor(this, R.color.primary_color);
if (mCurrentUiColor == Color.BLACK) {
mCurrentUiColor = defaultColor;
}
Palette.from(favicon).generate(new Palette.PaletteAsyncListener() {
@Override
public void onGenerated(Palette palette) {
// OR with opaque black to remove transparency glitches
int color = 0xff000000 | palette.getVibrantColor(defaultColor);
final int finalColor; // Lighten up the dark color if it is
// too dark
if (!mShowTabsInDrawer || Utils.isColorTooDark(color)) {
finalColor = Utils.mixTwoColors(defaultColor, color, 0.25f);
} else {
finalColor = color;
}
final Window window = getWindow();
if (!mShowTabsInDrawer) {
window.setBackgroundDrawable(new ColorDrawable(Color.BLACK));
}
final int startSearchColor = getSearchBarColor(mCurrentUiColor, defaultColor);
final int finalSearchColor = getSearchBarColor(finalColor, defaultColor);
Animation animation = new Animation() {
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final int color = DrawableUtils.mixColor(interpolatedTime, mCurrentUiColor, finalColor);
if (mShowTabsInDrawer) {
mBackground.setColor(color);
Handlers.MAIN.post(new Runnable() {
@Override
public void run() {
window.setBackgroundDrawable(mBackground);
}
});
} else if (tabBackground != null) {
tabBackground.setColorFilter(color, PorterDuff.Mode.SRC_IN);
}
mCurrentUiColor = color;
mToolbarLayout.setBackgroundColor(color);
mSearchBackground.getBackground().setColorFilter(DrawableUtils.mixColor(interpolatedTime,
startSearchColor, finalSearchColor), PorterDuff.Mode.SRC_IN);
}
};
animation.setDuration(300);
mToolbarLayout.startAnimation(animation);
}
});
}
private int getSearchBarColor(int requestedColor, int defaultColor) {
if (requestedColor == defaultColor) {
return mDarkTheme ? DrawableUtils.mixColor(0.25f, defaultColor, Color.WHITE) : Color.WHITE;
} else {
return DrawableUtils.mixColor(0.25f, requestedColor, Color.WHITE);
}
}
@Override
public boolean getUseDarkTheme() {
return mDarkTheme;
}
@ColorInt
@Override
public int getUiColor() {
return mCurrentUiColor;
}
@Override
public void updateUrl(@Nullable String url, boolean isLoading) {
if (url == null || mSearch == null || mSearch.hasFocus()) {
return;
}
final LightningView currentTab = mTabsManager.getCurrentTab();
mBookmarksView.handleUpdatedUrl(url);
String currentTitle = currentTab != null ? currentTab.getTitle() : null;
mSearch.setText(mSearchBoxModel.getDisplayContent(url, currentTitle, isLoading));
}
@Override
public void updateTabNumber(int number) {
if (mArrowImage != null && mShowTabsInDrawer) {
mArrowImage.setImageBitmap(DrawableUtils.getRoundedNumberImage(number, Utils.dpToPx(24),
Utils.dpToPx(24), ThemeUtils.getIconThemeColor(this, mDarkTheme), Utils.dpToPx(2.5f)));
}
}
@Override
public void updateProgress(int n) {
setIsLoading(n < 100);
mProgressBar.setProgress(n);
}
protected void addItemToHistory(@Nullable final String title, @NonNull final String url) {
if (UrlUtils.isSpecialUrl(url)) {
return;
}
mHistoryModel.visitHistoryItem(url, title)
.subscribeOn(Schedulers.io())
.subscribe(new CompletableOnSubscribe() {
@Override
public void onError(@NonNull Throwable throwable) {
Log.e(TAG, "Exception while updating history", throwable);
}
});
}
/**
* method to generate search suggestions for the AutoCompleteTextView from
* previously searched URLs
*/
private void initializeSearchSuggestions(final AutoCompleteTextView getUrl) {
mSuggestionsAdapter = new SuggestionsAdapter(this, mDarkTheme, isIncognito());
getUrl.setThreshold(1);
getUrl.setDropDownWidth(-1);
getUrl.setDropDownAnchor(R.id.toolbar_layout);
getUrl.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int pos, long l) {
String url = null;
CharSequence urlString = ((TextView) view.findViewById(R.id.url)).getText();
if (urlString != null) {
url = urlString.toString();
}
if (url == null || url.startsWith(getString(R.string.suggestion))) {
CharSequence searchString = ((TextView) view.findViewById(R.id.title)).getText();
if (searchString != null) {
url = searchString.toString();
}
}
if (url == null) {
return;
}
getUrl.setText(url);
searchTheWeb(url);
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getUrl.getWindowToken(), 0);
mPresenter.onAutoCompleteItemPressed();
}
});
getUrl.setSelectAllOnFocus(true);
getUrl.setAdapter(mSuggestionsAdapter);
}
/**
* function that opens the HTML history page in the browser
*/
private void openHistory() {
new HistoryPage().getHistoryPage()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.main())
.subscribe(new SingleOnSubscribe<String>() {
@Override
public void onItem(@Nullable String item) {
Preconditions.checkNonNull(item);
LightningView view = mTabsManager.getCurrentTab();
if (view != null) {
view.loadUrl(item);
}
}
});
}
private void openDownloads() {
new DownloadsPage().getDownloadsPage()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.main())
.subscribe(new SingleOnSubscribe<String>() {
@Override
public void onItem(@Nullable String item) {
Preconditions.checkNonNull(item);
LightningView view = mTabsManager.getCurrentTab();
if (view != null) {
view.loadUrl(item);
}
}
});
}
private View getBookmarkDrawer() {
return mSwapBookmarksAndTabs ? mDrawerLeft : mDrawerRight;
}
private View getTabDrawer() {
return mSwapBookmarksAndTabs ? mDrawerRight : mDrawerLeft;
}
/**
* helper function that opens the bookmark drawer
*/
private void openBookmarks() {
if (mDrawerLayout.isDrawerOpen(getTabDrawer())) {
mDrawerLayout.closeDrawers();
}
mDrawerLayout.openDrawer(getBookmarkDrawer());
}
/**
* This method closes any open drawer and executes
* the runnable after the drawers are completely closed.
*
* @param runnable an optional runnable to run after
* the drawers are closed.
*/
protected final void closeDrawers(@Nullable final Runnable runnable) {
if (!mDrawerLayout.isDrawerOpen(mDrawerLeft) && !mDrawerLayout.isDrawerOpen(mDrawerRight)) {
if (runnable != null) {
runnable.run();
return;
}
}
mDrawerLayout.closeDrawers();
mDrawerLayout.addDrawerListener(new DrawerListener() {
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {}
@Override
public void onDrawerOpened(View drawerView) {}
@Override
public void onDrawerClosed(View drawerView) {
if (runnable != null) {
runnable.run();
}
mDrawerLayout.removeDrawerListener(this);
}
@Override
public void onDrawerStateChanged(int newState) {}
});
}
@Override
public void setForwardButtonEnabled(boolean enabled) {
if (mForwardMenuItem != null && mForwardMenuItem.getIcon() != null) {
int colorFilter;
if (enabled) {
colorFilter = mIconColor;
} else {
colorFilter = mDisabledIconColor;
}
mForwardMenuItem.getIcon().setColorFilter(colorFilter, PorterDuff.Mode.SRC_IN);
mForwardMenuItem.setIcon(mForwardMenuItem.getIcon());
}
}
@Override
public void setBackButtonEnabled(boolean enabled) {
if (mBackMenuItem != null && mBackMenuItem.getIcon() != null) {
int colorFilter;
if (enabled) {
colorFilter = mIconColor;
} else {
colorFilter = mDisabledIconColor;
}
mBackMenuItem.getIcon().setColorFilter(colorFilter, PorterDuff.Mode.SRC_IN);
mBackMenuItem.setIcon(mBackMenuItem.getIcon());
}
}
private MenuItem mBackMenuItem;
private MenuItem mForwardMenuItem;
@Override
public boolean onCreateOptionsMenu(Menu menu) {
mBackMenuItem = menu.findItem(R.id.action_back);
mForwardMenuItem = menu.findItem(R.id.action_forward);
if (mBackMenuItem != null && mBackMenuItem.getIcon() != null)
mBackMenuItem.getIcon().setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN);
if (mForwardMenuItem != null && mForwardMenuItem.getIcon() != null)
mForwardMenuItem.getIcon().setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN);
return super.onCreateOptionsMenu(menu);
}
/**
* opens a file chooser
* param ValueCallback is the message from the WebView indicating a file chooser
* should be opened
*/
@Override
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
startActivityForResult(Intent.createChooser(i, getString(R.string.title_file_chooser)), 1);
}
/**
* used to allow uploading into the browser
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (API < Build.VERSION_CODES.LOLLIPOP) {
if (requestCode == 1) {
if (null == mUploadMessage) {
return;
}
Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData();
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
}
}
if (requestCode != 1 || mFilePathCallback == null) {
super.onActivityResult(requestCode, resultCode, intent);
return;
}
Uri[] results = null;
// Check that the response is a good one
if (resultCode == Activity.RESULT_OK) {
if (intent == null) {
// If there is not data, then we may have taken a photo
if (mCameraPhotoPath != null) {
results = new Uri[]{Uri.parse(mCameraPhotoPath)};
}
} else {
String dataString = intent.getDataString();
if (dataString != null) {
results = new Uri[]{Uri.parse(dataString)};
}
}
}
mFilePathCallback.onReceiveValue(results);
mFilePathCallback = null;
}
@Override
public void showFileChooser(ValueCallback<Uri[]> filePathCallback) {
if (mFilePathCallback != null) {
mFilePathCallback.onReceiveValue(null);
}
mFilePathCallback = filePathCallback;
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(this.getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = Utils.createImageFile();
takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath);
} catch (IOException ex) {
// Error occurred while creating the File
Log.e(TAG, "Unable to create Image File", ex);
}
// Continue only if the File was successfully created
if (photoFile != null) {
mCameraPhotoPath = "file:" + photoFile.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
} else {
takePictureIntent = null;
}
}
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("*/*");
Intent[] intentArray;
if (takePictureIntent != null) {
intentArray = new Intent[]{takePictureIntent};
} else {
intentArray = new Intent[0];
}
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
startActivityForResult(chooserIntent, 1);
}
@Override
public synchronized void onShowCustomView(View view, CustomViewCallback callback) {
int requestedOrientation = mOriginalOrientation = getRequestedOrientation();
onShowCustomView(view, callback, requestedOrientation);
}
@Override
public synchronized void onShowCustomView(final View view, CustomViewCallback callback, int requestedOrientation) {
final LightningView currentTab = mTabsManager.getCurrentTab();
if (view == null || mCustomView != null) {
if (callback != null) {
try {
callback.onCustomViewHidden();
} catch (Exception e) {
Log.e(TAG, "Error hiding custom view", e);
}
}
return;
}
try {
view.setKeepScreenOn(true);
} catch (SecurityException e) {
Log.e(TAG, "WebView is not allowed to keep the screen on");
}
mOriginalOrientation = getRequestedOrientation();
mCustomViewCallback = callback;
mCustomView = view;
setRequestedOrientation(requestedOrientation);
final FrameLayout decorView = (FrameLayout) getWindow().getDecorView();
mFullscreenContainer = new FrameLayout(this);
mFullscreenContainer.setBackgroundColor(ContextCompat.getColor(this, android.R.color.black));
if (view instanceof FrameLayout) {
if (((FrameLayout) view).getFocusedChild() instanceof VideoView) {
mVideoView = (VideoView) ((FrameLayout) view).getFocusedChild();
mVideoView.setOnErrorListener(new VideoCompletionListener());
mVideoView.setOnCompletionListener(new VideoCompletionListener());
}
} else if (view instanceof VideoView) {
mVideoView = (VideoView) view;
mVideoView.setOnErrorListener(new VideoCompletionListener());
mVideoView.setOnCompletionListener(new VideoCompletionListener());
}
decorView.addView(mFullscreenContainer, COVER_SCREEN_PARAMS);
mFullscreenContainer.addView(mCustomView, COVER_SCREEN_PARAMS);
decorView.requestLayout();
setFullscreen(true, true);
if (currentTab != null) {
currentTab.setVisibility(View.INVISIBLE);
}
}
@Override
public void closeBookmarksDrawer() {
mDrawerLayout.closeDrawer(getBookmarkDrawer());
}
@Override
public void onHideCustomView() {
final LightningView currentTab = mTabsManager.getCurrentTab();
if (mCustomView == null || mCustomViewCallback == null || currentTab == null) {
if (mCustomViewCallback != null) {
try {
mCustomViewCallback.onCustomViewHidden();
} catch (Exception e) {
Log.e(TAG, "Error hiding custom view", e);
}
mCustomViewCallback = null;
}
return;
}
Log.d(TAG, "onHideCustomView");
currentTab.setVisibility(View.VISIBLE);
try {
mCustomView.setKeepScreenOn(false);
} catch (SecurityException e) {
Log.e(TAG, "WebView is not allowed to keep the screen on");
}
setFullscreen(mPreferences.getHideStatusBarEnabled(), false);
if (mFullscreenContainer != null) {
ViewGroup parent = (ViewGroup) mFullscreenContainer.getParent();
if (parent != null) {
parent.removeView(mFullscreenContainer);
}
mFullscreenContainer.removeAllViews();
}
mFullscreenContainer = null;
mCustomView = null;
if (mVideoView != null) {
Log.d(TAG, "VideoView is being stopped");
mVideoView.stopPlayback();
mVideoView.setOnErrorListener(null);
mVideoView.setOnCompletionListener(null);
mVideoView = null;
}
if (mCustomViewCallback != null) {
try {
mCustomViewCallback.onCustomViewHidden();
} catch (Exception e) {
Log.e(TAG, "Error hiding custom view", e);
}
}
mCustomViewCallback = null;
setRequestedOrientation(mOriginalOrientation);
}
private class VideoCompletionListener implements MediaPlayer.OnCompletionListener,
MediaPlayer.OnErrorListener {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
return false;
}
@Override
public void onCompletion(MediaPlayer mp) {
onHideCustomView();
}
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
Log.d(TAG, "onWindowFocusChanged");
if (hasFocus) {
setFullscreen(mIsFullScreen, mIsImmersive);
}
}
@Override
public void onBackButtonPressed() {
final LightningView currentTab = mTabsManager.getCurrentTab();
if (currentTab != null) {
if (currentTab.canGoBack()) {
currentTab.goBack();
closeDrawers(null);
} else {
mPresenter.deleteTab(mTabsManager.positionOf(currentTab));
}
}
}
@Override
public void onForwardButtonPressed() {
final LightningView currentTab = mTabsManager.getCurrentTab();
if (currentTab != null) {
if (currentTab.canGoForward()) {
currentTab.goForward();
closeDrawers(null);
}
}
}
@Override
public void onHomeButtonPressed() {
final LightningView currentTab = mTabsManager.getCurrentTab();
if (currentTab != null) {
currentTab.loadHomepage();
closeDrawers(null);
}
}
/**
* This method sets whether or not the activity will display
* in full-screen mode (i.e. the ActionBar will be hidden) and
* whether or not immersive mode should be set. This is used to
* set both parameters correctly as during a full-screen video,
* both need to be set, but other-wise we leave it up to user
* preference.
*
* @param enabled true to enable full-screen, false otherwise
* @param immersive true to enable immersive mode, false otherwise
*/
private void setFullscreen(boolean enabled, boolean immersive) {
mIsFullScreen = enabled;
mIsImmersive = immersive;
Window window = getWindow();
View decor = window.getDecorView();
if (enabled) {
if (immersive) {
decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
} else {
decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
}
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
} else {
window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
}
}
/**
* This method handles the JavaScript callback to create a new tab.
* Basically this handles the event that JavaScript needs to create
* a popup.
*
* @param resultMsg the transport message used to send the URL to
* the newly created WebView.
*/
@Override
public synchronized void onCreateWindow(Message resultMsg) {
if (resultMsg == null) {
return;
}
if (newTab("", true)) {
LightningView newTab = mTabsManager.getTabAtPosition(mTabsManager.size() - 1);
if (newTab != null) {
final WebView webView = newTab.getWebView();
if (webView != null) {
WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
transport.setWebView(webView);
resultMsg.sendToTarget();
}
}
}
}
/**
* Closes the specified {@link LightningView}. This implements
* the JavaScript callback that asks the tab to close itself and
* is especially helpful when a page creates a redirect and does
* not need the tab to stay open any longer.
*
* @param view the LightningView to close, delete it.
*/
@Override
public void onCloseWindow(LightningView view) {
mPresenter.deleteTab(mTabsManager.positionOf(view));
}
/**
* Hide the ActionBar using an animation if we are in full-screen
* mode. This method also re-parents the ActionBar if its parent is
* incorrect so that the animation can happen correctly.
*/
@Override
public void hideActionBar() {
if (mFullScreen) {
if (mToolbarLayout == null || mBrowserFrame == null)
return;
final int height = mToolbarLayout.getHeight();
if (mToolbarLayout.getTranslationY() > -0.01f) {
Animation show = new Animation() {
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
float trans = interpolatedTime * height;
mToolbarLayout.setTranslationY(-trans);
setWebViewTranslation(height - trans);
}
};
show.setDuration(250);
show.setInterpolator(new BezierDecelerateInterpolator());
mBrowserFrame.startAnimation(show);
}
}
}
/**
* Display the ActionBar using an animation if we are in full-screen
* mode. This method also re-parents the ActionBar if its parent is
* incorrect so that the animation can happen correctly.
*/
@Override
public void showActionBar() {
if (mFullScreen) {
Log.d(TAG, "showActionBar");
if (mToolbarLayout == null)
return;
int height = mToolbarLayout.getHeight();
if (height == 0) {
mToolbarLayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
height = mToolbarLayout.getMeasuredHeight();
}
final int totalHeight = height;
if (mToolbarLayout.getTranslationY() < -(height - 0.01f)) {
Animation show = new Animation() {
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
float trans = interpolatedTime * totalHeight;
mToolbarLayout.setTranslationY(trans - totalHeight);
setWebViewTranslation(trans);
}
};
show.setDuration(250);
show.setInterpolator(new BezierDecelerateInterpolator());
mBrowserFrame.startAnimation(show);
}
}
}
@Override
public void handleBookmarksChange() {
final LightningView currentTab = mTabsManager.getCurrentTab();
if (currentTab != null && UrlUtils.isBookmarkUrl(currentTab.getUrl())) {
currentTab.loadBookmarkpage();
}
if (currentTab != null) {
mBookmarksView.handleUpdatedUrl(currentTab.getUrl());
}
}
@Override
public void handleDownloadDeleted() {
final LightningView currentTab = mTabsManager.getCurrentTab();
if (currentTab != null && UrlUtils.isDownloadsUrl(currentTab.getUrl())) {
currentTab.loadDownloadspage();
}
if (currentTab != null) {
mBookmarksView.handleUpdatedUrl(currentTab.getUrl());
}
}
@Override
public void handleBookmarkDeleted(@NonNull HistoryItem item) {
mBookmarksView.handleBookmarkDeleted(item);
handleBookmarksChange();
}
@Override
public void handleNewTab(@NonNull LightningDialogBuilder.NewTab newTabType, @NonNull String url) {
mDrawerLayout.closeDrawers();
switch (newTabType) {
case FOREGROUND:
newTab(url, true);
break;
case BACKGROUND:
newTab(url, false);
break;
case INCOGNITO:
Intent intent = new Intent(BrowserActivity.this, IncognitoActivity.class);
intent.setData(Uri.parse(url));
startActivity(intent);
overridePendingTransition(R.anim.slide_up_in, R.anim.fade_out_scale);
break;
}
}
/**
* Performs an action when the provided view is laid out.
*
* @param view the view to listen to for layouts.
* @param runnable the runnable to run when the view is
* laid out.
*/
private static void doOnLayout(@NonNull final View view, @NonNull final Runnable runnable) {
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
//noinspection deprecation
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
runnable.run();
}
});
}
/**
* 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
* pressing it stops the page from loading
*/
private void setIsLoading(boolean isLoading) {
if (!mSearch.hasFocus()) {
mIcon = isLoading ? mDeleteIcon : mRefreshIcon;
mSearch.setCompoundDrawables(null, null, mIcon, null);
}
}
/**
* handle presses on the refresh icon in the search bar, if the page is
* loading, stop the page, if it is done loading refresh the page.
* See setIsFinishedLoading and setIsLoading for displaying the correct icon
*/
private void refreshOrStop() {
final LightningView currentTab = mTabsManager.getCurrentTab();
if (currentTab != null) {
if (currentTab.getProgress() < 100) {
currentTab.stopLoading();
} else {
currentTab.reload();
}
}
}
/**
* Handle the click event for the views that are using
* this class as a click listener. This method should
* distinguish between the various views using their IDs.
*
* @param v the view that the user has clicked
*/
@Override
public void onClick(View v) {
final LightningView currentTab = mTabsManager.getCurrentTab();
if (currentTab == null) {
return;
}
switch (v.getId()) {
case R.id.arrow_button:
if (mSearch != null && mSearch.hasFocus()) {
currentTab.requestFocus();
} else if (mShowTabsInDrawer) {
mDrawerLayout.openDrawer(getTabDrawer());
} else {
currentTab.loadHomepage();
}
break;
case R.id.button_next:
currentTab.findNext();
break;
case R.id.button_back:
currentTab.findPrevious();
break;
case R.id.button_quit:
currentTab.clearFindMatches();
mSearchBar.setVisibility(View.GONE);
break;
case R.id.action_reading:
Intent read = new Intent(this, ReadingActivity.class);
read.putExtra(Constants.LOAD_READING_URL, currentTab.getUrl());
startActivity(read);
break;
case R.id.action_toggle_desktop:
currentTab.toggleDesktopUA(this);
currentTab.reload();
closeDrawers(null);
break;
}
}
/**
* This NetworkReceiver notifies each of the WebViews in the browser whether
* the network is currently connected or not. This is important because some
* JavaScript properties rely on the WebView knowing the current network state.
* It is used to help the browser be compliant with the HTML5 spec, sec. 5.7.7
*/
private final NetworkReceiver mNetworkReceiver = new NetworkReceiver() {
@Override
public void onConnectivityChange(boolean isConnected) {
Log.d(TAG, "Network Connected: " + isConnected);
mTabsManager.notifyConnectionStatus(isConnected);
}
};
/**
* Handle the callback that permissions requested have been granted or not.
* This method should act upon the results of the permissions request.
*
* @param requestCode the request code sent when initially making the request
* @param permissions the array of the permissions that was requested
* @param grantResults the results of the permissions requests that provides
* information on whether the request was granted or not
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
PermissionsManager.getInstance().notifyPermissionsChange(permissions, grantResults);
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}