From 969cab81e721ed390d0a9a6e5eef87dd430e9e4c Mon Sep 17 00:00:00 2001 From: Anthony Restaino Date: Sat, 18 Jul 2015 14:30:41 -0400 Subject: [PATCH] New Full-screen mode works better and doesn't hide the top of the WebView, +other * Now using material alertdialog on all versions * cleaned up some code * fixed lint issues and other inspection related problems * Attempted to fix bugs found --- Lightning-Browser.iml | 2 +- app/app.iml | 20 +- app/build.gradle | 5 +- .../browser/lightning/utils/ProxyUtils.java | 2 +- .../activity/AppCompatPreferenceActivity.java | 2 + .../lightning/activity/BrowserActivity.java | 5033 +++++++++-------- .../lightning/activity/IncognitoActivity.java | 109 +- .../lightning/activity/MainActivity.java | 104 +- .../lightning/activity/ReadingActivity.java | 390 +- .../lightning/activity/SettingsActivity.java | 2 +- .../lightning/activity/ThemableActivity.java | 4 +- .../activity/ThemableSettingsActivity.java | 11 +- .../controller/BrowserController.java | 4 +- .../lightning/database/BookmarkManager.java | 770 +-- .../lightning/database/HistoryDatabase.java | 4 +- .../lightning/download/DownloadHandler.java | 339 +- .../lightning/download/FetchUrlMimeType.java | 140 +- .../download/LightningDownloadListener.java | 5 +- .../fragment/AdvancedSettingsFragment.java | 2 +- .../fragment/BookmarkSettingsFragment.java | 60 +- .../fragment/DisplaySettingsFragment.java | 13 +- .../fragment/GeneralSettingsFragment.java | 14 +- .../fragment/PrivacySettingsFragment.java | 9 +- .../lightning/object/SearchAdapter.java | 699 ++- .../preference/PreferenceManager.java | 2 +- .../reading/ArticleTextExtractor.java | 15 +- .../browser/lightning/reading/Converter.java | 2 +- .../lightning/reading/HtmlFetcher.java | 2 +- .../browser/lightning/reading/SHelper.java | 9 +- .../browser/lightning/utils/IntentUtils.java | 2 +- .../browser/lightning/utils/ThemeUtils.java | 34 + .../acr/browser/lightning/utils/Utils.java | 37 +- .../browser/lightning/view/LightningView.java | 2195 +++---- .../main/res/layout/picker_manual_proxy.xml | 20 +- app/src/main/res/layout/toolbar_content.xml | 2 - .../main/res/layout/two_line_autocomplete.xml | 11 +- app/src/main/res/values-v21/styles.xml | 3 + app/src/main/res/values/styles.xml | 5 +- 38 files changed, 5113 insertions(+), 4969 deletions(-) create mode 100644 app/src/main/java/acr/browser/lightning/utils/ThemeUtils.java diff --git a/Lightning-Browser.iml b/Lightning-Browser.iml index 77ef0be..1f7c200 100644 --- a/Lightning-Browser.iml +++ b/Lightning-Browser.iml @@ -8,7 +8,7 @@ - + diff --git a/app/app.iml b/app/app.iml index d8dfc01..b9fc8ed 100644 --- a/app/app.iml +++ b/app/app.iml @@ -12,10 +12,12 @@ - + @@ -48,13 +50,13 @@ - - + + - + - - + + @@ -109,7 +111,7 @@ - + diff --git a/app/build.gradle b/app/build.gradle index 530ae30..9a2a86b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,8 +12,8 @@ android { buildTypes { debug { - minifyEnabled true - shrinkResources true + minifyEnabled false + shrinkResources false proguardFiles 'proguard-project.txt' } @@ -48,6 +48,7 @@ dependencies { compile 'com.android.support:appcompat-v7:22.2.0' compile 'org.jsoup:jsoup:1.8.1' + // Only Lightning Plus needs the proxy libraries lightningPlusCompile 'net.i2p.android:client:0.7' lightningPlusCompile(project(':libnetcipher')) } diff --git a/app/src/LightningPlus/java/acr/browser/lightning/utils/ProxyUtils.java b/app/src/LightningPlus/java/acr/browser/lightning/utils/ProxyUtils.java index 6d7be7f..31b2468 100644 --- a/app/src/LightningPlus/java/acr/browser/lightning/utils/ProxyUtils.java +++ b/app/src/LightningPlus/java/acr/browser/lightning/utils/ProxyUtils.java @@ -1,9 +1,9 @@ package acr.browser.lightning.utils; import android.app.Activity; -import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; +import android.support.v7.app.AlertDialog; import android.util.Log; import net.i2p.android.ui.I2PAndroidHelper; diff --git a/app/src/main/java/acr/browser/lightning/activity/AppCompatPreferenceActivity.java b/app/src/main/java/acr/browser/lightning/activity/AppCompatPreferenceActivity.java index 50b17c3..65b8783 100644 --- a/app/src/main/java/acr/browser/lightning/activity/AppCompatPreferenceActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/AppCompatPreferenceActivity.java @@ -4,6 +4,7 @@ import android.content.res.Configuration; import android.os.Bundle; import android.preference.PreferenceActivity; import android.support.annotation.LayoutRes; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatDelegate; @@ -44,6 +45,7 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity { getDelegate().setSupportActionBar(toolbar); } + @NonNull @Override public MenuInflater getMenuInflater() { return getDelegate().getMenuInflater(); diff --git a/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java b/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java index 8575519..9adf29e 100644 --- a/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java @@ -5,11 +5,11 @@ package acr.browser.lightning.activity; import android.animation.ArgbEvaluator; +import android.animation.LayoutTransition; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.SuppressLint; import android.app.Activity; -import android.app.AlertDialog; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; @@ -36,11 +36,13 @@ import android.os.Handler; import android.os.Message; import android.provider.Browser; import android.provider.MediaStore; +import android.support.annotation.NonNull; import android.support.v4.view.GravityCompat; import android.support.v4.view.ViewCompat; 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; @@ -62,7 +64,6 @@ import android.view.Window; import android.view.WindowManager; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; -import android.view.animation.AnimationUtils; import android.view.animation.DecelerateInterpolator; import android.view.animation.Transformation; import android.view.inputmethod.EditorInfo; @@ -129,2492 +130,2544 @@ import acr.browser.lightning.view.LightningView; public class BrowserActivity extends ThemableActivity implements BrowserController, OnClickListener { - // Layout - private DrawerLayout mDrawerLayout; - private FrameLayout mBrowserFrame; - private FullscreenHolder mFullscreenContainer; - private ListView mDrawerListLeft, mDrawerListRight; - private LinearLayout mDrawerLeft, mDrawerRight, mUiLayout, mToolbarLayout; - private RelativeLayout mSearchBar; - - // List - private final List mWebViews = new ArrayList<>(); - private List mBookmarkList; - private LightningView mCurrentView; - - private AnimatedProgressBar mProgressBar; - private AutoCompleteTextView mSearch; - private ImageView mArrowImage; - private VideoView mVideoView; - private View mCustomView, mVideoProgressView; - - // Adapter - private BookmarkViewAdapter mBookmarkAdapter; - private LightningViewAdapter mTitleAdapter; - private SearchAdapter mSearchAdapter; - - // Callback - private ClickHandler mClickHandler; - private CustomViewCallback mCustomViewCallback; - private ValueCallback mUploadMessage; - private ValueCallback mFilePathCallback; - - // Context - private Activity mActivity; - - // Primatives - private boolean mSystemBrowser = false, mIsNewIntent = false, mFullScreen, mColorMode, - mDarkTheme; - private int mOriginalOrientation, mBackgroundColor, mIdGenerator; - private String mSearchText, mUntitledTitle, mHomepage, mCameraPhotoPath; - - // Storage - private HistoryDatabase mHistoryDatabase; - private BookmarkManager mBookmarkManager; - private PreferenceManager mPreferences; - - // Image - private Bitmap mDefaultVideoPoster, mWebpageBitmap; - private final ColorDrawable mBackground = new ColorDrawable(); - private Drawable mDeleteIcon, mRefreshIcon, mCopyIcon, mIcon; - private DrawerArrowDrawable mArrowDrawable; - - // Proxy - private ProxyUtils mProxyUtils; - - // Constant - private static final int API = android.os.Build.VERSION.SDK_INT; - 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); - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - initialize(); - } - - @SuppressLint("NewApi") - @SuppressWarnings("deprecation") - private synchronized void initialize() { - setContentView(R.layout.activity_main); - Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - ActionBar actionBar = getSupportActionBar(); - - mPreferences = PreferenceManager.getInstance(); - //TODO make sure dark theme flag gets set correctly - mDarkTheme = mPreferences.getUseTheme() != 0 || isIncognito(); - mActivity = this; - mWebViews.clear(); - - mClickHandler = new ClickHandler(this); - mBrowserFrame = (FrameLayout) findViewById(R.id.content_frame); - mToolbarLayout = (LinearLayout) findViewById(R.id.toolbar_layout); - // initialize background ColorDrawable - mBackground.setColor(((ColorDrawable) mToolbarLayout.getBackground()).getColor()); - - mUiLayout = (LinearLayout) findViewById(R.id.ui_layout); - mProgressBar = (AnimatedProgressBar) findViewById(R.id.progress_view); - RelativeLayout newTab = (RelativeLayout) findViewById(R.id.new_tab_button); - mDrawerLeft = (LinearLayout) findViewById(R.id.left_drawer); - // Drawer stutters otherwise - mDrawerLeft.setLayerType(View.LAYER_TYPE_HARDWARE, null); - mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); - mDrawerListLeft = (ListView) findViewById(R.id.left_drawer_list); - mDrawerRight = (LinearLayout) findViewById(R.id.right_drawer); - mDrawerRight.setLayerType(View.LAYER_TYPE_HARDWARE, null); - mDrawerListRight = (ListView) findViewById(R.id.right_drawer_list); - - setNavigationDrawerWidth(); - mDrawerLayout.setDrawerListener(new DrawerLocker()); - - mWebpageBitmap = Utils.getWebpageBitmap(getResources(), mDarkTheme); - - mHomepage = mPreferences.getHomepage(); - - mTitleAdapter = new LightningViewAdapter(this, R.layout.tab_list_item, mWebViews); - mDrawerListLeft.setAdapter(mTitleAdapter); - mDrawerListLeft.setOnItemClickListener(new DrawerItemClickListener()); - mDrawerListLeft.setOnItemLongClickListener(new DrawerItemLongClickListener()); - - mDrawerListRight.setOnItemClickListener(new BookmarkItemClickListener()); - mDrawerListRight.setOnItemLongClickListener(new BookmarkItemLongClickListener()); - - mHistoryDatabase = HistoryDatabase.getInstance(getApplicationContext()); - - // set display options of the ActionBar - actionBar.setDisplayShowTitleEnabled(false); - actionBar.setDisplayShowHomeEnabled(false); - actionBar.setDisplayShowCustomEnabled(true); - actionBar.setCustomView(R.layout.toolbar_content); - - View v = actionBar.getCustomView(); - LayoutParams lp = v.getLayoutParams(); - lp.width = LayoutParams.MATCH_PARENT; - v.setLayoutParams(lp); - - mArrowDrawable = new DrawerArrowDrawable(this); - mArrowImage = (ImageView) actionBar.getCustomView().findViewById(R.id.arrow); - // Use hardware acceleration for the animation - mArrowImage.setLayerType(View.LAYER_TYPE_HARDWARE, null); - mArrowImage.setImageDrawable(mArrowDrawable); - LinearLayout arrowButton = (LinearLayout) actionBar.getCustomView().findViewById( - R.id.arrow_button); - arrowButton.setOnClickListener(this); - - mProxyUtils = ProxyUtils.getInstance(this); - - RelativeLayout back = (RelativeLayout) findViewById(R.id.action_back); - back.setOnClickListener(this); - - RelativeLayout forward = (RelativeLayout) findViewById(R.id.action_forward); - forward.setOnClickListener(this); - - // create the search EditText in the ToolBar - mSearch = (AutoCompleteTextView) actionBar.getCustomView().findViewById(R.id.search); - mUntitledTitle = getString(R.string.untitled); - mBackgroundColor = getResources().getColor(R.color.primary_color); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - mDeleteIcon = getResources().getDrawable(R.drawable.ic_action_delete); - mRefreshIcon = getResources().getDrawable(R.drawable.ic_action_refresh); - mCopyIcon = getResources().getDrawable(R.drawable.ic_action_copy); - } else { - Theme theme = getTheme(); - mDeleteIcon = getResources().getDrawable(R.drawable.ic_action_delete, theme); - mRefreshIcon = getResources().getDrawable(R.drawable.ic_action_refresh, theme); - mCopyIcon = getResources().getDrawable(R.drawable.ic_action_copy, theme); - } - - int iconBounds = Utils.convertDpToPixels(24); - mDeleteIcon.setBounds(0, 0, iconBounds, iconBounds); - mRefreshIcon.setBounds(0, 0, iconBounds, iconBounds); - mCopyIcon.setBounds(0, 0, iconBounds, iconBounds); - mIcon = mRefreshIcon; - SearchClass search = new SearchClass(); - mSearch.setCompoundDrawables(null, null, mRefreshIcon, null); - mSearch.setOnKeyListener(search.new KeyListener()); - mSearch.setOnFocusChangeListener(search.new FocusChangeListener()); - mSearch.setOnEditorActionListener(search.new EditorActionListener()); - mSearch.setOnTouchListener(search.new TouchListener()); - - mSystemBrowser = getSystemBrowser(); - Thread initialize = new Thread(new Runnable() { - - @Override - public void run() { - mBookmarkManager = BookmarkManager.getInstance(mActivity.getApplicationContext()); - mBookmarkList = mBookmarkManager.getBookmarks(true); - if (mBookmarkList.size() == 0 && mPreferences.getDefaultBookmarks()) { - for (String[] array : BookmarkManager.DEFAULT_BOOKMARKS) { - HistoryItem bookmark = new HistoryItem(array[0], array[1]); - if (mBookmarkManager.addBookmark(bookmark)) { - mBookmarkList.add(bookmark); - } - } - Collections.sort(mBookmarkList, new SortIgnoreCase()); - mPreferences.setDefaultBookmarks(false); - } - mBookmarkAdapter = new BookmarkViewAdapter(mActivity, R.layout.bookmark_list_item, - mBookmarkList); - mDrawerListRight.setAdapter(mBookmarkAdapter); - initializeSearchSuggestions(mSearch); - } - - }); - initialize.run(); - - newTab.setOnClickListener(this); - newTab.setOnLongClickListener(new OnLongClickListener() { - - @Override - public boolean onLongClick(View v) { - String url = mPreferences.getSavedUrl(); - if (url != null) { - newTab(url, true); - Toast.makeText(mActivity, R.string.deleted_tab, Toast.LENGTH_SHORT).show(); - } - mPreferences.setSavedUrl(null); - return true; - } - - }); - - mDrawerLayout.setDrawerShadow(R.drawable.drawer_right_shadow, GravityCompat.END); - mDrawerLayout.setDrawerShadow(R.drawable.drawer_left_shadow, GravityCompat.START); - initializeTabs(); - - if (API <= Build.VERSION_CODES.JELLY_BEAN_MR2) { - WebIconDatabase.getInstance().open(getDir("icons", MODE_PRIVATE).getPath()); - } - - mProxyUtils.checkForProxy(this); - } - - private class SearchClass { - - public class KeyListener implements OnKeyListener { - - @Override - public boolean onKey(View arg0, int arg1, KeyEvent arg2) { - - switch (arg1) { - case KeyEvent.KEYCODE_ENTER: - InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(mSearch.getWindowToken(), 0); - searchTheWeb(mSearch.getText().toString()); - if (mCurrentView != null) { - mCurrentView.requestFocus(); - } - return true; - default: - break; - } - return false; - } - - } - - public class EditorActionListener implements OnEditorActionListener { - @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()); - if (mCurrentView != null) { - mCurrentView.requestFocus(); - } - return true; - } - return false; - } - } - - public class FocusChangeListener implements OnFocusChangeListener { - @Override - public void onFocusChange(View v, final boolean hasFocus) { - if (!hasFocus && mCurrentView != null) { - if (mCurrentView.getProgress() < 100) { - setIsLoading(); - } else { - setIsFinishedLoading(); - } - updateUrl(mCurrentView.getUrl(), true); - } else if (hasFocus) { - String url = mCurrentView.getUrl(); - if (url == null || url.startsWith(Constants.FILE)) { - mSearch.setText(""); - } else { - mSearch.setText(url); - } - ((AutoCompleteTextView) v).selectAll(); // Hack to make sure - // the text gets - // selected - mIcon = mCopyIcon; - mSearch.setCompoundDrawables(null, null, mCopyIcon, null); - } - final Animation anim = new Animation() { - - @Override - protected void applyTransformation(float interpolatedTime, Transformation t) { - if (!hasFocus) { - mArrowDrawable.setProgress(1.0f - interpolatedTime); - } else { - mArrowDrawable.setProgress(interpolatedTime); - } - } - - @Override - public boolean willChangeBounds() { - return true; - } - - }; - anim.setDuration(300); - anim.setInterpolator(new DecelerateInterpolator()); - anim.setAnimationListener(new AnimationListener() { - - @Override - public void onAnimationStart(Animation animation) { - } - - @Override - public void onAnimationEnd(Animation animation) { - if (!hasFocus) { - mArrowDrawable.setProgress(0.0f); - } else { - mArrowDrawable.setProgress(1.0f); - } - } - - @Override - public void onAnimationRepeat(Animation animation) { - } - - }); - new Handler().postDelayed(new Runnable() { - - @Override - public void run() { - mArrowImage.startAnimation(anim); - } - - }, 100); - - if (!hasFocus) { - InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(mSearch.getWindowToken(), 0); - } - } - } - - public class TouchListener implements OnTouchListener { - - @SuppressLint("ClickableViewAccessibility") - @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()) { - ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText("label", mSearch.getText() - .toString()); - clipboard.setPrimaryClip(clip); - Utils.showToast( - mActivity, - mActivity.getResources().getString( - R.string.message_text_copied)); - } else { - refreshOrStop(); - } - } - return true; - } - } - return false; - } - - } - } - - private class DrawerLocker implements DrawerListener { - - @Override - public void onDrawerClosed(View v) { - if (v == mDrawerRight) { - mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, mDrawerLeft); - } else { - mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, mDrawerRight); - } - } - - @Override - public void onDrawerOpened(View v) { - if (v == mDrawerRight) { - mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, mDrawerLeft); - } else { - mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, mDrawerRight); - } - } - - @Override - public void onDrawerSlide(View v, float arg) { - } - - @Override - public void onDrawerStateChanged(int arg) { - } - - } - - private boolean isTablet() { - return (getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE; - } - - private void setNavigationDrawerWidth() { - int width = getResources().getDisplayMetrics().widthPixels - Utils.convertDpToPixels(56); - int maxWidth; - if (isTablet()) { - maxWidth = Utils.convertDpToPixels(320); - } else { - maxWidth = Utils.convertDpToPixels(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(); - } - } - - /* - * Override this class - */ - public synchronized void initializeTabs() { - - } - - public void restoreOrNewTab() { - mIdGenerator = 0; - - String url = null; - if (getIntent() != null) { - url = getIntent().getDataString(); - if (url != null) { - if (url.startsWith(Constants.FILE)) { - Utils.showToast(this, getResources().getString(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.length() > 0) { - 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() { - if (mPreferences == null) { - mPreferences = PreferenceManager.getInstance(); - } - mFullScreen = mPreferences.getFullScreenEnabled(); - mColorMode = mPreferences.getColorModeEnabled(); - mColorMode &= !mDarkTheme; - if (!isIncognito() && !mColorMode && !mDarkTheme && mWebpageBitmap != null) { - changeToolbarBackground(mWebpageBitmap); - } else if (!isIncognito() && mCurrentView != null && !mDarkTheme - && mCurrentView.getFavicon() != null) { - changeToolbarBackground(mCurrentView.getFavicon()); - } - - if (mFullScreen && mBrowserFrame.findViewById(R.id.toolbar_layout) == null) { - mUiLayout.removeView(mToolbarLayout); - mBrowserFrame.addView(mToolbarLayout); - mToolbarLayout.bringToFront(); - } else if (mBrowserFrame.findViewById(R.id.toolbar_layout) != null) { - mBrowserFrame.removeView(mToolbarLayout); - mUiLayout.addView(mToolbarLayout, 0); - } - if (mPreferences.getHideStatusBarEnabled()) { - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); - } else { - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); - } - - switch (mPreferences.getSearchChoice()) { - case 0: - mSearchText = mPreferences.getSearchUrl(); - if (!mSearchText.startsWith(Constants.HTTP) - && !mSearchText.startsWith(Constants.HTTPS)) { - mSearchText = Constants.GOOGLE_SEARCH; - } - break; - case 1: - mSearchText = Constants.GOOGLE_SEARCH; - break; - case 2: - mSearchText = Constants.ASK_SEARCH; - break; - case 3: - mSearchText = Constants.BING_SEARCH; - break; - case 4: - mSearchText = Constants.YAHOO_SEARCH; - break; - case 5: - mSearchText = Constants.STARTPAGE_SEARCH; - break; - case 6: - mSearchText = Constants.STARTPAGE_MOBILE_SEARCH; - break; - case 7: - mSearchText = Constants.DUCK_SEARCH; - break; - case 8: - mSearchText = Constants.DUCK_LITE_SEARCH; - break; - case 9: - mSearchText = Constants.BAIDU_SEARCH; - break; - case 10: - mSearchText = Constants.YANDEX_SEARCH; - break; - } - - updateCookiePreference(); - mProxyUtils.updateProxySettings(this); - } - - /* - * Override this if class overrides BrowserActivity - */ - public void updateCookiePreference() { - - } - - @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 <= 16) - && (Build.MANUFACTURER.compareTo("LGE") == 0)) { - // Workaround for stupid LG devices that crash - return true; - } - return super.onKeyDown(keyCode, event); - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - if ((keyCode == KeyEvent.KEYCODE_MENU) && (Build.VERSION.SDK_INT <= 16) - && (Build.MANUFACTURER.compareTo("LGE") == 0)) { - // Workaround for stupid LG devices that crash - openOptionsMenu(); - return true; - } - return super.onKeyUp(keyCode, event); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - return super.onCreateOptionsMenu(menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - // Handle action buttons - switch (item.getItemId()) { - case android.R.id.home: - if (mDrawerLayout.isDrawerOpen(mDrawerRight)) { - mDrawerLayout.closeDrawer(mDrawerRight); - } - return true; - case R.id.action_back: - if (mCurrentView != null) { - if (mCurrentView.canGoBack()) { - mCurrentView.goBack(); - } - } - return true; - case R.id.action_forward: - if (mCurrentView != null) { - if (mCurrentView.canGoForward()) { - mCurrentView.goForward(); - } - } - return true; - case R.id.action_new_tab: - newTab(null, true); - return true; - case R.id.action_incognito: - startActivity(new Intent(this, IncognitoActivity.class)); - return true; - case R.id.action_share: - if (!mCurrentView.getUrl().startsWith(Constants.FILE)) { - Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND); - shareIntent.setType("text/plain"); - shareIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, - mCurrentView.getTitle()); - String shareMessage = mCurrentView.getUrl(); - shareIntent.putExtra(android.content.Intent.EXTRA_TEXT, shareMessage); - startActivity(Intent.createChooser(shareIntent, - getResources().getString(R.string.dialog_title_share))); - } - return true; - case R.id.action_bookmarks: - openBookmarks(); - return true; - case R.id.action_copy: - if (mCurrentView != null) { - if (!mCurrentView.getUrl().startsWith(Constants.FILE)) { - ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText("label", mCurrentView.getUrl()); - clipboard.setPrimaryClip(clip); - Utils.showToast(mActivity, - mActivity.getResources().getString(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_add_bookmark: - if (!mCurrentView.getUrl().startsWith(Constants.FILE)) { - HistoryItem bookmark = new HistoryItem(mCurrentView.getUrl(), - mCurrentView.getTitle()); - if (mBookmarkManager.addBookmark(bookmark)) { - mBookmarkList.add(bookmark); - Collections.sort(mBookmarkList, new SortIgnoreCase()); - notifyBookmarkDataSetChanged(); - mSearchAdapter.refreshBookmarks(); - } - } - return true; - case R.id.action_find: - findInPage(); - return true; - case R.id.action_reading_mode: - Intent read = new Intent(this, ReadingActivity.class); - read.putExtra(Constants.LOAD_READING_URL, mCurrentView.getUrl()); - startActivity(read); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - /** - * refreshes the underlying list of the Bookmark adapter since the bookmark - * adapter doesn't always change when notifyDataChanged gets called. - */ - private void notifyBookmarkDataSetChanged() { - mBookmarkAdapter.clear(); - mBookmarkAdapter.addAll(mBookmarkList); - mBookmarkAdapter.notifyDataSetChanged(); - } - - /** - * method that shows a dialog asking what string the user wishes to search - * for. It highlights the text entered. - */ - private void findInPage() { - final AlertDialog.Builder finder = new AlertDialog.Builder(mActivity); - finder.setTitle(getResources().getString(R.string.action_find)); - final EditText getHome = new EditText(this); - getHome.setHint(getResources().getString(R.string.search_hint)); - finder.setView(getHome); - finder.setPositiveButton(getResources().getString(R.string.search_hint), - new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - String query = getHome.getText().toString(); - if (query.length() > 0) - showSearchInterfaceBar(query); - } - }); - finder.show(); - } - - private void showSearchInterfaceBar(String text) { - if (mCurrentView != null) { - mCurrentView.find(text); - } - mSearchBar = (RelativeLayout) findViewById(R.id.search_bar); - 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); - } - - private void showCloseDialog(final int position) { - AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); - ArrayAdapter adapter = new ArrayAdapter<>(mActivity, - android.R.layout.simple_dropdown_item_1line); - adapter.add(mActivity.getString(R.string.close_tab)); - adapter.add(mActivity.getString(R.string.close_all_tabs)); - builder.setAdapter(adapter, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - switch (which) { - case 0: - deleteTab(position); - break; - case 1: - closeBrowser(); - break; - default: - break; - } - } - }); - builder.show(); - } - - /** - * The click listener for ListView in the navigation drawer - */ - private class DrawerItemClickListener implements ListView.OnItemClickListener { - - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - mIsNewIntent = false; - showTab(mWebViews.get(position)); - } - } - - /** - * long click listener for Navigation Drawer - */ - private class DrawerItemLongClickListener implements ListView.OnItemLongClickListener { - - @Override - public boolean onItemLongClick(AdapterView arg0, View arg1, final int position, long arg3) { - showCloseDialog(position); - return true; - } - } - - private class BookmarkItemClickListener implements ListView.OnItemClickListener { - - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - if (mCurrentView != null) { - mCurrentView.loadUrl(mBookmarkList.get(position).getUrl()); - } - // keep any jank from happening when the drawer is closed after the - // URL starts to load - final Handler handler = new Handler(); - handler.postDelayed(new Runnable() { - @Override - public void run() { - mDrawerLayout.closeDrawer(mDrawerRight); - } - }, 150); - } - } - - private class BookmarkItemLongClickListener implements ListView.OnItemLongClickListener { - - @Override - public boolean onItemLongClick(AdapterView arg0, View arg1, final int position, long arg3) { - - AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); - builder.setTitle(mActivity.getResources().getString(R.string.action_bookmarks)); - builder.setMessage(getResources().getString(R.string.dialog_bookmark)) - .setCancelable(true) - .setPositiveButton(getResources().getString(R.string.action_new_tab), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - newTab(mBookmarkList.get(position).getUrl(), false); - mDrawerLayout.closeDrawers(); - } - }) - .setNegativeButton(getResources().getString(R.string.action_delete), - new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - if (mBookmarkManager.deleteBookmark(mBookmarkList.get(position) - .getUrl())) { - mBookmarkList.remove(position); - notifyBookmarkDataSetChanged(); - mSearchAdapter.refreshBookmarks(); - openBookmarks(); - } - } - }) - .setNeutralButton(getResources().getString(R.string.action_edit), - new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - editBookmark(position); - } - }); - AlertDialog alert = builder.create(); - alert.show(); - return true; - } - } - - /** - * Takes in the id of which bookmark was selected and shows a dialog that - * allows the user to rename and change the url of the bookmark - * - * @param id - * which id in the list was chosen - */ - private synchronized void editBookmark(final int id) { - final AlertDialog.Builder homePicker = new AlertDialog.Builder(mActivity); - homePicker.setTitle(getResources().getString(R.string.title_edit_bookmark)); - final EditText getTitle = new EditText(mActivity); - getTitle.setHint(getResources().getString(R.string.hint_title)); - getTitle.setText(mBookmarkList.get(id).getTitle()); - getTitle.setSingleLine(); - final EditText getUrl = new EditText(mActivity); - getUrl.setHint(getResources().getString(R.string.hint_url)); - getUrl.setText(mBookmarkList.get(id).getUrl()); - getUrl.setSingleLine(); - LinearLayout layout = new LinearLayout(mActivity); - layout.setOrientation(LinearLayout.VERTICAL); - layout.addView(getTitle); - layout.addView(getUrl); - homePicker.setView(layout); - homePicker.setPositiveButton(getResources().getString(R.string.action_ok), - new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - mBookmarkList.get(id).setTitle(getTitle.getText().toString()); - mBookmarkList.get(id).setUrl(getUrl.getText().toString()); - mBookmarkManager.overwriteBookmarks(mBookmarkList); - Collections.sort(mBookmarkList, new SortIgnoreCase()); - notifyBookmarkDataSetChanged(); - if (mCurrentView != null) { - if (mCurrentView.getUrl().startsWith(Constants.FILE) - && mCurrentView.getUrl().endsWith("bookmarks.html")) { - openBookmarkPage(mCurrentView.getWebView()); - } - } - } - }); - homePicker.show(); - } - - /** - * displays the WebView contained in the LightningView Also handles the - * removal of previous views - * - * @param view - * the LightningView to show - */ - private synchronized void showTab(LightningView view) { - // Set the background color so the color mode color doesn't show through - mBrowserFrame.setBackgroundColor(mBackgroundColor); - if (view == null) { - return; - } - mBrowserFrame.removeAllViews(); - if (mCurrentView != null) { - mCurrentView.setForegroundTab(false); - mCurrentView.onPause(); - } - mCurrentView = view; - mCurrentView.setForegroundTab(true); - if (mCurrentView.getWebView() != null) { - updateUrl(mCurrentView.getUrl(), true); - updateProgress(mCurrentView.getProgress()); - } else { - updateUrl("", true); - updateProgress(0); - } - - mBrowserFrame.addView(mCurrentView.getWebView(), MATCH_PARENT); - // Remove browser frame background to reduce overdraw - mBrowserFrame.setBackgroundColor(0); - mCurrentView.requestFocus(); - mCurrentView.onResume(); - - // Use a delayed handler to make the transition smooth - // otherwise it will get caught up with the showTab code - // and cause a janky motion - final Handler handler = new Handler(); - handler.postDelayed(new Runnable() { - @Override - public void run() { - mDrawerLayout.closeDrawers(); - } - }, 150); - } - - /** - * creates a new tab with the passed in URL if it isn't null - */ - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - } - - public void handleNewIntent(Intent intent) { - - String url = null; - if (intent != null) { - url = intent.getDataString(); - } - int num = 0; - if (intent != null && intent.getExtras() != null) { - num = intent.getExtras().getInt(getPackageName() + ".Origin"); - } - if (num == 1) { - mCurrentView.loadUrl(url); - } else if (url != null) { - if (url.startsWith(Constants.FILE)) { - Utils.showToast(this, getResources().getString(R.string.message_blocked_local)); - url = null; - } - newTab(url, true); - mIsNewIntent = true; - } - } - - @Override - public void closeEmptyTab() { - if (mCurrentView != null && mCurrentView.getWebView().copyBackForwardList().getSize() == 0) { - closeCurrentTab(); - } - } - - private void closeCurrentTab() { - // don't delete the tab because the browser will close and mess stuff up - } - - @SuppressWarnings("deprecation") - @Override - public void onTrimMemory(int level) { - if (level > TRIM_MEMORY_MODERATE && Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - Log.d(Constants.TAG, "Low Memory, Free Memory"); - for (LightningView view : mWebViews) { - view.getWebView().freeMemory(); - } - } - } - - protected synchronized boolean newTab(String url, boolean show) { - // Limit number of tabs for limited version of app - if (!Constants.FULL_VERSION && mWebViews.size() >= 10) { - Utils.showToast(this, this.getString(R.string.max_tabs)); - return false; - } - mIsNewIntent = false; - LightningView startingTab = new LightningView(mActivity, url, mDarkTheme); - if (mIdGenerator == 0) { - startingTab.resumeTimers(); - } - mIdGenerator++; - mWebViews.add(startingTab); - - mTitleAdapter.notifyDataSetChanged(); - if (show) { - mDrawerListLeft.setItemChecked(mWebViews.size() - 1, true); - showTab(startingTab); - } - return true; - } - - private synchronized void deleteTab(int position) { - if (position >= mWebViews.size()) { - return; - } - - int current = mDrawerListLeft.getCheckedItemPosition(); - LightningView reference = mWebViews.get(position); - if (reference == null) { - return; - } - if (reference.getUrl() != null && !reference.getUrl().startsWith(Constants.FILE) - && !isIncognito()) { - mPreferences.setSavedUrl(reference.getUrl()); - } - boolean isShown = reference.isShown(); - if (isShown) { - mBrowserFrame.setBackgroundColor(mBackgroundColor); - } - if (current > position) { - mWebViews.remove(position); - mDrawerListLeft.setItemChecked(current - 1, true); - reference.onDestroy(); - } else if (mWebViews.size() > position + 1) { - if (current == position) { - showTab(mWebViews.get(position + 1)); - mWebViews.remove(position); - mDrawerListLeft.setItemChecked(position, true); - } else { - mWebViews.remove(position); - } - - reference.onDestroy(); - } else if (mWebViews.size() > 1) { - if (current == position) { - showTab(mWebViews.get(position - 1)); - mWebViews.remove(position); - mDrawerListLeft.setItemChecked(position - 1, true); - } else { - mWebViews.remove(position); - } - - reference.onDestroy(); - } else { - if (mCurrentView.getUrl() == null || mCurrentView.getUrl().startsWith(Constants.FILE) - || mCurrentView.getUrl().equals(mHomepage)) { - closeActivity(); - } else { - mWebViews.remove(position); - if (mPreferences.getClearCacheExit() && mCurrentView != null && !isIncognito()) { - mCurrentView.clearCache(true); - Log.d(Constants.TAG, "Cache Cleared"); - - } - if (mPreferences.getClearHistoryExitEnabled() && !isIncognito()) { - clearHistory(); - Log.d(Constants.TAG, "History Cleared"); - - } - if (mPreferences.getClearCookiesExitEnabled() && !isIncognito()) { - clearCookies(); - Log.d(Constants.TAG, "Cookies Cleared"); - - } - reference.pauseTimers(); - reference.onDestroy(); - mCurrentView = null; - mTitleAdapter.notifyDataSetChanged(); - finish(); - - } - } - mTitleAdapter.notifyDataSetChanged(); - - if (mIsNewIntent && isShown) { - mIsNewIntent = false; - closeActivity(); - } - - Log.d(Constants.TAG, "deleted tab"); - } - - @Override - public boolean onKeyLongPress(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK) { - showCloseDialog(mDrawerListLeft.getCheckedItemPosition()); - } - return true; - } - - private void closeBrowser() { - mBrowserFrame.setBackgroundColor(mBackgroundColor); - if (mPreferences.getClearCacheExit() && mCurrentView != null && !isIncognito()) { - mCurrentView.clearCache(true); - Log.d(Constants.TAG, "Cache Cleared"); - - } - if (mPreferences.getClearHistoryExitEnabled() && !isIncognito()) { - clearHistory(); - Log.d(Constants.TAG, "History Cleared"); - - } - if (mPreferences.getClearCookiesExitEnabled() && !isIncognito()) { - clearCookies(); - Log.d(Constants.TAG, "Cookies Cleared"); - - } - mCurrentView = null; - for (int n = 0; n < mWebViews.size(); n++) { - if (mWebViews.get(n) != null) { - mWebViews.get(n).onDestroy(); - } - } - mWebViews.clear(); - mTitleAdapter.notifyDataSetChanged(); - finish(); - } - - @SuppressWarnings("deprecation") - private void clearHistory() { - this.deleteDatabase(HistoryDatabase.DATABASE_NAME); - WebViewDatabase m = WebViewDatabase.getInstance(this); - m.clearFormData(); - m.clearHttpAuthUsernamePassword(); - if (API < 18) { - m.clearUsernamePassword(); - WebIconDatabase.getInstance().removeAllIcons(); - } - if (mSystemBrowser) { - try { - Browser.clearHistory(getContentResolver()); - } catch (NullPointerException ignored) { - } - } - Utils.trimCache(this); - } - - @SuppressLint("NewApi") - @SuppressWarnings("deprecation") - private void clearCookies() { - // TODO Break out web storage deletion into its own option/action - // TODO clear web storage for all sites that are visited in Incognito mode - WebStorage storage = WebStorage.getInstance(); - storage.deleteAllData(); - CookieManager c = CookieManager.getInstance(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - c.removeAllCookies(null); - } else { - CookieSyncManager.createInstance(this); - c.removeAllCookie(); - } - } - - @Override - public void onBackPressed() { - if (mDrawerLayout.isDrawerOpen(mDrawerLeft)) { - mDrawerLayout.closeDrawer(mDrawerLeft); - } else if (mDrawerLayout.isDrawerOpen(mDrawerRight)) { - mDrawerLayout.closeDrawer(mDrawerRight); - } else { - if (mCurrentView != null) { - Log.d(Constants.TAG, "onBackPressed"); - if (mSearch.hasFocus()) { - mCurrentView.requestFocus(); - } else if (mCurrentView.canGoBack()) { - if (!mCurrentView.isShown()) { - onHideCustomView(); - } else { - mCurrentView.goBack(); - } - } else { - deleteTab(mDrawerListLeft.getCheckedItemPosition()); - } - } else { - Log.e(Constants.TAG, "This shouldn't happen ever"); - super.onBackPressed(); - } - } - } - - @Override - protected void onPause() { - super.onPause(); - Log.d(Constants.TAG, "onPause"); - if (mCurrentView != null) { - mCurrentView.pauseTimers(); - mCurrentView.onPause(); - } - } - - public void saveOpenTabs() { - if (mPreferences.getRestoreLostTabsEnabled()) { - String s = ""; - for (int n = 0; n < mWebViews.size(); n++) { - if (mWebViews.get(n).getUrl() != null) { - s = s + mWebViews.get(n).getUrl() + "|$|SEPARATOR|$|"; - } - } - mPreferences.setMemoryUrl(s); - } - } - - @Override - protected void onStop() { - super.onStop(); - mProxyUtils.onStop(); - } - - @Override - protected void onDestroy() { - Log.d(Constants.TAG, "onDestroy"); - if (mHistoryDatabase != null) { - mHistoryDatabase.close(); - } - super.onDestroy(); - } - - @Override - protected void onStart() { - super.onStart(); - mProxyUtils.onStart(this); - } - - @Override - protected void onResume() { - super.onResume(); - Log.d(Constants.TAG, "onResume"); - if (mSearchAdapter != null) { - mSearchAdapter.refreshPreferences(); - mSearchAdapter.refreshBookmarks(); - } - if (mCurrentView != null) { - mCurrentView.resumeTimers(); - mCurrentView.onResume(); - - mHistoryDatabase = HistoryDatabase.getInstance(getApplicationContext()); - mBookmarkList = mBookmarkManager.getBookmarks(true); - notifyBookmarkDataSetChanged(); - } - initializePreferences(); - if (mWebViews != null) { - for (int n = 0; n < mWebViews.size(); n++) { - if (mWebViews.get(n) != null) { - mWebViews.get(n).initializePreferences(this); - } else { - mWebViews.remove(n); - } - } - } - - supportInvalidateOptionsMenu(); - } - - /** - * 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(String query) { - if (query.equals("")) { - return; - } - String SEARCH = mSearchText; - query = query.trim(); - mCurrentView.stopLoading(); - - if (query.startsWith("www.")) { - query = Constants.HTTP + query; - } else if (query.startsWith("ftp.")) { - query = "ftp://" + query; - } - - boolean containsPeriod = query.contains("."); - boolean isIPAddress = (TextUtils.isDigitsOnly(query.replace(".", "")) - && (query.replace(".", "").length() >= 4) && query.contains(".")); - boolean aboutScheme = query.contains("about:"); - boolean validURL = (query.startsWith("ftp://") || query.startsWith(Constants.HTTP) - || query.startsWith(Constants.FILE) || query.startsWith(Constants.HTTPS)) - || isIPAddress; - boolean isSearch = ((query.contains(" ") || !containsPeriod) && !aboutScheme); - - if (isIPAddress - && (!query.startsWith(Constants.HTTP) || !query.startsWith(Constants.HTTPS))) { - query = Constants.HTTP + query; - } - - if (isSearch) { - try { - query = URLEncoder.encode(query, "UTF-8"); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } - mCurrentView.loadUrl(SEARCH + query); - } else if (!validURL) { - mCurrentView.loadUrl(Constants.HTTP + query); - } else { - mCurrentView.loadUrl(query); - } - } - - public class LightningViewAdapter extends ArrayAdapter { - - final Context context; - ColorMatrix colorMatrix; - ColorMatrixColorFilter filter; - Paint paint; - final int layoutResourceId; - List data = null; - final CloseTabListener mExitListener; - - public LightningViewAdapter(Context context, int layoutResourceId, List data) { - super(context, layoutResourceId, data); - this.layoutResourceId = layoutResourceId; - this.context = context; - this.data = data; - this.mExitListener = new CloseTabListener(); - } - - @Override - public View getView(final int position, View convertView, ViewGroup parent) { - View row = convertView; - LightningViewHolder holder; - if (row == null) { - LayoutInflater inflater = ((Activity) context).getLayoutInflater(); - row = inflater.inflate(layoutResourceId, parent, false); - - holder = new LightningViewHolder(); - holder.txtTitle = (TextView) row.findViewById(R.id.textTab); - holder.favicon = (ImageView) row.findViewById(R.id.faviconTab); - holder.exit = (ImageView) row.findViewById(R.id.deleteButton); - holder.exit.setTag(position); - row.setTag(holder); - } else { - holder = (LightningViewHolder) row.getTag(); - } - - holder.exit.setTag(position); - holder.exit.setOnClickListener(mExitListener); - - ViewCompat.jumpDrawablesToCurrentState(holder.exit); - - LightningView web = data.get(position); - holder.txtTitle.setText(web.getTitle()); - if (web.isForegroundTab()) { - holder.txtTitle.setTextAppearance(context, R.style.boldText); - } else { - holder.txtTitle.setTextAppearance(context, R.style.normalText); - } - - Bitmap favicon = web.getFavicon(); - if (web.isForegroundTab()) { - - holder.favicon.setImageBitmap(favicon); - if (!isIncognito() && mColorMode) - changeToolbarBackground(favicon); - } else { - Bitmap grayscaleBitmap = Bitmap.createBitmap(favicon.getWidth(), - favicon.getHeight(), Bitmap.Config.ARGB_8888); - - Canvas c = new Canvas(grayscaleBitmap); - if (colorMatrix == null || filter == null || paint == null) { - paint = new Paint(); - colorMatrix = new ColorMatrix(); - colorMatrix.setSaturation(0); - filter = new ColorMatrixColorFilter(colorMatrix); - paint.setColorFilter(filter); - } - - c.drawBitmap(favicon, 0, 0, paint); - holder.favicon.setImageBitmap(grayscaleBitmap); - } - return row; - } - - class LightningViewHolder { - TextView txtTitle; - ImageView favicon; - ImageView exit; - } - } - - private class CloseTabListener implements OnClickListener { - - @Override - public void onClick(View v) { - deleteTab((int) v.getTag()); - } - - } - - private void changeToolbarBackground(Bitmap favicon) { - 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(mActivity.getResources() - .getColor(R.color.primary_color)); - - int finalColor; // Lighten up the dark color if it is - // too dark - if (Utils.isColorTooDark(color)) { - finalColor = Utils.mixTwoColors( - mActivity.getResources().getColor(R.color.primary_color), - color, 0.25f); - } else { - finalColor = color; - } - - ValueAnimator anim = ValueAnimator.ofObject(new ArgbEvaluator(), - mBackground.getColor(), finalColor); - - anim.addUpdateListener(new AnimatorUpdateListener() { - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - int color = (Integer) animation.getAnimatedValue(); - mBackground.setColor(color); - getWindow().setBackgroundDrawable(mBackground); - mToolbarLayout.setBackgroundColor(color); - } - - }); - - anim.setDuration(300); - anim.start(); - - } - }); - } - - public class BookmarkViewAdapter extends ArrayAdapter { - - final Context context; - List data = null; - final int layoutResourceId; - - public BookmarkViewAdapter(Context context, int layoutResourceId, List data) { - super(context, layoutResourceId, data); - this.layoutResourceId = layoutResourceId; - this.context = context; - this.data = data; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View row = convertView; - BookmarkViewHolder holder; - - if (row == null) { - LayoutInflater inflater = ((Activity) context).getLayoutInflater(); - row = inflater.inflate(layoutResourceId, parent, false); - - holder = new BookmarkViewHolder(); - holder.txtTitle = (TextView) row.findViewById(R.id.textBookmark); - holder.favicon = (ImageView) row.findViewById(R.id.faviconBookmark); - row.setTag(holder); - } else { - holder = (BookmarkViewHolder) row.getTag(); - } - - HistoryItem web = data.get(position); - holder.txtTitle.setText(web.getTitle()); - holder.favicon.setImageBitmap(mWebpageBitmap); - if (web.getBitmap() == null) { - getImage(holder.favicon, web); - } else { - holder.favicon.setImageBitmap(web.getBitmap()); - } - return row; - } - - class BookmarkViewHolder { - TextView txtTitle; - ImageView favicon; - } - } - - private void getImage(ImageView image, HistoryItem web) { - new DownloadImageTask(image, web).execute(web.getUrl()); - } - - private class DownloadImageTask extends AsyncTask { - - final ImageView bmImage; - final HistoryItem mWeb; - - public DownloadImageTask(ImageView bmImage, HistoryItem web) { - this.bmImage = bmImage; - this.mWeb = web; - } - - protected Bitmap doInBackground(String... urls) { - String url = urls[0]; - Bitmap mIcon = null; - // unique path for each url that is bookmarked. - String hash = String.valueOf(Utils.getDomainName(url).hashCode()); - File image = new File(mActivity.getCacheDir(), hash + ".png"); - String urldisplay; - try { - urldisplay = Utils.getProtocol(url) + getDomainName(url) + "/favicon.ico"; - } catch (URISyntaxException e) { - e.printStackTrace(); - urldisplay = "https://www.google.com/s2/favicons?domain_url=" + url; - } - // checks to see if the image exists - if (!image.exists()) { - try { - // if not, download it... - URL urlDownload = new URL(urldisplay); - HttpURLConnection connection = (HttpURLConnection) urlDownload.openConnection(); - connection.setDoInput(true); - connection.connect(); - InputStream in = connection.getInputStream(); - - if (in != null) { - mIcon = BitmapFactory.decodeStream(in); - } - // ...and cache it - if (mIcon != null) { - FileOutputStream fos = new FileOutputStream(image); - mIcon.compress(Bitmap.CompressFormat.PNG, 100, fos); - fos.flush(); - fos.close(); - Log.d(Constants.TAG, "Downloaded: " + urldisplay); - } - - } catch (Exception e) { - e.printStackTrace(); - } - } else { - // if it exists, retrieve it from the cache - mIcon = BitmapFactory.decodeFile(image.getPath()); - } - if (mIcon == null) { - try { - // if not, download it... - URL urlDownload = new URL("https://www.google.com/s2/favicons?domain_url=" - + url); - HttpURLConnection connection = (HttpURLConnection) urlDownload.openConnection(); - connection.setDoInput(true); - connection.connect(); - InputStream in = connection.getInputStream(); - - if (in != null) { - mIcon = BitmapFactory.decodeStream(in); - } - // ...and cache it - if (mIcon != null) { - FileOutputStream fos = new FileOutputStream(image); - mIcon.compress(Bitmap.CompressFormat.PNG, 100, fos); - fos.flush(); - fos.close(); - } - - } catch (Exception e) { - e.printStackTrace(); - } - } - if (mIcon == null) { - return mWebpageBitmap; - } else { - return mIcon; - } - } - - protected void onPostExecute(Bitmap result) { - Bitmap fav = Utils.padFavicon(result); - bmImage.setImageBitmap(fav); - mWeb.setBitmap(fav); - notifyBookmarkDataSetChanged(); - } - } - - private static String getDomainName(String url) throws URISyntaxException { - URI uri = new URI(url); - String domain = uri.getHost(); - if (domain == null) { - return url; - } - return domain.startsWith("www.") ? domain.substring(4) : domain; - } - - @Override - public void updateUrl(String url, boolean shortUrl) { - if (url == null || mSearch == null || mSearch.hasFocus()) { - return; - } - if (shortUrl && !url.startsWith(Constants.FILE)) { - switch (mPreferences.getUrlBoxContentChoice()) { - case 0: // Default, show only the domain - url = url.replaceFirst(Constants.HTTP, ""); - url = Utils.getDomainName(url); - mSearch.setText(url); - break; - case 1: // URL, show the entire URL - mSearch.setText(url); - break; - case 2: // Title, show the page's title - if (mCurrentView != null && !mCurrentView.getTitle().isEmpty()) { - mSearch.setText(mCurrentView.getTitle()); - } else { - mSearch.setText(mUntitledTitle); - } - break; - } - - } else { - if (url.startsWith(Constants.FILE)) { - url = ""; - } - mSearch.setText(url); - } - } - - @Override - public void updateProgress(int n) { - if (n >= 100) { - setIsFinishedLoading(); - } else { - setIsLoading(); - } - mProgressBar.setProgress(n); - } - - @Override - public void updateHistory(final String title, final String url) { - - } - - public void addItemToHistory(final String title, final String url) { - Runnable update = new Runnable() { - @Override - public void run() { - if (isSystemBrowserAvailable() && mPreferences.getSyncHistoryEnabled()) { - try { - Browser.updateVisitedHistory(getContentResolver(), url, true); - } catch (NullPointerException ignored) { - } - } - try { - if (mHistoryDatabase == null) { - mHistoryDatabase = HistoryDatabase.getInstance(mActivity); - } - mHistoryDatabase.visitHistoryItem(url, title); - } catch (IllegalStateException e) { - Log.e(Constants.TAG, "IllegalStateException in updateHistory"); - } catch (NullPointerException e) { - Log.e(Constants.TAG, "NullPointerException in updateHistory"); - } catch (SQLiteException e) { - Log.e(Constants.TAG, "SQLiteException in updateHistory"); - } - } - }; - if (url != null && !url.startsWith(Constants.FILE)) { - new Thread(update).start(); - } - } - - private boolean isSystemBrowserAvailable() { - return mSystemBrowser; - } - - private boolean getSystemBrowser() { - Cursor c = null; - String[] columns = new String[] { "url", "title" }; - boolean browserFlag; - try { - - Uri bookmarks = Browser.BOOKMARKS_URI; - c = getContentResolver().query(bookmarks, columns, null, null, null); - } catch (SQLiteException | IllegalStateException | NullPointerException e) { - e.printStackTrace(); - } - - if (c != null) { - Log.d("Browser", "System Browser Available"); - browserFlag = true; - } else { - Log.e("Browser", "System Browser Unavailable"); - browserFlag = false; - } - if (c != null) { - c.close(); - } - mPreferences.setSystemBrowserPresent(browserFlag); - return browserFlag; - } - - /** - * method to generate search suggestions for the AutoCompleteTextView from - * previously searched URLs - */ - private void initializeSearchSuggestions(final AutoCompleteTextView getUrl) { - - getUrl.setThreshold(1); - getUrl.setDropDownWidth(-1); - getUrl.setDropDownAnchor(R.id.toolbar_layout); - getUrl.setOnItemClickListener(new OnItemClickListener() { - - @Override - public void onItemClick(AdapterView arg0, View arg1, int arg2, long arg3) { - try { - String url; - url = ((TextView) arg1.findViewById(R.id.url)).getText().toString(); - if (url.startsWith(mActivity.getString(R.string.suggestion))) { - url = ((TextView) arg1.findViewById(R.id.title)).getText().toString(); - } else { - getUrl.setText(url); - } - searchTheWeb(url); - InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(getUrl.getWindowToken(), 0); - if (mCurrentView != null) { - mCurrentView.requestFocus(); - } - } catch (NullPointerException e) { - Log.e("Browser Error: ", "NullPointerException on item click"); - } - } - - }); - - getUrl.setSelectAllOnFocus(true); - mSearchAdapter = new SearchAdapter(mActivity, mDarkTheme, isIncognito()); - getUrl.setAdapter(mSearchAdapter); - } - - @Override - public boolean isIncognito() { - return false; - } - - @Override - public boolean isProxyReady() { - return mProxyUtils.isProxyReady(this); - } - - /** - * function that opens the HTML history page in the browser - */ - private void openHistory() { - // use a thread so that history retrieval doesn't block the UI - Thread history = new Thread(new Runnable() { - - @Override - public void run() { - mCurrentView.loadUrl(HistoryPage.getHistoryPage(mActivity)); - mSearch.setText(""); - } - - }); - history.run(); - } - - /** - * helper function that opens the bookmark drawer - */ - private void openBookmarks() { - if (mDrawerLayout.isDrawerOpen(mDrawerLeft)) { - mDrawerLayout.closeDrawers(); - } - mDrawerLayout.openDrawer(mDrawerRight); - } - - public void closeDrawers() { - mDrawerLayout.closeDrawers(); - } - - @Override - /** - * open the HTML bookmarks page, parameter view is the WebView that should show the page - */ - public void openBookmarkPage(WebView view) { - StringBuilder bookmarkBuilder = new StringBuilder(); - bookmarkBuilder.append(BookmarkPage.HEADING); - Iterator iter = mBookmarkList.iterator(); - HistoryItem helper; - while (iter.hasNext()) { - helper = iter.next(); - bookmarkBuilder.append(BookmarkPage.PART1); - bookmarkBuilder.append(helper.getUrl()); - bookmarkBuilder.append(BookmarkPage.PART2); - bookmarkBuilder.append(helper.getUrl()); - bookmarkBuilder.append(BookmarkPage.PART3); - bookmarkBuilder.append(helper.getTitle()); - bookmarkBuilder.append(BookmarkPage.PART4); - } - bookmarkBuilder.append(BookmarkPage.END); - File bookmarkWebPage = new File(mActivity.getFilesDir(), BookmarkPage.FILENAME); - try { - FileWriter bookWriter = new FileWriter(bookmarkWebPage, false); - bookWriter.write(bookmarkBuilder.toString()); - bookWriter.close(); - } catch (IOException e) { - e.printStackTrace(); - } - - view.loadUrl(Constants.FILE + bookmarkWebPage); - } - - @Override - public void update() { - mTitleAdapter.notifyDataSetChanged(); - } - - @Override - /** - * opens a file chooser - * param ValueCallback is the message from the WebView indicating a file chooser - * should be opened - */ - public void openFileChooser(ValueCallback 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); - } - - @Override - /** - * used to allow uploading into the browser - */ - 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 filePathCallback) { - if (mFilePathCallback != null) { - mFilePathCallback.onReceiveValue(null); - } - mFilePathCallback = filePathCallback; - - Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - if (takePictureIntent.resolveActivity(getActivity().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(Constants.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("image/*"); - - 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); - - mActivity.startActivityForResult(chooserIntent, 1); - } - - @Override - /** - * handles long presses for the browser, tries to get the - * url of the item that was clicked and sends it (it can be null) - * to the click handler that does cool stuff with it - */ - public void onLongPress() { - if (mClickHandler == null) { - mClickHandler = new ClickHandler(mActivity); - } - Message click = mClickHandler.obtainMessage(); - if (click != null) { - click.setTarget(mClickHandler); - mCurrentView.getWebView().requestFocusNodeHref(click); - } - } - - @Override - public void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback) { - if (view == null) { - return; - } - if (mCustomView != null && callback != null) { - callback.onCustomViewHidden(); - return; - } - try { - view.setKeepScreenOn(true); - } catch (SecurityException e) { - Log.e(Constants.TAG, "WebView is not allowed to keep the screen on"); - } - mOriginalOrientation = getRequestedOrientation(); - FrameLayout decor = (FrameLayout) getWindow().getDecorView(); - mFullscreenContainer = new FullscreenHolder(this); - mCustomView = view; - mFullscreenContainer.addView(mCustomView, COVER_SCREEN_PARAMS); - decor.addView(mFullscreenContainer, COVER_SCREEN_PARAMS); - setFullscreen(true); - mCurrentView.setVisibility(View.GONE); - if (view instanceof FrameLayout) { - if (((FrameLayout) view).getFocusedChild() instanceof VideoView) { - mVideoView = (VideoView) ((FrameLayout) view).getFocusedChild(); - mVideoView.setOnErrorListener(new VideoCompletionListener()); - mVideoView.setOnCompletionListener(new VideoCompletionListener()); - } - } - mCustomViewCallback = callback; - } - - @Override - public void onHideCustomView() { - if (mCustomView == null || mCustomViewCallback == null || mCurrentView == null) { - return; - } - Log.d(Constants.TAG, "onHideCustomView"); - mCurrentView.setVisibility(View.VISIBLE); - try { - mCustomView.setKeepScreenOn(false); - } catch (SecurityException e) { - Log.e(Constants.TAG, "WebView is not allowed to keep the screen on"); - } - setFullscreen(mPreferences.getHideStatusBarEnabled()); - FrameLayout decor = (FrameLayout) getWindow().getDecorView(); - if (decor != null) { - decor.removeView(mFullscreenContainer); - } - - if (API < 19) { - try { - mCustomViewCallback.onCustomViewHidden(); - } catch (Throwable ignored) { - - } - } - mFullscreenContainer = null; - mCustomView = null; - if (mVideoView != null) { - mVideoView.setOnErrorListener(null); - mVideoView.setOnCompletionListener(null); - mVideoView = 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(); - } - - } - - /** - * turns on fullscreen mode in the app - * - * @param enabled - * whether to enable fullscreen or not - */ - private void setFullscreen(boolean enabled) { - Window win = getWindow(); - WindowManager.LayoutParams winParams = win.getAttributes(); - final int bits = WindowManager.LayoutParams.FLAG_FULLSCREEN; - if (enabled) { - winParams.flags |= bits; - } else { - winParams.flags &= ~bits; - if (mCustomView != null) { - mCustomView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); - } else { - mBrowserFrame.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); - } - } - win.setAttributes(winParams); - } - - /** - * a class extending FramLayout used to display fullscreen videos - */ - static class FullscreenHolder extends FrameLayout { - - public FullscreenHolder(Context ctx) { - super(ctx); - setBackgroundColor(ctx.getResources().getColor(android.R.color.black)); - } - - @SuppressLint("ClickableViewAccessibility") - @Override - public boolean onTouchEvent(MotionEvent evt) { - return true; - } - - } - - @Override - /** - * a stupid method that returns the bitmap image to display in place of - * a loading video - */ - public Bitmap getDefaultVideoPoster() { - if (mDefaultVideoPoster == null) { - mDefaultVideoPoster = BitmapFactory.decodeResource(getResources(), - android.R.drawable.ic_media_play); - } - return mDefaultVideoPoster; - } - - @SuppressLint("InflateParams") - @Override - /** - * dumb method that returns the loading progress for a video - */ - public View getVideoLoadingProgressView() { - if (mVideoProgressView == null) { - LayoutInflater inflater = LayoutInflater.from(this); - mVideoProgressView = inflater.inflate(R.layout.video_loading_progress, null); - } - return mVideoProgressView; - } - - @Override - /** - * handles javascript requests to create a new window in the browser - */ - public void onCreateWindow(boolean isUserGesture, Message resultMsg) { - if (resultMsg == null) { - return; - } - if (newTab("", true)) { - WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj; - transport.setWebView(mCurrentView.getWebView()); - resultMsg.sendToTarget(); - } - } - - @Override - /** - * returns the Activity instance for this activity, - * very helpful when creating things in other classes... I think - */ - public Activity getActivity() { - return mActivity; - } - - /** - * it hides the action bar, seriously what else were you expecting - */ - @Override - public void hideActionBar() { - if (mFullScreen) { - if (mBrowserFrame.findViewById(R.id.toolbar_layout) == null) { - mUiLayout.removeView(mToolbarLayout); - mBrowserFrame.addView(mToolbarLayout); - mToolbarLayout.bringToFront(); - Log.d(Constants.TAG, "Move view to browser frame"); - } - if (mToolbarLayout.getVisibility() != View.GONE) { - - Animation hide = AnimationUtils.loadAnimation(mActivity, R.anim.slide_up); - hide.setAnimationListener(new AnimationListener() { - - @Override - public void onAnimationStart(Animation animation) { - } - - @Override - public void onAnimationEnd(Animation animation) { - mToolbarLayout.setVisibility(View.GONE); - } - - @Override - public void onAnimationRepeat(Animation animation) { - } - - }); - mToolbarLayout.startAnimation(hide); - Log.d(Constants.TAG, "Hide"); - } - } - } - - @Override - public void toggleActionBar() { - if (mFullScreen) { - if (mToolbarLayout.getVisibility() != View.VISIBLE) { - showActionBar(); - } else { - hideActionBar(); - } - } - } - - @Override - /** - * obviously it shows the action bar if it's hidden - */ - public void showActionBar() { - if (mFullScreen) { - if (mBrowserFrame.findViewById(R.id.toolbar_layout) == null) { - mUiLayout.removeView(mToolbarLayout); - mBrowserFrame.addView(mToolbarLayout); - mToolbarLayout.bringToFront(); - Log.d(Constants.TAG, "Move view to browser frame"); - } - if (mToolbarLayout.getVisibility() != View.VISIBLE) { - Animation show = AnimationUtils.loadAnimation(mActivity, R.anim.slide_down); - show.setAnimationListener(new AnimationListener() { - - @Override - public void onAnimationStart(Animation animation) { - } - - @Override - public void onAnimationEnd(Animation animation) { - mToolbarLayout.setVisibility(View.VISIBLE); - } - - @Override - public void onAnimationRepeat(Animation animation) { - } - - }); - mToolbarLayout.startAnimation(show); - Log.d(Constants.TAG, "Show"); - } - - } - - } - - @Override - /** - * handles a long click on the page, parameter String url - * is the url that should have been obtained from the WebView touch node - * thingy, if it is null, this method tries to deal with it and find a workaround - */ - public void longClickPage(final String url) { - HitTestResult result = null; - if (mCurrentView.getWebView() != null) { - result = mCurrentView.getWebView().getHitTestResult(); - } - if (url != null) { - if (result != null) { - if (result.getType() == HitTestResult.SRC_IMAGE_ANCHOR_TYPE - || result.getType() == HitTestResult.IMAGE_TYPE) { - 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: - mCurrentView.loadUrl(url); - break; - - case DialogInterface.BUTTON_NEUTRAL: - if (API > 8) { - Utils.downloadFile(mActivity, url, - mCurrentView.getUserAgent(), "attachment", false); - } - break; - } - } - }; - - AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); // dialog - builder.setTitle(url.replace(Constants.HTTP, "")) - .setMessage(getResources().getString(R.string.dialog_image)) - .setPositiveButton(getResources().getString(R.string.action_new_tab), - dialogClickListener) - .setNegativeButton(getResources().getString(R.string.action_open), - dialogClickListener) - .setNeutralButton(getResources().getString(R.string.action_download), - dialogClickListener).show(); - - } else { - 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: - mCurrentView.loadUrl(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) - .setMessage(getResources().getString(R.string.dialog_link)) - .setPositiveButton(getResources().getString(R.string.action_new_tab), - dialogClickListener) - .setNegativeButton(getResources().getString(R.string.action_open), - dialogClickListener) - .setNeutralButton(getResources().getString(R.string.action_copy), - dialogClickListener).show(); - } - } else { - 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: - mCurrentView.loadUrl(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) - .setMessage(getResources().getString(R.string.dialog_link)) - .setPositiveButton(getResources().getString(R.string.action_new_tab), - dialogClickListener) - .setNegativeButton(getResources().getString(R.string.action_open), - dialogClickListener) - .setNeutralButton(getResources().getString(R.string.action_copy), - dialogClickListener).show(); - } - } else if (result != null) { - if (result.getExtra() != null) { - final String newUrl = result.getExtra(); - if (result.getType() == HitTestResult.SRC_IMAGE_ANCHOR_TYPE - || result.getType() == HitTestResult.IMAGE_TYPE) { - DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - switch (which) { - case DialogInterface.BUTTON_POSITIVE: - newTab(newUrl, false); - break; - - case DialogInterface.BUTTON_NEGATIVE: - mCurrentView.loadUrl(newUrl); - break; - - case DialogInterface.BUTTON_NEUTRAL: - if (API > 8) { - Utils.downloadFile(mActivity, newUrl, - mCurrentView.getUserAgent(), "attachment", false); - } - break; - } - } - }; - - AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); // dialog - builder.setTitle(newUrl.replace(Constants.HTTP, "")) - .setMessage(getResources().getString(R.string.dialog_image)) - .setPositiveButton(getResources().getString(R.string.action_new_tab), - dialogClickListener) - .setNegativeButton(getResources().getString(R.string.action_open), - dialogClickListener) - .setNeutralButton(getResources().getString(R.string.action_download), - dialogClickListener).show(); - - } else { - DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - switch (which) { - case DialogInterface.BUTTON_POSITIVE: - newTab(newUrl, false); - break; - - case DialogInterface.BUTTON_NEGATIVE: - mCurrentView.loadUrl(newUrl); - break; - - case DialogInterface.BUTTON_NEUTRAL: - ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText("label", newUrl); - clipboard.setPrimaryClip(clip); - - break; - } - } - }; - - AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); // dialog - builder.setTitle(newUrl) - .setMessage(getResources().getString(R.string.dialog_link)) - .setPositiveButton(getResources().getString(R.string.action_new_tab), - dialogClickListener) - .setNegativeButton(getResources().getString(R.string.action_open), - dialogClickListener) - .setNeutralButton(getResources().getString(R.string.action_copy), - dialogClickListener).show(); - } - - } - - } - - } - - /** - * 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() { - if (!mSearch.hasFocus()) { - mIcon = mDeleteIcon; - mSearch.setCompoundDrawables(null, null, mDeleteIcon, null); - } - } - - /** - * This tells the search bar that the page is finished loading and it should - * display the refresh icon - */ - private void setIsFinishedLoading() { - if (!mSearch.hasFocus()) { - mIcon = mRefreshIcon; - mSearch.setCompoundDrawables(null, null, mRefreshIcon, 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() { - if (mCurrentView != null) { - if (mCurrentView.getProgress() < 100) { - mCurrentView.stopLoading(); - } else { - mCurrentView.reload(); - } - } - } - - // Override this, use finish() for Incognito, moveTaskToBack for Main - public void closeActivity() { - finish(); - } - - private class SortIgnoreCase implements Comparator { - - public int compare(HistoryItem o1, HistoryItem o2) { - return o1.getTitle().toLowerCase(Locale.getDefault()) - .compareTo(o2.getTitle().toLowerCase(Locale.getDefault())); - } - - } - - @Override - public int getMenu() { - return R.menu.main; - } - - @Override - public void onClick(View v) { - switch (v.getId()) { - case R.id.action_back: - if (mCurrentView != null) { - if (mCurrentView.canGoBack()) { - mCurrentView.goBack(); - } else { - deleteTab(mDrawerListLeft.getCheckedItemPosition()); - } - } - break; - case R.id.action_forward: - if (mCurrentView != null) { - if (mCurrentView.canGoForward()) { - mCurrentView.goForward(); - } - } - break; - case R.id.arrow_button: - if (mSearch != null && mSearch.hasFocus()) { - mCurrentView.requestFocus(); - } else { - mDrawerLayout.openDrawer(mDrawerLeft); - } - break; - case R.id.new_tab_button: - newTab(null, true); - break; - case R.id.button_next: - mCurrentView.getWebView().findNext(false); - break; - case R.id.button_back: - mCurrentView.getWebView().findNext(true); - break; - case R.id.button_quit: - mCurrentView.getWebView().clearMatches(); - mSearchBar.setVisibility(View.GONE); - break; - } - } + // Layout + private DrawerLayout mDrawerLayout; + private FrameLayout mBrowserFrame; + private FullscreenHolder mFullscreenContainer; + private ListView mDrawerListLeft, mDrawerListRight; + private LinearLayout mDrawerLeft, mDrawerRight, mUiLayout, mToolbarLayout; + private RelativeLayout mSearchBar; + + // List + private final List mWebViewList = new ArrayList<>(); + private List mBookmarkList; + private LightningView mCurrentView; + private WebView mWebView; + + private AnimatedProgressBar mProgressBar; + private AutoCompleteTextView mSearch; + private ImageView mArrowImage; + private VideoView mVideoView; + private View mCustomView, mVideoProgressView; + + // Adapter + private BookmarkViewAdapter mBookmarkAdapter; + private LightningViewAdapter mTitleAdapter; + private SearchAdapter mSearchAdapter; + + // Callback + private ClickHandler mClickHandler; + private CustomViewCallback mCustomViewCallback; + private ValueCallback mUploadMessage; + private ValueCallback mFilePathCallback; + + // Context + private Activity mActivity; + + // Primatives + private boolean mSystemBrowser = false, mIsNewIntent = false, mFullScreen, mColorMode, + mDarkTheme; + private int mOriginalOrientation, mBackgroundColor, mIdGenerator; + private String mSearchText, mUntitledTitle, mHomepage, mCameraPhotoPath; + + // Storage + private HistoryDatabase mHistoryDatabase; + private BookmarkManager mBookmarkManager; + private PreferenceManager mPreferences; + + // Image + private Bitmap mDefaultVideoPoster, mWebpageBitmap; + private final ColorDrawable mBackground = new ColorDrawable(); + private Drawable mDeleteIcon, mRefreshIcon, mCopyIcon, mIcon; + private DrawerArrowDrawable mArrowDrawable; + + // Proxy + private ProxyUtils mProxyUtils; + + // Constant + private static final int API = android.os.Build.VERSION.SDK_INT; + 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); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + initialize(); + } + + @SuppressLint("NewApi") + @SuppressWarnings("deprecation") + private synchronized void initialize() { + setContentView(R.layout.activity_main); + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + ActionBar actionBar = getSupportActionBar(); + + mPreferences = PreferenceManager.getInstance(); + //TODO make sure dark theme flag gets set correctly + mDarkTheme = mPreferences.getUseTheme() != 0 || isIncognito(); + mActivity = this; + mWebViewList.clear(); + + mClickHandler = new ClickHandler(this); + mBrowserFrame = (FrameLayout) findViewById(R.id.content_frame); + mToolbarLayout = (LinearLayout) findViewById(R.id.toolbar_layout); + // initialize background ColorDrawable + mBackground.setColor(((ColorDrawable) mToolbarLayout.getBackground()).getColor()); + + mUiLayout = (LinearLayout) findViewById(R.id.ui_layout); + mProgressBar = (AnimatedProgressBar) findViewById(R.id.progress_view); + RelativeLayout newTab = (RelativeLayout) findViewById(R.id.new_tab_button); + mDrawerLeft = (LinearLayout) findViewById(R.id.left_drawer); + // Drawer stutters otherwise + mDrawerLeft.setLayerType(View.LAYER_TYPE_HARDWARE, null); + mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); + mDrawerListLeft = (ListView) findViewById(R.id.left_drawer_list); + mDrawerRight = (LinearLayout) findViewById(R.id.right_drawer); + mDrawerRight.setLayerType(View.LAYER_TYPE_HARDWARE, null); + mDrawerListRight = (ListView) findViewById(R.id.right_drawer_list); + + setNavigationDrawerWidth(); + mDrawerLayout.setDrawerListener(new DrawerLocker()); + + mWebpageBitmap = Utils.getWebpageBitmap(getResources(), mDarkTheme); + + mHomepage = mPreferences.getHomepage(); + + mTitleAdapter = new LightningViewAdapter(this, R.layout.tab_list_item, mWebViewList); + mDrawerListLeft.setAdapter(mTitleAdapter); + mDrawerListLeft.setOnItemClickListener(new DrawerItemClickListener()); + mDrawerListLeft.setOnItemLongClickListener(new DrawerItemLongClickListener()); + + mDrawerListRight.setOnItemClickListener(new BookmarkItemClickListener()); + mDrawerListRight.setOnItemLongClickListener(new BookmarkItemLongClickListener()); + + mHistoryDatabase = HistoryDatabase.getInstance(getApplicationContext()); + + if (actionBar == null) + return; + + // set display options of the ActionBar + actionBar.setDisplayShowTitleEnabled(false); + actionBar.setDisplayShowHomeEnabled(false); + actionBar.setDisplayShowCustomEnabled(true); + actionBar.setCustomView(R.layout.toolbar_content); + + View v = actionBar.getCustomView(); + LayoutParams lp = v.getLayoutParams(); + lp.width = LayoutParams.MATCH_PARENT; + v.setLayoutParams(lp); + + mArrowDrawable = new DrawerArrowDrawable(this); + mArrowImage = (ImageView) actionBar.getCustomView().findViewById(R.id.arrow); + // Use hardware acceleration for the animation + mArrowImage.setLayerType(View.LAYER_TYPE_HARDWARE, null); + mArrowImage.setImageDrawable(mArrowDrawable); + LinearLayout arrowButton = (LinearLayout) actionBar.getCustomView().findViewById( + R.id.arrow_button); + arrowButton.setOnClickListener(this); + + mProxyUtils = ProxyUtils.getInstance(this); + + RelativeLayout back = (RelativeLayout) findViewById(R.id.action_back); + back.setOnClickListener(this); + + RelativeLayout forward = (RelativeLayout) findViewById(R.id.action_forward); + forward.setOnClickListener(this); + + // create the search EditText in the ToolBar + mSearch = (AutoCompleteTextView) actionBar.getCustomView().findViewById(R.id.search); + mUntitledTitle = getString(R.string.untitled); + mBackgroundColor = getResources().getColor(R.color.primary_color); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + mDeleteIcon = getResources().getDrawable(R.drawable.ic_action_delete); + mRefreshIcon = getResources().getDrawable(R.drawable.ic_action_refresh); + mCopyIcon = getResources().getDrawable(R.drawable.ic_action_copy); + } else { + Theme theme = getTheme(); + mDeleteIcon = getResources().getDrawable(R.drawable.ic_action_delete, theme); + mRefreshIcon = getResources().getDrawable(R.drawable.ic_action_refresh, theme); + mCopyIcon = getResources().getDrawable(R.drawable.ic_action_copy, theme); + } + + int iconBounds = Utils.convertDpToPixels(24); + mDeleteIcon.setBounds(0, 0, iconBounds, iconBounds); + mRefreshIcon.setBounds(0, 0, iconBounds, iconBounds); + mCopyIcon.setBounds(0, 0, iconBounds, iconBounds); + mIcon = mRefreshIcon; + SearchClass search = new SearchClass(); + mSearch.setCompoundDrawables(null, null, mRefreshIcon, null); + mSearch.setOnKeyListener(search.new KeyListener()); + mSearch.setOnFocusChangeListener(search.new FocusChangeListener()); + mSearch.setOnEditorActionListener(search.new EditorActionListener()); + mSearch.setOnTouchListener(search.new TouchListener()); + + mSystemBrowser = getSystemBrowser(); + Thread initialize = new Thread(new Runnable() { + + @Override + public void run() { + mBookmarkManager = BookmarkManager.getInstance(mActivity.getApplicationContext()); + mBookmarkList = mBookmarkManager.getBookmarks(true); + if (mBookmarkList.size() == 0 && mPreferences.getDefaultBookmarks()) { + for (String[] array : BookmarkManager.DEFAULT_BOOKMARKS) { + HistoryItem bookmark = new HistoryItem(array[0], array[1]); + if (mBookmarkManager.addBookmark(bookmark)) { + mBookmarkList.add(bookmark); + } + } + Collections.sort(mBookmarkList, new SortIgnoreCase()); + mPreferences.setDefaultBookmarks(false); + } + mBookmarkAdapter = new BookmarkViewAdapter(mActivity, R.layout.bookmark_list_item, + mBookmarkList); + mDrawerListRight.setAdapter(mBookmarkAdapter); + initializeSearchSuggestions(mSearch); + } + + }); + initialize.run(); + + newTab.setOnClickListener(this); + newTab.setOnLongClickListener(new OnLongClickListener() { + + @Override + public boolean onLongClick(View v) { + String url = mPreferences.getSavedUrl(); + if (url != null) { + newTab(url, true); + Toast.makeText(mActivity, R.string.deleted_tab, Toast.LENGTH_SHORT).show(); + } + mPreferences.setSavedUrl(null); + return true; + } + + }); + + mDrawerLayout.setDrawerShadow(R.drawable.drawer_right_shadow, GravityCompat.END); + mDrawerLayout.setDrawerShadow(R.drawable.drawer_left_shadow, GravityCompat.START); + initializeTabs(); + + if (API <= Build.VERSION_CODES.JELLY_BEAN_MR2) { + WebIconDatabase.getInstance().open(getDir("icons", MODE_PRIVATE).getPath()); + } + + mProxyUtils.checkForProxy(this); + } + + private class SearchClass { + + public class KeyListener implements OnKeyListener { + + @Override + public boolean onKey(View arg0, int arg1, KeyEvent arg2) { + + switch (arg1) { + case KeyEvent.KEYCODE_ENTER: + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(mSearch.getWindowToken(), 0); + searchTheWeb(mSearch.getText().toString()); + if (mCurrentView != null) { + mCurrentView.requestFocus(); + } + return true; + default: + break; + } + return false; + } + + } + + public class EditorActionListener implements OnEditorActionListener { + @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()); + if (mCurrentView != null) { + mCurrentView.requestFocus(); + } + return true; + } + return false; + } + } + + public class FocusChangeListener implements OnFocusChangeListener { + @Override + public void onFocusChange(View v, final boolean hasFocus) { + if (!hasFocus && mCurrentView != null) { + if (mCurrentView.getProgress() < 100) { + setIsLoading(); + } else { + setIsFinishedLoading(); + } + updateUrl(mCurrentView.getUrl(), true); + } else if (hasFocus) { + String url = mCurrentView.getUrl(); + if (url.startsWith(Constants.FILE)) { + mSearch.setText(""); + } else { + mSearch.setText(url); + } + ((AutoCompleteTextView) v).selectAll(); // Hack to make sure + // the text gets + // selected + mIcon = mCopyIcon; + mSearch.setCompoundDrawables(null, null, mCopyIcon, null); + } + final Animation anim = new Animation() { + + @Override + protected void applyTransformation(float interpolatedTime, Transformation t) { + if (!hasFocus) { + mArrowDrawable.setProgress(1.0f - interpolatedTime); + } else { + mArrowDrawable.setProgress(interpolatedTime); + } + } + + @Override + public boolean willChangeBounds() { + return true; + } + + }; + anim.setDuration(300); + anim.setInterpolator(new DecelerateInterpolator()); + anim.setAnimationListener(new AnimationListener() { + + @Override + public void onAnimationStart(Animation animation) { + } + + @Override + public void onAnimationEnd(Animation animation) { + if (!hasFocus) { + mArrowDrawable.setProgress(0.0f); + } else { + mArrowDrawable.setProgress(1.0f); + } + } + + @Override + public void onAnimationRepeat(Animation animation) { + } + + }); + new Handler().postDelayed(new Runnable() { + + @Override + public void run() { + mArrowImage.startAnimation(anim); + } + + }, 100); + + if (!hasFocus) { + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(mSearch.getWindowToken(), 0); + } + } + } + + public class TouchListener implements OnTouchListener { + + @SuppressLint("ClickableViewAccessibility") + @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()) { + ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("label", mSearch.getText() + .toString()); + clipboard.setPrimaryClip(clip); + Utils.showToast( + mActivity, + mActivity.getResources().getString( + R.string.message_text_copied)); + } else { + refreshOrStop(); + } + } + return true; + } + } + return false; + } + + } + } + + private class DrawerLocker implements DrawerListener { + + @Override + public void onDrawerClosed(View v) { + if (v == mDrawerRight) { + mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, mDrawerLeft); + } else { + mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, mDrawerRight); + } + } + + @Override + public void onDrawerOpened(View v) { + if (v == mDrawerRight) { + mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, mDrawerLeft); + } else { + mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, mDrawerRight); + } + } + + @Override + public void onDrawerSlide(View v, float arg) { + } + + @Override + public void onDrawerStateChanged(int arg) { + } + + } + + private boolean isTablet() { + return (getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE; + } + + private void setNavigationDrawerWidth() { + int width = getResources().getDisplayMetrics().widthPixels - Utils.convertDpToPixels(56); + int maxWidth; + if (isTablet()) { + maxWidth = Utils.convertDpToPixels(320); + } else { + maxWidth = Utils.convertDpToPixels(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(); + } + } + + /* + * Override this class + */ + synchronized void initializeTabs() { + + } + + void restoreOrNewTab() { + mIdGenerator = 0; + + String url = null; + if (getIntent() != null) { + url = getIntent().getDataString(); + if (url != null) { + if (url.startsWith(Constants.FILE)) { + Utils.showToast(this, getResources().getString(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.length() > 0) { + 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() { + if (mPreferences == null) { + mPreferences = PreferenceManager.getInstance(); + } + mFullScreen = mPreferences.getFullScreenEnabled(); + mColorMode = mPreferences.getColorModeEnabled(); + mColorMode &= !mDarkTheme; + if (!isIncognito() && !mColorMode && !mDarkTheme && mWebpageBitmap != null) { + changeToolbarBackground(mWebpageBitmap); + } else if (!isIncognito() && mCurrentView != null && !mDarkTheme + && mCurrentView.getFavicon() != null) { + changeToolbarBackground(mCurrentView.getFavicon()); + } + + if (mFullScreen) { + mToolbarLayout.setTranslationY(0); + int height = mToolbarLayout.getHeight(); + if (height == 0) { + mToolbarLayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); + height = mToolbarLayout.getMeasuredHeight(); + } + if (mWebView != null) + mWebView.setTranslationY(height); + mBrowserFrame.setLayoutTransition(null); + if (mBrowserFrame.findViewById(R.id.toolbar_layout) == null) { + mUiLayout.removeView(mToolbarLayout); + mBrowserFrame.addView(mToolbarLayout); + mToolbarLayout.bringToFront(); + } + } else { + mToolbarLayout.setTranslationY(0); + if (mBrowserFrame.findViewById(R.id.toolbar_layout) != null) { + mBrowserFrame.removeView(mToolbarLayout); + mUiLayout.addView(mToolbarLayout, 0); + } + mBrowserFrame.setLayoutTransition(new LayoutTransition()); + if (mWebView != null) + mWebView.setTranslationY(0); + } + if (mPreferences.getHideStatusBarEnabled()) { + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + } else { + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + + switch (mPreferences.getSearchChoice()) { + case 0: + mSearchText = mPreferences.getSearchUrl(); + if (!mSearchText.startsWith(Constants.HTTP) + && !mSearchText.startsWith(Constants.HTTPS)) { + mSearchText = Constants.GOOGLE_SEARCH; + } + break; + case 1: + mSearchText = Constants.GOOGLE_SEARCH; + break; + case 2: + mSearchText = Constants.ASK_SEARCH; + break; + case 3: + mSearchText = Constants.BING_SEARCH; + break; + case 4: + mSearchText = Constants.YAHOO_SEARCH; + break; + case 5: + mSearchText = Constants.STARTPAGE_SEARCH; + break; + case 6: + mSearchText = Constants.STARTPAGE_MOBILE_SEARCH; + break; + case 7: + mSearchText = Constants.DUCK_SEARCH; + break; + case 8: + mSearchText = Constants.DUCK_LITE_SEARCH; + break; + case 9: + mSearchText = Constants.BAIDU_SEARCH; + break; + case 10: + mSearchText = Constants.YANDEX_SEARCH; + break; + } + + updateCookiePreference(); + mProxyUtils.updateProxySettings(this); + } + + /* + * Override this if class overrides BrowserActivity + */ + void updateCookiePreference() { + + } + + @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 <= 16) + && (Build.MANUFACTURER.compareTo("LGE") == 0)) { + // Workaround for stupid LG devices that crash + return true; + } + return super.onKeyDown(keyCode, event); + } + + @Override + public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) { + if ((keyCode == KeyEvent.KEYCODE_MENU) && (Build.VERSION.SDK_INT <= 16) + && (Build.MANUFACTURER.compareTo("LGE") == 0)) { + // Workaround for stupid LG devices that crash + openOptionsMenu(); + return true; + } + return super.onKeyUp(keyCode, event); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action buttons + switch (item.getItemId()) { + case android.R.id.home: + if (mDrawerLayout.isDrawerOpen(mDrawerRight)) { + mDrawerLayout.closeDrawer(mDrawerRight); + } + return true; + case R.id.action_back: + if (mCurrentView != null && mCurrentView.canGoBack()) { + mCurrentView.goBack(); + } + return true; + case R.id.action_forward: + if (mCurrentView != null && mCurrentView.canGoForward()) { + mCurrentView.goForward(); + } + return true; + case R.id.action_new_tab: + newTab(null, true); + return true; + case R.id.action_incognito: + startActivity(new Intent(this, IncognitoActivity.class)); + return true; + case R.id.action_share: + if (mCurrentView != null && !mCurrentView.getUrl().startsWith(Constants.FILE)) { + Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND); + shareIntent.setType("text/plain"); + shareIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, + mCurrentView.getTitle()); + String shareMessage = mCurrentView.getUrl(); + shareIntent.putExtra(android.content.Intent.EXTRA_TEXT, shareMessage); + startActivity(Intent.createChooser(shareIntent, + getResources().getString(R.string.dialog_title_share))); + } + return true; + case R.id.action_bookmarks: + openBookmarks(); + return true; + case R.id.action_copy: + if (mCurrentView != null && !mCurrentView.getUrl().startsWith(Constants.FILE)) { + ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("label", mCurrentView.getUrl()); + clipboard.setPrimaryClip(clip); + Utils.showToast(mActivity, + mActivity.getResources().getString(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_add_bookmark: + if (mCurrentView != null && !mCurrentView.getUrl().startsWith(Constants.FILE)) { + HistoryItem bookmark = new HistoryItem(mCurrentView.getUrl(), + mCurrentView.getTitle()); + if (mBookmarkManager.addBookmark(bookmark) && mBookmarkList != null) { + mBookmarkList.add(bookmark); + Collections.sort(mBookmarkList, new SortIgnoreCase()); + notifyBookmarkDataSetChanged(); + mSearchAdapter.refreshBookmarks(); + } + } + return true; + case R.id.action_find: + findInPage(); + return true; + case R.id.action_reading_mode: + Intent read = new Intent(this, ReadingActivity.class); + read.putExtra(Constants.LOAD_READING_URL, mCurrentView.getUrl()); + startActivity(read); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + /** + * refreshes the underlying list of the Bookmark adapter since the bookmark + * adapter doesn't always change when notifyDataChanged gets called. + */ + private void notifyBookmarkDataSetChanged() { + mBookmarkAdapter.clear(); + mBookmarkAdapter.addAll(mBookmarkList); + mBookmarkAdapter.notifyDataSetChanged(); + } + + /** + * method that shows a dialog asking what string the user wishes to search + * for. It highlights the text entered. + */ + private void findInPage() { + final AlertDialog.Builder finder = new AlertDialog.Builder(mActivity); + finder.setTitle(getResources().getString(R.string.action_find)); + final EditText getHome = new EditText(this); + getHome.setHint(getResources().getString(R.string.search_hint)); + finder.setView(getHome); + finder.setPositiveButton(getResources().getString(R.string.search_hint), + new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + String query = getHome.getText().toString(); + if (query.length() > 0) + showSearchInterfaceBar(query); + } + }); + finder.show(); + } + + private void showSearchInterfaceBar(String text) { + if (mCurrentView != null) { + mCurrentView.find(text); + } + mSearchBar = (RelativeLayout) findViewById(R.id.search_bar); + 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); + } + + private void showCloseDialog(final int position) { + AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); + ArrayAdapter adapter = new ArrayAdapter<>(mActivity, + android.R.layout.simple_dropdown_item_1line); + adapter.add(mActivity.getString(R.string.close_tab)); + adapter.add(mActivity.getString(R.string.close_all_tabs)); + builder.setAdapter(adapter, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case 0: + deleteTab(position); + break; + case 1: + closeBrowser(); + break; + default: + break; + } + } + }); + builder.show(); + } + + /** + * The click listener for ListView in the navigation drawer + */ + private class DrawerItemClickListener implements ListView.OnItemClickListener { + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + mIsNewIntent = false; + showTab(mWebViewList.get(position)); + } + } + + /** + * long click listener for Navigation Drawer + */ + private class DrawerItemLongClickListener implements ListView.OnItemLongClickListener { + + @Override + public boolean onItemLongClick(AdapterView arg0, View arg1, final int position, long arg3) { + showCloseDialog(position); + return true; + } + } + + private class BookmarkItemClickListener implements ListView.OnItemClickListener { + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + if (mCurrentView != null) { + mCurrentView.loadUrl(mBookmarkList.get(position).getUrl()); + } + // keep any jank from happening when the drawer is closed after the + // URL starts to load + final Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + @Override + public void run() { + mDrawerLayout.closeDrawer(mDrawerRight); + } + }, 150); + } + } + + private class BookmarkItemLongClickListener implements ListView.OnItemLongClickListener { + + @Override + public boolean onItemLongClick(AdapterView arg0, View arg1, final int position, long arg3) { + + AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); + builder.setTitle(mActivity.getResources().getString(R.string.action_bookmarks)); + builder.setMessage(getResources().getString(R.string.dialog_bookmark)) + .setCancelable(true) + .setPositiveButton(getResources().getString(R.string.action_new_tab), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + newTab(mBookmarkList.get(position).getUrl(), false); + mDrawerLayout.closeDrawers(); + } + }) + .setNegativeButton(getResources().getString(R.string.action_delete), + new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + if (mBookmarkManager.deleteBookmark(mBookmarkList.get(position) + .getUrl())) { + mBookmarkList.remove(position); + notifyBookmarkDataSetChanged(); + mSearchAdapter.refreshBookmarks(); + openBookmarks(); + } + } + }) + .setNeutralButton(getResources().getString(R.string.action_edit), + new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + editBookmark(position); + } + }); + AlertDialog alert = builder.create(); + alert.show(); + return true; + } + } + + /** + * Takes in the id of which bookmark was selected and shows a dialog that + * allows the user to rename and change the url of the bookmark + * + * @param id which id in the list was chosen + */ + private synchronized void editBookmark(final int id) { + final AlertDialog.Builder homePicker = new AlertDialog.Builder(mActivity); + homePicker.setTitle(getResources().getString(R.string.title_edit_bookmark)); + final EditText getTitle = new EditText(mActivity); + getTitle.setHint(getResources().getString(R.string.hint_title)); + getTitle.setText(mBookmarkList.get(id).getTitle()); + getTitle.setSingleLine(); + final EditText getUrl = new EditText(mActivity); + getUrl.setHint(getResources().getString(R.string.hint_url)); + getUrl.setText(mBookmarkList.get(id).getUrl()); + getUrl.setSingleLine(); + LinearLayout layout = new LinearLayout(mActivity); + layout.setOrientation(LinearLayout.VERTICAL); + layout.addView(getTitle); + layout.addView(getUrl); + homePicker.setView(layout); + homePicker.setPositiveButton(getResources().getString(R.string.action_ok), + new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + mBookmarkList.get(id).setTitle(getTitle.getText().toString()); + mBookmarkList.get(id).setUrl(getUrl.getText().toString()); + mBookmarkManager.overwriteBookmarks(mBookmarkList); + Collections.sort(mBookmarkList, new SortIgnoreCase()); + notifyBookmarkDataSetChanged(); + if (mCurrentView != null && mCurrentView.getUrl().startsWith(Constants.FILE) + && mCurrentView.getUrl().endsWith("bookmarks.html")) { + openBookmarkPage(mWebView); + } + } + }); + homePicker.show(); + } + + /** + * displays the WebView contained in the LightningView Also handles the + * removal of previous views + * + * @param view the LightningView to show + */ + private synchronized void showTab(LightningView view) { + // Set the background color so the color mode color doesn't show through + mBrowserFrame.setBackgroundColor(mBackgroundColor); + if (view == null) { + return; + } + final float translation = mToolbarLayout.getTranslationY(); + mBrowserFrame.removeAllViews(); + if (mCurrentView != null) { + mCurrentView.setForegroundTab(false); + mCurrentView.onPause(); + } + mCurrentView = view; + mWebView = view.getWebView(); + mCurrentView.setForegroundTab(true); + if (mWebView != null) { + updateUrl(mCurrentView.getUrl(), true); + updateProgress(mCurrentView.getProgress()); + } else { + updateUrl("", true); + updateProgress(0); + } + + mBrowserFrame.addView(mWebView, MATCH_PARENT); + mCurrentView.requestFocus(); + mCurrentView.onResume(); + + if (mFullScreen) { + // mToolbarLayout has already been removed + mBrowserFrame.addView(mToolbarLayout); + mToolbarLayout.bringToFront(); + Log.d(Constants.TAG, "Move view to browser frame"); + int height = mToolbarLayout.getHeight(); + if (height == 0) { + mToolbarLayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); + height = mToolbarLayout.getMeasuredHeight(); + } + mWebView.setTranslationY(translation + height); + mToolbarLayout.setTranslationY(translation); + } else { + mWebView.setTranslationY(0); + } + + 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 + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + mDrawerLayout.closeDrawers(); + } + }, 150); + + +// new Handler().postDelayed(new Runnable() { +// @Override +// public void run() { +// // Remove browser frame background to reduce overdraw +// //TODO evaluate performance +// mBrowserFrame.setBackgroundColor(0); +// } +// }, 300); + + } + + /** + * creates a new tab with the passed in URL if it isn't null + */ + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + } + + void handleNewIntent(Intent intent) { + + String url = null; + if (intent != null) { + url = intent.getDataString(); + } + int num = 0; + if (intent != null && intent.getExtras() != null) { + num = intent.getExtras().getInt(getPackageName() + ".Origin"); + } + if (num == 1) { + mCurrentView.loadUrl(url); + } else if (url != null) { + if (url.startsWith(Constants.FILE)) { + Utils.showToast(this, getResources().getString(R.string.message_blocked_local)); + url = null; + } + newTab(url, true); + mIsNewIntent = true; + } + } + + @Override + public void closeEmptyTab() { + if (mWebView != null && mWebView.copyBackForwardList().getSize() == 0) { + closeCurrentTab(); + } + } + + private void closeCurrentTab() { + // don't delete the tab because the browser will close and mess stuff up + } + + @SuppressWarnings("deprecation") + @Override + public void onTrimMemory(int level) { + if (level > TRIM_MEMORY_MODERATE && Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + Log.d(Constants.TAG, "Low Memory, Free Memory"); + for (int n = 0; n < mWebViewList.size(); n++) { + mWebViewList.get(n).freeMemory(); + } + } + } + + synchronized boolean newTab(String url, boolean show) { + // Limit number of tabs for limited version of app + if (!Constants.FULL_VERSION && mWebViewList.size() >= 10) { + Utils.showToast(this, this.getString(R.string.max_tabs)); + return false; + } + mIsNewIntent = false; + LightningView startingTab = new LightningView(mActivity, url, mDarkTheme); + if (mIdGenerator == 0) { + startingTab.resumeTimers(); + } + mIdGenerator++; + mWebViewList.add(startingTab); + + mTitleAdapter.notifyDataSetChanged(); + if (show) { + mDrawerListLeft.setItemChecked(mWebViewList.size() - 1, true); + showTab(startingTab); + } + return true; + } + + private synchronized void deleteTab(int position) { + if (position >= mWebViewList.size()) { + return; + } + + int current = mDrawerListLeft.getCheckedItemPosition(); + LightningView reference = mWebViewList.get(position); + if (reference == null) { + return; + } + if (!reference.getUrl().startsWith(Constants.FILE) && !isIncognito()) { + mPreferences.setSavedUrl(reference.getUrl()); + } + boolean isShown = reference.isShown(); + if (isShown) { + mBrowserFrame.setBackgroundColor(mBackgroundColor); + } + if (current > position) { + mWebViewList.remove(position); + mDrawerListLeft.setItemChecked(current - 1, true); + reference.onDestroy(); + } else if (mWebViewList.size() > position + 1) { + if (current == position) { + showTab(mWebViewList.get(position + 1)); + mWebViewList.remove(position); + mDrawerListLeft.setItemChecked(position, true); + } else { + mWebViewList.remove(position); + } + + reference.onDestroy(); + } else if (mWebViewList.size() > 1) { + if (current == position) { + showTab(mWebViewList.get(position - 1)); + mWebViewList.remove(position); + mDrawerListLeft.setItemChecked(position - 1, true); + } else { + mWebViewList.remove(position); + } + + reference.onDestroy(); + } else { + if (mCurrentView.getUrl().startsWith(Constants.FILE) || mCurrentView.getUrl().equals(mHomepage)) { + closeActivity(); + } else { + mWebViewList.remove(position); + if (mPreferences.getClearCacheExit() && mCurrentView != null && !isIncognito()) { + mCurrentView.clearCache(true); + Log.d(Constants.TAG, "Cache Cleared"); + + } + if (mPreferences.getClearHistoryExitEnabled() && !isIncognito()) { + clearHistory(); + Log.d(Constants.TAG, "History Cleared"); + + } + if (mPreferences.getClearCookiesExitEnabled() && !isIncognito()) { + clearCookies(); + Log.d(Constants.TAG, "Cookies Cleared"); + + } + reference.pauseTimers(); + reference.onDestroy(); + mCurrentView = null; + mWebView = null; + mTitleAdapter.notifyDataSetChanged(); + finish(); + + } + } + mTitleAdapter.notifyDataSetChanged(); + + if (mIsNewIntent && isShown) { + mIsNewIntent = false; + closeActivity(); + } + + Log.d(Constants.TAG, "deleted tab"); + } + + @Override + public boolean onKeyLongPress(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + showCloseDialog(mDrawerListLeft.getCheckedItemPosition()); + } + return true; + } + + private void closeBrowser() { + mBrowserFrame.setBackgroundColor(mBackgroundColor); + if (mPreferences.getClearCacheExit() && mCurrentView != null && !isIncognito()) { + mCurrentView.clearCache(true); + Log.d(Constants.TAG, "Cache Cleared"); + + } + if (mPreferences.getClearHistoryExitEnabled() && !isIncognito()) { + clearHistory(); + Log.d(Constants.TAG, "History Cleared"); + + } + if (mPreferences.getClearCookiesExitEnabled() && !isIncognito()) { + clearCookies(); + Log.d(Constants.TAG, "Cookies Cleared"); + + } + mCurrentView = null; + mWebView = null; + for (int n = 0; n < mWebViewList.size(); n++) { + if (mWebViewList.get(n) != null) { + mWebViewList.get(n).onDestroy(); + } + } + mWebViewList.clear(); + mTitleAdapter.notifyDataSetChanged(); + finish(); + } + + @SuppressWarnings("deprecation") + private void clearHistory() { + this.deleteDatabase(HistoryDatabase.DATABASE_NAME); + WebViewDatabase m = WebViewDatabase.getInstance(this); + m.clearFormData(); + m.clearHttpAuthUsernamePassword(); + if (API < 18) { + m.clearUsernamePassword(); + WebIconDatabase.getInstance().removeAllIcons(); + } + if (mSystemBrowser) { + try { + Browser.clearHistory(getContentResolver()); + } catch (NullPointerException ignored) { + } + } + Utils.trimCache(this); + } + + @SuppressLint("NewApi") + @SuppressWarnings("deprecation") + private void clearCookies() { + // TODO Break out web storage deletion into its own option/action + // TODO clear web storage for all sites that are visited in Incognito mode + WebStorage storage = WebStorage.getInstance(); + storage.deleteAllData(); + CookieManager c = CookieManager.getInstance(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + c.removeAllCookies(null); + } else { + CookieSyncManager.createInstance(this); + c.removeAllCookie(); + } + } + + @Override + public void onBackPressed() { + if (mDrawerLayout.isDrawerOpen(mDrawerLeft)) { + mDrawerLayout.closeDrawer(mDrawerLeft); + } else if (mDrawerLayout.isDrawerOpen(mDrawerRight)) { + mDrawerLayout.closeDrawer(mDrawerRight); + } else { + if (mCurrentView != null) { + Log.d(Constants.TAG, "onBackPressed"); + if (mSearch.hasFocus()) { + mCurrentView.requestFocus(); + } else if (mCurrentView.canGoBack()) { + if (!mCurrentView.isShown()) { + onHideCustomView(); + } else { + mCurrentView.goBack(); + } + } else { + deleteTab(mDrawerListLeft.getCheckedItemPosition()); + } + } else { + Log.e(Constants.TAG, "This shouldn't happen ever"); + super.onBackPressed(); + } + } + } + + @Override + protected void onPause() { + super.onPause(); + Log.d(Constants.TAG, "onPause"); + if (mCurrentView != null) { + mCurrentView.pauseTimers(); + mCurrentView.onPause(); + } + } + + void saveOpenTabs() { + if (mPreferences.getRestoreLostTabsEnabled()) { + String s = ""; + for (int n = 0; n < mWebViewList.size(); n++) { + if (mWebViewList.get(n).getUrl().length() > 0) { + s = s + mWebViewList.get(n).getUrl() + "|$|SEPARATOR|$|"; + } + } + mPreferences.setMemoryUrl(s); + } + } + + @Override + protected void onStop() { + super.onStop(); + mProxyUtils.onStop(); + } + + @Override + protected void onDestroy() { + Log.d(Constants.TAG, "onDestroy"); + if (mHistoryDatabase != null) { + mHistoryDatabase.close(); + } + super.onDestroy(); + } + + @Override + protected void onStart() { + super.onStart(); + mProxyUtils.onStart(this); + } + + @Override + protected void onResume() { + super.onResume(); + Log.d(Constants.TAG, "onResume"); + if (mSearchAdapter != null) { + mSearchAdapter.refreshPreferences(); + mSearchAdapter.refreshBookmarks(); + } + if (mCurrentView != null) { + mCurrentView.resumeTimers(); + mCurrentView.onResume(); + + mHistoryDatabase = HistoryDatabase.getInstance(getApplicationContext()); + mBookmarkList = mBookmarkManager.getBookmarks(true); + notifyBookmarkDataSetChanged(); + } + initializePreferences(); + if (mWebViewList != null) { + for (int n = 0; n < mWebViewList.size(); n++) { + if (mWebViewList.get(n) != null) { + mWebViewList.get(n).initializePreferences(this); + } else { + mWebViewList.remove(n); + } + } + } + + supportInvalidateOptionsMenu(); + } + + /** + * 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(String query) { + if (query.equals("")) { + return; + } + String SEARCH = mSearchText; + query = query.trim(); + mCurrentView.stopLoading(); + + if (query.startsWith("www.")) { + query = Constants.HTTP + query; + } else if (query.startsWith("ftp.")) { + query = "ftp://" + query; + } + + boolean containsPeriod = query.contains("."); + boolean isIPAddress = (TextUtils.isDigitsOnly(query.replace(".", "")) + && (query.replace(".", "").length() >= 4) && query.contains(".")); + boolean aboutScheme = query.contains("about:"); + boolean validURL = (query.startsWith("ftp://") || query.startsWith(Constants.HTTP) + || query.startsWith(Constants.FILE) || query.startsWith(Constants.HTTPS)) + || isIPAddress; + boolean isSearch = ((query.contains(" ") || !containsPeriod) && !aboutScheme); + + if (isIPAddress + && (!query.startsWith(Constants.HTTP) || !query.startsWith(Constants.HTTPS))) { + query = Constants.HTTP + query; + } + + if (isSearch) { + try { + query = URLEncoder.encode(query, "UTF-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + mCurrentView.loadUrl(SEARCH + query); + } else if (!validURL) { + mCurrentView.loadUrl(Constants.HTTP + query); + } else { + mCurrentView.loadUrl(query); + } + } + + public class LightningViewAdapter extends ArrayAdapter { + + final Context context; + ColorMatrix colorMatrix; + ColorMatrixColorFilter filter; + Paint paint; + final int layoutResourceId; + List data = null; + final CloseTabListener mExitListener; + + public LightningViewAdapter(Context context, int layoutResourceId, List data) { + super(context, layoutResourceId, data); + this.layoutResourceId = layoutResourceId; + this.context = context; + this.data = data; + this.mExitListener = new CloseTabListener(); + } + + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + View row = convertView; + LightningViewHolder holder; + if (row == null) { + LayoutInflater inflater = ((Activity) context).getLayoutInflater(); + row = inflater.inflate(layoutResourceId, parent, false); + + holder = new LightningViewHolder(); + holder.txtTitle = (TextView) row.findViewById(R.id.textTab); + holder.favicon = (ImageView) row.findViewById(R.id.faviconTab); + holder.exit = (ImageView) row.findViewById(R.id.deleteButton); + holder.exit.setTag(position); + row.setTag(holder); + } else { + holder = (LightningViewHolder) row.getTag(); + } + + holder.exit.setTag(position); + holder.exit.setOnClickListener(mExitListener); + + ViewCompat.jumpDrawablesToCurrentState(holder.exit); + + LightningView web = data.get(position); + holder.txtTitle.setText(web.getTitle()); + if (web.isForegroundTab()) { + holder.txtTitle.setTextAppearance(context, R.style.boldText); + } else { + holder.txtTitle.setTextAppearance(context, R.style.normalText); + } + + Bitmap favicon = web.getFavicon(); + if (web.isForegroundTab()) { + + holder.favicon.setImageBitmap(favicon); + if (!isIncognito() && mColorMode) + changeToolbarBackground(favicon); + } else { + Bitmap grayscaleBitmap = Bitmap.createBitmap(favicon.getWidth(), + favicon.getHeight(), Bitmap.Config.ARGB_8888); + + Canvas c = new Canvas(grayscaleBitmap); + if (colorMatrix == null || filter == null || paint == null) { + paint = new Paint(); + colorMatrix = new ColorMatrix(); + colorMatrix.setSaturation(0); + filter = new ColorMatrixColorFilter(colorMatrix); + paint.setColorFilter(filter); + } + + c.drawBitmap(favicon, 0, 0, paint); + holder.favicon.setImageBitmap(grayscaleBitmap); + } + return row; + } + + class LightningViewHolder { + TextView txtTitle; + ImageView favicon; + ImageView exit; + } + } + + private class CloseTabListener implements OnClickListener { + + @Override + public void onClick(View v) { + deleteTab((int) v.getTag()); + } + + } + + private void changeToolbarBackground(Bitmap favicon) { + 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(mActivity.getResources() + .getColor(R.color.primary_color)); + + int finalColor; // Lighten up the dark color if it is + // too dark + if (Utils.isColorTooDark(color)) { + finalColor = Utils.mixTwoColors( + mActivity.getResources().getColor(R.color.primary_color), + color, 0.25f); + } else { + finalColor = color; + } + + ValueAnimator anim = ValueAnimator.ofObject(new ArgbEvaluator(), + mBackground.getColor(), finalColor); + + anim.addUpdateListener(new AnimatorUpdateListener() { + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + int color = (Integer) animation.getAnimatedValue(); + mBackground.setColor(color); + getWindow().setBackgroundDrawable(mBackground); + mToolbarLayout.setBackgroundColor(color); + } + + }); + + anim.setDuration(300); + anim.start(); + + } + }); + } + + public class BookmarkViewAdapter extends ArrayAdapter { + + final Context context; + List data = null; + final int layoutResourceId; + + public BookmarkViewAdapter(Context context, int layoutResourceId, List data) { + super(context, layoutResourceId, data); + this.layoutResourceId = layoutResourceId; + this.context = context; + this.data = data; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View row = convertView; + BookmarkViewHolder holder; + + if (row == null) { + LayoutInflater inflater = ((Activity) context).getLayoutInflater(); + row = inflater.inflate(layoutResourceId, parent, false); + + holder = new BookmarkViewHolder(); + holder.txtTitle = (TextView) row.findViewById(R.id.textBookmark); + holder.favicon = (ImageView) row.findViewById(R.id.faviconBookmark); + row.setTag(holder); + } else { + holder = (BookmarkViewHolder) row.getTag(); + } + + HistoryItem web = data.get(position); + holder.txtTitle.setText(web.getTitle()); + holder.favicon.setImageBitmap(mWebpageBitmap); + if (web.getBitmap() == null) { + getImage(holder.favicon, web); + } else { + holder.favicon.setImageBitmap(web.getBitmap()); + } + return row; + } + + class BookmarkViewHolder { + TextView txtTitle; + ImageView favicon; + } + } + + private void getImage(ImageView image, HistoryItem web) { + new DownloadImageTask(image, web).execute(web.getUrl()); + } + + private class DownloadImageTask extends AsyncTask { + + final ImageView bmImage; + final HistoryItem mWeb; + + public DownloadImageTask(ImageView bmImage, HistoryItem web) { + this.bmImage = bmImage; + this.mWeb = web; + } + + protected Bitmap doInBackground(String... urls) { + String url = urls[0]; + Bitmap mIcon = null; + // unique path for each url that is bookmarked. + String hash = String.valueOf(Utils.getDomainName(url).hashCode()); + File image = new File(mActivity.getCacheDir(), hash + ".png"); + String urldisplay; + try { + urldisplay = Utils.getProtocol(url) + getDomainName(url) + "/favicon.ico"; + } catch (URISyntaxException e) { + e.printStackTrace(); + urldisplay = "https://www.google.com/s2/favicons?domain_url=" + url; + } + // checks to see if the image exists + if (!image.exists()) { + try { + // if not, download it... + URL urlDownload = new URL(urldisplay); + HttpURLConnection connection = (HttpURLConnection) urlDownload.openConnection(); + connection.setDoInput(true); + connection.connect(); + InputStream in = connection.getInputStream(); + + if (in != null) { + mIcon = BitmapFactory.decodeStream(in); + } + // ...and cache it + if (mIcon != null) { + FileOutputStream fos = new FileOutputStream(image); + mIcon.compress(Bitmap.CompressFormat.PNG, 100, fos); + fos.flush(); + fos.close(); + Log.d(Constants.TAG, "Downloaded: " + urldisplay); + } + + } catch (Exception e) { + e.printStackTrace(); + } + } else { + // if it exists, retrieve it from the cache + mIcon = BitmapFactory.decodeFile(image.getPath()); + } + if (mIcon == null) { + try { + // if not, download it... + URL urlDownload = new URL("https://www.google.com/s2/favicons?domain_url=" + + url); + HttpURLConnection connection = (HttpURLConnection) urlDownload.openConnection(); + connection.setDoInput(true); + connection.connect(); + InputStream in = connection.getInputStream(); + + if (in != null) { + mIcon = BitmapFactory.decodeStream(in); + } + // ...and cache it + if (mIcon != null) { + FileOutputStream fos = new FileOutputStream(image); + mIcon.compress(Bitmap.CompressFormat.PNG, 100, fos); + fos.flush(); + fos.close(); + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + if (mIcon == null) { + return mWebpageBitmap; + } else { + return mIcon; + } + } + + protected void onPostExecute(Bitmap result) { + Bitmap fav = Utils.padFavicon(result); + bmImage.setImageBitmap(fav); + mWeb.setBitmap(fav); + notifyBookmarkDataSetChanged(); + } + } + + private static String getDomainName(String url) throws URISyntaxException { + URI uri = new URI(url); + String domain = uri.getHost(); + if (domain == null) { + return url; + } + return domain.startsWith("www.") ? domain.substring(4) : domain; + } + + @Override + public void updateUrl(String url, boolean shortUrl) { + if (url == null || mSearch == null || mSearch.hasFocus()) { + return; + } + if (shortUrl && !url.startsWith(Constants.FILE)) { + switch (mPreferences.getUrlBoxContentChoice()) { + case 0: // Default, show only the domain + url = url.replaceFirst(Constants.HTTP, ""); + url = Utils.getDomainName(url); + mSearch.setText(url); + break; + case 1: // URL, show the entire URL + mSearch.setText(url); + break; + case 2: // Title, show the page's title + if (mCurrentView != null && !mCurrentView.getTitle().isEmpty()) { + mSearch.setText(mCurrentView.getTitle()); + } else { + mSearch.setText(mUntitledTitle); + } + break; + } + + } else { + if (url.startsWith(Constants.FILE)) { + url = ""; + } + mSearch.setText(url); + } + } + + @Override + public void updateProgress(int n) { + if (n >= 100) { + setIsFinishedLoading(); + } else { + setIsLoading(); + } + mProgressBar.setProgress(n); + } + + @Override + public void updateHistory(final String title, final String url) { + + } + + void addItemToHistory(final String title, final String url) { + Runnable update = new Runnable() { + @Override + public void run() { + if (isSystemBrowserAvailable() && mPreferences.getSyncHistoryEnabled()) { + try { + Browser.updateVisitedHistory(getContentResolver(), url, true); + } catch (NullPointerException ignored) { + } + } + try { + if (mHistoryDatabase == null) { + mHistoryDatabase = HistoryDatabase.getInstance(mActivity); + } + mHistoryDatabase.visitHistoryItem(url, title); + } catch (IllegalStateException e) { + Log.e(Constants.TAG, "IllegalStateException in updateHistory"); + } catch (NullPointerException e) { + Log.e(Constants.TAG, "NullPointerException in updateHistory"); + } catch (SQLiteException e) { + Log.e(Constants.TAG, "SQLiteException in updateHistory"); + } + } + }; + if (url != null && !url.startsWith(Constants.FILE)) { + new Thread(update).start(); + } + } + + private boolean isSystemBrowserAvailable() { + return mSystemBrowser; + } + + private boolean getSystemBrowser() { + Cursor c = null; + String[] columns = new String[]{"url", "title"}; + boolean browserFlag; + try { + + Uri bookmarks = Browser.BOOKMARKS_URI; + c = getContentResolver().query(bookmarks, columns, null, null, null); + } catch (SQLiteException | IllegalStateException | NullPointerException e) { + e.printStackTrace(); + } + + if (c != null) { + Log.d("Browser", "System Browser Available"); + browserFlag = true; + } else { + Log.e("Browser", "System Browser Unavailable"); + browserFlag = false; + } + if (c != null) { + c.close(); + } + mPreferences.setSystemBrowserPresent(browserFlag); + return browserFlag; + } + + /** + * method to generate search suggestions for the AutoCompleteTextView from + * previously searched URLs + */ + private void initializeSearchSuggestions(final AutoCompleteTextView getUrl) { + + getUrl.setThreshold(1); + getUrl.setDropDownWidth(-1); + getUrl.setDropDownAnchor(R.id.toolbar_layout); + getUrl.setOnItemClickListener(new OnItemClickListener() { + + @Override + public void onItemClick(AdapterView arg0, View arg1, int arg2, long arg3) { + try { + String url; + url = ((TextView) arg1.findViewById(R.id.url)).getText().toString(); + if (url.startsWith(mActivity.getString(R.string.suggestion))) { + url = ((TextView) arg1.findViewById(R.id.title)).getText().toString(); + } else { + getUrl.setText(url); + } + searchTheWeb(url); + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(getUrl.getWindowToken(), 0); + if (mCurrentView != null) { + mCurrentView.requestFocus(); + } + } catch (NullPointerException e) { + Log.e("Browser Error: ", "NullPointerException on item click"); + } + } + + }); + + getUrl.setSelectAllOnFocus(true); + mSearchAdapter = new SearchAdapter(mActivity, mDarkTheme, isIncognito()); + getUrl.setAdapter(mSearchAdapter); + } + + @Override + public boolean isIncognito() { + return false; + } + + @Override + public boolean isProxyReady() { + return mProxyUtils.isProxyReady(this); + } + + /** + * function that opens the HTML history page in the browser + */ + private void openHistory() { + // use a thread so that history retrieval doesn't block the UI + Thread history = new Thread(new Runnable() { + + @Override + public void run() { + mCurrentView.loadUrl(HistoryPage.getHistoryPage(mActivity)); + mSearch.setText(""); + } + + }); + history.run(); + } + + /** + * helper function that opens the bookmark drawer + */ + private void openBookmarks() { + if (mDrawerLayout.isDrawerOpen(mDrawerLeft)) { + mDrawerLayout.closeDrawers(); + } + mDrawerLayout.openDrawer(mDrawerRight); + } + + void closeDrawers() { + mDrawerLayout.closeDrawers(); + } + + @Override + /** + * open the HTML bookmarks page, parameter view is the WebView that should show the page + */ + public void openBookmarkPage(WebView view) { + StringBuilder bookmarkBuilder = new StringBuilder(); + bookmarkBuilder.append(BookmarkPage.HEADING); + Iterator iter = mBookmarkList.iterator(); + HistoryItem helper; + while (iter.hasNext()) { + helper = iter.next(); + bookmarkBuilder.append(BookmarkPage.PART1); + bookmarkBuilder.append(helper.getUrl()); + bookmarkBuilder.append(BookmarkPage.PART2); + bookmarkBuilder.append(helper.getUrl()); + bookmarkBuilder.append(BookmarkPage.PART3); + bookmarkBuilder.append(helper.getTitle()); + bookmarkBuilder.append(BookmarkPage.PART4); + } + bookmarkBuilder.append(BookmarkPage.END); + File bookmarkWebPage = new File(mActivity.getFilesDir(), BookmarkPage.FILENAME); + try { + FileWriter bookWriter = new FileWriter(bookmarkWebPage, false); + bookWriter.write(bookmarkBuilder.toString()); + bookWriter.close(); + } catch (IOException e) { + e.printStackTrace(); + } + + view.loadUrl(Constants.FILE + bookmarkWebPage); + } + + @Override + public void update() { + mTitleAdapter.notifyDataSetChanged(); + } + + @Override + /** + * opens a file chooser + * param ValueCallback is the message from the WebView indicating a file chooser + * should be opened + */ + public void openFileChooser(ValueCallback 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); + } + + @Override + /** + * used to allow uploading into the browser + */ + 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 filePathCallback) { + if (mFilePathCallback != null) { + mFilePathCallback.onReceiveValue(null); + } + mFilePathCallback = filePathCallback; + + Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + if (takePictureIntent.resolveActivity(getActivity().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(Constants.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("image/*"); + + 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); + + mActivity.startActivityForResult(chooserIntent, 1); + } + + @Override + /** + * handles long presses for the browser, tries to get the + * url of the item that was clicked and sends it (it can be null) + * to the click handler that does cool stuff with it + */ + public void onLongPress() { + if (mClickHandler == null) { + mClickHandler = new ClickHandler(mActivity); + } + Message click = mClickHandler.obtainMessage(); + if (click != null) { + click.setTarget(mClickHandler); + mWebView.requestFocusNodeHref(click); + } + } + + @Override + public void onShowCustomView(View view, CustomViewCallback callback) { + if (view == null) { + return; + } + if (mCustomView != null && callback != null) { + callback.onCustomViewHidden(); + return; + } + try { + view.setKeepScreenOn(true); + } catch (SecurityException e) { + Log.e(Constants.TAG, "WebView is not allowed to keep the screen on"); + } + mOriginalOrientation = getRequestedOrientation(); + FrameLayout decor = (FrameLayout) getWindow().getDecorView(); + mFullscreenContainer = new FullscreenHolder(this); + mCustomView = view; + mFullscreenContainer.addView(mCustomView, COVER_SCREEN_PARAMS); + decor.addView(mFullscreenContainer, COVER_SCREEN_PARAMS); + setFullscreen(true); + mCurrentView.setVisibility(View.GONE); + if (view instanceof FrameLayout) { + if (((FrameLayout) view).getFocusedChild() instanceof VideoView) { + mVideoView = (VideoView) ((FrameLayout) view).getFocusedChild(); + mVideoView.setOnErrorListener(new VideoCompletionListener()); + mVideoView.setOnCompletionListener(new VideoCompletionListener()); + } + } + mCustomViewCallback = callback; + } + + @Override + public void onHideCustomView() { + if (mCustomView == null || mCustomViewCallback == null || mCurrentView == null) { + return; + } + Log.d(Constants.TAG, "onHideCustomView"); + mCurrentView.setVisibility(View.VISIBLE); + try { + mCustomView.setKeepScreenOn(false); + } catch (SecurityException e) { + Log.e(Constants.TAG, "WebView is not allowed to keep the screen on"); + } + setFullscreen(mPreferences.getHideStatusBarEnabled()); + FrameLayout decor = (FrameLayout) getWindow().getDecorView(); + if (decor != null) { + decor.removeView(mFullscreenContainer); + } + + if (API < 19) { + try { + mCustomViewCallback.onCustomViewHidden(); + } catch (Throwable ignored) { + + } + } + mFullscreenContainer = null; + mCustomView = null; + if (mVideoView != null) { + mVideoView.setOnErrorListener(null); + mVideoView.setOnCompletionListener(null); + mVideoView = 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(); + } + + } + + /** + * turns on fullscreen mode in the app + * + * @param enabled whether to enable fullscreen or not + */ + private void setFullscreen(boolean enabled) { + Window win = getWindow(); + WindowManager.LayoutParams winParams = win.getAttributes(); + final int bits = WindowManager.LayoutParams.FLAG_FULLSCREEN; + if (enabled) { + winParams.flags |= bits; + } else { + winParams.flags &= ~bits; + if (mCustomView != null) { + mCustomView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); + } else { + mBrowserFrame.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); + } + } + win.setAttributes(winParams); + } + + /** + * a class extending FramLayout used to display fullscreen videos + */ + static class FullscreenHolder extends FrameLayout { + + public FullscreenHolder(Context ctx) { + super(ctx); + setBackgroundColor(ctx.getResources().getColor(android.R.color.black)); + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouchEvent(@NonNull MotionEvent evt) { + return true; + } + + } + + @Override + /** + * a stupid method that returns the bitmap image to display in place of + * a loading video + */ + public Bitmap getDefaultVideoPoster() { + if (mDefaultVideoPoster == null) { + mDefaultVideoPoster = BitmapFactory.decodeResource(getResources(), + android.R.drawable.ic_media_play); + } + return mDefaultVideoPoster; + } + + @SuppressLint("InflateParams") + @Override + /** + * dumb method that returns the loading progress for a video + */ + public View getVideoLoadingProgressView() { + if (mVideoProgressView == null) { + LayoutInflater inflater = LayoutInflater.from(this); + mVideoProgressView = inflater.inflate(R.layout.video_loading_progress, null); + } + return mVideoProgressView; + } + + @Override + /** + * handles javascript requests to create a new window in the browser + */ + public void onCreateWindow(Message resultMsg) { + if (resultMsg == null) { + return; + } + if (newTab("", true)) { + WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj; + transport.setWebView(mWebView); + resultMsg.sendToTarget(); + } + } + + @Override + /** + * returns the Activity instance for this activity, + * very helpful when creating things in other classes... I think + */ + public Activity getActivity() { + return mActivity; + } + + private boolean isToolbarVisible() { + return mToolbarLayout.getTranslationY() > -0.01f; + } + + /** + * it hides the action bar, seriously what else were you expecting + */ + @Override + public void hideActionBar() { + if (mFullScreen) { + if (mBrowserFrame.findViewById(R.id.toolbar_layout) == null) { + mUiLayout.removeView(mToolbarLayout); + mBrowserFrame.addView(mToolbarLayout); + mToolbarLayout.bringToFront(); + Log.d(Constants.TAG, "Move view to browser frame"); + mToolbarLayout.setTranslationY(0); + mWebView.setTranslationY(mToolbarLayout.getHeight()); + } + if (mToolbarLayout == null || mCurrentView == null) + return; + + final int height = mToolbarLayout.getHeight(); + final WebView view = mWebView; + if (mToolbarLayout.getTranslationY() > -0.01f) { + Animation show = new Animation() { + @Override + protected void applyTransformation(float interpolatedTime, Transformation t) { + float trans = (1.0f - interpolatedTime) * height; + mToolbarLayout.setTranslationY(trans - height); + if (view != null) + view.setTranslationY(trans); + } + }; + show.setDuration(250); + show.setInterpolator(new DecelerateInterpolator()); +// show.setFillAfter(true); + mWebView.startAnimation(show); + } + } + } + + @Override + public void toggleActionBar() { + if (mFullScreen) { + if (!isToolbarVisible()) { + showActionBar(); + } else { + hideActionBar(); + } + } + } + + @Override + /** + * obviously it shows the action bar if it's hidden + */ + public void showActionBar() { + if (mFullScreen) { + + if (mToolbarLayout == null) + return; + + int height = mToolbarLayout.getHeight(); + if (height == 0) { + mToolbarLayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); + height = mToolbarLayout.getMeasuredHeight(); + } + + if (mBrowserFrame.findViewById(R.id.toolbar_layout) == null) { + mUiLayout.removeView(mToolbarLayout); + mBrowserFrame.addView(mToolbarLayout); + mToolbarLayout.bringToFront(); + Log.d(Constants.TAG, "Move view to browser frame"); + mToolbarLayout.setTranslationY(0); + mWebView.setTranslationY(height); + } + if (mCurrentView == null) + return; + + final WebView view = mWebView; + 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); + // null pointer here on close + if (view != null) + view.setTranslationY(trans); + } + }; + show.setDuration(250); + show.setInterpolator(new DecelerateInterpolator()); +// show.setFillAfter(true); + mWebView.startAnimation(show); + } + } + } + + @Override + /** + * handles a long click on the page, parameter String url + * is the url that should have been obtained from the WebView touch node + * thingy, if it is null, this method tries to deal with it and find a workaround + */ + public void longClickPage(final String url) { + HitTestResult result = null; + if (mWebView != null) { + result = mWebView.getHitTestResult(); + } + if (url != null) { + if (result != null) { + if (result.getType() == HitTestResult.SRC_IMAGE_ANCHOR_TYPE + || result.getType() == HitTestResult.IMAGE_TYPE) { + 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: + mCurrentView.loadUrl(url); + break; + + case DialogInterface.BUTTON_NEUTRAL: + if (API > 8) { + Utils.downloadFile(mActivity, url, + mCurrentView.getUserAgent(), "attachment"); + } + break; + } + } + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); // dialog + builder.setTitle(url.replace(Constants.HTTP, "")) + .setMessage(getResources().getString(R.string.dialog_image)) + .setPositiveButton(getResources().getString(R.string.action_new_tab), + dialogClickListener) + .setNegativeButton(getResources().getString(R.string.action_open), + dialogClickListener) + .setNeutralButton(getResources().getString(R.string.action_download), + dialogClickListener).show(); + + } else { + 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: + mCurrentView.loadUrl(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) + .setMessage(getResources().getString(R.string.dialog_link)) + .setPositiveButton(getResources().getString(R.string.action_new_tab), + dialogClickListener) + .setNegativeButton(getResources().getString(R.string.action_open), + dialogClickListener) + .setNeutralButton(getResources().getString(R.string.action_copy), + dialogClickListener).show(); + } + } else { + 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: + mCurrentView.loadUrl(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) + .setMessage(getResources().getString(R.string.dialog_link)) + .setPositiveButton(getResources().getString(R.string.action_new_tab), + dialogClickListener) + .setNegativeButton(getResources().getString(R.string.action_open), + dialogClickListener) + .setNeutralButton(getResources().getString(R.string.action_copy), + dialogClickListener).show(); + } + } else if (result != null) { + if (result.getExtra() != null) { + final String newUrl = result.getExtra(); + if (result.getType() == HitTestResult.SRC_IMAGE_ANCHOR_TYPE + || result.getType() == HitTestResult.IMAGE_TYPE) { + DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case DialogInterface.BUTTON_POSITIVE: + newTab(newUrl, false); + break; + + case DialogInterface.BUTTON_NEGATIVE: + mCurrentView.loadUrl(newUrl); + break; + + case DialogInterface.BUTTON_NEUTRAL: + if (API > 8) { + Utils.downloadFile(mActivity, newUrl, + mCurrentView.getUserAgent(), "attachment"); + } + break; + } + } + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); // dialog + builder.setTitle(newUrl.replace(Constants.HTTP, "")) + .setMessage(getResources().getString(R.string.dialog_image)) + .setPositiveButton(getResources().getString(R.string.action_new_tab), + dialogClickListener) + .setNegativeButton(getResources().getString(R.string.action_open), + dialogClickListener) + .setNeutralButton(getResources().getString(R.string.action_download), + dialogClickListener).show(); + + } else { + DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case DialogInterface.BUTTON_POSITIVE: + newTab(newUrl, false); + break; + + case DialogInterface.BUTTON_NEGATIVE: + mCurrentView.loadUrl(newUrl); + break; + + case DialogInterface.BUTTON_NEUTRAL: + ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("label", newUrl); + clipboard.setPrimaryClip(clip); + + break; + } + } + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); // dialog + builder.setTitle(newUrl) + .setMessage(getResources().getString(R.string.dialog_link)) + .setPositiveButton(getResources().getString(R.string.action_new_tab), + dialogClickListener) + .setNegativeButton(getResources().getString(R.string.action_open), + dialogClickListener) + .setNeutralButton(getResources().getString(R.string.action_copy), + dialogClickListener).show(); + } + + } + + } + + } + + /** + * 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() { + if (!mSearch.hasFocus()) { + mIcon = mDeleteIcon; + mSearch.setCompoundDrawables(null, null, mDeleteIcon, null); + } + } + + /** + * This tells the search bar that the page is finished loading and it should + * display the refresh icon + */ + private void setIsFinishedLoading() { + if (!mSearch.hasFocus()) { + mIcon = mRefreshIcon; + mSearch.setCompoundDrawables(null, null, mRefreshIcon, 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() { + if (mCurrentView != null) { + if (mCurrentView.getProgress() < 100) { + mCurrentView.stopLoading(); + } else { + mCurrentView.reload(); + } + } + } + + // Override this, use finish() for Incognito, moveTaskToBack for Main + void closeActivity() { + finish(); + } + + private class SortIgnoreCase implements Comparator { + + public int compare(HistoryItem o1, HistoryItem o2) { + return o1.getTitle().toLowerCase(Locale.getDefault()) + .compareTo(o2.getTitle().toLowerCase(Locale.getDefault())); + } + + } + + @Override + public int getMenu() { + return R.menu.main; + } + + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.action_back: + if (mCurrentView != null) { + if (mCurrentView.canGoBack()) { + mCurrentView.goBack(); + } else { + deleteTab(mDrawerListLeft.getCheckedItemPosition()); + } + } + break; + case R.id.action_forward: + if (mCurrentView != null) { + if (mCurrentView.canGoForward()) { + mCurrentView.goForward(); + } + } + break; + case R.id.arrow_button: + if (mSearch != null && mSearch.hasFocus()) { + mCurrentView.requestFocus(); + } else { + mDrawerLayout.openDrawer(mDrawerLeft); + } + break; + case R.id.new_tab_button: + newTab(null, true); + break; + case R.id.button_next: + mWebView.findNext(false); + break; + case R.id.button_back: + mWebView.findNext(true); + break; + case R.id.button_quit: + mWebView.clearMatches(); + mSearchBar.setVisibility(View.GONE); + break; + } + } } diff --git a/app/src/main/java/acr/browser/lightning/activity/IncognitoActivity.java b/app/src/main/java/acr/browser/lightning/activity/IncognitoActivity.java index 7be8e5c..1562797 100644 --- a/app/src/main/java/acr/browser/lightning/activity/IncognitoActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/IncognitoActivity.java @@ -13,69 +13,66 @@ import acr.browser.lightning.R; @SuppressWarnings("deprecation") public class IncognitoActivity extends BrowserActivity { - private CookieManager mCookieManager; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } + @Override + public void updateCookiePreference() { + CookieManager cookieManager = CookieManager.getInstance(); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + CookieSyncManager.createInstance(this); + } + cookieManager.setAcceptCookie(PreferenceManager.getInstance().getIncognitoCookiesEnabled()); + super.updateCookiePreference(); + } - @Override - public void updateCookiePreference() { - mCookieManager = CookieManager.getInstance(); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - CookieSyncManager.createInstance(this); - } - mCookieManager - .setAcceptCookie(PreferenceManager.getInstance().getIncognitoCookiesEnabled()); - super.updateCookiePreference(); - } + @Override + public synchronized void initializeTabs() { + super.initializeTabs(); + // restoreOrNewTab(); + newTab(null, true); + // if incognito mode use newTab(null, true); instead + } - @Override - public synchronized void initializeTabs() { - super.initializeTabs(); - // restoreOrNewTab(); - newTab(null, true); - // if incognito mode use newTab(null, true); instead - } + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.incognito, menu); + return super.onCreateOptionsMenu(menu); + } - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.incognito, menu); - return super.onCreateOptionsMenu(menu); - } + @Override + protected void onNewIntent(Intent intent) { + // handleNewIntent(intent); + super.onNewIntent(intent); + } - @Override - protected void onNewIntent(Intent intent) { - // handleNewIntent(intent); - super.onNewIntent(intent); - } + @Override + protected void onPause() { + super.onPause(); + // saveOpenTabs(); + } - @Override - protected void onPause() { - super.onPause(); - // saveOpenTabs(); - } + @Override + public void updateHistory(String title, String url) { + super.updateHistory(title, url); + // addItemToHistory(title, url); + } - @Override - public void updateHistory(String title, String url) { - super.updateHistory(title, url); - // addItemToHistory(title, url); - } + @Override + public boolean isIncognito() { + return true; + } - @Override - public boolean isIncognito() { - return true; - } + @Override + public void closeActivity() { + closeDrawers(); + finish(); + } - @Override - public void closeActivity() { - closeDrawers(); - finish(); - } - - @Override - public int getMenu() { - return R.menu.incognito; - } + @Override + public int getMenu() { + return R.menu.incognito; + } } diff --git a/app/src/main/java/acr/browser/lightning/activity/MainActivity.java b/app/src/main/java/acr/browser/lightning/activity/MainActivity.java index b465d69..3f5b0c2 100644 --- a/app/src/main/java/acr/browser/lightning/activity/MainActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/MainActivity.java @@ -13,66 +13,64 @@ import acr.browser.lightning.R; @SuppressWarnings("deprecation") public class MainActivity extends BrowserActivity { - private CookieManager mCookieManager; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } + @Override + public void updateCookiePreference() { + CookieManager cookieManager = CookieManager.getInstance(); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + CookieSyncManager.createInstance(this); + } + cookieManager.setAcceptCookie(PreferenceManager.getInstance().getCookiesEnabled()); + super.updateCookiePreference(); + } - @Override - public void updateCookiePreference() { - mCookieManager = CookieManager.getInstance(); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - CookieSyncManager.createInstance(this); - } - mCookieManager.setAcceptCookie(PreferenceManager.getInstance().getCookiesEnabled()); - super.updateCookiePreference(); - } + @Override + public synchronized void initializeTabs() { + restoreOrNewTab(); + // if incognito mode use newTab(null, true); instead + } - @Override - public synchronized void initializeTabs() { - restoreOrNewTab(); - // if incognito mode use newTab(null, true); instead - } + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main, menu); + return super.onCreateOptionsMenu(menu); + } - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.main, menu); - return super.onCreateOptionsMenu(menu); - } + @Override + protected void onNewIntent(Intent intent) { + handleNewIntent(intent); + super.onNewIntent(intent); + } - @Override - protected void onNewIntent(Intent intent) { - handleNewIntent(intent); - super.onNewIntent(intent); - } + @Override + protected void onPause() { + super.onPause(); + saveOpenTabs(); + } - @Override - protected void onPause() { - super.onPause(); - saveOpenTabs(); - } + @Override + public void updateHistory(String title, String url) { + super.updateHistory(title, url); + addItemToHistory(title, url); + } - @Override - public void updateHistory(String title, String url) { - super.updateHistory(title, url); - addItemToHistory(title, url); - } + @Override + public boolean isIncognito() { + return false; + } - @Override - public boolean isIncognito() { - return false; - } + @Override + public int getMenu() { + return R.menu.main; + } - @Override - public int getMenu() { - return R.menu.main; - } - - @Override - public void closeActivity() { - closeDrawers(); - moveTaskToBack(true); - } + @Override + public void closeActivity() { + closeDrawers(); + moveTaskToBack(true); + } } diff --git a/app/src/main/java/acr/browser/lightning/activity/ReadingActivity.java b/app/src/main/java/acr/browser/lightning/activity/ReadingActivity.java index 7ad73a8..a96d321 100644 --- a/app/src/main/java/acr/browser/lightning/activity/ReadingActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/ReadingActivity.java @@ -1,7 +1,6 @@ package acr.browser.lightning.activity; import android.animation.ObjectAnimator; -import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; @@ -9,6 +8,7 @@ import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; +import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.LayoutInflater; @@ -31,223 +31,225 @@ import acr.browser.lightning.utils.Utils; public class ReadingActivity extends AppCompatActivity { - private TextView mTitle; - private TextView mBody; - private boolean mInvert; - private String mUrl = null; - private PreferenceManager mPreferences; - private int mTextSize; - private static final float XXLARGE = 30.0f; - private static final float XLARGE = 26.0f; - private static final float LARGE = 22.0f; - private static final float MEDIUM = 18.0f; - private static final float SMALL = 14.0f; - private static final float XSMALL = 10.0f; + private TextView mTitle; + private TextView mBody; + private boolean mInvert; + private String mUrl = null; + private PreferenceManager mPreferences; + private int mTextSize; + private static final float XXLARGE = 30.0f; + private static final float XLARGE = 26.0f; + private static final float LARGE = 22.0f; + private static final float MEDIUM = 18.0f; + private static final float SMALL = 14.0f; + private static final float XSMALL = 10.0f; - @Override - protected void onCreate(Bundle savedInstanceState) { - mPreferences = PreferenceManager.getInstance(); - mInvert = mPreferences.getInvertColors(); - if (mInvert) { - this.setTheme(R.style.Theme_SettingsTheme_Dark); - } - super.onCreate(savedInstanceState); - setContentView(R.layout.reading_view); - Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); - setSupportActionBar(toolbar); + @Override + protected void onCreate(Bundle savedInstanceState) { + mPreferences = PreferenceManager.getInstance(); + mInvert = mPreferences.getInvertColors(); + if (mInvert) { + this.setTheme(R.style.Theme_SettingsTheme_Dark); + } + super.onCreate(savedInstanceState); + setContentView(R.layout.reading_view); + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); + if (getSupportActionBar() != null) + getSupportActionBar().setDisplayHomeAsUpEnabled(true); - mTitle = (TextView) findViewById(R.id.textViewTitle); - mBody = (TextView) findViewById(R.id.textViewBody); + mTitle = (TextView) findViewById(R.id.textViewTitle); + mBody = (TextView) findViewById(R.id.textViewBody); - mTextSize = mPreferences.getReadingTextSize(); - mBody.setTextSize(getTextSize(mTextSize)); - mTitle.setText(getString(R.string.untitled)); - mBody.setText(getString(R.string.loading)); + mTextSize = mPreferences.getReadingTextSize(); + mBody.setTextSize(getTextSize(mTextSize)); + mTitle.setText(getString(R.string.untitled)); + mBody.setText(getString(R.string.loading)); - mTitle.setVisibility(View.INVISIBLE); - mBody.setVisibility(View.INVISIBLE); + mTitle.setVisibility(View.INVISIBLE); + mBody.setVisibility(View.INVISIBLE); - Intent intent = getIntent(); - if (!loadPage(intent)) { - setText(getString(R.string.untitled), getString(R.string.loading_failed)); - } - } + Intent intent = getIntent(); + if (!loadPage(intent)) { + setText(getString(R.string.untitled), getString(R.string.loading_failed)); + } + } - private float getTextSize(int size) { - switch (size) { - case 0: - return XSMALL; - case 1: - return SMALL; - case 2: - return MEDIUM; - case 3: - return LARGE; - case 4: - return XLARGE; - case 5: - return XXLARGE; - default: - return MEDIUM; - } - } + private float getTextSize(int size) { + switch (size) { + case 0: + return XSMALL; + case 1: + return SMALL; + case 2: + return MEDIUM; + case 3: + return LARGE; + case 4: + return XLARGE; + case 5: + return XXLARGE; + default: + return MEDIUM; + } + } - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.reading, menu); - return super.onCreateOptionsMenu(menu); - } + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.reading, menu); + return super.onCreateOptionsMenu(menu); + } - protected boolean loadPage(Intent intent) { - if (intent == null) { - return false; - } - mUrl = intent.getStringExtra(Constants.LOAD_READING_URL); - if (mUrl == null) { - return false; - } - getSupportActionBar().setTitle(Utils.getDomainName(mUrl)); - new PageLoader(this).execute(mUrl); - return true; - } + private boolean loadPage(Intent intent) { + if (intent == null) { + return false; + } + mUrl = intent.getStringExtra(Constants.LOAD_READING_URL); + if (mUrl == null) { + return false; + } + if (getSupportActionBar() != null) + getSupportActionBar().setTitle(Utils.getDomainName(mUrl)); + new PageLoader(this).execute(mUrl); + return true; + } - private class PageLoader extends AsyncTask { + private class PageLoader extends AsyncTask { - private final Context mContext; - private ProgressDialog mProgressDialog; - private String mTitleText; - private List mBodyText; + private final Context mContext; + private ProgressDialog mProgressDialog; + private String mTitleText; + private List mBodyText; - public PageLoader(Context context) { - mContext = context; - } + public PageLoader(Context context) { + mContext = context; + } - @Override - protected void onPreExecute() { - super.onPreExecute(); - mProgressDialog = new ProgressDialog(mContext); - mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); - mProgressDialog.setCancelable(false); - mProgressDialog.setIndeterminate(true); - mProgressDialog.setMessage(mContext.getString(R.string.loading)); - mProgressDialog.show(); - } + @Override + protected void onPreExecute() { + super.onPreExecute(); + mProgressDialog = new ProgressDialog(mContext); + mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); + mProgressDialog.setCancelable(false); + mProgressDialog.setIndeterminate(true); + mProgressDialog.setMessage(mContext.getString(R.string.loading)); + mProgressDialog.show(); + } - @Override - protected Void doInBackground(String... params) { + @Override + protected Void doInBackground(String... params) { - HtmlFetcher fetcher = new HtmlFetcher(); - try { - JResult result = fetcher.fetchAndExtract(params[0], 2500, true); - mTitleText = result.getTitle(); - mBodyText = result.getTextList(); - } catch (Exception e) { - mTitleText = ""; - mBodyText = new ArrayList<>(); - e.printStackTrace(); - } catch (OutOfMemoryError e) { - System.gc(); - mTitleText = ""; - mBodyText = new ArrayList<>(); - e.printStackTrace(); - } - return null; - } + HtmlFetcher fetcher = new HtmlFetcher(); + try { + JResult result = fetcher.fetchAndExtract(params[0], 2500, true); + mTitleText = result.getTitle(); + mBodyText = result.getTextList(); + } catch (Exception e) { + mTitleText = ""; + mBodyText = new ArrayList<>(); + e.printStackTrace(); + } catch (OutOfMemoryError e) { + System.gc(); + mTitleText = ""; + mBodyText = new ArrayList<>(); + e.printStackTrace(); + } + return null; + } - @Override - protected void onPostExecute(Void result) { - mProgressDialog.dismiss(); - if (mTitleText.isEmpty() || mBodyText.isEmpty()) { - setText(getString(R.string.untitled), getString(R.string.loading_failed)); - } else { - StringBuilder builder = new StringBuilder(); - for (String text : mBodyText) { - builder.append(text).append("\n\n"); - } - setText(mTitleText, builder.toString()); - } - super.onPostExecute(result); - } + @Override + protected void onPostExecute(Void result) { + mProgressDialog.dismiss(); + if (mTitleText.isEmpty() || mBodyText.isEmpty()) { + setText(getString(R.string.untitled), getString(R.string.loading_failed)); + } else { + StringBuilder builder = new StringBuilder(); + for (String text : mBodyText) { + builder.append(text).append("\n\n"); + } + setText(mTitleText, builder.toString()); + } + super.onPostExecute(result); + } - } + } - private void setText(String title, String body) { - if (mTitle.getVisibility() == View.INVISIBLE) { - mTitle.setAlpha(0.0f); - mTitle.setVisibility(View.VISIBLE); - mTitle.setText(title); - ObjectAnimator animator = ObjectAnimator.ofFloat(mTitle, "alpha", 1.0f); - animator.setDuration(300); - animator.start(); - } else { - mTitle.setText(title); - } + private void setText(String title, String body) { + if (mTitle.getVisibility() == View.INVISIBLE) { + mTitle.setAlpha(0.0f); + mTitle.setVisibility(View.VISIBLE); + mTitle.setText(title); + ObjectAnimator animator = ObjectAnimator.ofFloat(mTitle, "alpha", 1.0f); + animator.setDuration(300); + animator.start(); + } else { + mTitle.setText(title); + } - if (mBody.getVisibility() == View.INVISIBLE) { - mBody.setAlpha(0.0f); - mBody.setVisibility(View.VISIBLE); - mBody.setText(body); - ObjectAnimator animator = ObjectAnimator.ofFloat(mBody, "alpha", 1.0f); - animator.setDuration(300); - animator.start(); - } else { - mBody.setText(body); - } - } + if (mBody.getVisibility() == View.INVISIBLE) { + mBody.setAlpha(0.0f); + mBody.setVisibility(View.VISIBLE); + mBody.setText(body); + ObjectAnimator animator = ObjectAnimator.ofFloat(mBody, "alpha", 1.0f); + animator.setDuration(300); + animator.start(); + } else { + mBody.setText(body); + } + } - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.invert_item: - mPreferences.setInvertColors(!mInvert); - Intent read = new Intent(this, ReadingActivity.class); - read.putExtra(Constants.LOAD_READING_URL, mUrl); - startActivity(read); - finish(); - break; - case R.id.text_size_item: - AlertDialog.Builder builder = new AlertDialog.Builder(this); - LayoutInflater inflater = this.getLayoutInflater(); - View view = inflater.inflate(R.layout.seek_layout, null); - final SeekBar bar = (SeekBar) view.findViewById(R.id.text_size_seekbar); - bar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.invert_item: + mPreferences.setInvertColors(!mInvert); + Intent read = new Intent(this, ReadingActivity.class); + read.putExtra(Constants.LOAD_READING_URL, mUrl); + startActivity(read); + finish(); + break; + case R.id.text_size_item: + AlertDialog.Builder builder = new AlertDialog.Builder(this); + LayoutInflater inflater = this.getLayoutInflater(); + View view = inflater.inflate(R.layout.seek_layout, null); + final SeekBar bar = (SeekBar) view.findViewById(R.id.text_size_seekbar); + bar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { - @Override - public void onProgressChanged(SeekBar view, int size, boolean user) { - mBody.setTextSize(getTextSize(size)); - } + @Override + public void onProgressChanged(SeekBar view, int size, boolean user) { + mBody.setTextSize(getTextSize(size)); + } - @Override - public void onStartTrackingTouch(SeekBar arg0) { - } + @Override + public void onStartTrackingTouch(SeekBar arg0) { + } - @Override - public void onStopTrackingTouch(SeekBar arg0) { - } + @Override + public void onStopTrackingTouch(SeekBar arg0) { + } - }); - bar.setMax(5); - bar.setProgress(mTextSize); - builder.setView(view); - builder.setTitle(R.string.size); - builder.setPositiveButton(android.R.string.ok, new OnClickListener() { + }); + bar.setMax(5); + bar.setProgress(mTextSize); + builder.setView(view); + builder.setTitle(R.string.size); + builder.setPositiveButton(android.R.string.ok, new OnClickListener() { - @Override - public void onClick(DialogInterface arg0, int arg1) { - mTextSize = bar.getProgress(); - mBody.setTextSize(getTextSize(mTextSize)); - mPreferences.setReadingTextSize(bar.getProgress()); - } + @Override + public void onClick(DialogInterface arg0, int arg1) { + mTextSize = bar.getProgress(); + mBody.setTextSize(getTextSize(mTextSize)); + mPreferences.setReadingTextSize(bar.getProgress()); + } - }); - builder.show(); - break; - default: - finish(); - break; - } - return super.onOptionsItemSelected(item); - } + }); + builder.show(); + break; + default: + finish(); + break; + } + return super.onOptionsItemSelected(item); + } } diff --git a/app/src/main/java/acr/browser/lightning/activity/SettingsActivity.java b/app/src/main/java/acr/browser/lightning/activity/SettingsActivity.java index eea7094..8c8fa36 100644 --- a/app/src/main/java/acr/browser/lightning/activity/SettingsActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/SettingsActivity.java @@ -17,7 +17,7 @@ import acr.browser.lightning.R; public class SettingsActivity extends ThemableSettingsActivity { - private static List fragments = new ArrayList<>(); + private static final List fragments = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { diff --git a/app/src/main/java/acr/browser/lightning/activity/ThemableActivity.java b/app/src/main/java/acr/browser/lightning/activity/ThemableActivity.java index 948cc83..a4007e1 100644 --- a/app/src/main/java/acr/browser/lightning/activity/ThemableActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/ThemableActivity.java @@ -33,9 +33,7 @@ public abstract class ThemableActivity extends AppCompatActivity { } protected void restart() { - final Bundle outState = new Bundle(); - onSaveInstanceState(outState); - final Intent intent = new Intent(this, getClass()); + Intent intent = getIntent(); finish(); overridePendingTransition(0, 0); startActivity(intent); diff --git a/app/src/main/java/acr/browser/lightning/activity/ThemableSettingsActivity.java b/app/src/main/java/acr/browser/lightning/activity/ThemableSettingsActivity.java index 9c8913f..2c41602 100644 --- a/app/src/main/java/acr/browser/lightning/activity/ThemableSettingsActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/ThemableSettingsActivity.java @@ -6,8 +6,9 @@ import android.os.Bundle; import acr.browser.lightning.R; import acr.browser.lightning.preference.PreferenceManager; +import acr.browser.lightning.utils.ThemeUtils; -public abstract class ThemableSettingsActivity extends AppCompatPreferenceActivity { +public abstract class ThemableSettingsActivity extends AppCompatPreferenceActivity { private int mTheme; @@ -16,11 +17,15 @@ public abstract class ThemableSettingsActivity extends AppCompatPreferenceActivi mTheme = PreferenceManager.getInstance().getUseTheme(); // set the theme - if (mTheme == 1) { + if (mTheme == 0) { + setTheme(R.style.Theme_SettingsTheme); + this.getWindow().setBackgroundDrawable(new ColorDrawable(ThemeUtils.getPrimaryColor(this))); + } else if (mTheme == 1) { setTheme(R.style.Theme_SettingsTheme_Dark); + this.getWindow().setBackgroundDrawable(new ColorDrawable(ThemeUtils.getPrimaryColorDark(this))); } else if (mTheme == 2) { setTheme(R.style.Theme_SettingsTheme_Black); - this.getWindow().setBackgroundDrawable(new ColorDrawable(this.getResources().getColor(R.color.black))); + this.getWindow().setBackgroundDrawable(new ColorDrawable(ThemeUtils.getPrimaryColorDark(this))); } super.onCreate(savedInstanceState); } diff --git a/app/src/main/java/acr/browser/lightning/controller/BrowserController.java b/app/src/main/java/acr/browser/lightning/controller/BrowserController.java index c48e805..75e5b24 100644 --- a/app/src/main/java/acr/browser/lightning/controller/BrowserController.java +++ b/app/src/main/java/acr/browser/lightning/controller/BrowserController.java @@ -26,7 +26,7 @@ public interface BrowserController { void onLongPress(); - void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback); + void onShowCustomView(View view, CustomViewCallback callback); void onHideCustomView(); @@ -34,7 +34,7 @@ public interface BrowserController { View getVideoLoadingProgressView(); - void onCreateWindow(boolean isUserGesture, Message resultMsg); + void onCreateWindow(Message resultMsg); Activity getActivity(); diff --git a/app/src/main/java/acr/browser/lightning/database/BookmarkManager.java b/app/src/main/java/acr/browser/lightning/database/BookmarkManager.java index b42ebb6..3cdeef4 100644 --- a/app/src/main/java/acr/browser/lightning/database/BookmarkManager.java +++ b/app/src/main/java/acr/browser/lightning/database/BookmarkManager.java @@ -30,398 +30,422 @@ import acr.browser.lightning.utils.Utils; public class BookmarkManager { - private final Context mContext; - private static final String TITLE = "title"; - private static final String URL = "url"; - private static final String FOLDER = "folder"; - private static final String ORDER = "order"; - private static final String FILE_BOOKMARKS = "bookmarks.dat"; - private static SortedMap mBookmarkMap = new TreeMap<>( - String.CASE_INSENSITIVE_ORDER); - private static BookmarkManager mInstance; + private final Context mContext; + private static final String TITLE = "title"; + private static final String URL = "url"; + private static final String FOLDER = "folder"; + private static final String ORDER = "order"; + private static final String FILE_BOOKMARKS = "bookmarks.dat"; + private static SortedMap mBookmarkMap = new TreeMap<>( + String.CASE_INSENSITIVE_ORDER); + private static BookmarkManager mInstance; - public static BookmarkManager getInstance(Context context) { - if (mInstance == null) { - mInstance = new BookmarkManager(context); - } - return mInstance; - } + public static BookmarkManager getInstance(Context context) { + if (mInstance == null) { + mInstance = new BookmarkManager(context); + } + return mInstance; + } - private BookmarkManager(Context context) { - mContext = context; - mBookmarkMap = getBookmarkUrls(); - } + private BookmarkManager(Context context) { + mContext = context; + mBookmarkMap = getBookmarkUrls(); + } - /** - * This method adds the the HistoryItem item to permanent bookmark storage - * - * @param item - */ - public synchronized boolean addBookmark(HistoryItem item) { - File bookmarksFile = new File(mContext.getFilesDir(), FILE_BOOKMARKS); - if (item.getUrl() == null || mBookmarkMap.containsKey(item.getUrl())) { - return false; - } - try { - BufferedWriter bookmarkWriter = new BufferedWriter(new FileWriter(bookmarksFile, true)); - JSONObject object = new JSONObject(); - object.put(TITLE, item.getTitle()); - object.put(URL, item.getUrl()); - object.put(FOLDER, item.getFolder()); - object.put(ORDER, item.getOrder()); - bookmarkWriter.write(object.toString()); - bookmarkWriter.newLine(); - bookmarkWriter.close(); - mBookmarkMap.put(item.getUrl(), 1); - } catch (IOException | JSONException e) { - e.printStackTrace(); - } - return true; - } + /** + * This method adds the the HistoryItem item to permanent bookmark storage + * + * @param item the item to add + */ + public synchronized boolean addBookmark(HistoryItem item) { + File bookmarksFile = new File(mContext.getFilesDir(), FILE_BOOKMARKS); + if (item.getUrl() == null || mBookmarkMap.containsKey(item.getUrl())) { + return false; + } + BufferedWriter bookmarkWriter = null; + try { + bookmarkWriter = new BufferedWriter(new FileWriter(bookmarksFile, true)); + JSONObject object = new JSONObject(); + object.put(TITLE, item.getTitle()); + object.put(URL, item.getUrl()); + object.put(FOLDER, item.getFolder()); + object.put(ORDER, item.getOrder()); + bookmarkWriter.write(object.toString()); + bookmarkWriter.newLine(); + bookmarkWriter.close(); + mBookmarkMap.put(item.getUrl(), 1); + } catch (IOException | JSONException e) { + e.printStackTrace(); + } finally { + Utils.close(bookmarkWriter); + } + return true; + } - /** - * This method adds the list of HistoryItems to permanent bookmark storage - * - * @param list - */ - public synchronized void addBookmarkList(List list) { - File bookmarksFile = new File(mContext.getFilesDir(), FILE_BOOKMARKS); - try { - BufferedWriter bookmarkWriter = new BufferedWriter(new FileWriter(bookmarksFile, true)); - for (HistoryItem item : list) { - if (item.getUrl() != null && !mBookmarkMap.containsKey(item.getUrl())) { - JSONObject object = new JSONObject(); - object.put(TITLE, item.getTitle()); - object.put(URL, item.getUrl()); - object.put(FOLDER, item.getFolder()); - object.put(ORDER, item.getOrder()); - bookmarkWriter.write(object.toString()); - bookmarkWriter.newLine(); - mBookmarkMap.put(item.getUrl(), 1); - } - } - bookmarkWriter.close(); - } catch (IOException | JSONException e) { - e.printStackTrace(); - } - } + /** + * This method adds the list of HistoryItems to permanent bookmark storage + * + * @param list the list of HistoryItems to add to bookmarks + */ + private synchronized void addBookmarkList(List list) { + File bookmarksFile = new File(mContext.getFilesDir(), FILE_BOOKMARKS); + BufferedWriter bookmarkWriter = null; + try { + bookmarkWriter = new BufferedWriter(new FileWriter(bookmarksFile, true)); + JSONObject object = new JSONObject(); + for (HistoryItem item : list) { + if (item.getUrl() != null && !mBookmarkMap.containsKey(item.getUrl())) { + object.put(TITLE, item.getTitle()); + object.put(URL, item.getUrl()); + object.put(FOLDER, item.getFolder()); + object.put(ORDER, item.getOrder()); + bookmarkWriter.write(object.toString()); + bookmarkWriter.newLine(); + mBookmarkMap.put(item.getUrl(), 1); + } + } + } catch (IOException | JSONException e) { + e.printStackTrace(); + } finally { + Utils.close(bookmarkWriter); + } + } - /** - * This method deletes the bookmark with the given url - * - * @param url - */ - public synchronized boolean deleteBookmark(String url) { - List list; - if (url == null) { - return false; - } - mBookmarkMap.remove(url); - list = getBookmarks(false); - File bookmarksFile = new File(mContext.getFilesDir(), FILE_BOOKMARKS); - boolean bookmarkDeleted = false; - try { - BufferedWriter fileWriter = new BufferedWriter(new FileWriter(bookmarksFile, false)); - for (HistoryItem item : list) { - if (!item.getUrl().equalsIgnoreCase(url)) { - JSONObject object = new JSONObject(); - object.put(TITLE, item.getTitle()); - object.put(URL, item.getUrl()); - object.put(FOLDER, item.getFolder()); - object.put(ORDER, item.getOrder()); - fileWriter.write(object.toString()); - fileWriter.newLine(); - } else { - bookmarkDeleted = true; - } - } - fileWriter.close(); - } catch (IOException | JSONException e) { - e.printStackTrace(); - } - return bookmarkDeleted; - } + /** + * This method deletes the bookmark with the given url + * + * @param url the url of the bookmark to delete + */ + public synchronized boolean deleteBookmark(String url) { + List list; + if (url == null) { + return false; + } + mBookmarkMap.remove(url); + list = getBookmarks(false); + File bookmarksFile = new File(mContext.getFilesDir(), FILE_BOOKMARKS); + boolean bookmarkDeleted = false; + BufferedWriter fileWriter = null; + try { + fileWriter = new BufferedWriter(new FileWriter(bookmarksFile, false)); + JSONObject object = new JSONObject(); + for (HistoryItem item : list) { + if (!item.getUrl().equalsIgnoreCase(url)) { + object.put(TITLE, item.getTitle()); + object.put(URL, item.getUrl()); + object.put(FOLDER, item.getFolder()); + object.put(ORDER, item.getOrder()); + fileWriter.write(object.toString()); + fileWriter.newLine(); + } else { + bookmarkDeleted = true; + } + } + } catch (IOException | JSONException e) { + e.printStackTrace(); + } finally { + Utils.close(fileWriter); + } + return bookmarkDeleted; + } - /** - * This method exports the stored bookmarks to a text file in the device's - * external download directory - */ - public synchronized void exportBookmarks() { - List bookmarkList = getBookmarks(true); - File bookmarksExport = new File( - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), - "BookmarksExport.txt"); - int counter = 0; - while (bookmarksExport.exists()) { - counter++; - bookmarksExport = new File( - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), - "BookmarksExport-" + counter + ".txt"); - } - try { - BufferedWriter bookmarkWriter = new BufferedWriter(new FileWriter(bookmarksExport, - false)); - for (HistoryItem item : bookmarkList) { - JSONObject object = new JSONObject(); - object.put(TITLE, item.getTitle()); - object.put(URL, item.getUrl()); - object.put(FOLDER, item.getFolder()); - object.put(ORDER, item.getOrder()); - bookmarkWriter.write(object.toString()); - bookmarkWriter.newLine(); - } - bookmarkWriter.close(); - Toast.makeText( - mContext, - mContext.getString(R.string.bookmark_export_path) + " " - + bookmarksExport.getPath(), Toast.LENGTH_SHORT).show(); - } catch (IOException | JSONException e) { - e.printStackTrace(); - } + /** + * This method exports the stored bookmarks to a text file in the device's + * external download directory + */ + public synchronized void exportBookmarks() { + List bookmarkList = getBookmarks(true); + File bookmarksExport = new File( + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), + "BookmarksExport.txt"); + int counter = 0; + while (bookmarksExport.exists()) { + counter++; + bookmarksExport = new File( + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), + "BookmarksExport-" + counter + ".txt"); + } + BufferedWriter bookmarkWriter = null; + try { + bookmarkWriter = new BufferedWriter(new FileWriter(bookmarksExport, + false)); + JSONObject object = new JSONObject(); + for (HistoryItem item : bookmarkList) { + object.put(TITLE, item.getTitle()); + object.put(URL, item.getUrl()); + object.put(FOLDER, item.getFolder()); + object.put(ORDER, item.getOrder()); + bookmarkWriter.write(object.toString()); + bookmarkWriter.newLine(); + } + if (mContext != null) + Toast.makeText( + mContext, + mContext.getString(R.string.bookmark_export_path) + ' ' + + bookmarksExport.getPath(), Toast.LENGTH_SHORT).show(); + } catch (IOException | JSONException e) { + e.printStackTrace(); + } finally { + Utils.close(bookmarkWriter); + } - } + } - /** - * This method returns a list of all stored bookmarks - * - * @return - */ - public synchronized List getBookmarks(boolean sort) { - List bookmarks = new ArrayList<>(); - File bookmarksFile = new File(mContext.getFilesDir(), FILE_BOOKMARKS); - try { - BufferedReader bookmarksReader = new BufferedReader(new FileReader(bookmarksFile)); - String line; - while ((line = bookmarksReader.readLine()) != null) { - JSONObject object = new JSONObject(line); - HistoryItem item = new HistoryItem(); - item.setTitle(object.getString(TITLE)); - item.setUrl(object.getString(URL)); - item.setFolder(object.getString(FOLDER)); - item.setOrder(object.getInt(ORDER)); - item.setImageId(R.drawable.ic_bookmark); - bookmarks.add(item); - } - bookmarksReader.close(); - } catch (IOException | JSONException e) { - e.printStackTrace(); - } - if (sort) { - Collections.sort(bookmarks, new SortIgnoreCase()); - } - return bookmarks; - } + /** + * This method returns a list of all stored bookmarks + * + * @return returns a list of bookmarks that can be sorted + */ + public synchronized List getBookmarks(boolean sort) { + List bookmarks = new ArrayList<>(); + File bookmarksFile = new File(mContext.getFilesDir(), FILE_BOOKMARKS); + BufferedReader bookmarksReader = null; + try { + bookmarksReader = new BufferedReader(new FileReader(bookmarksFile)); + String line; + while ((line = bookmarksReader.readLine()) != null) { + JSONObject object = new JSONObject(line); + HistoryItem item = new HistoryItem(); + item.setTitle(object.getString(TITLE)); + item.setUrl(object.getString(URL)); + item.setFolder(object.getString(FOLDER)); + item.setOrder(object.getInt(ORDER)); + item.setImageId(R.drawable.ic_bookmark); + bookmarks.add(item); + } + } catch (IOException | JSONException e) { + e.printStackTrace(); + } finally { + Utils.close(bookmarksReader); + } + if (sort) { + Collections.sort(bookmarks, new SortIgnoreCase()); + } + return bookmarks; + } - /** - * This method returns a list of bookmarks located in the specified folder - * - * @param folder - * @return - */ - public synchronized List getBookmarksFromFolder(String folder) { - List bookmarks = new ArrayList<>(); - File bookmarksFile = new File(mContext.getFilesDir(), FILE_BOOKMARKS); - try { - BufferedReader bookmarksReader = new BufferedReader(new FileReader(bookmarksFile)); - String line; - while ((line = bookmarksReader.readLine()) != null) { - JSONObject object = new JSONObject(line); - if (object.getString(FOLDER).equals(folder)) { - HistoryItem item = new HistoryItem(); - item.setTitle(object.getString(TITLE)); - item.setUrl(object.getString(URL)); - item.setFolder(object.getString(FOLDER)); - item.setOrder(object.getInt(ORDER)); - item.setImageId(R.drawable.ic_bookmark); - bookmarks.add(item); - } - } - bookmarksReader.close(); - } catch (IOException | JSONException e) { - e.printStackTrace(); - } - return bookmarks; - } + /** + * This method returns a list of bookmarks located in the specified folder + * + * @param folder the name of the folder to retrieve bookmarks from + * @return a list of bookmarks found in that folder + */ + public synchronized List getBookmarksFromFolder(String folder) { + List bookmarks = new ArrayList<>(); + File bookmarksFile = new File(mContext.getFilesDir(), FILE_BOOKMARKS); + BufferedReader bookmarksReader = null; + try { + bookmarksReader = new BufferedReader(new FileReader(bookmarksFile)); + String line; + while ((line = bookmarksReader.readLine()) != null) { + JSONObject object = new JSONObject(line); + if (object.getString(FOLDER).equals(folder)) { + HistoryItem item = new HistoryItem(); + item.setTitle(object.getString(TITLE)); + item.setUrl(object.getString(URL)); + item.setFolder(object.getString(FOLDER)); + item.setOrder(object.getInt(ORDER)); + item.setImageId(R.drawable.ic_bookmark); + bookmarks.add(item); + } + } + bookmarksReader.close(); + } catch (IOException | JSONException e) { + e.printStackTrace(); + } finally { + Utils.close(bookmarksReader); + } + return bookmarks; + } - /** - * Method is used internally for searching the bookmarks - * - * @return - */ - private synchronized SortedMap getBookmarkUrls() { - SortedMap map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - File bookmarksFile = new File(mContext.getFilesDir(), FILE_BOOKMARKS); - try { - BufferedReader bookmarksReader = new BufferedReader(new FileReader(bookmarksFile)); - String line; - while ((line = bookmarksReader.readLine()) != null) { - JSONObject object = new JSONObject(line); - map.put(object.getString(URL), 1); - } - bookmarksReader.close(); - } catch (JSONException | IOException e) { - e.printStackTrace(); - } - return map; - } + /** + * Method is used internally for searching the bookmarks + * + * @return a sorted map of all bookmarks, useful for seeing if a bookmark exists + */ + private synchronized SortedMap getBookmarkUrls() { + SortedMap map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + File bookmarksFile = new File(mContext.getFilesDir(), FILE_BOOKMARKS); + BufferedReader bookmarksReader = null; + try { + bookmarksReader = new BufferedReader(new FileReader(bookmarksFile)); + String line; + while ((line = bookmarksReader.readLine()) != null) { + JSONObject object = new JSONObject(line); + map.put(object.getString(URL), 1); + } + bookmarksReader.close(); + } catch (JSONException | IOException e) { + e.printStackTrace(); + } finally { + Utils.close(bookmarksReader); + } + return map; + } - /** - * This method returns a list of all folders - * - * @return - */ - public synchronized List getFolders() { - List folders = new ArrayList<>(); - SortedMap folderMap = new TreeMap<>( - String.CASE_INSENSITIVE_ORDER); - File bookmarksFile = new File(mContext.getFilesDir(), FILE_BOOKMARKS); - try { - BufferedReader bookmarksReader = new BufferedReader(new FileReader(bookmarksFile)); - String line; - while ((line = bookmarksReader.readLine()) != null) { - JSONObject object = new JSONObject(line); - String folderName = object.getString(FOLDER); - if (!folderName.isEmpty() && !folderMap.containsKey(folderName)) { - HistoryItem item = new HistoryItem(); - item.setTitle(folderName); - item.setUrl(Constants.FOLDER + folderName); - folderMap.put(folderName, 1); - folders.add(item); - } - } - bookmarksReader.close(); - } catch (IOException | JSONException e) { - e.printStackTrace(); - } - return folders; - } + /** + * This method returns a list of all folders + * + * @return a list of all folders + */ + public synchronized List getFolders() { + List folders = new ArrayList<>(); + SortedMap folderMap = new TreeMap<>( + String.CASE_INSENSITIVE_ORDER); + File bookmarksFile = new File(mContext.getFilesDir(), FILE_BOOKMARKS); + BufferedReader bookmarksReader = null; + try { + bookmarksReader = new BufferedReader(new FileReader(bookmarksFile)); + String line; + while ((line = bookmarksReader.readLine()) != null) { + JSONObject object = new JSONObject(line); + String folderName = object.getString(FOLDER); + if (!folderName.isEmpty() && !folderMap.containsKey(folderName)) { + HistoryItem item = new HistoryItem(); + item.setTitle(folderName); + item.setUrl(Constants.FOLDER + folderName); + folderMap.put(folderName, 1); + folders.add(item); + } + } + } catch (IOException | JSONException e) { + e.printStackTrace(); + } finally { + Utils.close(bookmarksReader); + } + return folders; + } - /** - * This method imports all bookmarks that are included in the device's - * permanent bookmark storage - */ - public synchronized void importBookmarksFromBrowser(Context context) { - if (PreferenceManager.getInstance().getSystemBrowserPresent()) { + /** + * This method imports all bookmarks that are included in the device's + * permanent bookmark storage + */ + public synchronized void importBookmarksFromBrowser(Context context) { + if (PreferenceManager.getInstance().getSystemBrowserPresent()) { - List bookmarkList = new ArrayList<>(); - String[] columns = new String[] { Browser.BookmarkColumns.TITLE, - Browser.BookmarkColumns.URL }; - String selection = Browser.BookmarkColumns.BOOKMARK + " = 1"; - Cursor cursor = mContext.getContentResolver().query(Browser.BOOKMARKS_URI, columns, - selection, null, null); + List bookmarkList = new ArrayList<>(); + String[] columns = new String[]{Browser.BookmarkColumns.TITLE, + Browser.BookmarkColumns.URL}; + String selection = Browser.BookmarkColumns.BOOKMARK + " = 1"; + Cursor cursor = mContext.getContentResolver().query(Browser.BOOKMARKS_URI, columns, + selection, null, null); + if (cursor == null) + return; + String title, url; + int number = 0; + if (cursor.moveToFirst()) { + do { + title = cursor.getString(0); + url = cursor.getString(1); + if (title.isEmpty()) { + title = Utils.getDomainName(url); + } + number++; + bookmarkList.add(new HistoryItem(url, title)); + } while (cursor.moveToNext()); + } - String title, url; - int number = 0; - if (cursor.moveToFirst()) { - do { - title = cursor.getString(0); - url = cursor.getString(1); - if (title.isEmpty()) { - title = Utils.getDomainName(url); - } - number++; - bookmarkList.add(new HistoryItem(url, title)); - } while (cursor.moveToNext()); - } + cursor.close(); + addBookmarkList(bookmarkList); - cursor.close(); - addBookmarkList(bookmarkList); + Utils.showToast(context, + number + " " + mContext.getResources().getString(R.string.message_import)); + } else { + Utils.createInformativeDialog(context, + mContext.getResources().getString(R.string.title_error), mContext + .getResources().getString(R.string.dialog_import_error)); + } + } - Utils.showToast(mContext, - number + " " + mContext.getResources().getString(R.string.message_import)); - } else { - Utils.createInformativeDialog(context, - mContext.getResources().getString(R.string.title_error), mContext - .getResources().getString(R.string.dialog_import_error)); - } - } + /** + * This method imports the bookmarks from a backup file that is located on + * external storage + * + * @param file the file to attempt to import bookmarks from + */ + public synchronized void importBookmarksFromFile(File file, Context context) { + if (file == null) { + return; + } + List list = new ArrayList<>(); + BufferedReader bookmarksReader = null; + try { + bookmarksReader = new BufferedReader(new FileReader(file)); + String line; + int number = 0; + while ((line = bookmarksReader.readLine()) != null) { + JSONObject object = new JSONObject(line); + HistoryItem item = new HistoryItem(); + item.setTitle(object.getString(TITLE)); + item.setUrl(object.getString(URL)); + item.setFolder(object.getString(FOLDER)); + item.setOrder(object.getInt(ORDER)); + list.add(item); + number++; + } + addBookmarkList(list); + Utils.showToast(context, + number + " " + mContext.getResources().getString(R.string.message_import)); + } catch (IOException | JSONException e) { + e.printStackTrace(); + Utils.createInformativeDialog(context, + mContext.getResources().getString(R.string.title_error), mContext + .getResources().getString(R.string.import_bookmark_error)); + } finally { + Utils.close(bookmarksReader); + } + } - /** - * This method imports the bookmarks from a backup file that is located on - * external storage - * - * @param dir - * @param file - */ - public synchronized void importBookmarksFromFile(File file, Context context) { - if (file == null) { - return; - } - List list = new ArrayList<>(); - try { - BufferedReader bookmarksReader = new BufferedReader(new FileReader(file)); - String line; - int number = 0; - while ((line = bookmarksReader.readLine()) != null) { - JSONObject object = new JSONObject(line); - HistoryItem item = new HistoryItem(); - item.setTitle(object.getString(TITLE)); - item.setUrl(object.getString(URL)); - item.setFolder(object.getString(FOLDER)); - item.setOrder(object.getInt(ORDER)); - list.add(item); - number++; - } - bookmarksReader.close(); - addBookmarkList(list); - Utils.showToast(mContext, - number + " " + mContext.getResources().getString(R.string.message_import)); - } catch (IOException | JSONException e) { - e.printStackTrace(); - Utils.createInformativeDialog(context, - mContext.getResources().getString(R.string.title_error), mContext - .getResources().getString(R.string.import_bookmark_error)); - } - } + /** + * This method overwrites the entire bookmark file with the list of + * bookmarks. This is useful when an edit has been made to one or more + * bookmarks in the list + * + * @param list the list of bookmarks to overwrite the old ones with + */ + public synchronized void overwriteBookmarks(List list) { + File bookmarksFile = new File(mContext.getFilesDir(), FILE_BOOKMARKS); + BufferedWriter bookmarkWriter = null; + try { + bookmarkWriter = new BufferedWriter(new FileWriter(bookmarksFile, false)); + JSONObject object = new JSONObject(); + for (HistoryItem item : list) { + object.put(TITLE, item.getTitle()); + object.put(URL, item.getUrl()); + object.put(FOLDER, item.getFolder()); + object.put(ORDER, item.getOrder()); + bookmarkWriter.write(object.toString()); + bookmarkWriter.newLine(); + } + } catch (IOException | JSONException e) { + e.printStackTrace(); + } finally { + Utils.close(bookmarkWriter); + } + } - /** - * This method overwrites the entire bookmark file with the list of - * bookmarks. This is useful when an edit has been made to one or more - * bookmarks in the list - * - * @param list - */ - public synchronized void overwriteBookmarks(List list) { - File bookmarksFile = new File(mContext.getFilesDir(), FILE_BOOKMARKS); - try { - BufferedWriter bookmarkWriter = new BufferedWriter(new FileWriter(bookmarksFile, false)); - for (HistoryItem item : list) { - JSONObject object = new JSONObject(); - object.put(TITLE, item.getTitle()); - object.put(URL, item.getUrl()); - object.put(FOLDER, item.getFolder()); - object.put(ORDER, item.getOrder()); - bookmarkWriter.write(object.toString()); - bookmarkWriter.newLine(); - } - bookmarkWriter.close(); - } catch (IOException | JSONException e) { - e.printStackTrace(); - } - } + private class SortIgnoreCase implements Comparator { - private class SortIgnoreCase implements Comparator { + public int compare(HistoryItem o1, HistoryItem o2) { + if (o1 == null || o2 == null || o1.getTitle() == null || o2.getTitle() == null) { + return 0; + } + return o1.getTitle().toLowerCase(Locale.getDefault()) + .compareTo(o2.getTitle().toLowerCase(Locale.getDefault())); + } - public int compare(HistoryItem o1, HistoryItem o2) { - if (o1 == null || o2 == null || o1.getTitle() == null || o2.getTitle() == null) { - return 0; - } - return o1.getTitle().toLowerCase(Locale.getDefault()) - .compareTo(o2.getTitle().toLowerCase(Locale.getDefault())); - } + } - } - - private static final String[] DEV = {"https://twitter.com/RestainoAnthony", "The Developer"}; - private static final String[] FACEBOOK = {"https://www.facebook.com/", "Facebook"}; - private static final String[] TWITTER = {"https://twitter.com", "Twitter"}; - private static final String[] GOOGLE = {"https://www.google.com/", "Google"}; - private static final String[] YAHOO = {"https://www.yahoo.com/", "Yahoo"}; - public static final String[][] DEFAULT_BOOKMARKS = { - DEV, - FACEBOOK, - TWITTER, - GOOGLE, - YAHOO - }; + private static final String[] DEV = {"https://twitter.com/RestainoAnthony", "The Developer"}; + private static final String[] FACEBOOK = {"https://www.facebook.com/", "Facebook"}; + private static final String[] TWITTER = {"https://twitter.com", "Twitter"}; + private static final String[] GOOGLE = {"https://www.google.com/", "Google"}; + private static final String[] YAHOO = {"https://www.yahoo.com/", "Yahoo"}; + public static final String[][] DEFAULT_BOOKMARKS = { + DEV, + FACEBOOK, + TWITTER, + GOOGLE, + YAHOO + }; } diff --git a/app/src/main/java/acr/browser/lightning/database/HistoryDatabase.java b/app/src/main/java/acr/browser/lightning/database/HistoryDatabase.java index 0e20397..11ec694 100644 --- a/app/src/main/java/acr/browser/lightning/database/HistoryDatabase.java +++ b/app/src/main/java/acr/browser/lightning/database/HistoryDatabase.java @@ -65,7 +65,7 @@ public class HistoryDatabase extends SQLiteOpenHelper { onCreate(db); } - public boolean isClosed() { + private boolean isClosed() { return mDatabase == null || !mDatabase.isOpen(); } @@ -95,7 +95,7 @@ public class HistoryDatabase extends SQLiteOpenHelper { q.close(); } - public synchronized void addHistoryItem(HistoryItem item) { + private synchronized void addHistoryItem(HistoryItem item) { ContentValues values = new ContentValues(); values.put(KEY_URL, item.getUrl()); values.put(KEY_TITLE, item.getTitle()); diff --git a/app/src/main/java/acr/browser/lightning/download/DownloadHandler.java b/app/src/main/java/acr/browser/lightning/download/DownloadHandler.java index 58f56b0..41b6529 100644 --- a/app/src/main/java/acr/browser/lightning/download/DownloadHandler.java +++ b/app/src/main/java/acr/browser/lightning/download/DownloadHandler.java @@ -4,7 +4,6 @@ package acr.browser.lightning.download; import android.app.Activity; -import android.app.AlertDialog; import android.app.DownloadManager; import android.content.ActivityNotFoundException; import android.content.ComponentName; @@ -14,6 +13,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Environment; +import android.support.v7.app.AlertDialog; import android.text.TextUtils; import android.util.Log; import android.webkit.CookieManager; @@ -22,197 +22,190 @@ import android.widget.Toast; import acr.browser.lightning.R; import acr.browser.lightning.preference.PreferenceManager; +import acr.browser.lightning.utils.Utils; /** * Handle download requests */ public class DownloadHandler { - private static final String LOGTAG = "DLHandler"; + private static final String LOGTAG = "DLHandler"; - /** - * Notify the host application a download should be done, or that the data - * should be streamed if a streaming viewer is available. - * - * @param activity - * Activity requesting the download. - * @param url - * The full url to the content that should be downloaded - * @param userAgent - * User agent of the downloading application. - * @param contentDisposition - * Content-disposition http header, if present. - * @param mimetype - * The mimetype of the content reported by the server - * @param privateBrowsing - * If the request is coming from a private browsing tab. - */ - public static void onDownloadStart(Activity activity, String url, String userAgent, - String contentDisposition, String mimetype, boolean privateBrowsing) { - // if we're dealing wih A/V content that's not explicitly marked - // for download, check if it's streamable. - if (contentDisposition == null - || !contentDisposition.regionMatches(true, 0, "attachment", 0, 10)) { - // query the package manager to see if there's a registered handler - // that matches. - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setDataAndType(Uri.parse(url), mimetype); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - ResolveInfo info = activity.getPackageManager().resolveActivity(intent, - PackageManager.MATCH_DEFAULT_ONLY); - if (info != null) { - ComponentName myName = activity.getComponentName(); - // If we resolved to ourselves, we don't want to attempt to - // load the url only to try and download it again. - if (!myName.getPackageName().equals(info.activityInfo.packageName) - || !myName.getClassName().equals(info.activityInfo.name)) { - // someone (other than us) knows how to handle this mime - // type with this scheme, don't download. - try { - activity.startActivity(intent); - return; - } catch (ActivityNotFoundException ex) { - // Best behavior is to fall back to a download in this - // case - } - } - } - } - onDownloadStartNoStream(activity, url, userAgent, contentDisposition, mimetype, - privateBrowsing); - } + /** + * Notify the host application a download should be done, or that the data + * should be streamed if a streaming viewer is available. + * + * @param activity Activity requesting the download. + * @param url The full url to the content that should be downloaded + * @param userAgent User agent of the downloading application. + * @param contentDisposition Content-disposition http header, if present. + * @param mimetype The mimetype of the content reported by the server + */ + public static void onDownloadStart(Activity activity, String url, String userAgent, + String contentDisposition, String mimetype) { + // if we're dealing wih A/V content that's not explicitly marked + // for download, check if it's streamable. + if (contentDisposition == null + || !contentDisposition.regionMatches(true, 0, "attachment", 0, 10)) { + // query the package manager to see if there's a registered handler + // that matches. + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(Uri.parse(url), mimetype); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + ResolveInfo info = activity.getPackageManager().resolveActivity(intent, + PackageManager.MATCH_DEFAULT_ONLY); + if (info != null) { + ComponentName myName = activity.getComponentName(); + // If we resolved to ourselves, we don't want to attempt to + // load the url only to try and download it again. + if (!myName.getPackageName().equals(info.activityInfo.packageName) + || !myName.getClassName().equals(info.activityInfo.name)) { + // someone (other than us) knows how to handle this mime + // type with this scheme, don't download. + try { + activity.startActivity(intent); + return; + } catch (ActivityNotFoundException ex) { + // Best behavior is to fall back to a download in this + // case + } + } + } + } + onDownloadStartNoStream(activity, url, userAgent, contentDisposition, mimetype + ); + } - // This is to work around the fact that java.net.URI throws Exceptions - // instead of just encoding URL's properly - // Helper method for onDownloadStartNoStream - private static String encodePath(String path) { - char[] chars = path.toCharArray(); + // This is to work around the fact that java.net.URI throws Exceptions + // instead of just encoding URL's properly + // Helper method for onDownloadStartNoStream + private static String encodePath(String path) { + char[] chars = path.toCharArray(); - boolean needed = false; - for (char c : chars) { - if (c == '[' || c == ']' || c == '|') { - needed = true; - break; - } - } - if (!needed) { - return path; - } + boolean needed = false; + for (char c : chars) { + if (c == '[' || c == ']' || c == '|') { + needed = true; + break; + } + } + if (!needed) { + return path; + } - StringBuilder sb = new StringBuilder(""); - for (char c : chars) { - if (c == '[' || c == ']' || c == '|') { - sb.append('%'); - sb.append(Integer.toHexString(c)); - } else { - sb.append(c); - } - } + StringBuilder sb = new StringBuilder(""); + for (char c : chars) { + if (c == '[' || c == ']' || c == '|') { + sb.append('%'); + sb.append(Integer.toHexString(c)); + } else { + sb.append(c); + } + } - return sb.toString(); - } + return sb.toString(); + } - /** - * Notify the host application a download should be done, even if there is a - * streaming viewer available for thise type. - * - * @param activity - * Activity requesting the download. - * @param url - * The full url to the content that should be downloaded - * @param userAgent - * User agent of the downloading application. - * @param contentDisposition - * Content-disposition http header, if present. - * @param mimetype - * The mimetype of the content reported by the server - * @param privateBrowsing - * If the request is coming from a private browsing tab. - */ - /* package */ - private static void onDownloadStartNoStream(Activity activity, String url, String userAgent, - String contentDisposition, String mimetype, boolean privateBrowsing) { + /** + * Notify the host application a download should be done, even if there is a + * streaming viewer available for thise type. + * + * @param activity Activity requesting the download. + * @param url The full url to the content that should be downloaded + * @param userAgent User agent of the downloading application. + * @param contentDisposition Content-disposition http header, if present. + * @param mimetype The mimetype of the content reported by the server + */ + /* package */ + private static void onDownloadStartNoStream(final Activity activity, String url, String userAgent, + String contentDisposition, String mimetype) { - String filename = URLUtil.guessFileName(url, contentDisposition, mimetype); + String filename = URLUtil.guessFileName(url, contentDisposition, mimetype); - // Check to see if we have an SDCard - String status = Environment.getExternalStorageState(); - if (!status.equals(Environment.MEDIA_MOUNTED)) { - int title; - String msg; + // Check to see if we have an SDCard + String status = Environment.getExternalStorageState(); + if (!status.equals(Environment.MEDIA_MOUNTED)) { + int title; + String msg; - // Check to see if the SDCard is busy, same as the music app - if (status.equals(Environment.MEDIA_SHARED)) { - msg = activity.getString(R.string.download_sdcard_busy_dlg_msg); - title = R.string.download_sdcard_busy_dlg_title; - } else { - msg = activity.getString(R.string.download_no_sdcard_dlg_msg, filename); - title = R.string.download_no_sdcard_dlg_title; - } + // Check to see if the SDCard is busy, same as the music app + if (status.equals(Environment.MEDIA_SHARED)) { + msg = activity.getString(R.string.download_sdcard_busy_dlg_msg); + title = R.string.download_sdcard_busy_dlg_title; + } else { + msg = activity.getString(R.string.download_no_sdcard_dlg_msg, filename); + title = R.string.download_no_sdcard_dlg_title; + } - new AlertDialog.Builder(activity).setTitle(title) - .setIcon(android.R.drawable.ic_dialog_alert).setMessage(msg) - .setPositiveButton(R.string.action_ok, null).show(); - return; - } + new AlertDialog.Builder(activity).setTitle(title) + .setIcon(android.R.drawable.ic_dialog_alert).setMessage(msg) + .setPositiveButton(R.string.action_ok, null).show(); + return; + } - // java.net.URI is a lot stricter than KURL so we have to encode some - // extra characters. Fix for b 2538060 and b 1634719 - WebAddress webAddress; - try { - webAddress = new WebAddress(url); - webAddress.setPath(encodePath(webAddress.getPath())); - } catch (Exception e) { - // This only happens for very bad urls, we want to catch the - // exception here - Log.e(LOGTAG, "Exception while trying to parse url '" + url + '\'', e); - return; - } + // java.net.URI is a lot stricter than KURL so we have to encode some + // extra characters. Fix for b 2538060 and b 1634719 + WebAddress webAddress; + try { + webAddress = new WebAddress(url); + webAddress.setPath(encodePath(webAddress.getPath())); + } catch (Exception e) { + // This only happens for very bad urls, we want to catch the + // exception here + Log.e(LOGTAG, "Exception while trying to parse url '" + url + '\'', e); + return; + } - String addressString = webAddress.toString(); - Uri uri = Uri.parse(addressString); - final DownloadManager.Request request; - try { - request = new DownloadManager.Request(uri); - } catch (IllegalArgumentException e) { - Toast.makeText(activity, R.string.cannot_download, Toast.LENGTH_SHORT).show(); - return; - } - request.setMimeType(mimetype); - // set downloaded file destination to /sdcard/Download. - // or, should it be set to one of several Environment.DIRECTORY* dirs - // depending on mimetype? + String addressString = webAddress.toString(); + Uri uri = Uri.parse(addressString); + final DownloadManager.Request request; + try { + request = new DownloadManager.Request(uri); + } catch (IllegalArgumentException e) { + Toast.makeText(activity, R.string.cannot_download, Toast.LENGTH_SHORT).show(); + return; + } + request.setMimeType(mimetype); + // set downloaded file destination to /sdcard/Download. + // or, should it be set to one of several Environment.DIRECTORY* dirs + // depending on mimetype? - String location = PreferenceManager.getInstance().getDownloadDirectory(); - request.setDestinationInExternalPublicDir(location, filename); - // let this downloaded file be scanned by MediaScanner - so that it can - // show up in Gallery app, for example. - request.allowScanningByMediaScanner(); - request.setDescription(webAddress.getHost()); - // XXX: Have to use the old url since the cookies were stored using the - // old percent-encoded url. - String cookies = CookieManager.getInstance().getCookie(url); - request.addRequestHeader("cookie", cookies); - request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); - if (mimetype == null) { - if (TextUtils.isEmpty(addressString)) { - return; - } - // We must have long pressed on a link or image to download it. We - // are not sure of the mimetype in this case, so do a head request - new FetchUrlMimeType(activity, request, addressString, cookies, userAgent).start(); - } else { - final DownloadManager manager = (DownloadManager) activity - .getSystemService(Context.DOWNLOAD_SERVICE); - new Thread("Browser download") { - @Override - public void run() { - manager.enqueue(request); - } - }.start(); - Toast.makeText(activity, R.string.download_pending, Toast.LENGTH_SHORT).show(); - } + String location = PreferenceManager.getInstance().getDownloadDirectory(); + request.setDestinationInExternalPublicDir(location, filename); + // let this downloaded file be scanned by MediaScanner - so that it can + // show up in Gallery app, for example. + request.allowScanningByMediaScanner(); + request.setDescription(webAddress.getHost()); + // XXX: Have to use the old url since the cookies were stored using the + // old percent-encoded url. + String cookies = CookieManager.getInstance().getCookie(url); + request.addRequestHeader("cookie", cookies); + request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); + if (mimetype == null) { + if (TextUtils.isEmpty(addressString)) { + return; + } + // We must have long pressed on a link or image to download it. We + // are not sure of the mimetype in this case, so do a head request + new FetchUrlMimeType(activity, request, addressString, cookies, userAgent).start(); + } else { + final DownloadManager manager = (DownloadManager) activity + .getSystemService(Context.DOWNLOAD_SERVICE); + new Thread("Browser download") { + @Override + public void run() { + try { + manager.enqueue(request); + } catch (IllegalArgumentException e) { + // Probably got a bad URL or something + e.printStackTrace(); + Utils.showToast(activity, R.string.cannot_download); + } + } + }.start(); + Toast.makeText(activity, R.string.download_pending, Toast.LENGTH_SHORT).show(); + } - } + } } diff --git a/app/src/main/java/acr/browser/lightning/download/FetchUrlMimeType.java b/app/src/main/java/acr/browser/lightning/download/FetchUrlMimeType.java index 329387b..467b891 100644 --- a/app/src/main/java/acr/browser/lightning/download/FetchUrlMimeType.java +++ b/app/src/main/java/acr/browser/lightning/download/FetchUrlMimeType.java @@ -26,82 +26,82 @@ import acr.browser.lightning.R; */ public class FetchUrlMimeType extends Thread { - private final Context mContext; + private final Context mContext; - private final DownloadManager.Request mRequest; + private final DownloadManager.Request mRequest; - private final String mUri; + private final String mUri; - private final String mCookies; + private final String mCookies; - private final String mUserAgent; + private final String mUserAgent; - public FetchUrlMimeType(Context context, DownloadManager.Request request, String uri, - String cookies, String userAgent) { - mContext = context.getApplicationContext(); - mRequest = request; - mUri = uri; - mCookies = cookies; - mUserAgent = userAgent; - Toast.makeText(mContext, R.string.download_pending, Toast.LENGTH_SHORT).show(); - } + public FetchUrlMimeType(Context context, DownloadManager.Request request, String uri, + String cookies, String userAgent) { + mContext = context.getApplicationContext(); + mRequest = request; + mUri = uri; + mCookies = cookies; + mUserAgent = userAgent; + Toast.makeText(mContext, R.string.download_pending, Toast.LENGTH_SHORT).show(); + } - @Override - public void run() { - // User agent is likely to be null, though the AndroidHttpClient - // seems ok with that. - String mimeType = null; - String contentDisposition = null; - HttpURLConnection connection = null; - try { - URL url = new URL(mUri); - connection = (HttpURLConnection) url.openConnection(); - if (mCookies != null && mCookies.length() > 0) { - connection.addRequestProperty("Cookie", mCookies); - connection.setRequestProperty("User-Agent", mUserAgent); - } - connection.connect(); - // We could get a redirect here, but if we do lets let - // the download manager take care of it, and thus trust that - // the server sends the right mimetype - if (connection.getResponseCode() == 200) { - String header = connection.getHeaderField("Content-Type"); - if (header != null) { - mimeType = header; - final int semicolonIndex = mimeType.indexOf(';'); - if (semicolonIndex != -1) { - mimeType = mimeType.substring(0, semicolonIndex); - } - } - String contentDispositionHeader = connection.getHeaderField("Content-Disposition"); - if (contentDispositionHeader != null) { - contentDisposition = contentDispositionHeader; - } - } - } catch (IllegalArgumentException | IOException ex) { - if (connection != null) - connection.disconnect(); - } finally { - if (connection != null) - connection.disconnect(); - } + @Override + public void run() { + // User agent is likely to be null, though the AndroidHttpClient + // seems ok with that. + String mimeType = null; + String contentDisposition = null; + HttpURLConnection connection = null; + try { + URL url = new URL(mUri); + connection = (HttpURLConnection) url.openConnection(); + if (mCookies != null && mCookies.length() > 0) { + connection.addRequestProperty("Cookie", mCookies); + connection.setRequestProperty("User-Agent", mUserAgent); + } + connection.connect(); + // We could get a redirect here, but if we do lets let + // the download manager take care of it, and thus trust that + // the server sends the right mimetype + if (connection.getResponseCode() == 200) { + String header = connection.getHeaderField("Content-Type"); + if (header != null) { + mimeType = header; + final int semicolonIndex = mimeType.indexOf(';'); + if (semicolonIndex != -1) { + mimeType = mimeType.substring(0, semicolonIndex); + } + } + String contentDispositionHeader = connection.getHeaderField("Content-Disposition"); + if (contentDispositionHeader != null) { + contentDisposition = contentDispositionHeader; + } + } + } catch (IllegalArgumentException | IOException ex) { + if (connection != null) + connection.disconnect(); + } finally { + if (connection != null) + connection.disconnect(); + } - if (mimeType != null) { - if (mimeType.equalsIgnoreCase("text/plain") - || mimeType.equalsIgnoreCase("application/octet-stream")) { - String newMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension( - MimeTypeMap.getFileExtensionFromUrl(mUri)); - if (newMimeType != null) { - mRequest.setMimeType(newMimeType); - } - } - String filename = URLUtil.guessFileName(mUri, contentDisposition, mimeType); - mRequest.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename); - } + if (mimeType != null) { + if (mimeType.equalsIgnoreCase("text/plain") + || mimeType.equalsIgnoreCase("application/octet-stream")) { + String newMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension( + MimeTypeMap.getFileExtensionFromUrl(mUri)); + if (newMimeType != null) { + mRequest.setMimeType(newMimeType); + } + } + String filename = URLUtil.guessFileName(mUri, contentDisposition, mimeType); + mRequest.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename); + } - // Start the download - DownloadManager manager = (DownloadManager) mContext - .getSystemService(Context.DOWNLOAD_SERVICE); - manager.enqueue(mRequest); - } + // Start the download + DownloadManager manager = (DownloadManager) mContext + .getSystemService(Context.DOWNLOAD_SERVICE); + manager.enqueue(mRequest); + } } diff --git a/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java b/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java index 241a091..55f5860 100644 --- a/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java +++ b/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java @@ -4,15 +4,14 @@ package acr.browser.lightning.download; import android.app.Activity; -import android.app.AlertDialog; import android.content.DialogInterface; +import android.support.v7.app.AlertDialog; import android.util.Log; import android.webkit.DownloadListener; import android.webkit.URLUtil; import acr.browser.lightning.R; import acr.browser.lightning.constant.Constants; -import acr.browser.lightning.download.DownloadHandler; public class LightningDownloadListener implements DownloadListener { @@ -32,7 +31,7 @@ public class LightningDownloadListener implements DownloadListener { switch (which) { case DialogInterface.BUTTON_POSITIVE: DownloadHandler.onDownloadStart(mActivity, url, userAgent, - contentDisposition, mimetype, false); + contentDisposition, mimetype); break; case DialogInterface.BUTTON_NEGATIVE: diff --git a/app/src/main/java/acr/browser/lightning/fragment/AdvancedSettingsFragment.java b/app/src/main/java/acr/browser/lightning/fragment/AdvancedSettingsFragment.java index be16af7..7d0df2c 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/AdvancedSettingsFragment.java +++ b/app/src/main/java/acr/browser/lightning/fragment/AdvancedSettingsFragment.java @@ -4,12 +4,12 @@ package acr.browser.lightning.fragment; import android.app.Activity; -import android.app.AlertDialog; import android.content.DialogInterface; import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.Preference; import android.preference.PreferenceFragment; +import android.support.v7.app.AlertDialog; import acr.browser.lightning.R; import acr.browser.lightning.preference.PreferenceManager; diff --git a/app/src/main/java/acr/browser/lightning/fragment/BookmarkSettingsFragment.java b/app/src/main/java/acr/browser/lightning/fragment/BookmarkSettingsFragment.java index 99f10a0..c5c9da5 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/BookmarkSettingsFragment.java +++ b/app/src/main/java/acr/browser/lightning/fragment/BookmarkSettingsFragment.java @@ -4,13 +4,12 @@ package acr.browser.lightning.fragment; import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; import android.content.DialogInterface; import android.os.Bundle; import android.os.Environment; import android.preference.Preference; import android.preference.PreferenceFragment; +import android.support.v7.app.AlertDialog; import java.io.File; import java.util.Arrays; @@ -27,12 +26,10 @@ public class BookmarkSettingsFragment extends PreferenceFragment implements Pref private static final String SETTINGS_BROWSER_IMPORT = "import_browser_bookmark"; private Activity mActivity; - private PreferenceManager mPreferences; private BookmarkManager mBookmarkManager; private File[] mFileList; private String[] mFileNameList; private static final File mPath = new File(Environment.getExternalStorageDirectory().toString()); - private static final int DIALOG_LOAD_FILE = 1000; @Override public void onCreate(Bundle savedInstanceState) { @@ -42,14 +39,14 @@ public class BookmarkSettingsFragment extends PreferenceFragment implements Pref mActivity = getActivity(); - mBookmarkManager = BookmarkManager.getInstance(mActivity); + mBookmarkManager = BookmarkManager.getInstance(mActivity.getApplicationContext()); initPrefs(); } private void initPrefs() { // mPreferences storage - mPreferences = PreferenceManager.getInstance(); + PreferenceManager mPreferences = PreferenceManager.getInstance(); Preference exportpref = findPreference(SETTINGS_EXPORT); Preference importpref = findPreference(SETTINGS_IMPORT); @@ -74,10 +71,10 @@ public class BookmarkSettingsFragment extends PreferenceFragment implements Pref return true; case SETTINGS_IMPORT: loadFileList(null); - onCreateDialog(DIALOG_LOAD_FILE); + createDialog(); return true; case SETTINGS_BROWSER_IMPORT: - mBookmarkManager.importBookmarksFromBrowser(mActivity); + mBookmarkManager.importBookmarksFromBrowser(getActivity()); return true; default: return false; @@ -135,34 +132,29 @@ public class BookmarkSettingsFragment extends PreferenceFragment implements Pref } } - protected Dialog onCreateDialog(int id) { - Dialog dialog; + protected void createDialog() { final AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); - switch (id) { - case DIALOG_LOAD_FILE: - builder.setTitle(R.string.title_chooser); - if (mFileList == null) { - dialog = builder.create(); - return dialog; - } - builder.setItems(mFileNameList, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - if (mFileList[which].isDirectory()) { - loadFileList(mFileList[which]); - builder.setItems(mFileNameList, this); - builder.show(); - } else { - mBookmarkManager.importBookmarksFromFile(mFileList[which], mActivity); - } - } - - }); - break; + final String title = getString(R.string.title_chooser); + builder.setTitle(title + ": " + Environment.getExternalStorageDirectory()); + if (mFileList == null) { + builder.show(); } - dialog = builder.show(); - return dialog; + builder.setItems(mFileNameList, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + if (mFileList[which].isDirectory()) { + builder.setTitle(title + ": " + mFileList[which]); + loadFileList(mFileList[which]); + builder.setItems(mFileNameList, this); + builder.show(); + } else { + mBookmarkManager.importBookmarksFromFile(mFileList[which], getActivity()); + } + } + + }); + builder.show(); } } diff --git a/app/src/main/java/acr/browser/lightning/fragment/DisplaySettingsFragment.java b/app/src/main/java/acr/browser/lightning/fragment/DisplaySettingsFragment.java index 00f8120..9a48e65 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/DisplaySettingsFragment.java +++ b/app/src/main/java/acr/browser/lightning/fragment/DisplaySettingsFragment.java @@ -4,15 +4,14 @@ package acr.browser.lightning.fragment; import android.app.Activity; -import android.app.AlertDialog; import android.content.DialogInterface; import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.Preference; import android.preference.PreferenceFragment; +import android.support.v7.app.AlertDialog; import acr.browser.lightning.R; -import acr.browser.lightning.activity.SettingsActivity; import acr.browser.lightning.preference.PreferenceManager; public class DisplaySettingsFragment extends PreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { @@ -28,7 +27,7 @@ public class DisplaySettingsFragment extends PreferenceFragment implements Prefe private Activity mActivity; private PreferenceManager mPreferences; private CheckBoxPreference cbstatus, cbfullscreen, cbviewport, cboverview, cbreflow; - private Preference theme, textsize; + private Preference theme; private String[] mThemeOptions; private int mCurrentTheme; @@ -50,7 +49,7 @@ public class DisplaySettingsFragment extends PreferenceFragment implements Prefe mCurrentTheme = mPreferences.getUseTheme(); theme = findPreference(SETTINGS_THEME); - textsize = findPreference(SETTINGS_TEXTSIZE); + Preference textsize = findPreference(SETTINGS_TEXTSIZE); cbstatus = (CheckBoxPreference) findPreference(SETTINGS_HIDESTATUSBAR); cbfullscreen = (CheckBoxPreference) findPreference(SETTINGS_FULLSCREEN); cbviewport = (CheckBoxPreference) findPreference(SETTINGS_VIEWPORT); @@ -163,7 +162,8 @@ public class DisplaySettingsFragment extends PreferenceFragment implements Prefe @Override public void onClick(DialogInterface dialog, int which) { if (mCurrentTheme != mPreferences.getUseTheme()) { - ((SettingsActivity) getActivity()).restart(); +// ((SettingsActivity) getActivity()).restart(); + getActivity().onBackPressed(); } } }); @@ -171,7 +171,8 @@ public class DisplaySettingsFragment extends PreferenceFragment implements Prefe @Override public void onCancel(DialogInterface dialog) { if (mCurrentTheme != mPreferences.getUseTheme()) { - ((SettingsActivity) getActivity()).restart(); +// ((SettingsActivity) getActivity()).restart(); + getActivity().onBackPressed(); } } }); diff --git a/app/src/main/java/acr/browser/lightning/fragment/GeneralSettingsFragment.java b/app/src/main/java/acr/browser/lightning/fragment/GeneralSettingsFragment.java index 364b7c0..ffafe61 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/GeneralSettingsFragment.java +++ b/app/src/main/java/acr/browser/lightning/fragment/GeneralSettingsFragment.java @@ -4,14 +4,16 @@ package acr.browser.lightning.fragment; import android.app.Activity; -import android.app.AlertDialog; import android.content.DialogInterface; import android.graphics.Color; +import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.preference.CheckBoxPreference; import android.preference.Preference; import android.preference.PreferenceFragment; +import android.support.v7.app.AlertDialog; import android.util.Log; import android.util.TypedValue; import android.view.View; @@ -468,10 +470,16 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe v.setPadding(padding, padding, 0, padding); layout.addView(v); layout.addView(getDownload); - if (API < 16) { + if (API < Build.VERSION_CODES.JELLY_BEAN) { layout.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.edit_text)); } else { - layout.setBackground(getResources().getDrawable(android.R.drawable.edit_text)); + Drawable drawable; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + drawable = getResources().getDrawable(android.R.drawable.edit_text, getActivity().getTheme()); + } else { + drawable = getResources().getDrawable(android.R.drawable.edit_text); + } + layout.setBackground(drawable); } downLocationPicker.setView(layout); downLocationPicker.setPositiveButton(getResources().getString(R.string.action_ok), diff --git a/app/src/main/java/acr/browser/lightning/fragment/PrivacySettingsFragment.java b/app/src/main/java/acr/browser/lightning/fragment/PrivacySettingsFragment.java index 56c7335..9e9b685 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/PrivacySettingsFragment.java +++ b/app/src/main/java/acr/browser/lightning/fragment/PrivacySettingsFragment.java @@ -4,7 +4,6 @@ package acr.browser.lightning.fragment; import android.app.Activity; -import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.os.Build; @@ -15,6 +14,7 @@ import android.preference.CheckBoxPreference; import android.preference.Preference; import android.preference.PreferenceFragment; import android.provider.Browser; +import android.support.v7.app.AlertDialog; import android.webkit.CookieManager; import android.webkit.CookieSyncManager; import android.webkit.WebIconDatabase; @@ -45,7 +45,6 @@ public class PrivacySettingsFragment extends PreferenceFragment implements Prefe private PreferenceManager mPreferences; private CheckBoxPreference cblocation, cb3cookies, cbsavepasswords, cbcacheexit, cbhistoryexit, cbcookiesexit, cbsynchistory; - private Preference clearcache, clearhistory, clearcookies; private boolean mSystemBrowser; private Handler messageHandler; @@ -65,9 +64,9 @@ public class PrivacySettingsFragment extends PreferenceFragment implements Prefe mPreferences = PreferenceManager.getInstance(); mSystemBrowser = mPreferences.getSystemBrowserPresent(); - clearcache = findPreference(SETTINGS_CLEARCACHE); - clearhistory = findPreference(SETTINGS_CLEARHISTORY); - clearcookies = findPreference(SETTINGS_CLEARCOOKIES); + Preference clearcache = findPreference(SETTINGS_CLEARCACHE); + Preference clearhistory = findPreference(SETTINGS_CLEARHISTORY); + Preference clearcookies = findPreference(SETTINGS_CLEARCOOKIES); cblocation = (CheckBoxPreference) findPreference(SETTINGS_LOCATION); cb3cookies = (CheckBoxPreference) findPreference(SETTINGS_THIRDPCOOKIES); cbsavepasswords = (CheckBoxPreference) findPreference(SETTINGS_SAVEPASSWORD); diff --git a/app/src/main/java/acr/browser/lightning/object/SearchAdapter.java b/app/src/main/java/acr/browser/lightning/object/SearchAdapter.java index 81cf4da..1bb6a33 100644 --- a/app/src/main/java/acr/browser/lightning/object/SearchAdapter.java +++ b/app/src/main/java/acr/browser/lightning/object/SearchAdapter.java @@ -1,23 +1,5 @@ package acr.browser.lightning.object; -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FilenameFilter; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserFactory; - import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; @@ -36,399 +18,398 @@ import android.widget.Filterable; import android.widget.ImageView; import android.widget.TextView; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserFactory; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FilenameFilter; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + import acr.browser.lightning.R; import acr.browser.lightning.database.BookmarkManager; import acr.browser.lightning.database.HistoryDatabase; import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.preference.PreferenceManager; +import acr.browser.lightning.utils.Utils; public class SearchAdapter extends BaseAdapter implements Filterable { - private List mHistory; - private List mBookmarks; - private List mSuggestions; - private List mFilteredList; - private List mAllBookmarks; - private HistoryDatabase mDatabaseHandler; - private final Context mContext; - private boolean mUseGoogle = true; - private boolean mIsExecuting = false; - private final boolean mDarkTheme; - private final boolean mIncognito; - private final BookmarkManager mBookmarkManager; - private static final String ENCODING = "ISO-8859-1"; - private static final long INTERVAL_DAY = 86400000; - private final String mSearchSubtitle; - private static final int API = Build.VERSION.SDK_INT; - private final Theme mTheme; - private SearchFilter mFilter; + private final List mHistory = new ArrayList<>(); + private final List mBookmarks = new ArrayList<>(); + private final List mSuggestions = new ArrayList<>(); + private final List mFilteredList = new ArrayList<>(); + private final List mAllBookmarks = new ArrayList<>(); + private HistoryDatabase mDatabaseHandler; + private final Context mContext; + private boolean mUseGoogle = true; + private boolean mIsExecuting = false; + private final boolean mDarkTheme; + private final boolean mIncognito; + private final BookmarkManager mBookmarkManager; + private static final String ENCODING = "ISO-8859-1"; + private static final long INTERVAL_DAY = 86400000; + private final String mSearchSubtitle; + private static final int API = Build.VERSION.SDK_INT; + private final Theme mTheme; + private SearchFilter mFilter; - public SearchAdapter(Context context, boolean dark, boolean incognito) { - mDatabaseHandler = HistoryDatabase.getInstance(context.getApplicationContext()); - mTheme = context.getTheme(); - mFilteredList = new ArrayList<>(); - mHistory = new ArrayList<>(); - mBookmarks = new ArrayList<>(); - mSuggestions = new ArrayList<>(); - mBookmarkManager = BookmarkManager.getInstance(context.getApplicationContext()); - mAllBookmarks = mBookmarkManager.getBookmarks(true); - mUseGoogle = PreferenceManager.getInstance().getGoogleSearchSuggestionsEnabled(); - mContext = context; - mSearchSubtitle = mContext.getString(R.string.suggestion); - mDarkTheme = dark || incognito; - mIncognito = incognito; - Thread delete = new Thread(new Runnable() { + public SearchAdapter(Context context, boolean dark, boolean incognito) { + mDatabaseHandler = HistoryDatabase.getInstance(context.getApplicationContext()); + mTheme = context.getTheme(); + mBookmarkManager = BookmarkManager.getInstance(context.getApplicationContext()); + mAllBookmarks.addAll(mBookmarkManager.getBookmarks(true)); + mUseGoogle = PreferenceManager.getInstance().getGoogleSearchSuggestionsEnabled(); + mContext = context; + mSearchSubtitle = mContext.getString(R.string.suggestion); + mDarkTheme = dark || incognito; + mIncognito = incognito; + Thread delete = new Thread(new Runnable() { - @Override - public void run() { - deleteOldCacheFiles(mContext); - } + @Override + public void run() { + deleteOldCacheFiles(mContext); + } - }); - delete.start(); - } + }); + delete.start(); + } - private void deleteOldCacheFiles(Context context) { - File dir = new File(context.getCacheDir().toString()); - String[] fileList = dir.list(new NameFilter()); - long earliestTimeAllowed = System.currentTimeMillis() - INTERVAL_DAY; - for (String fileName : fileList) { - File file = new File(dir.getPath() + fileName); - if (earliestTimeAllowed > file.lastModified()) { - file.delete(); - } - } - } + private void deleteOldCacheFiles(Context context) { + File dir = new File(context.getCacheDir().toString()); + String[] fileList = dir.list(new NameFilter()); + long earliestTimeAllowed = System.currentTimeMillis() - INTERVAL_DAY; + for (String fileName : fileList) { + File file = new File(dir.getPath() + fileName); + if (earliestTimeAllowed > file.lastModified()) { + file.delete(); + } + } + } - private class NameFilter implements FilenameFilter { + private class NameFilter implements FilenameFilter { - private static final String ext = ".sgg"; + private static final String ext = ".sgg"; - @Override - public boolean accept(File dir, String filename) { - return filename.endsWith(ext); - } + @Override + public boolean accept(File dir, String filename) { + return filename.endsWith(ext); + } - } + } - public void refreshPreferences() { - mUseGoogle = PreferenceManager.getInstance().getGoogleSearchSuggestionsEnabled(); - if (!mUseGoogle && mSuggestions != null) { - mSuggestions.clear(); - } - mDatabaseHandler = HistoryDatabase.getInstance(mContext.getApplicationContext()); - } + public void refreshPreferences() { + mUseGoogle = PreferenceManager.getInstance().getGoogleSearchSuggestionsEnabled(); + if (!mUseGoogle) { + mSuggestions.clear(); + } + mDatabaseHandler = HistoryDatabase.getInstance(mContext.getApplicationContext()); + } - public void refreshBookmarks() { - mAllBookmarks = mBookmarkManager.getBookmarks(true); - } + public void refreshBookmarks() { + mAllBookmarks.clear(); + mAllBookmarks.addAll(mBookmarkManager.getBookmarks(true)); + } - @Override - public int getCount() { - if (mFilteredList != null) { - return mFilteredList.size(); - } else { - return 0; - } - } + @Override + public int getCount() { + return mFilteredList.size(); + } - @Override - public Object getItem(int position) { - return mFilteredList.get(position); - } + @Override + public Object getItem(int position) { + return mFilteredList.get(position); + } - @Override - public long getItemId(int position) { - return 0; - } + @Override + public long getItemId(int position) { + return 0; + } - @SuppressWarnings("deprecation") - @SuppressLint("NewApi") - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View row = convertView; - SuggestionHolder holder; + @SuppressWarnings("deprecation") + @SuppressLint("NewApi") + @Override + public View getView(int position, View convertView, ViewGroup parent) { + SuggestionHolder holder; - if (row == null) { - LayoutInflater inflater = ((Activity) mContext).getLayoutInflater(); - row = inflater.inflate(R.layout.two_line_autocomplete, parent, false); + if (convertView == null) { + LayoutInflater inflater = ((Activity) mContext).getLayoutInflater(); + convertView = inflater.inflate(R.layout.two_line_autocomplete, parent, false); - holder = new SuggestionHolder(); - holder.mTitle = (TextView) row.findViewById(R.id.title); - holder.mUrl = (TextView) row.findViewById(R.id.url); - holder.mImage = (ImageView) row.findViewById(R.id.suggestionIcon); - row.setTag(holder); - } else { - holder = (SuggestionHolder) row.getTag(); - } + holder = new SuggestionHolder(); + holder.mTitle = (TextView) convertView.findViewById(R.id.title); + holder.mUrl = (TextView) convertView.findViewById(R.id.url); + holder.mImage = (ImageView) convertView.findViewById(R.id.suggestionIcon); + convertView.setTag(holder); + } else { + holder = (SuggestionHolder) convertView.getTag(); + } + HistoryItem web; + web = mFilteredList.get(position); + holder.mTitle.setText(web.getTitle()); + holder.mUrl.setText(web.getUrl()); - HistoryItem web = mFilteredList.get(position); - holder.mTitle.setText(web.getTitle()); - holder.mUrl.setText(web.getUrl()); + int imageId = R.drawable.ic_bookmark; + switch (web.getImageId()) { + case R.drawable.ic_bookmark: { + if (!mDarkTheme) { + imageId = R.drawable.ic_bookmark; + } else { + holder.mTitle.setTextColor(Color.WHITE); + imageId = R.drawable.ic_bookmark_dark; + } + break; + } + case R.drawable.ic_search: { + if (!mDarkTheme) { + imageId = R.drawable.ic_search; + } else { + holder.mTitle.setTextColor(Color.WHITE); + imageId = R.drawable.ic_search_dark; + } + break; + } + case R.drawable.ic_history: { + if (!mDarkTheme) { + imageId = R.drawable.ic_history; + } else { + holder.mTitle.setTextColor(Color.WHITE); + imageId = R.drawable.ic_history_dark; + } + break; + } + } - int imageId = R.drawable.ic_bookmark; - switch (web.getImageId()) { - case R.drawable.ic_bookmark: { - if (!mDarkTheme) { - imageId = R.drawable.ic_bookmark; - } else { - holder.mTitle.setTextColor(Color.WHITE); - imageId = R.drawable.ic_bookmark_dark; - } - break; - } - case R.drawable.ic_search: { - if (!mDarkTheme) { - imageId = R.drawable.ic_search; - } else { - holder.mTitle.setTextColor(Color.WHITE); - imageId = R.drawable.ic_search_dark; - } - break; - } - case R.drawable.ic_history: { - if (!mDarkTheme) { - imageId = R.drawable.ic_history; - } else { - holder.mTitle.setTextColor(Color.WHITE); - imageId = R.drawable.ic_history_dark; - } - break; - } - } + if (API < Build.VERSION_CODES.LOLLIPOP) { + holder.mImage.setImageDrawable(mContext.getResources().getDrawable(imageId)); + } else { + holder.mImage.setImageDrawable(mContext.getResources().getDrawable(imageId, mTheme)); + } - if (API < Build.VERSION_CODES.LOLLIPOP) { - holder.mImage.setImageDrawable(mContext.getResources().getDrawable(imageId)); - } else { - holder.mImage.setImageDrawable(mContext.getResources().getDrawable(imageId, mTheme)); - } + return convertView; + } - return row; - } + public void setContents(List list) { + mFilteredList.clear(); + mFilteredList.addAll(list); + notifyDataSetChanged(); + } - public void setContents(List list) { - if (mFilteredList != null) { - mFilteredList.clear(); - mFilteredList.addAll(list); - } else { - mFilteredList = list; - } - notifyDataSetChanged(); - } + @Override + public Filter getFilter() { + if (mFilter == null) { + mFilter = new SearchFilter(); + } + return mFilter; + } - @Override - public Filter getFilter() { - if (mFilter == null) { - mFilter = new SearchFilter(); - } - return mFilter; - } + private class SearchFilter extends Filter { - private class SearchFilter extends Filter { + @Override + protected FilterResults performFiltering(CharSequence constraint) { + FilterResults results = new FilterResults(); + if (constraint == null) { + return results; + } + String query = constraint.toString().toLowerCase(Locale.getDefault()); + if (mUseGoogle && !mIncognito && !mIsExecuting) { + new RetrieveSearchSuggestions().execute(query); + } - @Override - protected FilterResults performFiltering(CharSequence constraint) { - FilterResults results = new FilterResults(); - if (constraint == null) { - return results; - } - String query = constraint.toString().toLowerCase(Locale.getDefault()); - if (mUseGoogle && !mIncognito && !mIsExecuting) { - new RetrieveSearchSuggestions().execute(query); - } + int counter = 0; + mBookmarks.clear(); + for (int n = 0; n < mAllBookmarks.size(); n++) { + if (counter >= 5) { + break; + } + if (mAllBookmarks.get(n).getTitle().toLowerCase(Locale.getDefault()) + .startsWith(query)) { + mBookmarks.add(mAllBookmarks.get(n)); + counter++; + } else if (mAllBookmarks.get(n).getUrl().contains(query)) { + mBookmarks.add(mAllBookmarks.get(n)); + counter++; + } - int counter = 0; - mBookmarks = new ArrayList<>(); - for (int n = 0; n < mAllBookmarks.size(); n++) { - if (counter >= 5) { - break; - } - if (mAllBookmarks.get(n).getTitle().toLowerCase(Locale.getDefault()) - .startsWith(query)) { - mBookmarks.add(mAllBookmarks.get(n)); - counter++; - } else if (mAllBookmarks.get(n).getUrl().contains(query)) { - mBookmarks.add(mAllBookmarks.get(n)); - counter++; - } + } + if (mDatabaseHandler == null) { + mDatabaseHandler = HistoryDatabase.getInstance(mContext.getApplicationContext()); + } + List historyList = mDatabaseHandler.findItemsContaining(constraint.toString()); + mHistory.clear(); + mHistory.addAll(historyList); + results.count = 1; + return results; + } - } - if (mDatabaseHandler == null) { - mDatabaseHandler = HistoryDatabase.getInstance(mContext.getApplicationContext()); - } - mHistory = mDatabaseHandler.findItemsContaining(constraint.toString()); + @Override + public CharSequence convertResultToString(Object resultValue) { + return ((HistoryItem) resultValue).getUrl(); + } - results.count = 1; - return results; - } + @Override + protected void publishResults(CharSequence constraint, FilterResults results) { + mFilteredList.clear(); + mFilteredList.addAll(getSuggestions()); + notifyDataSetChanged(); + } - @Override - public CharSequence convertResultToString(Object resultValue) { - return ((HistoryItem) resultValue).getUrl(); - } + } - @Override - protected void publishResults(CharSequence constraint, FilterResults results) { - mFilteredList = getSuggestions(); - notifyDataSetChanged(); - } + private class SuggestionHolder { + ImageView mImage; + TextView mTitle; + TextView mUrl; + } - } + private class RetrieveSearchSuggestions extends AsyncTask> { - private class SuggestionHolder { - ImageView mImage; - TextView mTitle; - TextView mUrl; - } + private XmlPullParserFactory mFactory; + private XmlPullParser mXpp; - private class RetrieveSearchSuggestions extends AsyncTask> { + @Override + protected List doInBackground(String... arg0) { + mIsExecuting = true; - private XmlPullParserFactory mFactory; - private XmlPullParser mXpp; + List filter = new ArrayList<>(); + String query = arg0[0]; + try { + query = query.replace(" ", "+"); + URLEncoder.encode(query, ENCODING); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + File cache = downloadSuggestionsForQuery(query); + if (!cache.exists()) { + return filter; + } + InputStream fileInput = null; + try { + fileInput = new BufferedInputStream(new FileInputStream(cache)); + if (mFactory == null) { + mFactory = XmlPullParserFactory.newInstance(); + mFactory.setNamespaceAware(true); + } + if (mXpp == null) { + mXpp = mFactory.newPullParser(); + } + mXpp.setInput(fileInput, ENCODING); + int eventType = mXpp.getEventType(); + int counter = 0; + while (eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG && "suggestion".equals(mXpp.getName())) { + String suggestion = mXpp.getAttributeValue(null, "data"); + filter.add(new HistoryItem(mSearchSubtitle + " \"" + suggestion + '"', + suggestion, R.drawable.ic_search)); + counter++; + if (counter >= 5) { + break; + } + } + eventType = mXpp.next(); + } + } catch (Exception e) { + return filter; + } finally { + Utils.close(fileInput); + } + return filter; + } - @Override - protected List doInBackground(String... arg0) { - mIsExecuting = true; + @Override + protected void onPostExecute(List result) { + mSuggestions.clear(); + mSuggestions.addAll(result); + mFilteredList.clear(); + mFilteredList.addAll(getSuggestions()); + notifyDataSetChanged(); + mIsExecuting = false; + } - List filter = new ArrayList<>(); - String query = arg0[0]; - try { - query = query.replace(" ", "+"); - URLEncoder.encode(query, ENCODING); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } - File cache = downloadSuggestionsForQuery(query); - if (!cache.exists()) { - return filter; - } - InputStream fileInput = null; - try { - fileInput = new BufferedInputStream(new FileInputStream(cache)); - if (mFactory == null) { - mFactory = XmlPullParserFactory.newInstance(); - mFactory.setNamespaceAware(true); - } - if (mXpp == null) { - mXpp = mFactory.newPullParser(); - } - mXpp.setInput(fileInput, ENCODING); - int eventType = mXpp.getEventType(); - int counter = 0; - while (eventType != XmlPullParser.END_DOCUMENT) { - if (eventType == XmlPullParser.START_TAG && "suggestion".equals(mXpp.getName())) { - String suggestion = mXpp.getAttributeValue(null, "data"); - filter.add(new HistoryItem(mSearchSubtitle + " \"" + suggestion + '"', - suggestion, R.drawable.ic_search)); - counter++; - if (counter >= 5) { - break; - } - } - eventType = mXpp.next(); - } - } catch (Exception e) { - return filter; - } finally { - if (fileInput != null) { - try { - fileInput.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - return filter; - } + } - @Override - protected void onPostExecute(List result) { - mSuggestions = result; - mFilteredList = getSuggestions(); - notifyDataSetChanged(); - mIsExecuting = false; - } + private File downloadSuggestionsForQuery(String query) { + File cacheFile = new File(mContext.getCacheDir(), query.hashCode() + ".sgg"); + if (System.currentTimeMillis() - INTERVAL_DAY < cacheFile.lastModified()) { + return cacheFile; + } + if (!isNetworkConnected(mContext)) { + return cacheFile; + } + try { + URL url = new URL("http://google.com/complete/search?q=" + query + + "&output=toolbar&hl=en"); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setDoInput(true); + connection.connect(); + InputStream in = connection.getInputStream(); - } + if (in != null) { + FileOutputStream fos = new FileOutputStream(cacheFile); + int buffer; + while ((buffer = in.read()) != -1) { + fos.write(buffer); + } + fos.flush(); + fos.close(); + } + cacheFile.setLastModified(System.currentTimeMillis()); + } catch (Exception e) { + e.printStackTrace(); + } + return cacheFile; + } - private File downloadSuggestionsForQuery(String query) { - File cacheFile = new File(mContext.getCacheDir(), query.hashCode() + ".sgg"); - if (System.currentTimeMillis() - INTERVAL_DAY < cacheFile.lastModified()) { - return cacheFile; - } - if (!isNetworkConnected(mContext)) { - return cacheFile; - } - try { - URL url = new URL("http://google.com/complete/search?q=" + query - + "&output=toolbar&hl=en"); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setDoInput(true); - connection.connect(); - InputStream in = connection.getInputStream(); + private boolean isNetworkConnected(Context context) { + NetworkInfo networkInfo = getActiveNetworkInfo(context); + return networkInfo != null && networkInfo.isConnected(); + } - if (in != null) { - FileOutputStream fos = new FileOutputStream(cacheFile); - int buffer; - while ((buffer = in.read()) != -1) { - fos.write(buffer); - } - fos.flush(); - fos.close(); - } - cacheFile.setLastModified(System.currentTimeMillis()); - } catch (Exception e) { - e.printStackTrace(); - } - return cacheFile; - } + private NetworkInfo getActiveNetworkInfo(Context context) { + ConnectivityManager connectivity = (ConnectivityManager) context + .getSystemService(Context.CONNECTIVITY_SERVICE); + if (connectivity == null) { + return null; + } + return connectivity.getActiveNetworkInfo(); + } - private boolean isNetworkConnected(Context context) { - NetworkInfo networkInfo = getActiveNetworkInfo(context); - return networkInfo != null && networkInfo.isConnected(); - } + //TODO Write simpler algorithm + private List getSuggestions() { + List filteredList = new ArrayList<>(); - private NetworkInfo getActiveNetworkInfo(Context context) { - ConnectivityManager connectivity = (ConnectivityManager) context - .getSystemService(Context.CONNECTIVITY_SERVICE); - if (connectivity == null) { - return null; - } - return connectivity.getActiveNetworkInfo(); - } + int suggestionsSize = mSuggestions.size(); + int historySize = mHistory.size(); + int bookmarkSize = mBookmarks.size(); - //TODO Write simpler algorithm - private List getSuggestions() { - List filteredList = new ArrayList<>(); + int maxSuggestions = (bookmarkSize + historySize < 3) ? (5 - bookmarkSize - historySize) + : (bookmarkSize < 2) ? (4 - bookmarkSize) : (historySize < 1) ? 3 : 2; + int maxHistory = (suggestionsSize + bookmarkSize < 4) ? (5 - suggestionsSize - bookmarkSize) + : 1; + int maxBookmarks = (suggestionsSize + historySize < 3) ? (5 - suggestionsSize - historySize) + : 2; - int suggestionsSize = (mSuggestions == null) ? 0 : mSuggestions.size(); - int historySize = (mHistory == null) ? 0 : mHistory.size(); - int bookmarkSize = (mBookmarks == null) ? 0 : mBookmarks.size(); + for (int n = 0; n < bookmarkSize && n < maxBookmarks; n++) { + filteredList.add(mBookmarks.get(n)); + } - int maxSuggestions = (bookmarkSize + historySize < 3) ? (5 - bookmarkSize - historySize) - : (bookmarkSize < 2) ? (4 - bookmarkSize) : (historySize < 1) ? 3 : 2; - int maxHistory = (suggestionsSize + bookmarkSize < 4) ? (5 - suggestionsSize - bookmarkSize) - : 1; - int maxBookmarks = (suggestionsSize + historySize < 3) ? (5 - suggestionsSize - historySize) - : 2; + for (int n = 0; n < historySize && n < maxHistory; n++) { + filteredList.add(mHistory.get(n)); + } - if (!mUseGoogle || mIncognito) { - maxHistory++; - maxBookmarks++; - } - - for (int n = 0; n < bookmarkSize && n < maxBookmarks; n++) { - filteredList.add(mBookmarks.get(n)); - } - - for (int n = 0; n < historySize && n < maxHistory; n++) { - filteredList.add(mHistory.get(n)); - } - - for (int n = 0; n < suggestionsSize && n < maxSuggestions; n++) { - filteredList.add(mSuggestions.get(n)); - } - return filteredList; - } + for (int n = 0; n < suggestionsSize && n < maxSuggestions; n++) { + filteredList.add(mSuggestions.get(n)); + } + return filteredList; + } } diff --git a/app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java b/app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java index 3a354e7..e5be00a 100644 --- a/app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java +++ b/app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java @@ -357,7 +357,7 @@ public class PreferenceManager { } public void setReadingTextSize(int size) { - putInt(Name.READING_TEXT_SIZE, 2); + putInt(Name.READING_TEXT_SIZE, size); } public void setRenderingMode(int mode) { diff --git a/app/src/main/java/acr/browser/lightning/reading/ArticleTextExtractor.java b/app/src/main/java/acr/browser/lightning/reading/ArticleTextExtractor.java index 952b253..68c248d 100644 --- a/app/src/main/java/acr/browser/lightning/reading/ArticleTextExtractor.java +++ b/app/src/main/java/acr/browser/lightning/reading/ArticleTextExtractor.java @@ -95,11 +95,10 @@ public class ArticleTextExtractor { } /** - * @param html + * @param doc the document to extract * extracts article text from given html string. wasn't tested * with improper HTML, although jSoup should be able to handle * minor stuff. - * @returns extracted article, all HTML tags stripped */ public JResult extractContent(Document doc) throws Exception { return extractContent(new JResult(), doc, formatter); @@ -235,14 +234,12 @@ public class ArticleTextExtractor { protected Collection extractKeywords(Document doc) { String content = SHelper.innerTrim(doc.select("head meta[name=keywords]").attr("content")); - if (content != null) { - if (content.startsWith("[") && content.endsWith("]")) - content = content.substring(1, content.length() - 1); + if (content.startsWith("[") && content.endsWith("]")) + content = content.substring(1, content.length() - 1); - String[] split = content.split("\\s*,\\s*"); - if (split.length > 1 || (split.length > 0 && !"".equals(split[0]))) - return Arrays.asList(split); - } + String[] split = content.split("\\s*,\\s*"); + if (split.length > 1 || (split.length > 0 && !"".equals(split[0]))) + return Arrays.asList(split); return Collections.emptyList(); } diff --git a/app/src/main/java/acr/browser/lightning/reading/Converter.java b/app/src/main/java/acr/browser/lightning/reading/Converter.java index fc1dc37..7f2ed23 100644 --- a/app/src/main/java/acr/browser/lightning/reading/Converter.java +++ b/app/src/main/java/acr/browser/lightning/reading/Converter.java @@ -94,7 +94,7 @@ public class Converter { /** * reads bytes off the string and returns a string * - * @param is + * @param is input stream to read * @param maxBytes * The max bytes that we want to read from the input stream * @return String diff --git a/app/src/main/java/acr/browser/lightning/reading/HtmlFetcher.java b/app/src/main/java/acr/browser/lightning/reading/HtmlFetcher.java index f55542f..df9cd8b 100644 --- a/app/src/main/java/acr/browser/lightning/reading/HtmlFetcher.java +++ b/app/src/main/java/acr/browser/lightning/reading/HtmlFetcher.java @@ -230,7 +230,7 @@ public class HtmlFetcher { } // if resolved url is longer then use it! - if (resUrl != null && resUrl.trim().length() > url.length()) { + if (resUrl.trim().length() > url.length()) { // this is necessary e.g. for some homebaken url resolvers which // return // the resolved url relative to url! diff --git a/app/src/main/java/acr/browser/lightning/reading/SHelper.java b/app/src/main/java/acr/browser/lightning/reading/SHelper.java index 08b0735..8476068 100644 --- a/app/src/main/java/acr/browser/lightning/reading/SHelper.java +++ b/app/src/main/java/acr/browser/lightning/reading/SHelper.java @@ -164,7 +164,7 @@ public class SHelper { * extract the domain from this url * @param path * this url does not have a domain - * @return + * @return returns the domain */ public static String useDomainOfFirstArg4Second(String urlForDomain, String path) { if (path.startsWith("http")) @@ -257,8 +257,7 @@ public class SHelper { } /** - * @see http - * ://blogs.sun.com/CoreJavaTechTips/entry/cookie_handling_in_java_se + * http://blogs.sun.com/CoreJavaTechTips/entry/cookie_handling_in_java_se */ public static void enableCookieMgmt() { CookieManager manager = new CookieManager(); @@ -267,9 +266,7 @@ public class SHelper { } /** - * @see http - * ://stackoverflow.com/questions/2529682/setting-user-agent-of-a-java - * -urlconnection + * http://stackoverflow.com/questions/2529682/setting-user-agent-of-a-java-urlconnection */ public static void enableUserAgentOverwrite() { System.setProperty("http.agent", ""); diff --git a/app/src/main/java/acr/browser/lightning/utils/IntentUtils.java b/app/src/main/java/acr/browser/lightning/utils/IntentUtils.java index 324314d..49075fd 100644 --- a/app/src/main/java/acr/browser/lightning/utils/IntentUtils.java +++ b/app/src/main/java/acr/browser/lightning/utils/IntentUtils.java @@ -21,7 +21,7 @@ public class IntentUtils { private final Activity mActivity; - static final Pattern ACCEPTED_URI_SCHEMA = Pattern.compile("(?i)" + private static final Pattern ACCEPTED_URI_SCHEMA = Pattern.compile("(?i)" + // switch on case insensitive matching "(" + // begin group for schema diff --git a/app/src/main/java/acr/browser/lightning/utils/ThemeUtils.java b/app/src/main/java/acr/browser/lightning/utils/ThemeUtils.java new file mode 100644 index 0000000..36ca4fb --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/utils/ThemeUtils.java @@ -0,0 +1,34 @@ +package acr.browser.lightning.utils; + +import android.content.Context; +import android.content.res.TypedArray; +import android.support.annotation.AttrRes; +import android.support.annotation.NonNull; +import android.util.TypedValue; + +import acr.browser.lightning.R; + +public class ThemeUtils { + + private static final TypedValue sTypedValue = new TypedValue(); + + public static int getPrimaryColor(@NonNull Context context) { + return getColor(context, R.attr.colorPrimary); + } + + public static int getPrimaryColorDark(@NonNull Context context) { + return getColor(context, R.attr.colorPrimaryDark); + } + + public static int getAccentColor(@NonNull Context context) { + return getColor(context, R.attr.colorAccent); + } + + private static int getColor(@NonNull Context context, @AttrRes int resource) { + TypedArray a = context.obtainStyledAttributes(sTypedValue.data, new int[]{resource}); + int color = a.getColor(0, 0); + a.recycle(); + return color; + } + +} diff --git a/app/src/main/java/acr/browser/lightning/utils/Utils.java b/app/src/main/java/acr/browser/lightning/utils/Utils.java index da74107..4d6ad13 100644 --- a/app/src/main/java/acr/browser/lightning/utils/Utils.java +++ b/app/src/main/java/acr/browser/lightning/utils/Utils.java @@ -5,7 +5,6 @@ package acr.browser.lightning.utils; import android.annotation.SuppressLint; import android.app.Activity; -import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -17,11 +16,14 @@ import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.os.Environment; +import android.support.annotation.StringRes; +import android.support.v7.app.AlertDialog; import android.util.DisplayMetrics; import android.util.Log; import android.webkit.URLUtil; import android.widget.Toast; +import java.io.Closeable; import java.io.File; import java.io.IOException; import java.net.URI; @@ -39,15 +41,15 @@ public final class Utils { } public static void downloadFile(final Activity activity, final String url, - final String userAgent, final String contentDisposition, final boolean privateBrowsing) { + final String userAgent, final String contentDisposition) { String fileName = URLUtil.guessFileName(url, null, null); - DownloadHandler.onDownloadStart(activity, url, userAgent, contentDisposition, null, - privateBrowsing); + DownloadHandler.onDownloadStart(activity, url, userAgent, contentDisposition, null + ); Log.i(Constants.TAG, "Downloading" + fileName); } - public static Intent newEmailIntent(Context context, String address, String subject, - String body, String cc) { + public static Intent newEmailIntent(String address, String subject, + String body, String cc) { Intent intent = new Intent(Intent.ACTION_SEND); intent.putExtra(Intent.EXTRA_EMAIL, new String[] { address }); intent.putExtra(Intent.EXTRA_TEXT, body); @@ -63,11 +65,11 @@ public final class Utils { builder.setMessage(message) .setCancelable(true) .setPositiveButton(context.getResources().getString(R.string.action_ok), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - } - }); + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + } + }); AlertDialog alert = builder.create(); alert.show(); } @@ -76,6 +78,10 @@ public final class Utils { Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); } + public static void showToast(Context context, @StringRes int resource) { + Toast.makeText(context, resource, Toast.LENGTH_SHORT).show(); + } + /** * Returns the number of pixels corresponding to the passed density pixels */ @@ -237,4 +243,13 @@ public final class Utils { private static Bitmap mWebIconLight; private static Bitmap mWebIconDark; + public static void close(Closeable closeable) { + if (closeable == null) + return; + try { + closeable.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } } diff --git a/app/src/main/java/acr/browser/lightning/view/LightningView.java b/app/src/main/java/acr/browser/lightning/view/LightningView.java index d0ce277..b573bd1 100644 --- a/app/src/main/java/acr/browser/lightning/view/LightningView.java +++ b/app/src/main/java/acr/browser/lightning/view/LightningView.java @@ -6,7 +6,6 @@ package acr.browser.lightning.view; import android.annotation.SuppressLint; import android.app.Activity; -import android.app.AlertDialog; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.DialogInterface; @@ -21,6 +20,8 @@ import android.net.http.SslError; import android.os.Build; import android.os.Message; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.app.AlertDialog; import android.text.InputType; import android.text.method.PasswordTransformationMethod; import android.util.Log; @@ -29,6 +30,7 @@ import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; +import android.view.ViewConfiguration; import android.webkit.CookieManager; import android.webkit.GeolocationPermissions; import android.webkit.HttpAuthHandler; @@ -64,1082 +66,1117 @@ import acr.browser.lightning.utils.Utils; public class LightningView { - private final Title mTitle; - private WebView mWebView; - private BrowserController mBrowserController; - private GestureDetector mGestureDetector; - private final Activity mActivity; - private WebSettings mSettings; - private static String mHomepage; - private static String mDefaultUserAgent; - private static Bitmap mWebpageBitmap; - private static PreferenceManager mPreferences; - private final AdBlock mAdBlock; - private IntentUtils mIntentUtils; - private final Paint mPaint = new Paint(); - private boolean isForegroundTab; - private boolean mTextReflow = false; - private boolean mInvertPage = false; - private static final int API = android.os.Build.VERSION.SDK_INT; - private static final int SCROLL_UP_THRESHOLD = Utils.convertDpToPixels(10); - private static final int SCROLL_DOWN_THRESHOLD = Utils.convertDpToPixels(100); - private static final float[] mNegativeColorArray = { -1.0f, 0, 0, 0, 255, // red - 0, -1.0f, 0, 0, 255, // green - 0, 0, -1.0f, 0, 255, // blue - 0, 0, 0, 1.0f, 0 // alpha - }; - - @SuppressWarnings("deprecation") - @SuppressLint("NewApi") - public LightningView(Activity activity, String url, boolean darkTheme) { - - mActivity = activity; - mWebView = new WebView(activity); - mTitle = new Title(activity, darkTheme); - mAdBlock = AdBlock.getInstance(activity.getApplicationContext()); - - mWebpageBitmap = Utils.getWebpageBitmap(activity.getResources(), darkTheme); - - try { - mBrowserController = (BrowserController) activity; - } catch (ClassCastException e) { - throw new ClassCastException(activity + " must implement BrowserController"); - } - mIntentUtils = new IntentUtils(mBrowserController); - mWebView.setDrawingCacheBackgroundColor(0x00000000); - mWebView.setFocusableInTouchMode(true); - mWebView.setFocusable(true); - mWebView.setAnimationCacheEnabled(false); - mWebView.setDrawingCacheEnabled(false); - mWebView.setWillNotCacheDrawing(true); - mWebView.setAlwaysDrawnWithCacheEnabled(false); - mWebView.setBackgroundColor(activity.getResources().getColor(android.R.color.white)); - - if (API > 15) { - mWebView.setBackground(null); - mWebView.getRootView().setBackground(null); - } else if (mWebView.getRootView() != null) { - mWebView.getRootView().setBackgroundDrawable(null); - } - mWebView.setScrollbarFadingEnabled(true); - mWebView.setSaveEnabled(true); - mWebView.setWebChromeClient(new LightningChromeClient(activity)); - mWebView.setWebViewClient(new LightningWebClient(activity)); - mWebView.setDownloadListener(new LightningDownloadListener(activity)); - mGestureDetector = new GestureDetector(activity, new CustomGestureListener()); - mWebView.setOnTouchListener(new TouchListener()); - mDefaultUserAgent = mWebView.getSettings().getUserAgentString(); - mSettings = mWebView.getSettings(); - initializeSettings(mWebView.getSettings(), activity); - initializePreferences(activity); - - if (url != null) { - if (!url.trim().isEmpty()) { - mWebView.loadUrl(url); - } else { - // don't load anything, the user is looking for a blank tab - } - } else { - if (mHomepage.startsWith("about:home")) { - mWebView.loadUrl(getHomepage()); - } else if (mHomepage.startsWith("about:bookmarks")) { - mBrowserController.openBookmarkPage(mWebView); - } else { - mWebView.loadUrl(mHomepage); - } - } - } - - private String getHomepage() { - StringBuilder homepageBuilder = new StringBuilder(); - homepageBuilder.append(StartPage.HEAD); - String icon; - String searchUrl; - switch (mPreferences.getSearchChoice()) { - case 0: - // CUSTOM SEARCH - icon = "file:///android_asset/lightning.png"; - searchUrl = mPreferences.getSearchUrl(); - break; - case 1: - // GOOGLE_SEARCH; - icon = "file:///android_asset/google.png"; - // "https://www.google.com/images/srpr/logo11w.png"; - searchUrl = Constants.GOOGLE_SEARCH; - break; - case 2: - // ANDROID SEARCH; - icon = "file:///android_asset/ask.png"; - searchUrl = Constants.ASK_SEARCH; - break; - case 3: - // BING_SEARCH; - icon = "file:///android_asset/bing.png"; - // "http://upload.wikimedia.org/wikipedia/commons/thumb/b/b1/Bing_logo_%282013%29.svg/500px-Bing_logo_%282013%29.svg.png"; - searchUrl = Constants.BING_SEARCH; - break; - case 4: - // YAHOO_SEARCH; - icon = "file:///android_asset/yahoo.png"; - // "http://upload.wikimedia.org/wikipedia/commons/thumb/2/24/Yahoo%21_logo.svg/799px-Yahoo%21_logo.svg.png"; - searchUrl = Constants.YAHOO_SEARCH; - break; - case 5: - // STARTPAGE_SEARCH; - icon = "file:///android_asset/startpage.png"; - // "https://startpage.com/graphics/startp_logo.gif"; - searchUrl = Constants.STARTPAGE_SEARCH; - break; - case 6: - // STARTPAGE_MOBILE - icon = "file:///android_asset/startpage.png"; - // "https://startpage.com/graphics/startp_logo.gif"; - searchUrl = Constants.STARTPAGE_MOBILE_SEARCH; - break; - case 7: - // DUCK_SEARCH; - icon = "file:///android_asset/duckduckgo.png"; - // "https://duckduckgo.com/assets/logo_homepage.normal.v101.png"; - searchUrl = Constants.DUCK_SEARCH; - break; - case 8: - // DUCK_LITE_SEARCH; - icon = "file:///android_asset/duckduckgo.png"; - // "https://duckduckgo.com/assets/logo_homepage.normal.v101.png"; - searchUrl = Constants.DUCK_LITE_SEARCH; - break; - case 9: - // BAIDU_SEARCH; - icon = "file:///android_asset/baidu.png"; - // "http://www.baidu.com/img/bdlogo.gif"; - searchUrl = Constants.BAIDU_SEARCH; - break; - case 10: - // YANDEX_SEARCH; - icon = "file:///android_asset/yandex.png"; - // "http://upload.wikimedia.org/wikipedia/commons/thumb/9/91/Yandex.svg/600px-Yandex.svg.png"; - searchUrl = Constants.YANDEX_SEARCH; - break; - default: - // DEFAULT GOOGLE_SEARCH; - icon = "file:///android_asset/google.png"; - searchUrl = Constants.GOOGLE_SEARCH; - break; - - } - - homepageBuilder.append(icon); - homepageBuilder.append(StartPage.MIDDLE); - homepageBuilder.append(searchUrl); - homepageBuilder.append(StartPage.END); - - File homepage = new File(mActivity.getFilesDir(), "homepage.html"); - try { - FileWriter hWriter = new FileWriter(homepage, false); - hWriter.write(homepageBuilder.toString()); - hWriter.close(); - } catch (IOException e) { - e.printStackTrace(); - } - - return Constants.FILE + homepage; - } - - @SuppressWarnings("deprecation") - @SuppressLint({ "NewApi", "SetJavaScriptEnabled" }) - public synchronized void initializePreferences(Context context) { - mPreferences = PreferenceManager.getInstance(); - mHomepage = mPreferences.getHomepage(); - mAdBlock.updatePreference(); - if (mSettings == null && mWebView != null) { - mSettings = mWebView.getSettings(); - } else if (mSettings == null) { - return; - } - - setColorMode(mPreferences.getRenderingMode()); - - if (!mBrowserController.isIncognito()) { - mSettings.setGeolocationEnabled(mPreferences.getLocationEnabled()); - } else { - mSettings.setGeolocationEnabled(false); - } - if (API < 19) { - switch (mPreferences.getFlashSupport()) { - case 0: - mSettings.setPluginState(PluginState.OFF); - break; - case 1: - mSettings.setPluginState(PluginState.ON_DEMAND); - break; - case 2: - mSettings.setPluginState(PluginState.ON); - break; - default: - break; - } - } - - switch (mPreferences.getUserAgentChoice()) { - case 1: - if (API > 16) { - mSettings.setUserAgentString(WebSettings.getDefaultUserAgent(context)); - } else { - mSettings.setUserAgentString(mDefaultUserAgent); - } - break; - case 2: - mSettings.setUserAgentString(Constants.DESKTOP_USER_AGENT); - break; - case 3: - mSettings.setUserAgentString(Constants.MOBILE_USER_AGENT); - break; - case 4: - mSettings.setUserAgentString(mPreferences.getUserAgentString(mDefaultUserAgent)); - break; - } - - if (mPreferences.getSavePasswordsEnabled() && !mBrowserController.isIncognito()) { - if (API < 18) { - mSettings.setSavePassword(true); - } - mSettings.setSaveFormData(true); - } else { - if (API < 18) { - mSettings.setSavePassword(false); - } - mSettings.setSaveFormData(false); - } - - if (mPreferences.getJavaScriptEnabled()) { - mSettings.setJavaScriptEnabled(true); - mSettings.setJavaScriptCanOpenWindowsAutomatically(true); - } - - if (mPreferences.getTextReflowEnabled()) { - mTextReflow = true; - mSettings.setLayoutAlgorithm(LayoutAlgorithm.NARROW_COLUMNS); - if (API >= android.os.Build.VERSION_CODES.KITKAT) { - try { - mSettings.setLayoutAlgorithm(LayoutAlgorithm.TEXT_AUTOSIZING); - } catch (Exception e) { - // This shouldn't be necessary, but there are a number - // of KitKat devices that crash trying to set this - Log.e(Constants.TAG, "Problem setting LayoutAlgorithm to TEXT_AUTOSIZING"); - } - } - } else { - mTextReflow = false; - mSettings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL); - } - - mSettings.setBlockNetworkImage(mPreferences.getBlockImagesEnabled()); - mSettings.setSupportMultipleWindows(mPreferences.getPopupsEnabled()); - mSettings.setUseWideViewPort(mPreferences.getUseWideViewportEnabled()); - mSettings.setLoadWithOverviewMode(mPreferences.getOverviewModeEnabled()); - switch (mPreferences.getTextSize()) { - case 1: - mSettings.setTextZoom(200); - break; - case 2: - mSettings.setTextZoom(150); - break; - case 3: - mSettings.setTextZoom(100); - break; - case 4: - mSettings.setTextZoom(75); - break; - case 5: - mSettings.setTextZoom(50); - break; - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - CookieManager.getInstance().setAcceptThirdPartyCookies(mWebView, - !mPreferences.getBlockThirdPartyCookiesEnabled()); - } - } - - @SuppressWarnings("deprecation") - @SuppressLint({ "SetJavaScriptEnabled", "NewApi" }) - private void initializeSettings(WebSettings settings, Context context) { - if (API < 18) { - settings.setAppCacheMaxSize(Long.MAX_VALUE); - } - if (API < 17) { - settings.setEnableSmoothTransition(true); - } - if (API > 16) { - settings.setMediaPlaybackRequiresUserGesture(true); - } - if (API >= Build.VERSION_CODES.LOLLIPOP && !mBrowserController.isIncognito()) { - settings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE); - } else if (API >= Build.VERSION_CODES.LOLLIPOP) { - // We're in Incognito mode, reject - settings.setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW); - } - settings.setDomStorageEnabled(true); - settings.setAppCacheEnabled(true); - settings.setCacheMode(WebSettings.LOAD_DEFAULT); - settings.setDatabaseEnabled(true); - settings.setSupportZoom(true); - settings.setBuiltInZoomControls(true); - settings.setDisplayZoomControls(false); - settings.setAllowContentAccess(true); - settings.setAllowFileAccess(true); - settings.setDefaultTextEncodingName("utf-8"); - if (API > 16) { - settings.setAllowFileAccessFromFileURLs(false); - settings.setAllowUniversalAccessFromFileURLs(false); - } - - settings.setAppCachePath(context.getDir("appcache", 0).getPath()); - settings.setGeolocationDatabasePath(context.getDir("geolocation", 0).getPath()); - if (API < Build.VERSION_CODES.KITKAT) { - settings.setDatabasePath(context.getDir("databases", 0).getPath()); - } - } - - public boolean isShown() { - return mWebView != null && mWebView.isShown(); - } - - public synchronized void onPause() { - if (mWebView != null) { - mWebView.onPause(); - } - } - - public synchronized void onResume() { - if (mWebView != null) { - mWebView.onResume(); - } - } - - public void setForegroundTab(boolean isForeground) { - isForegroundTab = isForeground; - mBrowserController.update(); - } - - public boolean isForegroundTab() { - return isForegroundTab; - } - - public int getProgress() { - if (mWebView != null) { - return mWebView.getProgress(); - } else { - return 100; - } - } - - public synchronized void stopLoading() { - if (mWebView != null) { - mWebView.stopLoading(); - } - } - - public void setHardwareRendering() { - mWebView.setLayerType(View.LAYER_TYPE_HARDWARE, mPaint); - } - - public void setNormalRendering() { - mWebView.setLayerType(View.LAYER_TYPE_NONE, null); - } - - public void setSoftwareRendering() { - mWebView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); - } - - public void setColorMode(int mode) { - mInvertPage = false; - switch (mode) { - case 0: - mPaint.setColorFilter(null); - // setSoftwareRendering(); // Some devices get segfaults - // in the WebView with Hardware Acceleration enabled, - // the only fix is to disable hardware rendering - setNormalRendering(); - mInvertPage = false; - break; - case 1: - ColorMatrixColorFilter filterInvert = new ColorMatrixColorFilter( - mNegativeColorArray); - mPaint.setColorFilter(filterInvert); - setHardwareRendering(); - - mInvertPage = true; - break; - case 2: - ColorMatrix cm = new ColorMatrix(); - cm.setSaturation(0); - ColorMatrixColorFilter filterGray = new ColorMatrixColorFilter(cm); - mPaint.setColorFilter(filterGray); - setHardwareRendering(); - break; - case 3: - ColorMatrix matrix = new ColorMatrix(); - matrix.set(mNegativeColorArray); - ColorMatrix matrixGray = new ColorMatrix(); - matrixGray.setSaturation(0); - ColorMatrix concat = new ColorMatrix(); - concat.setConcat(matrix, matrixGray); - ColorMatrixColorFilter filterInvertGray = new ColorMatrixColorFilter(concat); - mPaint.setColorFilter(filterInvertGray); - setHardwareRendering(); - - mInvertPage = true; - break; - - } - - } - - public synchronized void pauseTimers() { - if (mWebView != null) { - mWebView.pauseTimers(); - } - } - - public synchronized void resumeTimers() { - if (mWebView != null) { - mWebView.resumeTimers(); - } - } - - public void requestFocus() { - if (mWebView != null && !mWebView.hasFocus()) { - mWebView.requestFocus(); - } - } - - public void setVisibility(int visible) { - if (mWebView != null) { - mWebView.setVisibility(visible); - } - } - - public void clearCache(boolean disk) { - if (mWebView != null) { - mWebView.clearCache(disk); - } - } - - public synchronized void reload() { - // Check if configured proxy is available - if (!mBrowserController.isProxyReady()) { - // User has been notified - return; - } - - if (mWebView != null) { - mWebView.reload(); - } - } - - private void cacheFavicon(Bitmap icon) { - String hash = String.valueOf(Utils.getDomainName(getUrl()).hashCode()); - Log.d(Constants.TAG, "Caching icon for " + Utils.getDomainName(getUrl())); - File image = new File(mActivity.getCacheDir(), hash + ".png"); - try { - FileOutputStream fos = new FileOutputStream(image); - icon.compress(Bitmap.CompressFormat.PNG, 100, fos); - fos.flush(); - fos.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - @SuppressWarnings("deprecation") - @SuppressLint("NewApi") - public synchronized void find(String text) { - if (mWebView != null) { - if (API > 16) { - mWebView.findAllAsync(text); - } else { - mWebView.findAll(text); - } - } - } - - public Activity getActivity() { - return mActivity; - } - - public synchronized void onDestroy() { - if (mWebView != null) { - mWebView.stopLoading(); - mWebView.onPause(); - mWebView.clearHistory(); - mWebView.setVisibility(View.GONE); - mWebView.removeAllViews(); - mWebView.destroyDrawingCache(); - // mWebView.destroy(); //this is causing the segfault - mWebView = null; - } - } - - public synchronized void goBack() { - if (mWebView != null) { - mWebView.goBack(); - } - } - - public String getUserAgent() { - if (mWebView != null) { - return mWebView.getSettings().getUserAgentString(); - } else { - return ""; - } - } - - public synchronized void goForward() { - if (mWebView != null) { - mWebView.goForward(); - } - } - - public boolean canGoBack() { - return mWebView != null && mWebView.canGoBack(); - } - - public boolean canGoForward() { - return mWebView != null && mWebView.canGoForward(); - } - - public WebView getWebView() { - return mWebView; - } - - public Bitmap getFavicon() { - return mTitle.getFavicon(); - } - - public synchronized void loadUrl(String url) { - // Check if configured proxy is available - if (!mBrowserController.isProxyReady()) { - // User has been notified - return; - } - - if (mWebView != null) { - mWebView.loadUrl(url); - } - } - - public synchronized void invalidate() { - if (mWebView != null) { - mWebView.invalidate(); - } - } - - public String getTitle() { - return mTitle.getTitle(); - } - - public String getUrl() { - if (mWebView != null) { - return mWebView.getUrl(); - } else { - return ""; - } - } - - public class LightningWebClient extends WebViewClient { - - final Context mActivity; - - LightningWebClient(Context context) { - mActivity = context; - } - - @Override - public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { - if (mAdBlock.isAd(request.getUrl().getHost())) { - 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; - } - - @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); - } - mBrowserController.update(); - } - - @Override - public void onPageStarted(WebView view, String url, Bitmap favicon) { - if (isShown()) { - mBrowserController.updateUrl(url, false); - mBrowserController.showActionBar(); - } - mTitle.setFavicon(mWebpageBitmap); - mBrowserController.update(); - } - - @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; - - @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 (!mBrowserController.isProxyReady()) { - // User has been notified - return true; - } - - if (mBrowserController.isIncognito()) { - 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(mActivity, 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 Context mActivity; - - LightningChromeClient(Context context) { - mActivity = context; - } - - @Override - public void onProgressChanged(WebView view, int newProgress) { - if (isShown()) { - mBrowserController.updateProgress(newProgress); - } - } - - @Override - public void onReceivedIcon(WebView view, Bitmap icon) { - mTitle.setFavicon(icon); - mBrowserController.update(); - cacheFavicon(icon); - } - - @Override - public void onReceivedTitle(WebView view, String title) { - if (!title.isEmpty()) { - mTitle.setTitle(title); - } else { - mTitle.setTitle(mActivity.getString(R.string.untitled)); - } - mBrowserController.update(); - mBrowserController.updateHistory(title, view.getUrl()); - } - - @Override - public void onGeolocationPermissionsShowPrompt(final String origin, - final GeolocationPermissions.Callback callback) { - 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(isUserGesture, resultMsg); - return true; - } - - @Override - public void onCloseWindow(WebView window) { - // TODO Auto-generated method stub - super.onCloseWindow(window); - } - - public void openFileChooser(ValueCallback uploadMsg) { - mBrowserController.openFileChooser(uploadMsg); - } - - public void openFileChooser(ValueCallback uploadMsg, String acceptType) { - mBrowserController.openFileChooser(uploadMsg); - } - - public void openFileChooser(ValueCallback uploadMsg, String acceptType, String capture) { - mBrowserController.openFileChooser(uploadMsg); - } - - public boolean onShowFileChooser(WebView webView, ValueCallback 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 { - Activity activity = mBrowserController.getActivity(); - mBrowserController.onShowCustomView(view, activity.getRequestedOrientation(), 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, requestedOrientation, 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 = Utils.getWebpageBitmap(context.getResources(), 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 { - - float mLocation; - float mY; - int mAction; - - @SuppressLint("ClickableViewAccessibility") - @Override - public boolean onTouch(View view, MotionEvent arg1) { - if (view != null && !view.hasFocus()) { - view.requestFocus(); - } - mAction = arg1.getAction(); - mY = arg1.getY(); - if (mAction == MotionEvent.ACTION_DOWN) { - mLocation = mY; - } else if (mAction == MotionEvent.ACTION_UP) { - if ((mY - mLocation) > SCROLL_DOWN_THRESHOLD) { - if (mWebView.getScrollY() != 0) { - mBrowserController.showActionBar(); - } else { - mBrowserController.toggleActionBar(); - } - } else if ((mY - mLocation) < -SCROLL_UP_THRESHOLD) { - mBrowserController.hideActionBar(); - } - mLocation = 0; - } - mGestureDetector.onTouchEvent(arg1); - return false; - } - } - - private class CustomGestureListener extends SimpleOnGestureListener { - - /** - * Without this, onLongPress is not called when user is zooming using - * two fingers, but is when using only one. - * - * The required behaviour is to not trigger this when the user is - * zooming, it shouldn't matter how much fingers the user's using. - */ - private boolean mCanTriggerLongPress = true; - - @Override - public void onLongPress(MotionEvent e) { - if (mCanTriggerLongPress) - mBrowserController.onLongPress(); - } - - /** - * Is called when the user is swiping after the doubletap, which in our - * case means that he is zooming. - */ - @Override - public boolean onDoubleTapEvent(MotionEvent e) { - mCanTriggerLongPress = false; - return false; - } - - /** - * Is called when something is starting being pressed, always before - * onLongPress. - */ - @Override - public void onShowPress(MotionEvent e) { - mCanTriggerLongPress = true; - } - } + private final Title mTitle; + private WebView mWebView; + private BrowserController mBrowserController; + private GestureDetector mGestureDetector; + private final Activity mActivity; + private WebSettings mSettings; + private static String mHomepage; + private static String mDefaultUserAgent; + private static Bitmap mWebpageBitmap; + private static PreferenceManager mPreferences; + private final AdBlock mAdBlock; + private IntentUtils mIntentUtils; + private final Paint mPaint = new Paint(); + private boolean isForegroundTab; + private boolean mTextReflow = false; + private boolean mInvertPage = false; + private static float mMaxFling; + private static final int API = android.os.Build.VERSION.SDK_INT; + private static final int SCROLL_UP_THRESHOLD = Utils.convertDpToPixels(10); + private static final int SCROLL_DOWN_THRESHOLD = Utils.convertDpToPixels(100); + private static final float[] mNegativeColorArray = {-1.0f, 0, 0, 0, 255, // red + 0, -1.0f, 0, 0, 255, // green + 0, 0, -1.0f, 0, 255, // blue + 0, 0, 0, 1.0f, 0 // alpha + }; + + @SuppressWarnings("deprecation") + @SuppressLint("NewApi") + public LightningView(Activity activity, String url, boolean darkTheme) { + + mActivity = activity; + mWebView = new WebView(activity); + mTitle = new Title(activity, darkTheme); + mAdBlock = AdBlock.getInstance(activity.getApplicationContext()); + + mWebpageBitmap = Utils.getWebpageBitmap(activity.getResources(), darkTheme); + + mMaxFling = ViewConfiguration.get(activity).getScaledMaximumFlingVelocity(); + + try { + mBrowserController = (BrowserController) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity + " must implement BrowserController"); + } + mIntentUtils = new IntentUtils(mBrowserController); + mWebView.setDrawingCacheBackgroundColor(0x00000000); + mWebView.setFocusableInTouchMode(true); + mWebView.setFocusable(true); + mWebView.setAnimationCacheEnabled(false); + mWebView.setDrawingCacheEnabled(false); + mWebView.setWillNotCacheDrawing(true); + mWebView.setAlwaysDrawnWithCacheEnabled(false); + mWebView.setBackgroundColor(0); + + if (API > 15) { + mWebView.setBackground(null); + mWebView.getRootView().setBackground(null); + } else if (mWebView.getRootView() != null) { + mWebView.getRootView().setBackgroundDrawable(null); + } + mWebView.setScrollbarFadingEnabled(true); + mWebView.setSaveEnabled(true); + mWebView.setWebChromeClient(new LightningChromeClient(activity)); + mWebView.setWebViewClient(new LightningWebClient(activity)); + mWebView.setDownloadListener(new LightningDownloadListener(activity)); + mGestureDetector = new GestureDetector(activity, new CustomGestureListener()); + mWebView.setOnTouchListener(new TouchListener()); + mDefaultUserAgent = mWebView.getSettings().getUserAgentString(); + mSettings = mWebView.getSettings(); + initializeSettings(mWebView.getSettings(), activity); + initializePreferences(activity); + + if (url != null) { + if (!url.trim().isEmpty()) { + mWebView.loadUrl(url); + } else { + // don't load anything, the user is looking for a blank tab + } + } else { + if (mHomepage.startsWith("about:home")) { + mWebView.loadUrl(getHomepage()); + } else if (mHomepage.startsWith("about:bookmarks")) { + mBrowserController.openBookmarkPage(mWebView); + } else { + mWebView.loadUrl(mHomepage); + } + } + } + + private String getHomepage() { + StringBuilder homepageBuilder = new StringBuilder(); + homepageBuilder.append(StartPage.HEAD); + String icon; + String searchUrl; + switch (mPreferences.getSearchChoice()) { + case 0: + // CUSTOM SEARCH + icon = "file:///android_asset/lightning.png"; + searchUrl = mPreferences.getSearchUrl(); + break; + case 1: + // GOOGLE_SEARCH; + icon = "file:///android_asset/google.png"; + // "https://www.google.com/images/srpr/logo11w.png"; + searchUrl = Constants.GOOGLE_SEARCH; + break; + case 2: + // ANDROID SEARCH; + icon = "file:///android_asset/ask.png"; + searchUrl = Constants.ASK_SEARCH; + break; + case 3: + // BING_SEARCH; + icon = "file:///android_asset/bing.png"; + // "http://upload.wikimedia.org/wikipedia/commons/thumb/b/b1/Bing_logo_%282013%29.svg/500px-Bing_logo_%282013%29.svg.png"; + searchUrl = Constants.BING_SEARCH; + break; + case 4: + // YAHOO_SEARCH; + icon = "file:///android_asset/yahoo.png"; + // "http://upload.wikimedia.org/wikipedia/commons/thumb/2/24/Yahoo%21_logo.svg/799px-Yahoo%21_logo.svg.png"; + searchUrl = Constants.YAHOO_SEARCH; + break; + case 5: + // STARTPAGE_SEARCH; + icon = "file:///android_asset/startpage.png"; + // "https://startpage.com/graphics/startp_logo.gif"; + searchUrl = Constants.STARTPAGE_SEARCH; + break; + case 6: + // STARTPAGE_MOBILE + icon = "file:///android_asset/startpage.png"; + // "https://startpage.com/graphics/startp_logo.gif"; + searchUrl = Constants.STARTPAGE_MOBILE_SEARCH; + break; + case 7: + // DUCK_SEARCH; + icon = "file:///android_asset/duckduckgo.png"; + // "https://duckduckgo.com/assets/logo_homepage.normal.v101.png"; + searchUrl = Constants.DUCK_SEARCH; + break; + case 8: + // DUCK_LITE_SEARCH; + icon = "file:///android_asset/duckduckgo.png"; + // "https://duckduckgo.com/assets/logo_homepage.normal.v101.png"; + searchUrl = Constants.DUCK_LITE_SEARCH; + break; + case 9: + // BAIDU_SEARCH; + icon = "file:///android_asset/baidu.png"; + // "http://www.baidu.com/img/bdlogo.gif"; + searchUrl = Constants.BAIDU_SEARCH; + break; + case 10: + // YANDEX_SEARCH; + icon = "file:///android_asset/yandex.png"; + // "http://upload.wikimedia.org/wikipedia/commons/thumb/9/91/Yandex.svg/600px-Yandex.svg.png"; + searchUrl = Constants.YANDEX_SEARCH; + break; + default: + // DEFAULT GOOGLE_SEARCH; + icon = "file:///android_asset/google.png"; + searchUrl = Constants.GOOGLE_SEARCH; + break; + + } + + homepageBuilder.append(icon); + homepageBuilder.append(StartPage.MIDDLE); + homepageBuilder.append(searchUrl); + homepageBuilder.append(StartPage.END); + + File homepage = new File(mActivity.getFilesDir(), "homepage.html"); + try { + FileWriter hWriter = new FileWriter(homepage, false); + hWriter.write(homepageBuilder.toString()); + hWriter.close(); + } catch (IOException e) { + e.printStackTrace(); + } + + return Constants.FILE + homepage; + } + + @SuppressWarnings("deprecation") + @SuppressLint({"NewApi", "SetJavaScriptEnabled"}) + public synchronized void initializePreferences(Context context) { + mPreferences = PreferenceManager.getInstance(); + mHomepage = mPreferences.getHomepage(); + mAdBlock.updatePreference(); + if (mSettings == null && mWebView != null) { + mSettings = mWebView.getSettings(); + } else if (mSettings == null) { + return; + } + + setColorMode(mPreferences.getRenderingMode()); + + if (!mBrowserController.isIncognito()) { + mSettings.setGeolocationEnabled(mPreferences.getLocationEnabled()); + } else { + mSettings.setGeolocationEnabled(false); + } + if (API < 19) { + switch (mPreferences.getFlashSupport()) { + case 0: + mSettings.setPluginState(PluginState.OFF); + break; + case 1: + mSettings.setPluginState(PluginState.ON_DEMAND); + break; + case 2: + mSettings.setPluginState(PluginState.ON); + break; + default: + break; + } + } + + switch (mPreferences.getUserAgentChoice()) { + case 1: + if (API > 16) { + mSettings.setUserAgentString(WebSettings.getDefaultUserAgent(context)); + } else { + mSettings.setUserAgentString(mDefaultUserAgent); + } + break; + case 2: + mSettings.setUserAgentString(Constants.DESKTOP_USER_AGENT); + break; + case 3: + mSettings.setUserAgentString(Constants.MOBILE_USER_AGENT); + break; + case 4: + mSettings.setUserAgentString(mPreferences.getUserAgentString(mDefaultUserAgent)); + break; + } + + if (mPreferences.getSavePasswordsEnabled() && !mBrowserController.isIncognito()) { + if (API < 18) { + mSettings.setSavePassword(true); + } + mSettings.setSaveFormData(true); + } else { + if (API < 18) { + mSettings.setSavePassword(false); + } + mSettings.setSaveFormData(false); + } + + if (mPreferences.getJavaScriptEnabled()) { + mSettings.setJavaScriptEnabled(true); + mSettings.setJavaScriptCanOpenWindowsAutomatically(true); + } + + if (mPreferences.getTextReflowEnabled()) { + mTextReflow = true; + mSettings.setLayoutAlgorithm(LayoutAlgorithm.NARROW_COLUMNS); + if (API >= android.os.Build.VERSION_CODES.KITKAT) { + try { + mSettings.setLayoutAlgorithm(LayoutAlgorithm.TEXT_AUTOSIZING); + } catch (Exception e) { + // This shouldn't be necessary, but there are a number + // of KitKat devices that crash trying to set this + Log.e(Constants.TAG, "Problem setting LayoutAlgorithm to TEXT_AUTOSIZING"); + } + } + } else { + mTextReflow = false; + mSettings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL); + } + + mSettings.setBlockNetworkImage(mPreferences.getBlockImagesEnabled()); + mSettings.setSupportMultipleWindows(mPreferences.getPopupsEnabled()); + mSettings.setUseWideViewPort(mPreferences.getUseWideViewportEnabled()); + mSettings.setLoadWithOverviewMode(mPreferences.getOverviewModeEnabled()); + switch (mPreferences.getTextSize()) { + case 1: + mSettings.setTextZoom(200); + break; + case 2: + mSettings.setTextZoom(150); + break; + case 3: + mSettings.setTextZoom(100); + break; + case 4: + mSettings.setTextZoom(75); + break; + case 5: + mSettings.setTextZoom(50); + break; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + CookieManager.getInstance().setAcceptThirdPartyCookies(mWebView, + !mPreferences.getBlockThirdPartyCookiesEnabled()); + } + } + + @SuppressWarnings("deprecation") + @SuppressLint({"SetJavaScriptEnabled", "NewApi"}) + private void initializeSettings(WebSettings settings, Context context) { + if (API < Build.VERSION_CODES.JELLY_BEAN_MR2) { + settings.setAppCacheMaxSize(Long.MAX_VALUE); + } + if (API < Build.VERSION_CODES.JELLY_BEAN_MR1) { + settings.setEnableSmoothTransition(true); + } + if (API > Build.VERSION_CODES.JELLY_BEAN) { + settings.setMediaPlaybackRequiresUserGesture(true); + } + if (API >= Build.VERSION_CODES.LOLLIPOP && !mBrowserController.isIncognito()) { + settings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE); + } else if (API >= Build.VERSION_CODES.LOLLIPOP) { + // We're in Incognito mode, reject + settings.setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW); + } + settings.setDomStorageEnabled(true); + settings.setAppCacheEnabled(true); + settings.setCacheMode(WebSettings.LOAD_DEFAULT); + settings.setDatabaseEnabled(true); + settings.setSupportZoom(true); + settings.setBuiltInZoomControls(true); + settings.setDisplayZoomControls(false); + settings.setAllowContentAccess(true); + settings.setAllowFileAccess(true); + settings.setDefaultTextEncodingName("utf-8"); + if (API >= Build.VERSION_CODES.JELLY_BEAN) { + settings.setAllowFileAccessFromFileURLs(false); + settings.setAllowUniversalAccessFromFileURLs(false); + } + + settings.setAppCachePath(context.getDir("appcache", 0).getPath()); + settings.setGeolocationDatabasePath(context.getDir("geolocation", 0).getPath()); + if (API < Build.VERSION_CODES.KITKAT) { + settings.setDatabasePath(context.getDir("databases", 0).getPath()); + } + } + + public boolean isShown() { + return mWebView != null && mWebView.isShown(); + } + + public synchronized void onPause() { + if (mWebView != null) + mWebView.onPause(); + } + + public synchronized void onResume() { + if (mWebView != null) + mWebView.onResume(); + } + + public synchronized void freeMemory() { + if (mWebView != null && Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) + mWebView.freeMemory(); + } + + public void setForegroundTab(boolean isForeground) { + isForegroundTab = isForeground; + mBrowserController.update(); + } + + public boolean isForegroundTab() { + return isForegroundTab; + } + + public int getProgress() { + if (mWebView != null) { + return mWebView.getProgress(); + } else { + return 100; + } + } + + public synchronized void stopLoading() { + if (mWebView != null) { + mWebView.stopLoading(); + } + } + + public void setHardwareRendering() { + mWebView.setLayerType(View.LAYER_TYPE_HARDWARE, mPaint); + } + + public void setNormalRendering() { + mWebView.setLayerType(View.LAYER_TYPE_NONE, null); + } + + public void setSoftwareRendering() { + mWebView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); + } + + private void setColorMode(int mode) { + mInvertPage = false; + switch (mode) { + case 0: + mPaint.setColorFilter(null); + // setSoftwareRendering(); // Some devices get segfaults + // in the WebView with Hardware Acceleration enabled, + // the only fix is to disable hardware rendering + setNormalRendering(); + mInvertPage = false; + break; + case 1: + ColorMatrixColorFilter filterInvert = new ColorMatrixColorFilter( + mNegativeColorArray); + mPaint.setColorFilter(filterInvert); + setHardwareRendering(); + + mInvertPage = true; + break; + case 2: + ColorMatrix cm = new ColorMatrix(); + cm.setSaturation(0); + ColorMatrixColorFilter filterGray = new ColorMatrixColorFilter(cm); + mPaint.setColorFilter(filterGray); + setHardwareRendering(); + break; + case 3: + ColorMatrix matrix = new ColorMatrix(); + matrix.set(mNegativeColorArray); + ColorMatrix matrixGray = new ColorMatrix(); + matrixGray.setSaturation(0); + ColorMatrix concat = new ColorMatrix(); + concat.setConcat(matrix, matrixGray); + ColorMatrixColorFilter filterInvertGray = new ColorMatrixColorFilter(concat); + mPaint.setColorFilter(filterInvertGray); + setHardwareRendering(); + + mInvertPage = true; + break; + + } + + } + + public synchronized void pauseTimers() { + if (mWebView != null) { + mWebView.pauseTimers(); + } + } + + public synchronized void resumeTimers() { + if (mWebView != null) { + mWebView.resumeTimers(); + } + } + + public void requestFocus() { + if (mWebView != null && !mWebView.hasFocus()) { + mWebView.requestFocus(); + } + } + + public void setVisibility(int visible) { + if (mWebView != null) { + mWebView.setVisibility(visible); + } + } + + public void clearCache(boolean disk) { + if (mWebView != null) { + mWebView.clearCache(disk); + } + } + + public synchronized void reload() { + // Check if configured proxy is available + if (!mBrowserController.isProxyReady()) { + // User has been notified + return; + } + + if (mWebView != null) { + mWebView.reload(); + } + } + + private void cacheFavicon(Bitmap icon) { + String hash = String.valueOf(Utils.getDomainName(getUrl()).hashCode()); + Log.d(Constants.TAG, "Caching icon for " + Utils.getDomainName(getUrl())); + FileOutputStream fos = null; + try { + File image = new File(mActivity.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); + } + } + + @SuppressWarnings("deprecation") + @SuppressLint("NewApi") + public synchronized void find(String text) { + if (mWebView != null) { + if (API > 16) { + mWebView.findAllAsync(text); + } else { + mWebView.findAll(text); + } + } + } + + @Nullable + public Activity getActivity() { + return mActivity; + } + + public synchronized void onDestroy() { + if (mWebView != null) { + mWebView.stopLoading(); + mWebView.onPause(); + mWebView.clearHistory(); + mWebView.setVisibility(View.GONE); + mWebView.removeAllViews(); + mWebView.destroyDrawingCache(); + // mWebView.destroy(); //this is causing the segfault + mWebView = null; + } + } + + public synchronized void goBack() { + if (mWebView != null) { + mWebView.goBack(); + } + } + + public String getUserAgent() { + if (mWebView != null) { + return mWebView.getSettings().getUserAgentString(); + } else { + return ""; + } + } + + public synchronized void goForward() { + if (mWebView != null) { + mWebView.goForward(); + } + } + + public boolean canGoBack() { + return mWebView != null && mWebView.canGoBack(); + } + + public boolean canGoForward() { + return mWebView != null && mWebView.canGoForward(); + } + + @Nullable + public WebView getWebView() { + return mWebView; + } + + public Bitmap getFavicon() { + return mTitle.getFavicon(); + } + + public synchronized void loadUrl(String url) { + // Check if configured proxy is available + if (!mBrowserController.isProxyReady()) { + // User has been notified + return; + } + + if (mWebView != null) { + mWebView.loadUrl(url); + } + } + + public synchronized void invalidate() { + if (mWebView != null) { + mWebView.invalidate(); + } + } + + public String getTitle() { + return mTitle.getTitle(); + } + + @NonNull + public String getUrl() { + if (mWebView != null && mWebView.getUrl() != null) { + return mWebView.getUrl(); + } else { + return ""; + } + } + + public class LightningWebClient extends WebViewClient { + + final Context mActivity; + + LightningWebClient(Context context) { + mActivity = context; + } + + @Override + public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + if (mAdBlock.isAd(request.getUrl().getHost())) { + 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; + } + + @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); + } + mBrowserController.update(); + } + + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + if (isShown()) { + mBrowserController.updateUrl(url, false); + mBrowserController.showActionBar(); + } + mTitle.setFavicon(mWebpageBitmap); + mBrowserController.update(); + } + + @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; + + @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 (!mBrowserController.isProxyReady()) { + // User has been notified + return true; + } + + if (mBrowserController.isIncognito()) { + 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 Context mActivity; + + LightningChromeClient(Context context) { + mActivity = context; + } + + @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); + mBrowserController.update(); + 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)); + } + mBrowserController.update(); + if (view != null) + mBrowserController.updateHistory(title, view.getUrl()); + } + + @Override + public void onGeolocationPermissionsShowPrompt(final String origin, + final GeolocationPermissions.Callback callback) { + 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) { + // TODO Auto-generated method stub + super.onCloseWindow(window); + } + + public void openFileChooser(ValueCallback uploadMsg) { + mBrowserController.openFileChooser(uploadMsg); + } + + public void openFileChooser(ValueCallback uploadMsg, String acceptType) { + mBrowserController.openFileChooser(uploadMsg); + } + + public void openFileChooser(ValueCallback uploadMsg, String acceptType, String capture) { + mBrowserController.openFileChooser(uploadMsg); + } + + public boolean onShowFileChooser(WebView webView, ValueCallback 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 { +// Activity activity = mBrowserController.getActivity(); + 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 = Utils.getWebpageBitmap(context.getResources(), 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 { + + float mLocation; + float mY; + int mAction; + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouch(View view, MotionEvent arg1) { + if (view == null) + return false; + + if (!view.hasFocus()) { + view.requestFocus(); + } + mAction = arg1.getAction(); + mY = arg1.getY(); + if (mAction == MotionEvent.ACTION_DOWN) { + mLocation = mY; + } else if (mAction == MotionEvent.ACTION_UP) { + final float distance = (mY - mLocation); + if (distance > SCROLL_UP_THRESHOLD && view.getScrollY() < SCROLL_UP_THRESHOLD) { +// if (mWebView.getScrollY() != 0) { + mBrowserController.showActionBar(); +// } else { +// mBrowserController.toggleActionBar(); +// } + } else if (distance < -SCROLL_UP_THRESHOLD) { + mBrowserController.hideActionBar(); + } + mLocation = 0; + } + mGestureDetector.onTouchEvent(arg1); + return false; + } + } + + private class CustomGestureListener extends SimpleOnGestureListener { + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + return super.onScroll(e1, e2, distanceX, distanceY); + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + int power = (int) (velocityY * 100 / mMaxFling); + Log.e("YOLO", "POWER: " + power); + if (power < -10) { + mBrowserController.hideActionBar(); + } else if (power > 15) { + mBrowserController.showActionBar(); + } + return super.onFling(e1, e2, velocityX, velocityY); + } + + /** + * Without this, onLongPress is not called when user is zooming using + * two fingers, but is when using only one. + *

+ * The required behaviour is to not trigger this when the user is + * zooming, it shouldn't matter how much fingers the user's using. + */ + private boolean mCanTriggerLongPress = true; + + @Override + public void onLongPress(MotionEvent e) { + if (mCanTriggerLongPress) + mBrowserController.onLongPress(); + } + + /** + * Is called when the user is swiping after the doubletap, which in our + * case means that he is zooming. + */ + @Override + public boolean onDoubleTapEvent(MotionEvent e) { + mCanTriggerLongPress = false; + return false; + } + + /** + * Is called when something is starting being pressed, always before + * onLongPress. + */ + @Override + public void onShowPress(MotionEvent e) { + mCanTriggerLongPress = true; + } + } } diff --git a/app/src/main/res/layout/picker_manual_proxy.xml b/app/src/main/res/layout/picker_manual_proxy.xml index f8568ac..c59d030 100644 --- a/app/src/main/res/layout/picker_manual_proxy.xml +++ b/app/src/main/res/layout/picker_manual_proxy.xml @@ -1,10 +1,12 @@ + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:paddingEnd="4dp" + android:paddingLeft="4dp" + android:paddingRight="4dp" + android:paddingStart="4dp"> + android:text="@string/host" /> + android:inputType="text" /> + android:text="@string/port" /> + android:inputType="number" /> \ No newline at end of file diff --git a/app/src/main/res/layout/toolbar_content.xml b/app/src/main/res/layout/toolbar_content.xml index ccd0f49..0d2737c 100644 --- a/app/src/main/res/layout/toolbar_content.xml +++ b/app/src/main/res/layout/toolbar_content.xml @@ -1,6 +1,5 @@ @@ -9,7 +8,6 @@ android:id="@+id/arrow_button" android:layout_width="?attr/actionBarSize" android:layout_height="?attr/actionBarSize" - android:layout_marginLeft="2dp" android:layout_marginTop="2dp" android:background="?attr/actionBarItemBackground" android:clickable="true" diff --git a/app/src/main/res/layout/two_line_autocomplete.xml b/app/src/main/res/layout/two_line_autocomplete.xml index 5a100f4..5c6497a 100644 --- a/app/src/main/res/layout/two_line_autocomplete.xml +++ b/app/src/main/res/layout/two_line_autocomplete.xml @@ -3,22 +3,25 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" android:clickable="false" + android:gravity="center_vertical" android:orientation="horizontal" android:paddingBottom="3dp" + android:paddingEnd="6dp" android:paddingLeft="6dp" - android:paddingTop="3dp" - android:gravity="center_vertical"> + android:paddingRight="6dp" + android:paddingStart="6dp" + android:paddingTop="3dp"> + android:layout_height="25dp" /> + android:paddingLeft="5dp"> + @color/transparent @color/primary_color ?attr/listChoiceBackgroundIndicator @color/primary_color @@ -20,6 +21,7 @@