diff --git a/.gitignore b/.gitignore index 7f036ba..39bb026 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,5 @@ out # Source: # https://raw.githubusercontent.com/github/gitignore/master/Android.gitignore # https://gitlab.com/fdroid/fdroidclient/raw/master/.gitignore + +*.iml diff --git a/app/app.iml b/app/app.iml deleted file mode 100644 index d6f1ddb..0000000 --- a/app/app.iml +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/LightningLite/java/acr/browser/lightning/utils/ProxyUtils.java b/app/src/LightningLite/java/acr/browser/lightning/utils/ProxyUtils.java index d49f9f2..ed8211b 100644 --- a/app/src/LightningLite/java/acr/browser/lightning/utils/ProxyUtils.java +++ b/app/src/LightningLite/java/acr/browser/lightning/utils/ProxyUtils.java @@ -6,10 +6,13 @@ import android.content.DialogInterface; import android.support.v7.app.AlertDialog; import android.util.Log; +import com.squareup.otto.Bus; + import net.i2p.android.ui.I2PAndroidHelper; import acr.browser.lightning.R; import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.bus.BrowserEvents; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.preference.PreferenceManager; import info.guardianproject.netcipher.proxy.OrbotHelper; @@ -26,8 +29,11 @@ public class ProxyUtils { private final PreferenceManager mPreferences; private static ProxyUtils mInstance; + private final Bus mEventBus; + private ProxyUtils(Context context) { - mPreferences = PreferenceManager.getInstance(); + mPreferences = BrowserApp.getAppComponent().getPreferenceManager(); + mEventBus = BrowserApp.getAppComponent().getBus(); mI2PHelper = new I2PAndroidHelper(context.getApplicationContext()); } @@ -143,13 +149,13 @@ public class ProxyUtils { } - public boolean isProxyReady(Activity activity) { + public boolean isProxyReady() { if (mPreferences.getProxyChoice() == Constants.PROXY_I2P) { if (!mI2PHelper.isI2PAndroidRunning()) { - Utils.showSnackbar(activity, R.string.i2p_not_running); + mEventBus.post(new BrowserEvents.ShowSnackBarMessage(R.string.i2p_not_running)); return false; } else if (!mI2PHelper.areTunnelsActive()) { - Utils.showSnackbar(activity, R.string.i2p_tunnels_not_ready); + mEventBus.post(new BrowserEvents.ShowSnackBarMessage(R.string.i2p_tunnels_not_ready)); return false; } } 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 d49f9f2..f450692 100644 --- a/app/src/LightningPlus/java/acr/browser/lightning/utils/ProxyUtils.java +++ b/app/src/LightningPlus/java/acr/browser/lightning/utils/ProxyUtils.java @@ -10,6 +10,7 @@ import net.i2p.android.ui.I2PAndroidHelper; import acr.browser.lightning.R; import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.bus.BrowserEvents; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.preference.PreferenceManager; import info.guardianproject.netcipher.proxy.OrbotHelper; @@ -27,7 +28,7 @@ public class ProxyUtils { private static ProxyUtils mInstance; private ProxyUtils(Context context) { - mPreferences = PreferenceManager.getInstance(); + mPreferences = BrowserApp.getAppComponent().getPreferenceManager(); mI2PHelper = new I2PAndroidHelper(context.getApplicationContext()); } @@ -143,13 +144,15 @@ public class ProxyUtils { } - public boolean isProxyReady(Activity activity) { + public boolean isProxyReady() { if (mPreferences.getProxyChoice() == Constants.PROXY_I2P) { if (!mI2PHelper.isI2PAndroidRunning()) { - Utils.showSnackbar(activity, R.string.i2p_not_running); + BrowserApp.getAppComponent().getBus() + .post(new BrowserEvents.ShowSnackBarMessage(R.string.i2p_not_running)); return false; } else if (!mI2PHelper.areTunnelsActive()) { - Utils.showSnackbar(activity, R.string.i2p_tunnels_not_ready); + BrowserApp.getAppComponent().getBus() + .post(new BrowserEvents.ShowSnackBarMessage(R.string.i2p_tunnels_not_ready)); return false; } } @@ -200,7 +203,7 @@ public class ProxyUtils { break; case Constants.PROXY_I2P: - I2PAndroidHelper ih = new I2PAndroidHelper(activity.getApplicationContext()); + I2PAndroidHelper ih = new I2PAndroidHelper(BrowserApp.getAppContext()); if (!ih.isI2PAndroidInstalled()) { choice = Constants.NO_PROXY; ih.promptToInstall(activity); 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 dd33312..88df5b2 100644 --- a/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java @@ -11,6 +11,7 @@ import android.animation.ValueAnimator.AnimatorUpdateListener; import android.app.Activity; import android.content.ClipData; import android.content.ClipboardManager; +import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -18,14 +19,8 @@ import android.content.IntentFilter; import android.database.sqlite.SQLiteException; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.ColorFilter; -import android.graphics.ColorMatrix; -import android.graphics.ColorMatrixColorFilter; -import android.graphics.Paint; import android.graphics.PorterDuff; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.media.MediaPlayer; @@ -38,17 +33,15 @@ import android.provider.MediaStore; import android.support.annotation.IdRes; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.v4.app.FragmentManager; import android.support.v4.content.ContextCompat; 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.graphics.drawable.DrawerArrowDrawable; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.KeyEvent; @@ -76,7 +69,6 @@ import android.webkit.ValueCallback; import android.webkit.WebChromeClient.CustomViewCallback; import android.webkit.WebIconDatabase; import android.webkit.WebView; -import android.webkit.WebView.HitTestResult; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; @@ -95,8 +87,6 @@ import com.squareup.otto.Bus; import com.squareup.otto.Subscribe; import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -107,14 +97,17 @@ import acr.browser.lightning.R; import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.bus.BookmarkEvents; import acr.browser.lightning.bus.BrowserEvents; +import acr.browser.lightning.bus.NavigationEvents; +import acr.browser.lightning.bus.TabEvents; import acr.browser.lightning.constant.BookmarkPage; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.constant.HistoryPage; import acr.browser.lightning.controller.BrowserController; import acr.browser.lightning.database.BookmarkManager; import acr.browser.lightning.database.HistoryDatabase; -import acr.browser.lightning.dialog.BookmarksDialogBuilder; -import acr.browser.lightning.object.ClickHandler; +import acr.browser.lightning.dialog.LightningDialogBuilder; +import acr.browser.lightning.fragment.BookmarksFragment; +import acr.browser.lightning.fragment.TabsFragment; import acr.browser.lightning.object.SearchAdapter; import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.receiver.NetworkReceiver; @@ -157,12 +150,6 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements RelativeLayout mSearchBar; - // Browser Views - private final List mWebViewList = new ArrayList<>(); - private LightningView mCurrentView; - private WebView mWebView; - private RecyclerView mTabListView; - // Toolbar Views private AutoCompleteTextView mSearch; private ImageView mArrowImage; @@ -173,11 +160,9 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements private View mCustomView; // Adapter - private LightningViewAdapter mTabAdapter; private SearchAdapter mSearchAdapter; // Callback - private ClickHandler mClickHandler; private CustomViewCallback mCustomViewCallback; private ValueCallback mUploadMessage; private ValueCallback mFilePathCallback; @@ -192,10 +177,6 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements mCurrentUiColor = Color.BLACK; private String mSearchText, mUntitledTitle, mHomepage, mCameraPhotoPath; - // Storage - private HistoryDatabase mHistoryDatabase; - private final PreferenceManager mPreferences = PreferenceManager.getInstance(); - // The singleton BookmarkManager @Inject BookmarkManager mBookmarkManager; @@ -208,7 +189,15 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements BookmarkPage mBookmarkPage; @Inject - BookmarksDialogBuilder mBookmarksDialogBuilder; + LightningDialogBuilder bookmarksDialogBuilder; + + @Inject + TabsManager tabsManager; + + // Preference manager was moved on ThemeableBrowserActivity + + @Inject + HistoryDatabase mHistoryDatabase; // Image private Bitmap mWebpageBitmap; @@ -229,7 +218,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements public abstract boolean isIncognito(); - abstract void initializeTabs(); +// abstract void initializeTabs(); abstract void closeActivity(); @@ -257,18 +246,16 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements mIconColor = mDarkTheme ? ThemeUtils.getIconDarkThemeColor(this) : ThemeUtils.getIconLightThemeColor(this); mShowTabsInDrawer = mPreferences.getShowTabsInDrawer(!isTablet()); - mWebViewList.clear(); - mClickHandler = new ClickHandler(this); // initialize background ColorDrawable mBackground.setColor(((ColorDrawable) mToolbarLayout.getBackground()).getColor()); - setupFrameLayoutButton(R.id.new_tab_button, R.id.icon_plus); // Drawer stutters otherwise mDrawerLeft.setLayerType(View.LAYER_TYPE_HARDWARE, null); mDrawerRight.setLayerType(View.LAYER_TYPE_HARDWARE, null); - ImageView tabTitleImage = (ImageView) findViewById(R.id.plusIcon); - tabTitleImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); +// TODO Please review this +// ImageView tabTitleImage = (ImageView) findViewById(R.id.plusIcon); +// tabTitleImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !mShowTabsInDrawer) { getWindow().setStatusBarColor(Color.BLACK); @@ -281,31 +268,28 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements mHomepage = mPreferences.getHomepage(); - RecyclerView horizontalListView = (RecyclerView) findViewById(R.id.twv_list); + final TabsFragment tabsFragment = new TabsFragment(); + final int containerId = mShowTabsInDrawer ? R.id.left_drawer : R.id.tabs_toolbar_container; + final Bundle tabsFragmentArguments = new Bundle(); + tabsFragmentArguments.putBoolean(TabsFragment.IS_INCOGNITO, isIncognito()); + tabsFragmentArguments.putBoolean(TabsFragment.VERTICAL_MODE, mShowTabsInDrawer); + tabsFragment.setArguments(tabsFragmentArguments); + final BookmarksFragment bookmarksFragment = new BookmarksFragment(); + final Bundle bookmarksFragmentArguments = new Bundle(); + bookmarksFragmentArguments.putBoolean(BookmarksFragment.INCOGNITO_MODE, isIncognito()); + bookmarksFragment.setArguments(bookmarksFragmentArguments); + final FragmentManager fragmentManager = getSupportFragmentManager(); + fragmentManager + .beginTransaction() + .add(containerId, tabsFragment) + .add(R.id.right_drawer, bookmarksFragment) + .commit(); if (mShowTabsInDrawer) { - mTabAdapter = new LightningViewAdapter(this, R.layout.tab_list_item, mWebViewList); - mTabListView = (RecyclerView) findViewById(R.id.left_drawer_list); - mTabListView.setOverScrollMode(View.OVER_SCROLL_IF_CONTENT_SCROLLS); - RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); - mTabListView.setLayoutManager(layoutManager); - mTabListView.setHasFixedSize(true); - mToolbarLayout.removeView(horizontalListView); - } else { - mTabAdapter = new LightningViewAdapter(this, R.layout.tab_list_item_horizontal, mWebViewList); - mTabListView = horizontalListView; - mTabListView.setOverScrollMode(View.OVER_SCROLL_NEVER); - mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, mDrawerLeft); - RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false); - mTabListView.setLayoutManager(layoutManager); - mTabListView.setHasFixedSize(true); + mToolbarLayout.removeView(findViewById(R.id.tabs_toolbar_container)); } - mTabListView.setAdapter(mTabAdapter); - - mHistoryDatabase = HistoryDatabase.getInstance(); - if (actionBar == null) return; @@ -336,12 +320,6 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements mProxyUtils = ProxyUtils.getInstance(); - setupFrameLayoutButton(R.id.action_back, R.id.icon_back); - setupFrameLayoutButton(R.id.action_forward, R.id.icon_forward); - setupFrameLayoutButton(R.id.action_toggle_desktop, R.id.icon_desktop); - setupFrameLayoutButton(R.id.action_reading, R.id.icon_reading); - setupFrameLayoutButton(R.id.action_home, R.id.icon_home); - // create the search EditText in the ToolBar mSearch = (AutoCompleteTextView) customView.findViewById(R.id.search); mUntitledTitle = getString(R.string.untitled); @@ -379,7 +357,9 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements WebIconDatabase.getInstance().open(getDir("icons", MODE_PRIVATE).getPath()); } - initializeTabs(); + tabsManager.restoreTabs(this, mDarkTheme, isIncognito()); + // At this point we always have at least a tab in the tab manager + showTab(0); mProxyUtils.checkForProxy(this); } @@ -394,8 +374,9 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(mSearch.getWindowToken(), 0); searchTheWeb(mSearch.getText().toString()); - if (mCurrentView != null) { - mCurrentView.requestFocus(); + final LightningView currentView = tabsManager.getCurrentTab(); + if (currentView != null) { + currentView.requestFocus(); } return true; default: @@ -416,8 +397,9 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(mSearch.getWindowToken(), 0); searchTheWeb(mSearch.getText().toString()); - if (mCurrentView != null) { - mCurrentView.requestFocus(); + final LightningView currentView = tabsManager.getCurrentTab(); + if (currentView != null) { + currentView.requestFocus(); } return true; } @@ -426,11 +408,12 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements @Override public void onFocusChange(View v, final boolean hasFocus) { - if (!hasFocus && mCurrentView != null) { - setIsLoading(mCurrentView.getProgress() < 100); - updateUrl(mCurrentView.getUrl(), true); + final LightningView currentView = tabsManager.getCurrentTab(); + if (!hasFocus && currentView != null) { + setIsLoading(currentView.getProgress() < 100); + updateUrl(currentView.getUrl(), true); } else if (hasFocus) { - String url = mCurrentView.getUrl(); + String url = currentView.getUrl(); if (url.startsWith(Constants.FILE)) { mSearch.setText(""); } else { @@ -581,54 +564,17 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements } } - void restoreOrNewTab() { - mIdGenerator = 0; - - String url = null; - if (getIntent() != null) { - url = getIntent().getDataString(); - if (url != null) { - if (url.startsWith(Constants.FILE)) { - Utils.showSnackbar(this, R.string.message_blocked_local); - url = null; - } - } - } - if (mPreferences.getRestoreLostTabsEnabled()) { - String mem = mPreferences.getMemoryUrl(); - mPreferences.setMemoryUrl(""); - String[] array = Utils.getArray(mem); - int count = 0; - for (String urlString : array) { - if (!urlString.isEmpty()) { - if (url != null && url.compareTo(urlString) == 0) { - url = null; - } - newTab(urlString, true); - count++; - } - } - if (url != null) { - newTab(url, true); - } else if (count == 0) { - newTab(null, true); - } - } else { - newTab(url, true); - } - } - private void initializePreferences() { + final LightningView currentView = tabsManager.getCurrentTab(); + final WebView currentWebView = currentView.getWebView(); mFullScreen = mPreferences.getFullScreenEnabled(); mColorMode = mPreferences.getColorModeEnabled(); mColorMode &= !mDarkTheme; if (!isIncognito() && !mColorMode && !mDarkTheme && mWebpageBitmap != null) { changeToolbarBackground(mWebpageBitmap, null); - mTabAdapter.notifyDataSetChanged(); - } else if (!isIncognito() && mCurrentView != null && !mDarkTheme - && mCurrentView.getFavicon() != null) { - changeToolbarBackground(mCurrentView.getFavicon(), null); - mTabAdapter.notifyDataSetChanged(); + } else if (!isIncognito() && currentView != null && !mDarkTheme + && currentView.getFavicon() != null) { + changeToolbarBackground(currentView.getFavicon(), null); } if (mFullScreen) { @@ -638,8 +584,8 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements mToolbarLayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); height = mToolbarLayout.getMeasuredHeight(); } - if (mWebView != null) - mWebView.setTranslationY(height); + if (currentWebView != null) + currentWebView.setTranslationY(height); mBrowserFrame.setLayoutTransition(null); if (mBrowserFrame.findViewById(R.id.toolbar_layout) == null) { mUiLayout.removeView(mToolbarLayout); @@ -653,8 +599,8 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements mUiLayout.addView(mToolbarLayout, 0); } mBrowserFrame.setLayoutTransition(new LayoutTransition()); - if (mWebView != null) - mWebView.setTranslationY(0); + if (currentWebView != null) + currentWebView.setTranslationY(0); } setFullscreen(mPreferences.getHideStatusBarEnabled(), false); @@ -731,6 +677,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements @Override public boolean onOptionsItemSelected(MenuItem item) { + final LightningView currentView = tabsManager.getCurrentTab(); // Handle action buttons switch (item.getItemId()) { case android.R.id.home: @@ -739,13 +686,13 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements } return true; case R.id.action_back: - if (mCurrentView != null && mCurrentView.canGoBack()) { - mCurrentView.goBack(); + if (currentView != null && currentView.canGoBack()) { + currentView.goBack(); } return true; case R.id.action_forward: - if (mCurrentView != null && mCurrentView.canGoForward()) { - mCurrentView.goForward(); + if (currentView != null && currentView.canGoForward()) { + currentView.goForward(); } return true; case R.id.action_new_tab: @@ -756,11 +703,11 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements overridePendingTransition(R.anim.slide_up_in, R.anim.fade_out_scale); return true; case R.id.action_share: - if (mCurrentView != null && !mCurrentView.getUrl().startsWith(Constants.FILE)) { + if (currentView != null && !currentView.getUrl().startsWith(Constants.FILE)) { Intent shareIntent = new Intent(Intent.ACTION_SEND); shareIntent.setType("text/plain"); - shareIntent.putExtra(Intent.EXTRA_SUBJECT, mCurrentView.getTitle()); - shareIntent.putExtra(Intent.EXTRA_TEXT, mCurrentView.getUrl()); + shareIntent.putExtra(Intent.EXTRA_SUBJECT, currentView.getTitle()); + shareIntent.putExtra(Intent.EXTRA_TEXT, currentView.getUrl()); startActivity(Intent.createChooser(shareIntent, getResources().getString(R.string.dialog_title_share))); } return true; @@ -768,9 +715,9 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements openBookmarks(); return true; case R.id.action_copy: - if (mCurrentView != null && !mCurrentView.getUrl().startsWith(Constants.FILE)) { + if (currentView != null && !currentView.getUrl().startsWith(Constants.FILE)) { ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText("label", mCurrentView.getUrl()); + ClipData clip = ClipData.newPlainText("label", currentView.getUrl()); clipboard.setPrimaryClip(clip); Utils.showSnackbar(this, R.string.message_link_copied); } @@ -782,9 +729,9 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements openHistory(); return true; case R.id.action_add_bookmark: - if (mCurrentView != null && !mCurrentView.getUrl().startsWith(Constants.FILE)) { - mEventBus.post(new BrowserEvents.AddBookmark(mCurrentView.getTitle(), - mCurrentView.getUrl())); + if (currentView != null && !currentView.getUrl().startsWith(Constants.FILE)) { + mEventBus.post(new BrowserEvents.AddBookmark(currentView.getTitle(), + currentView.getUrl())); } return true; case R.id.action_find: @@ -792,7 +739,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements return true; case R.id.action_reading_mode: Intent read = new Intent(this, ReadingActivity.class); - read.putExtra(Constants.LOAD_READING_URL, mCurrentView.getUrl()); + read.putExtra(Constants.LOAD_READING_URL, currentView.getUrl()); startActivity(read); return true; default: @@ -824,8 +771,9 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements } private void showSearchInterfaceBar(String text) { - if (mCurrentView != null) { - mCurrentView.find(text); + final LightningView currentView = tabsManager.getCurrentTab(); + if (currentView != null) { + currentView.find(text); } mSearchBar.setVisibility(View.VISIBLE); @@ -870,66 +818,43 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements builder.show(); } - /** - * The click listener for ListView in the navigation drawer - */ - private class DrawerItemClickListener implements OnClickListener { - - @Override - public void onClick(View v) { - int position = mTabListView.getChildAdapterPosition(v); - if (mCurrentView != mWebViewList.get(position)) { - mIsNewIntent = false; - showTab(mWebViewList.get(position)); - } - } - } - - /** - * long click listener for Navigation Drawer - */ - private class DrawerItemLongClickListener implements OnLongClickListener { - - @Override - public boolean onLongClick(View v) { - int position = mTabListView.getChildAdapterPosition(v); - showCloseDialog(position); - return true; - } - } - /** * displays the WebView contained in the LightningView Also handles the * removal of previous views * - * @param view the LightningView to show + * @param position the poition of the tab to display */ - private synchronized void showTab(LightningView view) { + private synchronized void showTab(final int position) { + final LightningView currentView = tabsManager.getCurrentTab(); + final WebView currentWebView = currentView != null ? currentView.getWebView() : null; + final LightningView newView = tabsManager.switchToTab(position); + final WebView newWebView = newView != null ? newView.getWebView() : null; + // Set the background color so the color mode color doesn't show through mBrowserFrame.setBackgroundColor(mBackgroundColor); - if (view == null || (view == mCurrentView && !mCurrentView.isShown())) { + if (newView == currentView && !currentView.isShown()) { return; } + mIsNewIntent = false; + final float translation = mToolbarLayout.getTranslationY(); mBrowserFrame.removeAllViews(); - if (mCurrentView != null) { - mCurrentView.setForegroundTab(false); - mCurrentView.onPause(); + if (currentView != null) { + currentView.setForegroundTab(false); + currentView.onPause(); } - mCurrentView = view; - mWebView = view.getWebView(); - mCurrentView.setForegroundTab(true); - if (mWebView != null) { - updateUrl(mCurrentView.getUrl(), true); - updateProgress(mCurrentView.getProgress()); + newView.setForegroundTab(true); + if (currentWebView != null) { + updateUrl(newView.getUrl(), true); + updateProgress(newView.getProgress()); } else { updateUrl("", true); updateProgress(0); } - mBrowserFrame.addView(mWebView, MATCH_PARENT); - mCurrentView.requestFocus(); - mCurrentView.onResume(); + mBrowserFrame.addView(newWebView, MATCH_PARENT); + newView.requestFocus(); + newView.onResume(); if (mFullScreen) { // mToolbarLayout has already been removed @@ -941,10 +866,10 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements mToolbarLayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); height = mToolbarLayout.getMeasuredHeight(); } - mWebView.setTranslationY(translation + height); + newWebView.setTranslationY(translation + height); mToolbarLayout.setTranslationY(translation); } else { - mWebView.setTranslationY(0); + newWebView.setTranslationY(0); } showActionBar(); @@ -960,7 +885,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements }, 200); // Should update the bookmark status in BookmarksFragment - mEventBus.post(new BrowserEvents.CurrentPageUrl(mCurrentView.getUrl())); + mEventBus.post(new BrowserEvents.CurrentPageUrl(newView.getUrl())); // new Handler().postDelayed(new Runnable() { // @Override @@ -998,17 +923,20 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements } private void loadUrlInCurrentView(final String url) { - if (mCurrentView == null) { + final LightningView currentTab = tabsManager.getCurrentTab(); + if (currentTab == null) { + // This is a problem, probably an assert will be better than a return return; } - mCurrentView.loadUrl(url); + currentTab.loadUrl(url); mEventBus.post(new BrowserEvents.CurrentPageUrl(url)); } @Override public void closeEmptyTab() { - if (mWebView != null && mWebView.copyBackForwardList().getSize() == 0) { + final WebView currentWebView = tabsManager.getCurrentWebView(); + if (currentWebView != null && currentWebView.copyBackForwardList().getSize() == 0) { closeCurrentTab(); } } @@ -1021,99 +949,93 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements 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, size = mWebViewList.size(); n < size; n++) { - mWebViewList.get(n).freeMemory(); - } + tabsManager.freeMemory(); } } synchronized boolean newTab(String url, boolean show) { // Limit number of tabs for limited version of app - if (!Constants.FULL_VERSION && mWebViewList.size() >= 10) { + if (!Constants.FULL_VERSION && tabsManager.size() >= 10) { Utils.showSnackbar(this, R.string.max_tabs); return false; } mIsNewIntent = false; - LightningView startingTab = new LightningView(this, url, mDarkTheme, isIncognito(), this); + LightningView startingTab = tabsManager.newTab(this, url, mDarkTheme, isIncognito()); if (mIdGenerator == 0) { startingTab.resumeTimers(); } mIdGenerator++; - mWebViewList.add(startingTab); if (show) { - showTab(startingTab); + showTab(tabsManager.size() - 1); } - updateTabs(); - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - mTabListView.smoothScrollToPosition(mWebViewList.size() - 1); - } - }, 300); + // TODO Check is this is callable directly from LightningView + mEventBus.post(new BrowserEvents.TabsChanged()); + +// TODO Restore this +// new Handler().postDelayed(new Runnable() { +// @Override +// public void run() { +// mDrawerListLeft.smoothScrollToPosition(tabsManager.size() - 1); +// } +// }, 300); return true; } private synchronized void deleteTab(int position) { - if (position >= mWebViewList.size()) { + final LightningView tabToDelete = tabsManager.getTabAtPosition(position); + final LightningView currentTab = tabsManager.getCurrentTab(); + + if (tabToDelete == null) { return; } - int current = mWebViewList.indexOf(mCurrentView); +// What? + int current = tabsManager.positionOf(currentTab); if (current < 0) { return; } - LightningView reference = mWebViewList.get(position); - if (reference == null) { - return; + if (!tabToDelete.getUrl().startsWith(Constants.FILE) && !isIncognito()) { + mPreferences.setSavedUrl(tabToDelete.getUrl()); } - if (!reference.getUrl().startsWith(Constants.FILE) && !isIncognito()) { - mPreferences.setSavedUrl(reference.getUrl()); - } - boolean isShown = reference.isShown(); + final boolean isShown = tabToDelete.isShown(); if (isShown) { mBrowserFrame.setBackgroundColor(mBackgroundColor); } if (current > position) { - mWebViewList.remove(position); - updateTabs(); - reference.onDestroy(); - } else if (mWebViewList.size() > position + 1) { + tabsManager.deleteTab(position); + showTab(current - 1); + mEventBus.post(new BrowserEvents.TabsChanged()); + } else if (tabsManager.size() > position + 1) { if (current == position) { - showTab(mWebViewList.get(position + 1)); - mWebViewList.remove(position); - updateTabs(); + showTab(position + 1); + tabsManager.deleteTab(position); + showTab(position); + mEventBus.post(new BrowserEvents.TabsChanged()); } else { - mWebViewList.remove(position); + tabsManager.deleteTab(position); } - - reference.onDestroy(); - } else if (mWebViewList.size() > 1) { + } else if (tabsManager.size() > 1) { if (current == position) { - showTab(mWebViewList.get(position - 1)); - mWebViewList.remove(position); - updateTabs(); + showTab(position - 1); + tabsManager.deleteTab(position); + showTab(position - 1); + mEventBus.post(new BrowserEvents.TabsChanged()); } else { - mWebViewList.remove(position); } - - reference.onDestroy(); } else { - if (mCurrentView.getUrl().startsWith(Constants.FILE) || mCurrentView.getUrl().equals(mHomepage)) { + if (currentTab.getUrl().startsWith(Constants.FILE) || currentTab.getUrl().equals(mHomepage)) { closeActivity(); } else { - mWebViewList.remove(position); + tabsManager.deleteTab(position); performExitCleanUp(); - reference.pauseTimers(); - reference.onDestroy(); - mCurrentView = null; - mWebView = null; - mTabAdapter.notifyDataSetChanged(); + tabToDelete.pauseTimers(); + mEventBus.post(new BrowserEvents.TabsChanged()); finish(); } } - mTabAdapter.notifyDataSetChanged(); + mEventBus.post(new BrowserEvents.TabsChanged()); if (mIsNewIntent && isShown) { mIsNewIntent = false; @@ -1124,8 +1046,9 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements } private void performExitCleanUp() { - if (mPreferences.getClearCacheExit() && mCurrentView != null && !isIncognito()) { - WebUtils.clearCache(mCurrentView.getWebView()); + final LightningView currentTab = tabsManager.getCurrentTab(); + if (mPreferences.getClearCacheExit() && currentTab != null && !isIncognito()) { + WebUtils.clearCache(currentTab.getWebView()); Log.d(Constants.TAG, "Cache Cleared"); } if (mPreferences.getClearHistoryExitEnabled() && !isIncognito()) { @@ -1146,8 +1069,9 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements @Override public boolean onKeyLongPress(int keyCode, KeyEvent event) { + final LightningView currentTab = tabsManager.getCurrentTab(); if (keyCode == KeyEvent.KEYCODE_BACK) { - showCloseDialog(mWebViewList.indexOf(mCurrentView)); + showCloseDialog(tabsManager.positionOf(currentTab)); } return true; } @@ -1155,38 +1079,32 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements private void closeBrowser() { mBrowserFrame.setBackgroundColor(mBackgroundColor); performExitCleanUp(); - mCurrentView = null; - mWebView = null; - for (int n = 0, size = mWebViewList.size(); n < size; n++) { - if (mWebViewList.get(n) != null) { - mWebViewList.get(n).onDestroy(); - } - } - mWebViewList.clear(); - mTabAdapter.notifyDataSetChanged(); + tabsManager.shutdown(); + mEventBus.post(new BrowserEvents.TabsChanged()); finish(); } @Override public void onBackPressed() { + final LightningView currentTab = tabsManager.getCurrentTab(); if (mDrawerLayout.isDrawerOpen(mDrawerLeft)) { mDrawerLayout.closeDrawer(mDrawerLeft); } else if (mDrawerLayout.isDrawerOpen(mDrawerRight)) { mEventBus .post(new BrowserEvents.UserPressedBack()); } else { - if (mCurrentView != null) { + if (currentTab != null) { Log.d(Constants.TAG, "onBackPressed"); if (mSearch.hasFocus()) { - mCurrentView.requestFocus(); - } else if (mCurrentView.canGoBack()) { - if (!mCurrentView.isShown()) { + currentTab.requestFocus(); + } else if (currentTab.canGoBack()) { + if (!currentTab.isShown()) { onHideCustomView(); } else { - mCurrentView.goBack(); + currentTab.goBack(); } } else { - deleteTab(mWebViewList.indexOf(mCurrentView)); + deleteTab(tabsManager.positionOf(currentTab)); } } else { Log.e(Constants.TAG, "This shouldn't happen ever"); @@ -1198,10 +1116,11 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements @Override protected void onPause() { super.onPause(); + final LightningView currentTab = tabsManager.getCurrentTab(); Log.d(Constants.TAG, "onPause"); - if (mCurrentView != null) { - mCurrentView.pauseTimers(); - mCurrentView.onPause(); + if (currentTab != null) { + currentTab.pauseTimers(); + currentTab.onPause(); } try { unregisterReceiver(mNetworkReceiver); @@ -1217,12 +1136,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements void saveOpenTabs() { if (mPreferences.getRestoreLostTabsEnabled()) { - StringBuilder s = new StringBuilder(mWebViewList.size() * 50); - for (int n = 0, size = mWebViewList.size(); n < size; n++) { - if (!mWebViewList.get(n).getUrl().isEmpty()) { - s.append(mWebViewList.get(n).getUrl()).append("|$|SEPARATOR|$|"); - } - } + final String s = tabsManager.tabsString(); mPreferences.setMemoryUrl(s.toString()); } } @@ -1252,24 +1166,18 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements @Override protected void onResume() { super.onResume(); + final LightningView currentTab = tabsManager.getCurrentTab(); Log.d(Constants.TAG, "onResume"); if (mSearchAdapter != null) { mSearchAdapter.refreshPreferences(); mSearchAdapter.refreshBookmarks(); } - if (mCurrentView != null) { - mCurrentView.resumeTimers(); - mCurrentView.onResume(); + if (currentTab != null) { + currentTab.resumeTimers(); + currentTab.onResume(); } - mHistoryDatabase = HistoryDatabase.getInstance(); initializePreferences(); - for (int n = 0, size = mWebViewList.size(); n < size; n++) { - if (mWebViewList.get(n) != null) { - mWebViewList.get(n).initializePreferences(null, this); - } else { - mWebViewList.remove(n); - } - } + tabsManager.resume(this); supportInvalidateOptionsMenu(); @@ -1285,173 +1193,18 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements * checks if it is a search, url, etc. */ private void searchTheWeb(@NonNull String query) { + final LightningView currentTab = tabsManager.getCurrentTab(); if (query.isEmpty()) { return; } String searchUrl = mSearchText + UrlUtils.QUERY_PLACE_HOLDER; query = query.trim(); - mCurrentView.stopLoading(); - if (mCurrentView != null) { + currentTab.stopLoading(); + if (currentTab != null) { loadUrlInCurrentView(UrlUtils.smartUrlFilter(query, true, searchUrl)); } } - private class LightningViewAdapter extends RecyclerView.Adapter { - - private final Context context; - private final int layoutResourceId; - private List data = null; - private final CloseTabListener mExitListener; - private final Drawable mBackgroundTabDrawable; - private final Drawable mForegroundTabDrawable; - private final Bitmap mForegroundTabBitmap; - private final DrawerItemClickListener mClickListener; - private final DrawerItemLongClickListener mLongClickListener; - private ColorMatrix mColorMatrix; - private Paint mPaint; - private ColorFilter mFilter; - private static final float DESATURATED = 0.5f; - - public LightningViewAdapter(Context context, int layoutResourceId, List data) { - this.layoutResourceId = layoutResourceId; - this.context = context; - this.data = data; - this.mExitListener = new CloseTabListener(); - this.mClickListener = new DrawerItemClickListener(); - this.mLongClickListener = new DrawerItemLongClickListener(); - - if (mShowTabsInDrawer) { - mBackgroundTabDrawable = null; - mForegroundTabBitmap = null; - mForegroundTabDrawable = ThemeUtils.getSelectedBackground(context, mDarkTheme); - } else { - int backgroundColor = Utils.mixTwoColors(ThemeUtils.getPrimaryColor(context), Color.BLACK, 0.75f); - Bitmap backgroundTabBitmap = Bitmap.createBitmap(Utils.dpToPx(175), Utils.dpToPx(30), Bitmap.Config.ARGB_8888); - Utils.drawTrapezoid(new Canvas(backgroundTabBitmap), backgroundColor, true); - mBackgroundTabDrawable = new BitmapDrawable(getResources(), backgroundTabBitmap); - - int foregroundColor = ThemeUtils.getPrimaryColor(context); - mForegroundTabBitmap = Bitmap.createBitmap(Utils.dpToPx(175), Utils.dpToPx(30), Bitmap.Config.ARGB_8888); - Utils.drawTrapezoid(new Canvas(mForegroundTabBitmap), foregroundColor, false); - mForegroundTabDrawable = null; - } - } - - @Override - public LightningViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { - LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext()); - View view = inflater.inflate(layoutResourceId, viewGroup, false); - return new LightningViewHolder(view); - } - - @Override - public void onBindViewHolder(final LightningViewHolder holder, int position) { - holder.exitButton.setTag(position); - holder.exitButton.setOnClickListener(mExitListener); - holder.layout.setOnClickListener(mClickListener); - holder.layout.setOnLongClickListener(mLongClickListener); - - ViewCompat.jumpDrawablesToCurrentState(holder.exitButton); - - LightningView web = data.get(position); - holder.txtTitle.setText(web.getTitle()); - - final Bitmap favicon = web.getFavicon(); - if (web.isForegroundTab()) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - holder.txtTitle.setTextAppearance(R.style.boldText); - } else { - //noinspection deprecation - holder.txtTitle.setTextAppearance(context, R.style.boldText); - } - Drawable foregroundDrawable; - if (!mShowTabsInDrawer) { - foregroundDrawable = new BitmapDrawable(getResources(), mForegroundTabBitmap); - if (!isIncognito() && mColorMode) { - foregroundDrawable.setColorFilter(mCurrentUiColor, PorterDuff.Mode.SRC_IN); - } - } else { - foregroundDrawable = mForegroundTabDrawable; - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - holder.layout.setBackground(foregroundDrawable); - } else { - //noinspection deprecation - holder.layout.setBackgroundDrawable(foregroundDrawable); - } - if (!isIncognito() && mColorMode) { - changeToolbarBackground(favicon, foregroundDrawable); - } - holder.favicon.setImageBitmap(favicon); - } else { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - holder.txtTitle.setTextAppearance(R.style.normalText); - } else { - //noinspection deprecation - holder.txtTitle.setTextAppearance(context, R.style.normalText); - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - holder.layout.setBackground(mBackgroundTabDrawable); - } else { - //noinspection deprecation - holder.layout.setBackgroundDrawable(mBackgroundTabDrawable); - } - - holder.favicon.setImageBitmap(getDesaturatedBitmap(favicon)); - } - } - - @Override - public int getItemCount() { - return (data != null) ? data.size() : 0; - } - - public Bitmap getDesaturatedBitmap(Bitmap favicon) { - Bitmap grayscaleBitmap = Bitmap.createBitmap(favicon.getWidth(), - favicon.getHeight(), Bitmap.Config.ARGB_8888); - - Canvas c = new Canvas(grayscaleBitmap); - if (mColorMatrix == null || mFilter == null || mPaint == null) { - mPaint = new Paint(); - mColorMatrix = new ColorMatrix(); - mColorMatrix.setSaturation(DESATURATED); - mFilter = new ColorMatrixColorFilter(mColorMatrix); - mPaint.setColorFilter(mFilter); - } - - c.drawBitmap(favicon, 0, 0, mPaint); - return grayscaleBitmap; - } - - public class LightningViewHolder extends RecyclerView.ViewHolder { - - public LightningViewHolder(View view) { - super(view); - txtTitle = (TextView) view.findViewById(R.id.textTab); - favicon = (ImageView) view.findViewById(R.id.faviconTab); - exit = (ImageView) view.findViewById(R.id.deleteButton); - layout = (LinearLayout) view.findViewById(R.id.tab_item_background); - exitButton = (FrameLayout) view.findViewById(R.id.deleteAction); - exit.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); - } - - final TextView txtTitle; - final ImageView favicon; - final ImageView exit; - final FrameLayout exitButton; - final LinearLayout layout; - } - } - - private class CloseTabListener implements OnClickListener { - - @Override - public void onClick(View v) { - deleteTab((int) v.getTag()); - } - - } - /** * Animates the color of the toolbar from one color to another. Optionally animates * the color of the tab background, for use when the tabs are displayed on the top @@ -1510,6 +1263,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements @Override public void updateUrl(String url, boolean shortUrl) { + final LightningView currentTab = tabsManager.getCurrentTab(); if (url == null || mSearch == null || mSearch.hasFocus()) { return; } @@ -1525,8 +1279,8 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements mSearch.setText(url); break; case 2: // Title, show the page's title - if (mCurrentView != null && !mCurrentView.getTitle().isEmpty()) { - mSearch.setText(mCurrentView.getTitle()); + if (currentTab != null && !currentTab.getTitle().isEmpty()) { + mSearch.setText(currentTab.getTitle()); } else { mSearch.setText(mUntitledTitle); } @@ -1551,9 +1305,6 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements @Override public void run() { try { - if (mHistoryDatabase == null) { - mHistoryDatabase = HistoryDatabase.getInstance(); - } mHistoryDatabase.visitHistoryItem(url, title); } catch (IllegalStateException e) { Log.e(Constants.TAG, "IllegalStateException in updateHistory", e); @@ -1574,7 +1325,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements * previously searched URLs */ private void initializeSearchSuggestions(final AutoCompleteTextView getUrl) { - + final LightningView currentTab = tabsManager.getCurrentTab(); getUrl.setThreshold(1); getUrl.setDropDownWidth(-1); getUrl.setDropDownAnchor(R.id.toolbar_layout); @@ -1593,8 +1344,8 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements searchTheWeb(url); InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(getUrl.getWindowToken(), 0); - if (mCurrentView != null) { - mCurrentView.requestFocus(); + if (currentTab != null) { + currentTab.requestFocus(); } } catch (NullPointerException e) { Log.e("Browser Error: ", "NullPointerException on item click"); @@ -1608,11 +1359,6 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements getUrl.setAdapter(mSearchAdapter); } - @Override - public boolean proxyIsNotReady() { - return !mProxyUtils.isProxyReady(this); - } - /** * function that opens the HTML history page in the browser */ @@ -1655,36 +1401,6 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements return super.onCreateOptionsMenu(menu); } - /** - * open the HTML bookmarks page, parameter view is the WebView that should show the page - */ - @Override - public void openBookmarkPage(WebView view) { - if (view == null) - return; - Bitmap folderIcon = ThemeUtils.getThemedBitmap(this, R.drawable.ic_folder, false); - FileOutputStream outputStream = null; - File image = new File(this.getCacheDir(), "folder.png"); - try { - outputStream = new FileOutputStream(image); - folderIcon.compress(Bitmap.CompressFormat.PNG, 100, outputStream); - folderIcon.recycle(); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } finally { - Utils.close(outputStream); - } - File bookmarkWebPage = new File(this.getFilesDir(), Constants.BOOKMARKS_FILENAME); - - mBookmarkPage.buildBookmarkPage(null, mBookmarkManager.getBookmarksFromFolder(null, true)); - view.loadUrl(Constants.FILE + bookmarkWebPage); - } - - @Override - public void updateTabs() { - mTabAdapter.notifyDataSetChanged(); - } - /** * opens a file chooser * param ValueCallback is the message from the WebView indicating a file chooser @@ -1789,25 +1505,9 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements this.startActivityForResult(chooserIntent, 1); } - /** - * 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 - */ - @Override - public void onLongPress() { - if (mClickHandler == null) { - mClickHandler = new ClickHandler(this); - } - Message click = mClickHandler.obtainMessage(); - if (click != null) { - click.setTarget(mClickHandler); - mWebView.requestFocusNodeHref(click); - } - } - @Override public void onShowCustomView(View view, CustomViewCallback callback) { + final LightningView currentTab = tabsManager.getCurrentTab(); if (view == null) { return; } @@ -1828,7 +1528,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements mFullscreenContainer.addView(mCustomView, COVER_SCREEN_PARAMS); decor.addView(mFullscreenContainer, COVER_SCREEN_PARAMS); setFullscreen(true, true); - mCurrentView.setVisibility(View.GONE); + currentTab.setVisibility(View.GONE); if (view instanceof FrameLayout) { if (((FrameLayout) view).getFocusedChild() instanceof VideoView) { mVideoView = (VideoView) ((FrameLayout) view).getFocusedChild(); @@ -1841,11 +1541,12 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements @Override public void onHideCustomView() { - if (mCustomView == null || mCustomViewCallback == null || mCurrentView == null) { + final LightningView currentTab = tabsManager.getCurrentTab(); + if (mCustomView == null || mCustomViewCallback == null || currentTab == null) { return; } Log.d(Constants.TAG, "onHideCustomView"); - mCurrentView.setVisibility(View.VISIBLE); + currentTab.setVisibility(View.VISIBLE); try { mCustomView.setKeepScreenOn(false); } catch (SecurityException e) { @@ -1930,32 +1631,6 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements } } - /** - * This interface method is used by the LightningView to obtain an - * image that is displayed as a placeholder on a video until the video - * has initialized and can begin loading. - * - * @return a Bitmap that can be used as a place holder for videos. - */ - @Override - public Bitmap getDefaultVideoPoster() { - return BitmapFactory.decodeResource(getResources(), android.R.drawable.spinner_background); - } - - /** - * An interface method so that we can inflate a view to send to - * a LightningView when it needs to display a video and has to - * show a loading dialog. Inflates a progress view and returns it. - * - * @return A view that should be used to display the state - * of a video's loading progress. - */ - @Override - public View getVideoLoadingProgressView() { - LayoutInflater inflater = LayoutInflater.from(this); - return inflater.inflate(R.layout.video_loading_progress, null); - } - /** * This method handles the JavaScript callback to create a new tab. * Basically this handles the event that JavaScript needs to create @@ -1970,8 +1645,10 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements return; } if (newTab("", true)) { + // TODO Review this + final WebView webView = tabsManager.getTabAtPosition(tabsManager.size() - 1).getWebView(); WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj; - transport.setWebView(mWebView); + transport.setWebView(webView); resultMsg.sendToTarget(); } } @@ -1986,7 +1663,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements */ @Override public void onCloseWindow(LightningView view) { - deleteTab(mWebViewList.indexOf(view)); + deleteTab(tabsManager.positionOf(view)); } /** @@ -1996,6 +1673,8 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements */ @Override public void hideActionBar() { + final LightningView currentTab = tabsManager.getCurrentTab(); + final WebView currentWebView = currentTab.getWebView(); if (mFullScreen) { if (mBrowserFrame.findViewById(R.id.toolbar_layout) == null) { mUiLayout.removeView(mToolbarLayout); @@ -2003,13 +1682,13 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements mToolbarLayout.bringToFront(); Log.d(Constants.TAG, "Move view to browser frame"); mToolbarLayout.setTranslationY(0); - mWebView.setTranslationY(mToolbarLayout.getHeight()); + currentWebView.setTranslationY(mToolbarLayout.getHeight()); } - if (mToolbarLayout == null || mCurrentView == null) + if (mToolbarLayout == null || currentTab == null) return; final int height = mToolbarLayout.getHeight(); - final WebView view = mWebView; + final WebView view = currentWebView; if (mToolbarLayout.getTranslationY() > -0.01f) { Animation show = new Animation() { @Override @@ -2022,7 +1701,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements }; show.setDuration(250); show.setInterpolator(new DecelerateInterpolator()); - mWebView.startAnimation(show); + currentWebView.startAnimation(show); } } } @@ -2035,6 +1714,7 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements @Override public void showActionBar() { if (mFullScreen) { + final WebView view = tabsManager.getCurrentWebView(); if (mToolbarLayout == null) return; @@ -2051,12 +1731,14 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements mToolbarLayout.bringToFront(); Log.d(Constants.TAG, "Move view to browser frame"); mToolbarLayout.setTranslationY(0); - mWebView.setTranslationY(height); + if (view != null) { + view.setTranslationY(height); } - if (mCurrentView == null) + } + final LightningView currentTab = tabsManager.getCurrentTab(); + if (currentTab == null) return; - final WebView view = mWebView; final int totalHeight = height; if (mToolbarLayout.getTranslationY() < -(height - 0.01f)) { Animation show = new Animation() { @@ -2071,190 +1753,11 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements }; show.setDuration(250); show.setInterpolator(new DecelerateInterpolator()); - mWebView.startAnimation(show); + if (view != null) { + view.startAnimation(show); } - } - } - - /** - * Handle a long-press on the current web page. The WebView provides - * a URL to this method that it thinks is what the user has long-pressed. - * This may be different than what the user actually long-pressed, e.g. in - * the case of an image link you may want to provide options for both the - * image and the link. This method also handles the event that the WebView - * was not able to get a URL from the event in which case we need to drill - * down using {@link HitTestResult} and obtain the URL that is at the root - * of their press. This method is responsible with delegating the dialog - * creation to one of the several other methods in this class. - * - * @param url the URL that was retrieved from the WebView in - * the long-press event. - */ - @Override - public void longClickPage(final String url) { - HitTestResult result = null; - String currentUrl = null; - if (mWebView != null) { - result = mWebView.getHitTestResult(); - currentUrl = mWebView.getUrl(); - } - if (currentUrl != null && currentUrl.startsWith(Constants.FILE)) { - if (currentUrl.endsWith(HistoryPage.FILENAME)) { - if (url != null) { - longPressHistoryLink(url); - } else if (result != null && result.getExtra() != null) { - final String newUrl = result.getExtra(); - longPressHistoryLink(newUrl); - } - } else if (currentUrl.endsWith(Constants.BOOKMARKS_FILENAME)) { - if (url != null) { - mBookmarksDialogBuilder.showLongPressedDialogForUrl(this, url); - } else if (result != null && result.getExtra() != null) { - final String newUrl = result.getExtra(); - mBookmarksDialogBuilder.showLongPressedDialogForUrl(this, newUrl); } } - } else { - if (url != null) { - if (result != null) { - if (result.getType() == HitTestResult.SRC_IMAGE_ANCHOR_TYPE || result.getType() == HitTestResult.IMAGE_TYPE) { - longPressImage(url); - } else { - longPressLink(url); - } - } else { - longPressLink(url); - } - } else if (result != null && result.getExtra() != null) { - final String newUrl = result.getExtra(); - if (result.getType() == HitTestResult.SRC_IMAGE_ANCHOR_TYPE || result.getType() == HitTestResult.IMAGE_TYPE) { - longPressImage(newUrl); - } else { - longPressLink(newUrl); - } - } - } - } - - /** - * Handle the event that the user has long-pressed an item on the {@link HistoryPage}. - * In this case we wish to present the user with a dialog with a few options: - * open the history item in a new tab, delete the history item, or open the - * item in the current tab. - * - * @param url the URL of the history item. - */ - private void longPressHistoryLink(final String url) { - DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - switch (which) { - case DialogInterface.BUTTON_POSITIVE: - newTab(url, false); - mDrawerLayout.closeDrawers(); - break; - - case DialogInterface.BUTTON_NEGATIVE: - mHistoryDatabase.deleteHistoryItem(url); - openHistory(); - break; - - case DialogInterface.BUTTON_NEUTRAL: - if (mCurrentView != null) { - loadUrlInCurrentView(url); - } - break; - } - } - }; - - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.action_history) - .setMessage(R.string.dialog_history_long_press) - .setCancelable(true) - .setPositiveButton(R.string.action_new_tab, dialogClickListener) - .setNegativeButton(R.string.action_delete, dialogClickListener) - .setNeutralButton(R.string.action_open, dialogClickListener) - .show(); - } - - /** - * Handle the event that the user has long-pressed on an image link - * and provide them with various options for things to do with that image. - * Options include opening the image in a new tab, loading the image in the - * current tab, or downloading the image. - * - * @param url the URL of the image that was retrieved from long-press. - */ - private void longPressImage(final String url) { - DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - switch (which) { - case DialogInterface.BUTTON_POSITIVE: - newTab(url, false); - break; - - case DialogInterface.BUTTON_NEGATIVE: - loadUrlInCurrentView(url); - break; - - case DialogInterface.BUTTON_NEUTRAL: - Utils.downloadFile(BrowserActivity.this, url, - mCurrentView.getUserAgent(), "attachment"); - break; - } - } - }; - - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(url.replace(Constants.HTTP, "")) - .setCancelable(true) - .setMessage(R.string.dialog_image) - .setPositiveButton(R.string.action_new_tab, dialogClickListener) - .setNegativeButton(R.string.action_open, dialogClickListener) - .setNeutralButton(R.string.action_download, dialogClickListener) - .show(); - } - - /** - * Handle the event that the user has long-pressed a link on a web page. - * It creates a dialog with a few options on what the user can do with the - * URL that has been retrieved from their long-press, namely, create a - * new tab, load the URL in the current tab, or copy the link. - * - * @param url the URL that has been retrieved in the long-press event - */ - private void longPressLink(final String url) { - DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - switch (which) { - case DialogInterface.BUTTON_POSITIVE: - newTab(url, false); - break; - - case DialogInterface.BUTTON_NEGATIVE: - loadUrlInCurrentView(url); - break; - - case DialogInterface.BUTTON_NEUTRAL: - ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText("label", url); - clipboard.setPrimaryClip(clip); - break; - } - } - }; - - AlertDialog.Builder builder = new AlertDialog.Builder(this); // dialog - builder.setTitle(url) - .setCancelable(true) - .setMessage(R.string.dialog_link) - .setPositiveButton(R.string.action_new_tab, dialogClickListener) - .setNegativeButton(R.string.action_open, dialogClickListener) - .setNeutralButton(R.string.action_copy, dialogClickListener) - .show(); } /** @@ -2275,11 +1778,12 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements * See setIsFinishedLoading and setIsLoading for displaying the correct icon */ private void refreshOrStop() { - if (mCurrentView != null) { - if (mCurrentView.getProgress() < 100) { - mCurrentView.stopLoading(); + final LightningView currentTab = tabsManager.getCurrentTab(); + if (currentTab != null) { + if (currentTab.getProgress() < 100) { + currentTab.stopLoading(); } else { - mCurrentView.reload(); + currentTab.reload(); } } } @@ -2293,57 +1797,36 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements */ @Override public void onClick(View v) { + final LightningView currentTab = tabsManager.getCurrentTab(); + final WebView currentWebView = currentTab.getWebView(); switch (v.getId()) { - case R.id.action_back: - if (mCurrentView != null) { - if (mCurrentView.canGoBack()) { - mCurrentView.goBack(); - } else { - deleteTab(mWebViewList.indexOf(mCurrentView)); - } - } - 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(); + currentTab.requestFocus(); } else if (mShowTabsInDrawer) { mDrawerLayout.openDrawer(mDrawerLeft); - } else if (mCurrentView != null) { - mCurrentView.loadHomepage(); + } else if (currentTab != null) { + currentTab.loadHomepage(); } break; - case R.id.new_tab_button: - newTab(null, true); - break; - case R.id.action_home: - mCurrentView.loadHomepage(); - closeDrawers(); - break; case R.id.button_next: - mWebView.findNext(false); + currentWebView.findNext(false); break; case R.id.button_back: - mWebView.findNext(true); + currentWebView.findNext(true); break; case R.id.button_quit: - mWebView.clearMatches(); + currentWebView.clearMatches(); mSearchBar.setVisibility(View.GONE); break; case R.id.action_reading: Intent read = new Intent(this, ReadingActivity.class); - read.putExtra(Constants.LOAD_READING_URL, mCurrentView.getUrl()); + read.putExtra(Constants.LOAD_READING_URL, currentTab.getUrl()); startActivity(read); break; case R.id.action_toggle_desktop: - mCurrentView.toggleDesktopUA(this); - mCurrentView.reload(); + currentTab.toggleDesktopUA(this); + currentTab.reload(); closeDrawers(); break; } @@ -2361,19 +1844,10 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements */ @Override public boolean onLongClick(View view) { - switch (view.getId()) { - case R.id.new_tab_button: - String url = mPreferences.getSavedUrl(); - if (url != null) { - newTab(url, true); - Utils.showSnackbar(this, R.string.deleted_tab); - } - mPreferences.setSavedUrl(null); - break; - } return true; } + // TODO Check if all the calls are relative to TabsFragement /** * A utility method that creates a FrameLayout button with the given ID and * sets the image of the button to the given image ID. The OnClick and OnLongClick @@ -2386,10 +1860,10 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements * @param imageId the image to set as the button image */ private void setupFrameLayoutButton(@IdRes int buttonId, @IdRes int imageId) { - View frameButton = findViewById(buttonId); + final View frameButton = findViewById(buttonId); + final ImageView buttonImage = (ImageView) findViewById(imageId); frameButton.setOnClickListener(this); frameButton.setOnLongClickListener(this); - ImageView buttonImage = (ImageView) findViewById(imageId); buttonImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); } @@ -2405,12 +1879,8 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements super.onReceive(context, intent); boolean isConnected = isConnected(context); Log.d(Constants.TAG, "Network Connected: " + String.valueOf(isConnected)); - for (int n = 0, size = mWebViewList.size(); n < size; n++) { - WebView view = mWebViewList.get(n).getWebView(); - if (view != null) - view.setNetworkAvailable(isConnected); + tabsManager.notifyConnectioneStatus(isConnected); } - } }; /** @@ -2431,14 +1901,15 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements private final Object mBusEventListener = new Object() { /** - * Load the given bookmark in the current tab, used by the the - * {@link acr.browser.lightning.fragment.BookmarksFragment} + * Load the given url in the current tab, used by the the + * {@link acr.browser.lightning.fragment.BookmarksFragment} and by the + * {@link LightningDialogBuilder} * * @param event Bus event indicating that the user has clicked a bookmark */ @Subscribe - public void loadBookmarkInCurrentTab(final BookmarkEvents.Clicked event) { - loadUrlInCurrentView(event.bookmark.getUrl()); + public void loadUrlInCurrentTab(final BrowserEvents.OpenUrlInCurrentTab event) { + loadUrlInCurrentView(event.url); // keep any jank from happening when the drawer is closed after the // URL starts to load final Handler handler = new Handler(); @@ -2451,15 +1922,16 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements } /** - * Load the given bookmark in a new tab, used by the the - * {@link acr.browser.lightning.fragment.BookmarksFragment} + * Load the given url in a new tab, used by the the + * {@link acr.browser.lightning.fragment.BookmarksFragment} and by the + * {@link LightningDialogBuilder} * * @param event Bus event indicating that the user wishes * to open a bookmark in a new tab */ @Subscribe - public void loadBookmarkInNewTab(final BookmarkEvents.AsNewTab event) { - newTab(event.bookmark.getUrl(), true); + public void loadUrlInNewTab(final BrowserEvents.OpenUrlInNewTab event) { + BrowserActivity.this.newTab(event.url, true); mDrawerLayout.closeDrawers(); } @@ -2472,8 +1944,9 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements */ @Subscribe public void bookmarkCurrentPage(final BookmarkEvents.WantToBookmarkCurrentPage event) { - if (mCurrentView != null) { - mEventBus.post(new BrowserEvents.AddBookmark(mCurrentView.getTitle(), mCurrentView.getUrl())); + final LightningView currentTab = tabsManager.getCurrentTab(); + if (currentTab != null) { + mEventBus.post(new BrowserEvents.AddBookmark(currentTab.getTitle(), currentTab.getUrl())); } } @@ -2495,12 +1968,14 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements */ @Subscribe public void bookmarkChanged(final BookmarkEvents.BookmarkChanged event) { - if (mCurrentView != null && mCurrentView.getUrl().startsWith(Constants.FILE) - && mCurrentView.getUrl().endsWith(Constants.BOOKMARKS_FILENAME)) { - openBookmarkPage(mWebView); + final LightningView currentTab = tabsManager.getCurrentTab(); + final WebView currentWebView = currentTab.getWebView(); + if (currentTab != null && currentTab.getUrl().startsWith(Constants.FILE) + && currentTab.getUrl().endsWith(Constants.BOOKMARKS_FILENAME)) { + currentTab.loadBookmarkpage(); } - if (mCurrentView != null) { - mEventBus.post(new BrowserEvents.CurrentPageUrl(mCurrentView.getUrl())); + if (currentTab != null) { + mEventBus.post(new BrowserEvents.CurrentPageUrl(currentTab.getUrl())); } } @@ -2511,12 +1986,14 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements */ @Subscribe public void bookmarkDeleted(final BookmarkEvents.Deleted event) { - if (mCurrentView != null && mCurrentView.getUrl().startsWith(Constants.FILE) - && mCurrentView.getUrl().endsWith(Constants.BOOKMARKS_FILENAME)) { - openBookmarkPage(mWebView); + final LightningView currentTab = tabsManager.getCurrentTab(); + final WebView currentWebView = currentTab.getWebView(); + if (currentTab != null && currentTab.getUrl().startsWith(Constants.FILE) + && currentTab.getUrl().endsWith(Constants.BOOKMARKS_FILENAME)) { + currentTab.loadBookmarkpage(); } - if (mCurrentView != null) { - mEventBus.post(new BrowserEvents.CurrentPageUrl(mCurrentView.getUrl())); + if (currentTab != null) { + mEventBus.post(new BrowserEvents.CurrentPageUrl(currentTab.getUrl())); } } @@ -2532,5 +2009,111 @@ public abstract class BrowserActivity extends ThemableBrowserActivity implements public void closeBookmarks(final BookmarkEvents.CloseBookmarks event) { mDrawerLayout.closeDrawer(mDrawerRight); } + + /** + * The user wants to close a tab + * + * @param event contains the position inside the tabs adapter + */ + @Subscribe + public void closeTab(final TabEvents.CloseTab event) { + deleteTab(event.position); + } + + /** + * The user clicked on a tab, let's show it + * + * @param event contains the tab position in the tabs adapter + */ + @Subscribe + public void showTab(final TabEvents.ShowTab event) { + BrowserActivity.this.showTab(event.position); + } + + /** + * The user long pressed on a tab, ask him if he want to close the tab + * + * @param event contains the tab position in the tabs adapter + */ + @Subscribe + public void showCloseDialog(final TabEvents.ShowCloseDialog event) { + BrowserActivity.this.showCloseDialog(event.position); + } + + /** + * The user wants to create a new tab + * + * @param event a marker + */ + @Subscribe + public void newTab(final TabEvents.NewTab event) { + BrowserActivity.this.newTab(null, true); + } + + /** + * The user wants to go back on current tab + * + * @param event a marker + */ + @Subscribe + public void goBack(final NavigationEvents.GoBack event) { + final LightningView currentTab = tabsManager.getCurrentTab(); + if (currentTab != null) { + if (currentTab.canGoBack()) { + currentTab.goBack(); + } else { + deleteTab(tabsManager.positionOf(currentTab)); + } + } + } + + /** + * The user wants to go forward on current tab + * + * @param event a marker + */ + @Subscribe + public void goForward(final NavigationEvents.GoForward event) { + final LightningView currentTab = tabsManager.getCurrentTab(); + if (currentTab != null) { + if (currentTab.canGoForward()) { + currentTab.goForward(); + } + } + } + + @Subscribe + public void goHome(final NavigationEvents.GoHome event) { + final LightningView currentTab = tabsManager.getCurrentTab(); + if (currentTab != null) { + currentTab.loadHomepage(); + closeDrawers(); + } + } + + /** + * The user long pressed the new tab button + * + * @param event a marker + */ + @Subscribe + public void newTabLongPress(final TabEvents.NewTabLongPress event) { + String url = mPreferences.getSavedUrl(); + if (url != null) { + BrowserActivity.this.newTab(url, true); + + Utils.showSnackbar(BrowserActivity.this, R.string.deleted_tab); + } + mPreferences.setSavedUrl(null); + } + + @Subscribe + public void displayInSnackbar(final BrowserEvents.ShowSnackBarMessage event) { + if (event.message != null) { + Utils.showSnackbar(BrowserActivity.this, event.message); + } else { + Utils.showSnackbar(BrowserActivity.this, event.stringRes); + } + } }; } diff --git a/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java.orig b/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java.orig new file mode 100644 index 0000000..7e0fa1d --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java.orig @@ -0,0 +1,2129 @@ +/* + * Copyright 2015 Anthony Restaino + */ + +package acr.browser.lightning.activity; + +import android.animation.ArgbEvaluator; +import android.animation.LayoutTransition; +import android.animation.ValueAnimator; +import android.animation.ValueAnimator.AnimatorUpdateListener; +import android.app.Activity; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.database.sqlite.SQLiteException; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.media.MediaPlayer; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.provider.MediaStore; +import android.support.annotation.IdRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.FragmentManager; +import android.support.v4.content.ContextCompat; +import android.support.v4.view.GravityCompat; +import android.support.v4.widget.DrawerLayout; +import android.support.v4.widget.DrawerLayout.DrawerListener; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AlertDialog; +import android.support.v7.graphics.Palette; +import android.support.v7.graphics.drawable.DrawerArrowDrawable; +import android.support.v7.widget.Toolbar; +import android.util.Log; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnFocusChangeListener; +import android.view.View.OnKeyListener; +import android.view.View.OnLongClickListener; +import android.view.View.OnTouchListener; +import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; +import android.view.Window; +import android.view.WindowManager; +import android.view.animation.Animation; +import android.view.animation.Animation.AnimationListener; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Transformation; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.webkit.ValueCallback; +import android.webkit.WebChromeClient.CustomViewCallback; +import android.webkit.WebIconDatabase; +import android.webkit.WebView; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ArrayAdapter; +import android.widget.AutoCompleteTextView; +import android.widget.EditText; +import android.widget.FrameLayout; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; +import android.widget.TextView.OnEditorActionListener; +import android.widget.VideoView; + +import com.squareup.otto.Bus; +import com.squareup.otto.Subscribe; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import acr.browser.lightning.R; +import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.bus.BookmarkEvents; +import acr.browser.lightning.bus.BrowserEvents; +import acr.browser.lightning.bus.NavigationEvents; +import acr.browser.lightning.bus.TabEvents; +import acr.browser.lightning.constant.BookmarkPage; +import acr.browser.lightning.constant.Constants; +import acr.browser.lightning.constant.HistoryPage; +import acr.browser.lightning.controller.BrowserController; +import acr.browser.lightning.database.BookmarkManager; +import acr.browser.lightning.database.HistoryDatabase; +import acr.browser.lightning.dialog.LightningDialogBuilder; +import acr.browser.lightning.fragment.BookmarksFragment; +import acr.browser.lightning.fragment.TabsFragment; +import acr.browser.lightning.object.SearchAdapter; +import acr.browser.lightning.preference.PreferenceManager; +import acr.browser.lightning.receiver.NetworkReceiver; +import acr.browser.lightning.utils.PermissionsManager; +import acr.browser.lightning.utils.ProxyUtils; +import acr.browser.lightning.utils.ThemeUtils; +import acr.browser.lightning.utils.UrlUtils; +import acr.browser.lightning.utils.Utils; +import acr.browser.lightning.utils.WebUtils; +import acr.browser.lightning.view.AnimatedProgressBar; +import acr.browser.lightning.view.LightningView; +import butterknife.Bind; +import butterknife.ButterKnife; + +public abstract class BrowserActivity extends ThemableBrowserActivity implements BrowserController, OnClickListener, OnLongClickListener { + + // Static Layout + @Bind(R.id.drawer_layout) + DrawerLayout mDrawerLayout; + + @Bind(R.id.content_frame) + FrameLayout mBrowserFrame; + + @Bind(R.id.left_drawer) + ViewGroup mDrawerLeft; + + @Bind(R.id.right_drawer) + ViewGroup mDrawerRight; + + @Bind(R.id.ui_layout) + ViewGroup mUiLayout; + + @Bind(R.id.toolbar_layout) + ViewGroup mToolbarLayout; + + @Bind(R.id.progress_view) + AnimatedProgressBar mProgressBar; + + @Bind(R.id.search_bar) + RelativeLayout mSearchBar; + + + // Toolbar Views + private AutoCompleteTextView mSearch; + private ImageView mArrowImage; + + // Full Screen Video Views + private FrameLayout mFullscreenContainer; + private VideoView mVideoView; + private View mCustomView; + + // Adapter + private SearchAdapter mSearchAdapter; + + // Callback + private CustomViewCallback mCustomViewCallback; + private ValueCallback mUploadMessage; + private ValueCallback mFilePathCallback; + + // Primatives + private boolean mFullScreen, mColorMode, mDarkTheme, + mIsNewIntent = false, + mIsFullScreen = false, + mIsImmersive = false, + mShowTabsInDrawer; + private int mOriginalOrientation, mBackgroundColor, mIdGenerator, mIconColor, + mCurrentUiColor = Color.BLACK; + private String mSearchText, mUntitledTitle, mHomepage, mCameraPhotoPath; + + // The singleton BookmarkManager + @Inject + BookmarkManager mBookmarkManager; + + // Event bus + @Inject + Bus mEventBus; + + @Inject + BookmarkPage mBookmarkPage; + + @Inject + LightningDialogBuilder bookmarksDialogBuilder; + + @Inject + TabsManager tabsManager; + + // Preference manager was moved on ThemeableBrowserActivity + + @Inject + HistoryDatabase mHistoryDatabase; + + // Image + private Bitmap mWebpageBitmap; + private final ColorDrawable mBackground = new ColorDrawable(); + private Drawable mDeleteIcon, mRefreshIcon, mClearIcon, mIcon; + private DrawerArrowDrawable mArrowDrawable; + + // Proxy + private ProxyUtils mProxyUtils; + + // Constant + private static final int API = android.os.Build.VERSION.SDK_INT; + private static final String NETWORK_BROADCAST_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; + private static final LayoutParams MATCH_PARENT = new LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT); + private static final FrameLayout.LayoutParams COVER_SCREEN_PARAMS = new FrameLayout.LayoutParams( + LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + + public abstract boolean isIncognito(); + +// abstract void initializeTabs(); + + abstract void closeActivity(); + + public abstract void updateHistory(final String title, final String url); + + abstract void updateCookiePreference(); + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + BrowserApp.getAppComponent().inject(this); + setContentView(R.layout.activity_main); + ButterKnife.bind(this); + initialize(); + } + + private synchronized void initialize() { + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + ActionBar actionBar = getSupportActionBar(); + + //TODO make sure dark theme flag gets set correctly + mDarkTheme = mPreferences.getUseTheme() != 0 || isIncognito(); + mIconColor = mDarkTheme ? ThemeUtils.getIconDarkThemeColor(this) : ThemeUtils.getIconLightThemeColor(this); + mShowTabsInDrawer = mPreferences.getShowTabsInDrawer(!isTablet()); + + + // initialize background ColorDrawable + mBackground.setColor(((ColorDrawable) mToolbarLayout.getBackground()).getColor()); + + // Drawer stutters otherwise + mDrawerLeft.setLayerType(View.LAYER_TYPE_HARDWARE, null); + mDrawerRight.setLayerType(View.LAYER_TYPE_HARDWARE, null); +// TODO Please review this +// ImageView tabTitleImage = (ImageView) findViewById(R.id.plusIcon); +// tabTitleImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !mShowTabsInDrawer) { + getWindow().setStatusBarColor(Color.BLACK); + } + + setNavigationDrawerWidth(); + mDrawerLayout.setDrawerListener(new DrawerLocker()); + + mWebpageBitmap = ThemeUtils.getThemedBitmap(this, R.drawable.ic_webpage, mDarkTheme); + + mHomepage = mPreferences.getHomepage(); + + final TabsFragment tabsFragment = new TabsFragment(); + final int containerId = mShowTabsInDrawer ? R.id.left_drawer : R.id.tabs_toolbar_container; + final Bundle tabsFragmentArguments = new Bundle(); + tabsFragmentArguments.putBoolean(TabsFragment.IS_INCOGNITO, isIncognito()); + tabsFragmentArguments.putBoolean(TabsFragment.VERTICAL_MODE, mShowTabsInDrawer); + tabsFragment.setArguments(tabsFragmentArguments); + + final BookmarksFragment bookmarksFragment = new BookmarksFragment(); + final Bundle bookmarksFragmentArguments = new Bundle(); + bookmarksFragmentArguments.putBoolean(BookmarksFragment.INCOGNITO_MODE, isIncognito()); + bookmarksFragment.setArguments(bookmarksFragmentArguments); + + final FragmentManager fragmentManager = getSupportFragmentManager(); + fragmentManager + .beginTransaction() + .add(containerId, tabsFragment) + .add(R.id.right_drawer, bookmarksFragment) + .commit(); + if (mShowTabsInDrawer) { + mToolbarLayout.removeView(findViewById(R.id.tabs_toolbar_container)); + } + + 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 customView = actionBar.getCustomView(); + LayoutParams lp = customView.getLayoutParams(); + lp.width = LayoutParams.MATCH_PARENT; + lp.height = LayoutParams.MATCH_PARENT; + customView.setLayoutParams(lp); + + mArrowImage = (ImageView) customView.findViewById(R.id.arrow); + FrameLayout arrowButton = (FrameLayout) customView.findViewById(R.id.arrow_button); + if (mShowTabsInDrawer) { + // Use hardware acceleration for the animation + mArrowDrawable = new DrawerArrowDrawable(this); + mArrowImage.setLayerType(View.LAYER_TYPE_HARDWARE, null); + mArrowImage.setImageDrawable(mArrowDrawable); + } else { + mArrowImage.setImageResource(R.drawable.ic_action_home); + mArrowImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); + } + arrowButton.setOnClickListener(this); + + mProxyUtils = ProxyUtils.getInstance(); + +<<<<<<< HEAD +======= + setupFrameLayoutButton(R.id.action_back, R.id.icon_back); + setupFrameLayoutButton(R.id.action_forward, R.id.icon_forward); + setupFrameLayoutButton(R.id.action_toggle_desktop, R.id.icon_desktop); + setupFrameLayoutButton(R.id.action_reading, R.id.icon_reading); + setupFrameLayoutButton(R.id.action_home, R.id.icon_home); + +>>>>>>> d3867d29bd4002682fc3c0a15d6f10cb1869c137 + // create the search EditText in the ToolBar + mSearch = (AutoCompleteTextView) customView.findViewById(R.id.search); + mUntitledTitle = getString(R.string.untitled); + mBackgroundColor = ContextCompat.getColor(this, R.color.primary_color); + mDeleteIcon = ThemeUtils.getLightThemedDrawable(this, R.drawable.ic_action_delete); + mRefreshIcon = ThemeUtils.getLightThemedDrawable(this, R.drawable.ic_action_refresh); + mClearIcon = ThemeUtils.getLightThemedDrawable(this, R.drawable.ic_action_delete); + + int iconBounds = Utils.dpToPx(30); + mDeleteIcon.setBounds(0, 0, iconBounds, iconBounds); + mRefreshIcon.setBounds(0, 0, iconBounds, iconBounds); + mClearIcon.setBounds(0, 0, iconBounds, iconBounds); + mIcon = mRefreshIcon; + SearchListenerClass search = new SearchListenerClass(); + mSearch.setCompoundDrawables(null, null, mRefreshIcon, null); + mSearch.setOnKeyListener(search); + mSearch.setOnFocusChangeListener(search); + mSearch.setOnEditorActionListener(search); + mSearch.setOnTouchListener(search); + + new Thread(new Runnable() { + + @Override + public void run() { + initializeSearchSuggestions(mSearch); + } + + }).run(); + + mDrawerLayout.setDrawerShadow(R.drawable.drawer_right_shadow, GravityCompat.END); + mDrawerLayout.setDrawerShadow(R.drawable.drawer_left_shadow, GravityCompat.START); + + if (API <= Build.VERSION_CODES.JELLY_BEAN_MR2) { + //noinspection deprecation + WebIconDatabase.getInstance().open(getDir("icons", MODE_PRIVATE).getPath()); + } + + tabsManager.restoreTabs(this, mDarkTheme, isIncognito()); + // At this point we always have at least a tab in the tab manager + showTab(0); + + mProxyUtils.checkForProxy(this); + } + + private class SearchListenerClass implements OnKeyListener, OnEditorActionListener, OnFocusChangeListener, OnTouchListener { + + @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()); + final LightningView currentView = tabsManager.getCurrentTab(); + if (currentView != null) { + currentView.requestFocus(); + } + return true; + default: + break; + } + return false; + } + + @Override + public boolean onEditorAction(TextView arg0, int actionId, KeyEvent arg2) { + // hide the keyboard and search the web when the enter key + // button is pressed + if (actionId == EditorInfo.IME_ACTION_GO || actionId == EditorInfo.IME_ACTION_DONE + || actionId == EditorInfo.IME_ACTION_NEXT + || actionId == EditorInfo.IME_ACTION_SEND + || actionId == EditorInfo.IME_ACTION_SEARCH + || (arg2.getAction() == KeyEvent.KEYCODE_ENTER)) { + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(mSearch.getWindowToken(), 0); + searchTheWeb(mSearch.getText().toString()); + final LightningView currentView = tabsManager.getCurrentTab(); + if (currentView != null) { + currentView.requestFocus(); + } + return true; + } + return false; + } + + @Override + public void onFocusChange(View v, final boolean hasFocus) { + final LightningView currentView = tabsManager.getCurrentTab(); + if (!hasFocus && currentView != null) { + setIsLoading(currentView.getProgress() < 100); + updateUrl(currentView.getUrl(), true); + } else if (hasFocus) { + String url = currentView.getUrl(); + if (url.startsWith(Constants.FILE)) { + mSearch.setText(""); + } else { + mSearch.setText(url); + } + ((AutoCompleteTextView) v).selectAll(); // Hack to make sure + // the text gets + // selected + mIcon = mClearIcon; + mSearch.setCompoundDrawables(null, null, mClearIcon, 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() { + if (mArrowDrawable != null) { + mArrowImage.startAnimation(anim); + } + } + + }, 100); + + if (!hasFocus) { + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(mSearch.getWindowToken(), 0); + } + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (mSearch.getCompoundDrawables()[2] != null) { + boolean tappedX = event.getX() > (mSearch.getWidth() + - mSearch.getPaddingRight() - mIcon.getIntrinsicWidth()); + if (tappedX) { + if (event.getAction() == MotionEvent.ACTION_UP) { + if (mSearch.hasFocus()) { + mSearch.setText(""); + } else { + refreshOrStop(); + } + } + return true; + } + } + return false; + } + } + + private class DrawerLocker implements DrawerListener { + + @Override + public void onDrawerClosed(View v) { + if (v == mDrawerRight && mShowTabsInDrawer) { + 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 void setNavigationDrawerWidth() { + int width = getResources().getDisplayMetrics().widthPixels - Utils.dpToPx(56); + int maxWidth; + if (isTablet()) { + maxWidth = Utils.dpToPx(320); + } else { + maxWidth = Utils.dpToPx(300); + } + if (width > maxWidth) { + DrawerLayout.LayoutParams params = (android.support.v4.widget.DrawerLayout.LayoutParams) mDrawerLeft + .getLayoutParams(); + params.width = maxWidth; + mDrawerLeft.setLayoutParams(params); + mDrawerLeft.requestLayout(); + DrawerLayout.LayoutParams paramsRight = (android.support.v4.widget.DrawerLayout.LayoutParams) mDrawerRight + .getLayoutParams(); + paramsRight.width = maxWidth; + mDrawerRight.setLayoutParams(paramsRight); + mDrawerRight.requestLayout(); + } else { + DrawerLayout.LayoutParams params = (android.support.v4.widget.DrawerLayout.LayoutParams) mDrawerLeft + .getLayoutParams(); + params.width = width; + mDrawerLeft.setLayoutParams(params); + mDrawerLeft.requestLayout(); + DrawerLayout.LayoutParams paramsRight = (android.support.v4.widget.DrawerLayout.LayoutParams) mDrawerRight + .getLayoutParams(); + paramsRight.width = width; + mDrawerRight.setLayoutParams(paramsRight); + mDrawerRight.requestLayout(); + } + } + + private void initializePreferences() { + final LightningView currentView = tabsManager.getCurrentTab(); + final WebView currentWebView = currentView.getWebView(); + mFullScreen = mPreferences.getFullScreenEnabled(); + mColorMode = mPreferences.getColorModeEnabled(); + mColorMode &= !mDarkTheme; + if (!isIncognito() && !mColorMode && !mDarkTheme && mWebpageBitmap != null) { + changeToolbarBackground(mWebpageBitmap, null); + } else if (!isIncognito() && currentView != null && !mDarkTheme + && currentView.getFavicon() != null) { + changeToolbarBackground(currentView.getFavicon(), null); + } + + if (mFullScreen) { + mToolbarLayout.setTranslationY(0); + int height = mToolbarLayout.getHeight(); + if (height == 0) { + mToolbarLayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); + height = mToolbarLayout.getMeasuredHeight(); + } + if (currentWebView != null) + currentWebView.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 (currentWebView != null) + currentWebView.setTranslationY(0); + } + setFullscreen(mPreferences.getHideStatusBarEnabled(), false); + + 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 + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_ENTER) { + if (mSearch.hasFocus()) { + searchTheWeb(mSearch.getText().toString()); + } + } else if ((keyCode == KeyEvent.KEYCODE_MENU) + && (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) + && (Build.MANUFACTURER.compareTo("LGE") == 0)) { + // Workaround for stupid LG devices that crash + return true; + } + return super.onKeyDown(keyCode, event); + } + + @Override + public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) { + if ((keyCode == KeyEvent.KEYCODE_MENU) + && (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) + && (Build.MANUFACTURER.compareTo("LGE") == 0)) { + // Workaround for stupid LG devices that crash + openOptionsMenu(); + return true; + } + return super.onKeyUp(keyCode, event); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + final LightningView currentView = tabsManager.getCurrentTab(); + // 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 (currentView != null && currentView.canGoBack()) { + currentView.goBack(); + } + return true; + case R.id.action_forward: + if (currentView != null && currentView.canGoForward()) { + currentView.goForward(); + } + return true; + case R.id.action_new_tab: + newTab(null, true); + return true; + case R.id.action_incognito: + startActivity(new Intent(this, IncognitoActivity.class)); + overridePendingTransition(R.anim.slide_up_in, R.anim.fade_out_scale); + return true; + case R.id.action_share: + if (currentView != null && !currentView.getUrl().startsWith(Constants.FILE)) { + Intent shareIntent = new Intent(Intent.ACTION_SEND); + shareIntent.setType("text/plain"); + shareIntent.putExtra(Intent.EXTRA_SUBJECT, currentView.getTitle()); + shareIntent.putExtra(Intent.EXTRA_TEXT, currentView.getUrl()); + 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 (currentView != null && !currentView.getUrl().startsWith(Constants.FILE)) { + ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("label", currentView.getUrl()); + clipboard.setPrimaryClip(clip); + Utils.showSnackbar(this, R.string.message_link_copied); + } + return true; + case R.id.action_settings: + startActivity(new Intent(this, SettingsActivity.class)); + return true; + case R.id.action_history: + openHistory(); + return true; + case R.id.action_add_bookmark: + if (currentView != null && !currentView.getUrl().startsWith(Constants.FILE)) { + mEventBus.post(new BrowserEvents.AddBookmark(currentView.getTitle(), + currentView.getUrl())); + } + 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, currentView.getUrl()); + startActivity(read); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + /** + * 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(this); + 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.isEmpty()) + showSearchInterfaceBar(query); + } + }); + finder.show(); + } + + private void showSearchInterfaceBar(String text) { + final LightningView currentView = tabsManager.getCurrentTab(); + if (currentView != null) { + currentView.find(text); + } + mSearchBar.setVisibility(View.VISIBLE); + + TextView tw = (TextView) findViewById(R.id.search_query); + tw.setText('\'' + text + '\''); + + ImageButton up = (ImageButton) findViewById(R.id.button_next); + up.setOnClickListener(this); + + ImageButton down = (ImageButton) findViewById(R.id.button_back); + down.setOnClickListener(this); + + ImageButton quit = (ImageButton) findViewById(R.id.button_quit); + quit.setOnClickListener(this); + } + + private void showCloseDialog(final int position) { + if (position < 0) { + return; + } + AlertDialog.Builder builder = new AlertDialog.Builder(this); + ArrayAdapter adapter = new ArrayAdapter<>(this, + android.R.layout.simple_dropdown_item_1line); + adapter.add(this.getString(R.string.close_tab)); + adapter.add(this.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(); + } + + /** + * displays the WebView contained in the LightningView Also handles the + * removal of previous views + * + * @param position the poition of the tab to display + */ + private synchronized void showTab(final int position) { + final LightningView currentView = tabsManager.getCurrentTab(); + final WebView currentWebView = currentView != null ? currentView.getWebView() : null; + final LightningView newView = tabsManager.switchToTab(position); + final WebView newWebView = newView != null ? newView.getWebView() : null; + + // Set the background color so the color mode color doesn't show through + mBrowserFrame.setBackgroundColor(mBackgroundColor); + if (newView == currentView && !currentView.isShown()) { + return; + } + mIsNewIntent = false; + + final float translation = mToolbarLayout.getTranslationY(); + mBrowserFrame.removeAllViews(); + if (currentView != null) { + currentView.setForegroundTab(false); + currentView.onPause(); + } + newView.setForegroundTab(true); + if (currentWebView != null) { + updateUrl(newView.getUrl(), true); + updateProgress(newView.getProgress()); + } else { + updateUrl("", true); + updateProgress(0); + } + + mBrowserFrame.addView(newWebView, MATCH_PARENT); + newView.requestFocus(); + newView.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(); + } + newWebView.setTranslationY(translation + height); + mToolbarLayout.setTranslationY(translation); + } else { + newWebView.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(); + } + }, 200); + + // Should update the bookmark status in BookmarksFragment + mEventBus.post(new BrowserEvents.CurrentPageUrl(newView.getUrl())); + +// new Handler().postDelayed(new Runnable() { +// @Override +// public void run() { + // Remove browser frame background to reduce overdraw + //TODO evaluate performance +// mBrowserFrame.setBackgroundColor(Color.TRANSPARENT); +// } +// }, 300); + + } + + void handleNewIntent(Intent intent) { + + String url = null; + if (intent != null) { + url = intent.getDataString(); + } + int num = 0; + String source = null; + if (intent != null && intent.getExtras() != null) { + num = intent.getExtras().getInt(getPackageName() + ".Origin"); + source = intent.getExtras().getString("SOURCE"); + } + if (num == 1) { + loadUrlInCurrentView(url); + } else if (url != null) { + if (url.startsWith(Constants.FILE)) { + Utils.showSnackbar(this, R.string.message_blocked_local); + url = null; + } + newTab(url, true); + mIsNewIntent = (source == null); + } + } + + private void loadUrlInCurrentView(final String url) { + final LightningView currentTab = tabsManager.getCurrentTab(); + if (currentTab == null) { + // This is a problem, probably an assert will be better than a return + return; + } + + currentTab.loadUrl(url); + mEventBus.post(new BrowserEvents.CurrentPageUrl(url)); + } + + @Override + public void closeEmptyTab() { + final WebView currentWebView = tabsManager.getCurrentWebView(); + if (currentWebView != null && currentWebView.copyBackForwardList().getSize() == 0) { + closeCurrentTab(); + } + } + + private void closeCurrentTab() { + // don't delete the tab because the browser will close and mess stuff up + } + + @Override + public void onTrimMemory(int level) { + if (level > TRIM_MEMORY_MODERATE && Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + Log.d(Constants.TAG, "Low Memory, Free Memory"); + tabsManager.freeMemory(); + } + } + + synchronized boolean newTab(String url, boolean show) { + // Limit number of tabs for limited version of app + if (!Constants.FULL_VERSION && tabsManager.size() >= 10) { + Utils.showSnackbar(this, R.string.max_tabs); + return false; + } + mIsNewIntent = false; + LightningView startingTab = tabsManager.newTab(this, url, mDarkTheme, isIncognito()); + if (mIdGenerator == 0) { + startingTab.resumeTimers(); + } + mIdGenerator++; + + if (show) { + showTab(tabsManager.size() - 1); + } + // TODO Check is this is callable directly from LightningView + mEventBus.post(new BrowserEvents.TabsChanged()); + +// TODO Restore this +// new Handler().postDelayed(new Runnable() { +// @Override +// public void run() { +// mDrawerListLeft.smoothScrollToPosition(tabsManager.size() - 1); +// } +// }, 300); + + return true; + } + + private synchronized void deleteTab(int position) { + final LightningView tabToDelete = tabsManager.getTabAtPosition(position); + final LightningView currentTab = tabsManager.getCurrentTab(); + + if (tabToDelete == null) { + return; + } + +// What? + int current = tabsManager.positionOf(currentTab); + if (current < 0) { + return; + } + if (!tabToDelete.getUrl().startsWith(Constants.FILE) && !isIncognito()) { + mPreferences.setSavedUrl(tabToDelete.getUrl()); + } + final boolean isShown = tabToDelete.isShown(); + if (isShown) { + mBrowserFrame.setBackgroundColor(mBackgroundColor); + } + if (current > position) { + tabsManager.deleteTab(position); + showTab(current - 1); + mEventBus.post(new BrowserEvents.TabsChanged()); + } else if (tabsManager.size() > position + 1) { + if (current == position) { + showTab(position + 1); + tabsManager.deleteTab(position); + showTab(position); + mEventBus.post(new BrowserEvents.TabsChanged()); + } else { + tabsManager.deleteTab(position); + } + } else if (tabsManager.size() > 1) { + if (current == position) { + showTab(position - 1); + tabsManager.deleteTab(position); + showTab(position - 1); + mEventBus.post(new BrowserEvents.TabsChanged()); + } else { + } + } else { + if (currentTab.getUrl().startsWith(Constants.FILE) || currentTab.getUrl().equals(mHomepage)) { + closeActivity(); + } else { + tabsManager.deleteTab(position); + performExitCleanUp(); + tabToDelete.pauseTimers(); + mEventBus.post(new BrowserEvents.TabsChanged()); + finish(); + } + } + mEventBus.post(new BrowserEvents.TabsChanged()); + + if (mIsNewIntent && isShown) { + mIsNewIntent = false; + closeActivity(); + } + + Log.d(Constants.TAG, "deleted tab"); + } + + private void performExitCleanUp() { + final LightningView currentTab = tabsManager.getCurrentTab(); + if (mPreferences.getClearCacheExit() && currentTab != null && !isIncognito()) { + WebUtils.clearCache(currentTab.getWebView()); + Log.d(Constants.TAG, "Cache Cleared"); + } + if (mPreferences.getClearHistoryExitEnabled() && !isIncognito()) { + WebUtils.clearHistory(this); + Log.d(Constants.TAG, "History Cleared"); + } + if (mPreferences.getClearCookiesExitEnabled() && !isIncognito()) { + WebUtils.clearCookies(this); + Log.d(Constants.TAG, "Cookies Cleared"); + } + if (mPreferences.getClearWebStorageExitEnabled() && !isIncognito()) { + WebUtils.clearWebStorage(); + Log.d(Constants.TAG, "WebStorage Cleared"); + } else if (isIncognito()) { + WebUtils.clearWebStorage(); // We want to make sure incognito mode is secure + } + } + + @Override + public boolean onKeyLongPress(int keyCode, KeyEvent event) { + final LightningView currentTab = tabsManager.getCurrentTab(); + if (keyCode == KeyEvent.KEYCODE_BACK) { + showCloseDialog(tabsManager.positionOf(currentTab)); + } + return true; + } + + private void closeBrowser() { + mBrowserFrame.setBackgroundColor(mBackgroundColor); + performExitCleanUp(); + tabsManager.shutdown(); + mEventBus.post(new BrowserEvents.TabsChanged()); + finish(); + } + + @Override + public void onBackPressed() { + final LightningView currentTab = tabsManager.getCurrentTab(); + if (mDrawerLayout.isDrawerOpen(mDrawerLeft)) { + mDrawerLayout.closeDrawer(mDrawerLeft); + } else if (mDrawerLayout.isDrawerOpen(mDrawerRight)) { + mEventBus + .post(new BrowserEvents.UserPressedBack()); + } else { + if (currentTab != null) { + Log.d(Constants.TAG, "onBackPressed"); + if (mSearch.hasFocus()) { + currentTab.requestFocus(); + } else if (currentTab.canGoBack()) { + if (!currentTab.isShown()) { + onHideCustomView(); + } else { + currentTab.goBack(); + } + } else { + deleteTab(tabsManager.positionOf(currentTab)); + } + } else { + Log.e(Constants.TAG, "This shouldn't happen ever"); + super.onBackPressed(); + } + } + } + + @Override + protected void onPause() { + super.onPause(); + final LightningView currentTab = tabsManager.getCurrentTab(); + Log.d(Constants.TAG, "onPause"); + if (currentTab != null) { + currentTab.pauseTimers(); + currentTab.onPause(); + } + try { + unregisterReceiver(mNetworkReceiver); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } + if (isIncognito() && isFinishing()) { + overridePendingTransition(R.anim.fade_in_scale, R.anim.slide_down_out); + } + + mEventBus.unregister(mBusEventListener); + } + + void saveOpenTabs() { + if (mPreferences.getRestoreLostTabsEnabled()) { + final String s = tabsManager.tabsString(); + mPreferences.setMemoryUrl(s.toString()); + } + } + + @Override + protected void onStop() { + super.onStop(); + mProxyUtils.onStop(); + } + + @Override + protected void onDestroy() { + Log.d(Constants.TAG, "onDestroy"); + if (mHistoryDatabase != null) { + mHistoryDatabase.close(); + mHistoryDatabase = null; + } + super.onDestroy(); + } + + @Override + protected void onStart() { + super.onStart(); + mProxyUtils.onStart(this); + } + + @Override + protected void onResume() { + super.onResume(); + final LightningView currentTab = tabsManager.getCurrentTab(); + Log.d(Constants.TAG, "onResume"); + if (mSearchAdapter != null) { + mSearchAdapter.refreshPreferences(); + mSearchAdapter.refreshBookmarks(); + } + if (currentTab != null) { + currentTab.resumeTimers(); + currentTab.onResume(); + } + initializePreferences(); + tabsManager.resume(this); + + supportInvalidateOptionsMenu(); + + IntentFilter filter = new IntentFilter(); + filter.addAction(NETWORK_BROADCAST_ACTION); + registerReceiver(mNetworkReceiver, filter); + + mEventBus.register(mBusEventListener); + } + + /** + * searches the web for the query fixing any and all problems with the input + * checks if it is a search, url, etc. + */ + private void searchTheWeb(@NonNull String query) { + final LightningView currentTab = tabsManager.getCurrentTab(); + if (query.isEmpty()) { + return; + } + String searchUrl = mSearchText + UrlUtils.QUERY_PLACE_HOLDER; + query = query.trim(); + currentTab.stopLoading(); + if (currentTab != null) { + loadUrlInCurrentView(UrlUtils.smartUrlFilter(query, true, searchUrl)); + } + } + + /** + * Animates the color of the toolbar from one color to another. Optionally animates + * the color of the tab background, for use when the tabs are displayed on the top + * of the screen. + * + * @param favicon the Bitmap to extract the color from + * @param tabBackground the optional LinearLayout to color + */ + private void changeToolbarBackground(@NonNull Bitmap favicon, @Nullable final Drawable tabBackground) { + final int defaultColor = ContextCompat.getColor(this, R.color.primary_color); + if (mCurrentUiColor == Color.BLACK) { + mCurrentUiColor = defaultColor; + } + Palette.from(favicon).generate(new Palette.PaletteAsyncListener() { + @Override + public void onGenerated(Palette palette) { + + // OR with opaque black to remove transparency glitches + int color = 0xff000000 | palette.getVibrantColor(defaultColor); + + int finalColor; // Lighten up the dark color if it is + // too dark + if (!mShowTabsInDrawer || Utils.isColorTooDark(color)) { + finalColor = Utils.mixTwoColors(defaultColor, color, 0.25f); + } else { + finalColor = color; + } + + ValueAnimator anim = ValueAnimator.ofInt(mCurrentUiColor, finalColor); + anim.setEvaluator(new ArgbEvaluator()); + final Window window = getWindow(); + if (!mShowTabsInDrawer) { + window.setBackgroundDrawable(new ColorDrawable(Color.BLACK)); + } + anim.addUpdateListener(new AnimatorUpdateListener() { + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + final int color = (Integer) animation.getAnimatedValue(); + if (mShowTabsInDrawer) { + mBackground.setColor(color); + window.setBackgroundDrawable(mBackground); + } else if (tabBackground != null) { + tabBackground.setColorFilter(color, PorterDuff.Mode.SRC_IN); + } + mCurrentUiColor = color; + mToolbarLayout.setBackgroundColor(color); + } + + }); + anim.setDuration(300); + anim.start(); + } + }); + } + + @Override + public void updateUrl(String url, boolean shortUrl) { + final LightningView currentTab = tabsManager.getCurrentTab(); + if (url == null || mSearch == null || mSearch.hasFocus()) { + return; + } + mEventBus.post(new BrowserEvents.CurrentPageUrl(url)); + 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 (currentTab != null && !currentTab.getTitle().isEmpty()) { + mSearch.setText(currentTab.getTitle()); + } else { + mSearch.setText(mUntitledTitle); + } + break; + } + } else { + if (url.startsWith(Constants.FILE)) { + url = ""; + } + mSearch.setText(url); + } + } + + @Override + public void updateProgress(int n) { + setIsLoading(n < 100); + mProgressBar.setProgress(n); + } + + void addItemToHistory(final String title, final String url) { + Runnable update = new Runnable() { + @Override + public void run() { + try { + mHistoryDatabase.visitHistoryItem(url, title); + } catch (IllegalStateException e) { + Log.e(Constants.TAG, "IllegalStateException in updateHistory", e); + } catch (NullPointerException e) { + Log.e(Constants.TAG, "NullPointerException in updateHistory", e); + } catch (SQLiteException e) { + Log.e(Constants.TAG, "SQLiteException in updateHistory", e); + } + } + }; + if (url != null && !url.startsWith(Constants.FILE)) { + new Thread(update).start(); + } + } + + /** + * method to generate search suggestions for the AutoCompleteTextView from + * previously searched URLs + */ + private void initializeSearchSuggestions(final AutoCompleteTextView getUrl) { + final LightningView currentTab = tabsManager.getCurrentTab(); + 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(BrowserActivity.this.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 (currentTab != null) { + currentTab.requestFocus(); + } + } catch (NullPointerException e) { + Log.e("Browser Error: ", "NullPointerException on item click"); + } + } + + }); + + getUrl.setSelectAllOnFocus(true); + mSearchAdapter = new SearchAdapter(this, mDarkTheme, isIncognito()); + getUrl.setAdapter(mSearchAdapter); + } + + /** + * 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() { + loadUrlInCurrentView(HistoryPage.getHistoryPage(BrowserActivity.this)); + 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 + public boolean onCreateOptionsMenu(Menu menu) { + MenuItem back = menu.findItem(R.id.action_back); + MenuItem forward = menu.findItem(R.id.action_forward); + if (back != null && back.getIcon() != null) + back.getIcon().setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); + if (forward != null && forward.getIcon() != null) + forward.getIcon().setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); + return super.onCreateOptionsMenu(menu); + } + + /** + * opens a file chooser + * param ValueCallback is the message from the WebView indicating a file chooser + * should be opened + */ + @Override + public void openFileChooser(ValueCallback uploadMsg) { + mUploadMessage = uploadMsg; + Intent i = new Intent(Intent.ACTION_GET_CONTENT); + i.addCategory(Intent.CATEGORY_OPENABLE); + i.setType("*/*"); + startActivityForResult(Intent.createChooser(i, getString(R.string.title_file_chooser)), 1); + } + + /** + * used to allow uploading into the browser + */ + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent intent) { + if (API < Build.VERSION_CODES.LOLLIPOP) { + if (requestCode == 1) { + if (null == mUploadMessage) { + return; + } + Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData(); + mUploadMessage.onReceiveValue(result); + mUploadMessage = null; + + } + } + + if (requestCode != 1 || mFilePathCallback == null) { + super.onActivityResult(requestCode, resultCode, intent); + return; + } + + Uri[] results = null; + + // Check that the response is a good one + if (resultCode == Activity.RESULT_OK) { + if (intent == null) { + // If there is not data, then we may have taken a photo + if (mCameraPhotoPath != null) { + results = new Uri[]{Uri.parse(mCameraPhotoPath)}; + } + } else { + String dataString = intent.getDataString(); + if (dataString != null) { + results = new Uri[]{Uri.parse(dataString)}; + } + } + } + + mFilePathCallback.onReceiveValue(results); + mFilePathCallback = null; + } + + @Override + public void showFileChooser(ValueCallback filePathCallback) { + if (mFilePathCallback != null) { + mFilePathCallback.onReceiveValue(null); + } + mFilePathCallback = filePathCallback; + + Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + if (takePictureIntent.resolveActivity(this.getPackageManager()) != null) { + // Create the File where the photo should go + File photoFile = null; + try { + photoFile = Utils.createImageFile(); + takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath); + } catch (IOException ex) { + // Error occurred while creating the File + Log.e(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("*/*"); + + 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); + + this.startActivityForResult(chooserIntent, 1); + } + + @Override + public void onShowCustomView(View view, CustomViewCallback callback) { + final LightningView currentTab = tabsManager.getCurrentTab(); + 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 FrameLayout(this); + mFullscreenContainer.setBackgroundColor(ContextCompat.getColor(this, android.R.color.black)); + mCustomView = view; + mFullscreenContainer.addView(mCustomView, COVER_SCREEN_PARAMS); + decor.addView(mFullscreenContainer, COVER_SCREEN_PARAMS); + setFullscreen(true, true); + currentTab.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() { + final LightningView currentTab = tabsManager.getCurrentTab(); + if (mCustomView == null || mCustomViewCallback == null || currentTab == null) { + return; + } + Log.d(Constants.TAG, "onHideCustomView"); + currentTab.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(), false); + FrameLayout decor = (FrameLayout) getWindow().getDecorView(); + if (decor != null) { + decor.removeView(mFullscreenContainer); + } + + if (API < Build.VERSION_CODES.KITKAT) { + 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(); + } + + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + if (hasFocus) { + setFullscreen(mIsFullScreen, mIsImmersive); + } + } + + /** + * This method sets whether or not the activity will display + * in full-screen mode (i.e. the ActionBar will be hidden) and + * whether or not immersive mode should be set. This is used to + * set both parameters correctly as during a full-screen video, + * both need to be set, but other-wise we leave it up to user + * preference. + * + * @param enabled true to enable full-screen, false otherwise + * @param immersive true to enable immersive mode, false otherwise + */ + private void setFullscreen(boolean enabled, boolean immersive) { + mIsFullScreen = enabled; + mIsImmersive = immersive; + Window window = getWindow(); + View decor = window.getDecorView(); + if (enabled) { + window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + if (immersive) { + decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + } + } else { + window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); + } + } + + /** + * This method handles the JavaScript callback to create a new tab. + * Basically this handles the event that JavaScript needs to create + * a popup. + * + * @param resultMsg the transport message used to send the URL to + * the newly created WebView. + */ + @Override + public void onCreateWindow(Message resultMsg) { + if (resultMsg == null) { + return; + } + if (newTab("", true)) { + // TODO Review this + final WebView webView = tabsManager.getTabAtPosition(tabsManager.size() - 1).getWebView(); + WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj; + transport.setWebView(webView); + resultMsg.sendToTarget(); + } + } + + /** + * Closes the specified {@link LightningView}. This implements + * the JavaScript callback that asks the tab to close itself and + * is especially helpful when a page creates a redirect and does + * not need the tab to stay open any longer. + * + * @param view the LightningView to close, delete it. + */ + @Override + public void onCloseWindow(LightningView view) { + deleteTab(tabsManager.positionOf(view)); + } + + /** + * Hide the ActionBar using an animation if we are in full-screen + * mode. This method also re-parents the ActionBar if its parent is + * incorrect so that the animation can happen correctly. + */ + @Override + public void hideActionBar() { + final LightningView currentTab = tabsManager.getCurrentTab(); + final WebView currentWebView = currentTab.getWebView(); + 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); + currentWebView.setTranslationY(mToolbarLayout.getHeight()); + } + if (mToolbarLayout == null || currentTab == null) + return; + + final int height = mToolbarLayout.getHeight(); + final WebView view = currentWebView; + 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()); + currentWebView.startAnimation(show); + } + } + } + + /** + * Display the ActionBar using an animation if we are in full-screen + * mode. This method also re-parents the ActionBar if its parent is + * incorrect so that the animation can happen correctly. + */ + @Override + public void showActionBar() { + if (mFullScreen) { + final WebView view = tabsManager.getCurrentWebView(); + + 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); + if (view != null) { + view.setTranslationY(height); + } + } + final LightningView currentTab = tabsManager.getCurrentTab(); + if (currentTab == null) + return; + + 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()); + if (view != null) { + view.startAnimation(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(boolean isLoading) { + if (!mSearch.hasFocus()) { + mIcon = isLoading ? mDeleteIcon : mRefreshIcon; + mSearch.setCompoundDrawables(null, null, mIcon, null); + } + } + + /** + * handle presses on the refresh icon in the search bar, if the page is + * loading, stop the page, if it is done loading refresh the page. + * See setIsFinishedLoading and setIsLoading for displaying the correct icon + */ + private void refreshOrStop() { + final LightningView currentTab = tabsManager.getCurrentTab(); + if (currentTab != null) { + if (currentTab.getProgress() < 100) { + currentTab.stopLoading(); + } else { + currentTab.reload(); + } + } + } + + /** + * Handle the click event for the views that are using + * this class as a click listener. This method should + * distinguish between the various views using their IDs. + * + * @param v the view that the user has clicked + */ + @Override + public void onClick(View v) { + final LightningView currentTab = tabsManager.getCurrentTab(); + final WebView currentWebView = currentTab.getWebView(); + switch (v.getId()) { + case R.id.arrow_button: + if (mSearch != null && mSearch.hasFocus()) { + currentTab.requestFocus(); + } else if (mShowTabsInDrawer) { + mDrawerLayout.openDrawer(mDrawerLeft); + } else if (currentTab != null) { + currentTab.loadHomepage(); + } + break; +<<<<<<< HEAD +======= + case R.id.new_tab_button: + newTab(null, true); + break; + case R.id.action_home: + mCurrentView.loadHomepage(); + closeDrawers(); + break; +>>>>>>> d3867d29bd4002682fc3c0a15d6f10cb1869c137 + case R.id.button_next: + currentWebView.findNext(false); + break; + case R.id.button_back: + currentWebView.findNext(true); + break; + case R.id.button_quit: + currentWebView.clearMatches(); + mSearchBar.setVisibility(View.GONE); + break; + case R.id.action_reading: + Intent read = new Intent(this, ReadingActivity.class); + read.putExtra(Constants.LOAD_READING_URL, currentTab.getUrl()); + startActivity(read); + break; + case R.id.action_toggle_desktop: + currentTab.toggleDesktopUA(this); + currentTab.reload(); + closeDrawers(); + break; + } + } + + /** + * Handle long presses on views that use this class + * as their OnLongClickListener. This method should + * distinguish between the IDs of the views that are + * getting clicked. + * + * @param view the view that has been long pressed + * @return returns true since the method handles the long press + * event + */ + @Override + public boolean onLongClick(View view) { + return true; + } + + // TODO Check if all the calls are relative to TabsFragement + /** + * A utility method that creates a FrameLayout button with the given ID and + * sets the image of the button to the given image ID. The OnClick and OnLongClick + * listeners are set to this class, so BrowserActivity should handle those events + * there. Additionally, it tints the images according to the current theme. + * This method only is a convenience so that this code does not have to be repeated + * for the several "Buttons" that use this. + * + * @param buttonId the view id of the button + * @param imageId the image to set as the button image + */ + private void setupFrameLayoutButton(@IdRes int buttonId, @IdRes int imageId) { + final View frameButton = findViewById(buttonId); + final ImageView buttonImage = (ImageView) findViewById(imageId); + frameButton.setOnClickListener(this); + frameButton.setOnLongClickListener(this); + buttonImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); + } + + /** + * This NetworkReceiver notifies each of the WebViews in the browser whether + * the network is currently connected or not. This is important because some + * JavaScript properties rely on the WebView knowing the current network state. + * It is used to help the browser be compliant with the HTML5 spec, sec. 5.7.7 + */ + private final NetworkReceiver mNetworkReceiver = new NetworkReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + super.onReceive(context, intent); + boolean isConnected = isConnected(context); + Log.d(Constants.TAG, "Network Connected: " + String.valueOf(isConnected)); + tabsManager.notifyConnectioneStatus(isConnected); + } + }; + + /** + * Handle the callback that permissions requested have been granted or not. + * This method should act upon the results of the permissions request. + * + * @param requestCode the request code sent when initially making the request + * @param permissions the array of the permissions that was requested + * @param grantResults the results of the permissions requests that provides + * information on whether the request was granted or not + */ + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + PermissionsManager.getInstance().notifyPermissionsChange(permissions); + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + + private final Object mBusEventListener = new Object() { + + /** + * Load the given url in the current tab, used by the the + * {@link acr.browser.lightning.fragment.BookmarksFragment} and by the + * {@link LightningDialogBuilder} + * + * @param event Bus event indicating that the user has clicked a bookmark + */ + @Subscribe + public void loadUrlInCurrentTab(final BrowserEvents.OpenUrlInCurrentTab event) { + loadUrlInCurrentView(event.url); + // 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); + } + + /** + * Load the given url in a new tab, used by the the + * {@link acr.browser.lightning.fragment.BookmarksFragment} and by the + * {@link LightningDialogBuilder} + * + * @param event Bus event indicating that the user wishes + * to open a bookmark in a new tab + */ + @Subscribe + public void loadUrlInNewTab(final BrowserEvents.OpenUrlInNewTab event) { + BrowserActivity.this.newTab(event.url, true); + mDrawerLayout.closeDrawers(); + } + + /** + * When receive a {@link acr.browser.lightning.bus.BookmarkEvents.WantToBookmarkCurrentPage} + * message this receiver answer firing the + * {@link acr.browser.lightning.bus.BrowserEvents.AddBookmark} message + * + * @param event an event that the user wishes to bookmark the current page + */ + @Subscribe + public void bookmarkCurrentPage(final BookmarkEvents.WantToBookmarkCurrentPage event) { + final LightningView currentTab = tabsManager.getCurrentTab(); + if (currentTab != null) { + mEventBus.post(new BrowserEvents.AddBookmark(currentTab.getTitle(), currentTab.getUrl())); + } + } + + /** + * This message is received when a bookmark was added by the + * {@link acr.browser.lightning.fragment.BookmarksFragment} + * + * @param event the event that a bookmark has been added + */ + @Subscribe + public void bookmarkAdded(final BookmarkEvents.Added event) { + mSearchAdapter.refreshBookmarks(); + } + + /** + * This method is called when the user edits a bookmark. + * + * @param event the event that the bookmark has changed. + */ + @Subscribe + public void bookmarkChanged(final BookmarkEvents.BookmarkChanged event) { + final LightningView currentTab = tabsManager.getCurrentTab(); + final WebView currentWebView = currentTab.getWebView(); + if (currentTab != null && currentTab.getUrl().startsWith(Constants.FILE) + && currentTab.getUrl().endsWith(Constants.BOOKMARKS_FILENAME)) { + currentTab.loadBookmarkpage(); + } + if (currentTab != null) { + mEventBus.post(new BrowserEvents.CurrentPageUrl(currentTab.getUrl())); + } + } + + /** + * Notify the browser that a bookmark was deleted. + * + * @param event the event that the bookmark has been deleted + */ + @Subscribe + public void bookmarkDeleted(final BookmarkEvents.Deleted event) { + final LightningView currentTab = tabsManager.getCurrentTab(); + final WebView currentWebView = currentTab.getWebView(); + if (currentTab != null && currentTab.getUrl().startsWith(Constants.FILE) + && currentTab.getUrl().endsWith(Constants.BOOKMARKS_FILENAME)) { + currentTab.loadBookmarkpage(); + } + if (currentTab != null) { + mEventBus.post(new BrowserEvents.CurrentPageUrl(currentTab.getUrl())); + } + } + + /** + * The {@link acr.browser.lightning.fragment.BookmarksFragment} send this message on reply + * to {@link acr.browser.lightning.bus.BrowserEvents.UserPressedBack} message if the + * fragement is showing the boomarks root folder. + * + * @param event an event notifying the browser that the bookmark drawer + * should be closed. + */ + @Subscribe + public void closeBookmarks(final BookmarkEvents.CloseBookmarks event) { + mDrawerLayout.closeDrawer(mDrawerRight); + } + + /** + * The user wants to close a tab + * + * @param event contains the position inside the tabs adapter + */ + @Subscribe + public void closeTab(final TabEvents.CloseTab event) { + deleteTab(event.position); + } + + /** + * The user clicked on a tab, let's show it + * + * @param event contains the tab position in the tabs adapter + */ + @Subscribe + public void showTab(final TabEvents.ShowTab event) { + BrowserActivity.this.showTab(event.position); + } + + /** + * The user long pressed on a tab, ask him if he want to close the tab + * + * @param event contains the tab position in the tabs adapter + */ + @Subscribe + public void showCloseDialog(final TabEvents.ShowCloseDialog event) { + BrowserActivity.this.showCloseDialog(event.position); + } + + /** + * The user wants to create a new tab + * + * @param event a marker + */ + @Subscribe + public void newTab(final TabEvents.NewTab event) { + BrowserActivity.this.newTab(null, true); + } + + /** + * The user wants to go back on current tab + * + * @param event a marker + */ + @Subscribe + public void goBack(final NavigationEvents.GoBack event) { + final LightningView currentTab = tabsManager.getCurrentTab(); + if (currentTab != null) { + if (currentTab.canGoBack()) { + currentTab.goBack(); + } else { + deleteTab(tabsManager.positionOf(currentTab)); + } + } + } + + /** + * The user wants to go forward on current tab + * + * @param event a marker + */ + @Subscribe + public void goForward(final NavigationEvents.GoForward event) { + final LightningView currentTab = tabsManager.getCurrentTab(); + if (currentTab != null) { + if (currentTab.canGoForward()) { + currentTab.goForward(); + } + } + } + + /** + * The user long pressed the new tab button + * + * @param event a marker + */ + @Subscribe + public void newTabLongPress(final TabEvents.NewTabLongPress event) { + String url = mPreferences.getSavedUrl(); + if (url != null) { + BrowserActivity.this.newTab(url, true); + + Utils.showSnackbar(BrowserActivity.this, R.string.deleted_tab); + } + mPreferences.setSavedUrl(null); + } + + @Subscribe + public void displayInSnackbar(final BrowserEvents.ShowSnackBarMessage event) { + if (event.message != null) { + Utils.showSnackbar(BrowserActivity.this, event.message); + } else { + Utils.showSnackbar(BrowserActivity.this, event.stringRes); + } + } + }; +} 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 b7be9d3..b46e42f 100644 --- a/app/src/main/java/acr/browser/lightning/activity/IncognitoActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/IncognitoActivity.java @@ -18,13 +18,13 @@ public class IncognitoActivity extends BrowserActivity { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { CookieSyncManager.createInstance(this); } - cookieManager.setAcceptCookie(PreferenceManager.getInstance().getIncognitoCookiesEnabled()); + cookieManager.setAcceptCookie(mPreferences.getIncognitoCookiesEnabled()); } - @Override - public synchronized void initializeTabs() { - newTab(null, true); - } +// @Override +// public synchronized void initializeTabs() { +// newTab(null, true); +// } @Override public boolean onCreateOptionsMenu(Menu menu) { 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 68d6022..808ea0c 100644 --- a/app/src/main/java/acr/browser/lightning/activity/MainActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/MainActivity.java @@ -18,14 +18,14 @@ public class MainActivity extends BrowserActivity { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { CookieSyncManager.createInstance(this); } - cookieManager.setAcceptCookie(PreferenceManager.getInstance().getCookiesEnabled()); + cookieManager.setAcceptCookie(mPreferences.getCookiesEnabled()); } - @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) { 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 8474e85..e1847b6 100644 --- a/app/src/main/java/acr/browser/lightning/activity/ReadingActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/ReadingActivity.java @@ -22,6 +22,7 @@ import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; import acr.browser.lightning.R; +import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.reading.HtmlFetcher; @@ -49,7 +50,7 @@ public class ReadingActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { overridePendingTransition(R.anim.slide_in_from_right, R.anim.fade_out_scale); - mPreferences = PreferenceManager.getInstance(); + mPreferences = BrowserApp.getAppComponent().getPreferenceManager(); mInvert = mPreferences.getInvertColors(); final int color; if (mInvert) { diff --git a/app/src/main/java/acr/browser/lightning/activity/TabsManager.java b/app/src/main/java/acr/browser/lightning/activity/TabsManager.java new file mode 100644 index 0000000..19f159b --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/activity/TabsManager.java @@ -0,0 +1,222 @@ +package acr.browser.lightning.activity; + +import android.app.Activity; +import android.content.Context; +import android.support.annotation.Nullable; +import android.webkit.WebView; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import acr.browser.lightning.controller.BrowserController; +import acr.browser.lightning.preference.PreferenceManager; +import acr.browser.lightning.utils.Utils; +import acr.browser.lightning.view.LightningView; + +/** + * @author Stefano Pacifici + * @date 2015/09/14 + */ +@Singleton +public class TabsManager { + + private final List mWebViewList = new ArrayList<>(); + private LightningView mCurrentTab; + + @Inject + PreferenceManager mPreferenceManager; + + @Inject + public TabsManager() { + } + + public void restoreTabs(BrowserActivity activity, boolean darkTheme, boolean incognito) { + mWebViewList.clear(); + mCurrentTab = null; + if (!incognito && mPreferenceManager.getRestoreLostTabsEnabled()) { + final String mem = mPreferenceManager.getMemoryUrl(); + mPreferenceManager.setMemoryUrl(""); + String[] array = Utils.getArray(mem); + int count = 0; + for (String urlString : array) { + if (!urlString.isEmpty()) { + newTab(activity, urlString, darkTheme, incognito); + } + } + } + if (mWebViewList.size() == 0) { + newTab(activity, null, darkTheme, incognito); + } + // mCurrentTab = mWebViewList.get(0); + } + + /** + * Return a clone of the current tabs list. The list will not be updated, the user has to fetch + * a new copy when notified. + * + * @return a copy of the current tabs list + */ + public List getTabsList() { + return new ArrayList<>(mWebViewList); + } + + /** + * Return the tab at the given position in tabs list, or null if position is not in tabs list + * range. + * + * @param position the index in tabs list + * @return the corespondent {@link LightningView}, or null if the index is invalid + */ + @Nullable + public LightningView getTabAtPosition(final int position) { + if (position < 0 || position >= mWebViewList.size()) { + return null; + } + + return mWebViewList.get(position); + } + + /** + * Try to low memory pressure + */ + public void freeMemory() { + for (LightningView tab: mWebViewList) { + tab.freeMemory(); + } + } + + /** + * Shutdown the manager + */ + public synchronized void shutdown() { + for (LightningView tab: mWebViewList) { + tab.onDestroy(); + } + mWebViewList.clear(); + } + + /** + * Resume the tabs + * + * @param context + */ + public synchronized void resume(final Context context) { + for (LightningView tab: mWebViewList) { + tab.initializePreferences(null, context); + } + } + + /** + * Forward network connection status to the webviews. + * + * @param isConnected + */ + public synchronized void notifyConnectioneStatus(final boolean isConnected) { + for (LightningView tab: mWebViewList) { + final WebView webView = tab.getWebView(); + if (webView != null) { + webView.setNetworkAvailable(isConnected); + } + } + } + /** + * @return The number of currently opened tabs + */ + public int size() { + return mWebViewList.size(); + } + + /** + * Create and return a new tab. The tab is automatically added to the tabs list. + * + * @param activity + * @param url + * @param darkTheme + * @param isIncognito + * @return + */ + public synchronized LightningView newTab(final BrowserActivity activity, + final String url, final boolean darkTheme, + final boolean isIncognito) { + final LightningView tab = new LightningView(activity, url, darkTheme, isIncognito); + mWebViewList.add(tab); + return tab; + } + + /** + * Remove a tab and return its reference or null if the position is not in tabs range + * + * @param position The position of the tab to remove + * @return The removed tab reference or null + */ + @Nullable + public synchronized LightningView deleteTab(final int position) { + if (position >= mWebViewList.size()) { + return null; + } + final LightningView tab = mWebViewList.remove(position); + tab.onDestroy(); + return tab; + } + + /** + * Return the position of the given tab + * @param tab + * @return + */ + public int positionOf(final LightningView tab) { + return mWebViewList.indexOf(tab); + } + + /** + * @return A string representation of the currently opened tabs + */ + public String tabsString() { + final StringBuilder builder = new StringBuilder(); + for (LightningView tab: mWebViewList) { + final String url = tab.getUrl(); + if (url != null && !url.isEmpty()) { + builder.append(url).append("|$|SEPARATOR|$|"); + } + } + return builder.toString(); + } + + /** + * Return the {@link WebView} associated to the current tab, or null if there is no current tab + * @return a {@link WebView} or null + */ + @Nullable + public WebView getCurrentWebView() { + return mCurrentTab != null ? mCurrentTab.getWebView() : null; + } + + /** + * TODO We should remove also this, but probably not + * @return + */ + public LightningView getCurrentTab() { + return mCurrentTab; + } + + /** + * Switch the current tab to the one at the given position. It returns the selected. After this + * call {@link TabsManager#getCurrentTab()} return the same reference returned by this method if + * position is valid. + * + * @return the selected tab or null if position is out of tabs range + */ + @Nullable + public LightningView switchToTab(final int position) { + if (position < 0 || position >= mWebViewList.size()) { + return null; + } else { + final LightningView tab = mWebViewList.get(position); + mCurrentTab = tab; + return tab; + } + } +} diff --git a/app/src/main/java/acr/browser/lightning/activity/ThemableBrowserActivity.java b/app/src/main/java/acr/browser/lightning/activity/ThemableBrowserActivity.java index 88a01dc..13f210e 100644 --- a/app/src/main/java/acr/browser/lightning/activity/ThemableBrowserActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/ThemableBrowserActivity.java @@ -5,18 +5,25 @@ import android.content.res.Configuration; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; +import javax.inject.Inject; + import acr.browser.lightning.R; +import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.preference.PreferenceManager; public abstract class ThemableBrowserActivity extends AppCompatActivity { + @Inject + PreferenceManager mPreferences; + private int mTheme; private boolean mShowTabsInDrawer; @Override protected void onCreate(Bundle savedInstanceState) { - mTheme = PreferenceManager.getInstance().getUseTheme(); - mShowTabsInDrawer = PreferenceManager.getInstance().getShowTabsInDrawer(!isTablet()); + BrowserApp.getAppComponent().inject(this); + mTheme = mPreferences.getUseTheme(); + mShowTabsInDrawer = mPreferences.getShowTabsInDrawer(!isTablet()); // set the theme if (mTheme == 1) { @@ -30,8 +37,8 @@ public abstract class ThemableBrowserActivity extends AppCompatActivity { @Override protected void onResume() { super.onResume(); - int theme = PreferenceManager.getInstance().getUseTheme(); - boolean drawerTabs = PreferenceManager.getInstance().getShowTabsInDrawer(!isTablet()); + int theme = mPreferences.getUseTheme(); + boolean drawerTabs = mPreferences.getShowTabsInDrawer(!isTablet()); if (theme != mTheme || mShowTabsInDrawer != drawerTabs) { restart(); } 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 c5a4573..475c0ee 100644 --- a/app/src/main/java/acr/browser/lightning/activity/ThemableSettingsActivity.java +++ b/app/src/main/java/acr/browser/lightning/activity/ThemableSettingsActivity.java @@ -4,6 +4,7 @@ import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import acr.browser.lightning.R; +import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.utils.ThemeUtils; @@ -13,7 +14,7 @@ public abstract class ThemableSettingsActivity extends AppCompatPreferenceActivi @Override protected void onCreate(Bundle savedInstanceState) { - mTheme = PreferenceManager.getInstance().getUseTheme(); + mTheme = BrowserApp.getAppComponent().getPreferenceManager().getUseTheme(); // set the theme if (mTheme == 0) { @@ -32,7 +33,7 @@ public abstract class ThemableSettingsActivity extends AppCompatPreferenceActivi @Override protected void onResume() { super.onResume(); - if (PreferenceManager.getInstance().getUseTheme() != mTheme) { + if (BrowserApp.getAppComponent().getPreferenceManager().getUseTheme() != mTheme) { restart(); } } diff --git a/app/src/main/java/acr/browser/lightning/app/AppComponent.java b/app/src/main/java/acr/browser/lightning/app/AppComponent.java index dbaf0bc..6be83ca 100644 --- a/app/src/main/java/acr/browser/lightning/app/AppComponent.java +++ b/app/src/main/java/acr/browser/lightning/app/AppComponent.java @@ -1,13 +1,23 @@ package acr.browser.lightning.app; +import android.content.Context; + +import com.squareup.otto.Bus; + import javax.inject.Singleton; import acr.browser.lightning.activity.BrowserActivity; +import acr.browser.lightning.activity.ThemableBrowserActivity; import acr.browser.lightning.constant.BookmarkPage; -import acr.browser.lightning.dialog.BookmarksDialogBuilder; +import acr.browser.lightning.database.HistoryDatabase; +import acr.browser.lightning.dialog.LightningDialogBuilder; import acr.browser.lightning.fragment.BookmarkSettingsFragment; import acr.browser.lightning.fragment.BookmarksFragment; +import acr.browser.lightning.fragment.LightningPreferenceFragment; +import acr.browser.lightning.fragment.TabsFragment; import acr.browser.lightning.object.SearchAdapter; +import acr.browser.lightning.preference.PreferenceManager; +import acr.browser.lightning.view.LightningView; import dagger.Component; /** @@ -25,7 +35,25 @@ public interface AppComponent { void inject(SearchAdapter adapter); - void inject(BookmarksDialogBuilder builder); + void inject(LightningDialogBuilder builder); void inject(BookmarkPage bookmarkPage); + + void inject(TabsFragment fragment); + + PreferenceManager getPreferenceManager(); + + void inject(LightningPreferenceFragment fragment); + + BookmarkPage getBookmarkPage(); + + Bus getBus(); + + HistoryDatabase getHistoryDatabase(); + + Context getApplicationContext(); + + void inject(LightningView lightningView); + + void inject(ThemableBrowserActivity activity); } diff --git a/app/src/main/java/acr/browser/lightning/bus/BookmarkEvents.java b/app/src/main/java/acr/browser/lightning/bus/BookmarkEvents.java index 3331eb7..1bff1cb 100644 --- a/app/src/main/java/acr/browser/lightning/bus/BookmarkEvents.java +++ b/app/src/main/java/acr/browser/lightning/bus/BookmarkEvents.java @@ -11,28 +11,6 @@ public final class BookmarkEvents { // No instances } - /** - * A bookmark was clicked - */ - public final static class Clicked { - public final HistoryItem bookmark; - - public Clicked(final HistoryItem bookmark) { - this.bookmark = bookmark; - } - } - - /** - * The user ask to open the bookmark as new tab - */ - public final static class AsNewTab { - public final HistoryItem bookmark; - - public AsNewTab(final HistoryItem bookmark) { - this.bookmark = bookmark; - } - } - /** * The user ask to delete the selected bookmark */ @@ -61,13 +39,6 @@ public final class BookmarkEvents { } } - /** - * The {@link acr.browser.lightning.fragment.BookmarksFragment} want to know the url (and title) - * of the currently shown web page. - */ - // public static class WantInfoAboutCurrentPage { - // } - /** * Sended by the {@link acr.browser.lightning.fragment.BookmarksFragment} when it wants to close * itself (generally in reply to a {@link acr.browser.lightning.bus.BrowserEvents.UserPressedBack} diff --git a/app/src/main/java/acr/browser/lightning/bus/BrowserEvents.java b/app/src/main/java/acr/browser/lightning/bus/BrowserEvents.java index 15ff373..478dbce 100644 --- a/app/src/main/java/acr/browser/lightning/bus/BrowserEvents.java +++ b/app/src/main/java/acr/browser/lightning/bus/BrowserEvents.java @@ -1,5 +1,7 @@ package acr.browser.lightning.bus; +import android.support.annotation.StringRes; + /** * Created by Stefano Pacifici on 26/08/15. */ @@ -24,9 +26,8 @@ public final class BrowserEvents { } /** - * Used to reply to {@link acr.browser.lightning.fragment.BookmarksFragment} message - * {@link acr.browser.lightning.bus.BookmarkEvents.WantInfoAboutCurrentPage}. This is generally - * used to update the {@link acr.browser.lightning.fragment.BookmarksFragment} interface. + * Notify the current page has a new url. This is generally used to update the + * {@link acr.browser.lightning.fragment.BookmarksFragment} interface. */ public static class CurrentPageUrl { public final String url; @@ -41,4 +42,55 @@ public final class BrowserEvents { */ public static class UserPressedBack { } + + /** + * Notify that the user closed or opened a tab + */ + public static class TabsChanged { + } + + /** + * + */ + + /** + * Notify the Browser to display a SnackBar in the main activity + */ + public static class ShowSnackBarMessage { + public final String message; + @StringRes + public final int stringRes; + + public ShowSnackBarMessage(final String message) { + this.message = message; + this.stringRes = -1; + } + + public ShowSnackBarMessage(@StringRes final int stringRes) { + this.message = null; + this.stringRes = stringRes; + } + } + + /** + * The user want to open the given url in the current tab + */ + public final static class OpenUrlInCurrentTab { + public final String url; + + public OpenUrlInCurrentTab(final String url) { + this.url = url; + } + } + + /** + * The user ask to open the given url as new tab + */ + public final static class OpenUrlInNewTab { + public final String url; + + public OpenUrlInNewTab(final String url) { + this.url = url; + } + } } diff --git a/app/src/main/java/acr/browser/lightning/bus/NavigationEvents.java b/app/src/main/java/acr/browser/lightning/bus/NavigationEvents.java new file mode 100644 index 0000000..5f7519f --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/bus/NavigationEvents.java @@ -0,0 +1,34 @@ +package acr.browser.lightning.bus; + +/** + * Collections of navigation events, like go back or go forward + * + * @author Stefano Pacifici + * @date 2015/09/15 + */ +public class NavigationEvents { + private NavigationEvents() { + // No instances please + } + + /** + * Fired by {@link acr.browser.lightning.fragment.TabsFragment} when the user presses back + * button. + */ + public static class GoBack { + } + + /** + * Fired by {@link acr.browser.lightning.fragment.TabsFragment} when the user presses forward + * button. + */ + public static class GoForward { + } + + /** + * Fired by {@link acr.browser.lightning.fragment.TabsFragment} when the user presses the home + * button. + */ + public static class GoHome { + } +} diff --git a/app/src/main/java/acr/browser/lightning/bus/TabEvents.java b/app/src/main/java/acr/browser/lightning/bus/TabEvents.java new file mode 100644 index 0000000..a2bac5c --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/bus/TabEvents.java @@ -0,0 +1,65 @@ +package acr.browser.lightning.bus; + +/** + * A collection of events been sent by {@link acr.browser.lightning.fragment.TabsFragment} + * + * @author Stefano Pacifici + * @date 2015/09/14 + */ +public final class TabEvents { + + private TabEvents() { + // No instances + } + + + /** + * Sended by {@link acr.browser.lightning.fragment.TabsFragment} when the user click on the + * tab exit button + */ + public static class CloseTab { + public final int position; + + public CloseTab(int position) { + this.position = position; + } + } + + /** + * Sended by {@link acr.browser.lightning.fragment.TabsFragment} when the user click on the + * tab itself. + */ + public static class ShowTab { + public final int position; + + public ShowTab(int position) { + this.position = position; + } + } + + /** + * Sended by {@link acr.browser.lightning.fragment.TabsFragment} when the user long press on the + * tab itself. + */ + public static class ShowCloseDialog { + public final int position; + + public ShowCloseDialog(int position) { + this.position = position; + } + } + + /** + * Sended by {@link acr.browser.lightning.fragment.TabsFragment} when the user want to create a + * new tab. + */ + public static class NewTab { + } + + /** + * Sended by {@link acr.browser.lightning.fragment.TabsFragment} when the user long presses on + * new tab button. + */ + public static class NewTabLongPress { + } +} diff --git a/app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java b/app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java index 67c1765..1355844 100644 --- a/app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java +++ b/app/src/main/java/acr/browser/lightning/constant/BookmarkPage.java @@ -62,7 +62,8 @@ public final class BookmarkPage { CACHE_DIR = context.getCacheDir(); } - public void buildBookmarkPage(final String folder, final List list) { + public void buildBookmarkPage(final String folder) { + final List list = manager.getBookmarksFromFolder(folder, true); final File bookmarkWebPage; if (folder == null || folder.isEmpty()) { bookmarkWebPage = new File(FILES_DIR, Constants.BOOKMARKS_FILENAME); @@ -83,7 +84,7 @@ public final class BookmarkPage { new Thread(new Runnable() { @Override public void run() { - buildBookmarkPage(item.getTitle(), manager.getBookmarksFromFolder(item.getTitle(), true)); + buildBookmarkPage(item.getTitle()); } }).run(); } else { diff --git a/app/src/main/java/acr/browser/lightning/constant/HistoryPage.java b/app/src/main/java/acr/browser/lightning/constant/HistoryPage.java index 41dc907..8e7d936 100644 --- a/app/src/main/java/acr/browser/lightning/constant/HistoryPage.java +++ b/app/src/main/java/acr/browser/lightning/constant/HistoryPage.java @@ -66,7 +66,7 @@ public class HistoryPage { } private static List getWebHistory(Context context) { - HistoryDatabase databaseHandler = HistoryDatabase.getInstance(); + HistoryDatabase databaseHandler = BrowserApp.getAppComponent().getHistoryDatabase(); return databaseHandler.getLastHundredItems(); } } diff --git a/app/src/main/java/acr/browser/lightning/constant/StartPage.java b/app/src/main/java/acr/browser/lightning/constant/StartPage.java index 35b5453..f640023 100644 --- a/app/src/main/java/acr/browser/lightning/constant/StartPage.java +++ b/app/src/main/java/acr/browser/lightning/constant/StartPage.java @@ -57,11 +57,12 @@ public class StartPage { StringBuilder homepageBuilder = new StringBuilder(StartPage.HEAD); String icon; String searchUrl; - switch (PreferenceManager.getInstance().getSearchChoice()) { + final PreferenceManager preferenceManager = BrowserApp.getAppComponent().getPreferenceManager(); + switch (preferenceManager.getSearchChoice()) { case 0: // CUSTOM SEARCH icon = "file:///android_asset/lightning.png"; - searchUrl = PreferenceManager.getInstance().getSearchUrl(); + searchUrl = preferenceManager.getSearchUrl(); break; case 1: // GOOGLE_SEARCH; 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 73c8e7f..a454386 100644 --- a/app/src/main/java/acr/browser/lightning/controller/BrowserController.java +++ b/app/src/main/java/acr/browser/lightning/controller/BrowserController.java @@ -23,18 +23,12 @@ public interface BrowserController { void openFileChooser(ValueCallback uploadMsg); - void updateTabs(); - - void onLongPress(); +// void onLongPress(); void onShowCustomView(View view, CustomViewCallback callback); void onHideCustomView(); - Bitmap getDefaultVideoPoster(); - - View getVideoLoadingProgressView(); - void onCreateWindow(Message resultMsg); void onCloseWindow(LightningView view); @@ -43,16 +37,12 @@ public interface BrowserController { void showActionBar(); - void longClickPage(String url); - - void openBookmarkPage(WebView view); + // void longClickPage(String url); void showFileChooser(ValueCallback filePathCallback); void closeEmptyTab(); - boolean proxyIsNotReady(); - // void updateBookmarkIndicator(String url); } 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 82801c5..36498fb 100644 --- a/app/src/main/java/acr/browser/lightning/database/HistoryDatabase.java +++ b/app/src/main/java/acr/browser/lightning/database/HistoryDatabase.java @@ -12,9 +12,13 @@ import android.database.sqlite.SQLiteOpenHelper; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; +import javax.inject.Singleton; + import acr.browser.lightning.R; import acr.browser.lightning.app.BrowserApp; +@Singleton public class HistoryDatabase extends SQLiteOpenHelper { // All Static variables @@ -39,14 +43,8 @@ public class HistoryDatabase extends SQLiteOpenHelper { private boolean mLock; - public static HistoryDatabase getInstance() { - if (mInstance == null || mInstance.isClosed()) { - mInstance = new HistoryDatabase(BrowserApp.getAppContext()); - } - return mInstance; - } - - private HistoryDatabase(Context context) { + @Inject + public HistoryDatabase(Context context) { super(context.getApplicationContext(), DATABASE_NAME, null, DATABASE_VERSION); mDatabase = this.getWritableDatabase(); } @@ -91,7 +89,7 @@ public class HistoryDatabase extends SQLiteOpenHelper { } private void openIfNecessary() { - if (mDatabase == null) { + if (isClosed()) { mDatabase = this.getWritableDatabase(); } } diff --git a/app/src/main/java/acr/browser/lightning/dialog/BookmarksDialogBuilder.java b/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java similarity index 60% rename from app/src/main/java/acr/browser/lightning/dialog/BookmarksDialogBuilder.java rename to app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java index ec8282c..923472a 100644 --- a/app/src/main/java/acr/browser/lightning/dialog/BookmarksDialogBuilder.java +++ b/app/src/main/java/acr/browser/lightning/dialog/LightningDialogBuilder.java @@ -1,8 +1,13 @@ package acr.browser.lightning.dialog; +import android.app.Activity; +import android.content.ClipData; +import android.content.ClipboardManager; import android.content.Context; import android.content.DialogInterface; import android.net.Uri; +import android.os.Build; +import android.support.annotation.NonNull; import android.support.v7.app.AlertDialog; import android.view.View; import android.widget.ArrayAdapter; @@ -19,24 +24,32 @@ import javax.inject.Inject; import acr.browser.lightning.R; import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.bus.BookmarkEvents; +import acr.browser.lightning.bus.BrowserEvents; import acr.browser.lightning.constant.Constants; +import acr.browser.lightning.constant.HistoryPage; import acr.browser.lightning.database.BookmarkManager; +import acr.browser.lightning.database.HistoryDatabase; import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.utils.Utils; /** + * TODO Rename this class it doesn't build dialogs only for bookmarks + * * Created by Stefano Pacifici on 02/09/15, based on Anthony C. Restaino's code. */ -public class BookmarksDialogBuilder { +public class LightningDialogBuilder { @Inject BookmarkManager bookmarkManager; + @Inject + HistoryDatabase mHistoryDatabase; + @Inject Bus eventBus; @Inject - public BookmarksDialogBuilder() { + public LightningDialogBuilder() { BrowserApp.getAppComponent().inject(this); } @@ -46,7 +59,7 @@ public class BookmarksDialogBuilder { * @param context used to show the dialog * @param url the long pressed url */ - public void showLongPressedDialogForUrl(final Context context, final String url) { + public void showLongPressedDialogForBookmarkUrl(final Context context, final String url) { final HistoryItem item; if (url.startsWith(Constants.FILE) && url.endsWith(Constants.BOOKMARKS_FILENAME)) { // TODO hacky, make a better bookmark mechanism in the future @@ -65,19 +78,19 @@ public class BookmarksDialogBuilder { if (item.isFolder()) { showBookmarkFolderLongPressedDialog(context, item); } else { - showLongPressedDialogForUrl(context, item); + showLongPressedDialogForBookmarkUrl(context, item); } } } - public void showLongPressedDialogForUrl(final Context context, final HistoryItem item) { + public void showLongPressedDialogForBookmarkUrl(final Context context, final HistoryItem item) { final DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch (which) { case DialogInterface.BUTTON_POSITIVE: - eventBus.post(new BookmarkEvents.AsNewTab(item)); + eventBus.post(new BrowserEvents.OpenUrlInNewTab(item.getUrl())); break; case DialogInterface.BUTTON_NEGATIVE: if (bookmarkManager.deleteBookmark(item)) { @@ -197,4 +210,103 @@ public class BookmarksDialogBuilder { }); editFolderDialog.show(); } + + public void showLongPressedHistoryLinkDialog(final Context context, final String url) { + DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case DialogInterface.BUTTON_POSITIVE: + eventBus.post(new BrowserEvents.OpenUrlInNewTab(url)); + break; + case DialogInterface.BUTTON_NEGATIVE: + mHistoryDatabase.deleteHistoryItem(url); + // openHistory(); + eventBus.post(new BrowserEvents.OpenUrlInCurrentTab(HistoryPage.getHistoryPage(context))); + break; + case DialogInterface.BUTTON_NEUTRAL: + eventBus.post(new BrowserEvents.OpenUrlInCurrentTab(url)); + break; + default: + break; + } + } + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.action_history) + .setMessage(R.string.dialog_history_long_press) + .setCancelable(true) + .setPositiveButton(R.string.action_new_tab, dialogClickListener) + .setNegativeButton(R.string.action_delete, dialogClickListener) + .setNeutralButton(R.string.action_open, dialogClickListener) + .show(); + } + + // TODO There should be a way in which we do not need an activity reference to dowload a file + public void showLongPressImageDialog(@NonNull final Activity activity, @NonNull final String url, + @NonNull final String userAgent) { + DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case DialogInterface.BUTTON_POSITIVE: + eventBus.post(new BrowserEvents.OpenUrlInNewTab(url)); + break; + case DialogInterface.BUTTON_NEGATIVE: + eventBus.post(new BrowserEvents.OpenUrlInCurrentTab(url)); + break; + case DialogInterface.BUTTON_NEUTRAL: + if (Build.VERSION.SDK_INT > 8) { + // Should be better to send an event on the bus here + Utils.downloadFile(activity, url, + userAgent, "attachment"); + } + break; + } + } + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setTitle(url.replace(Constants.HTTP, "")) + .setCancelable(true) + .setMessage(R.string.dialog_image) + .setPositiveButton(R.string.action_new_tab, dialogClickListener) + .setNegativeButton(R.string.action_open, dialogClickListener) + .setNeutralButton(R.string.action_download, dialogClickListener) + .show(); + } + + public void showLongPressLinkDialog(final Context context, final String url) { + DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case DialogInterface.BUTTON_POSITIVE: + eventBus.post(new BrowserEvents.OpenUrlInNewTab(url)); + break; + + case DialogInterface.BUTTON_NEGATIVE: + eventBus.post(new BrowserEvents.OpenUrlInCurrentTab(url)); + break; + + case DialogInterface.BUTTON_NEUTRAL: + ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("label", url); + clipboard.setPrimaryClip(clip); + break; + } + } + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(context); // dialog + builder.setTitle(url) + .setCancelable(true) + .setMessage(R.string.dialog_link) + .setPositiveButton(R.string.action_new_tab, dialogClickListener) + .setNegativeButton(R.string.action_open, dialogClickListener) + .setNeutralButton(R.string.action_copy, dialogClickListener) + .show(); + } + } 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 82d25aa..e182811 100644 --- a/app/src/main/java/acr/browser/lightning/download/DownloadHandler.java +++ b/app/src/main/java/acr/browser/lightning/download/DownloadHandler.java @@ -3,10 +3,8 @@ */ package acr.browser.lightning.download; -import android.app.Activity; import android.app.DownloadManager; import android.content.ActivityNotFoundException; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; @@ -19,13 +17,17 @@ import android.util.Log; import android.webkit.CookieManager; import android.webkit.URLUtil; +import com.squareup.otto.Bus; + import java.io.File; import java.io.IOException; +import acr.browser.lightning.BuildConfig; import acr.browser.lightning.R; +import acr.browser.lightning.activity.MainActivity; +import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.bus.BrowserEvents; import acr.browser.lightning.constant.Constants; -import acr.browser.lightning.preference.PreferenceManager; -import acr.browser.lightning.utils.Utils; /** * Handle download requests @@ -44,13 +46,13 @@ public class DownloadHandler { * 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 context The context in which the download was requested. + * @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, + public static void onDownloadStart(Context context, 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. @@ -61,18 +63,17 @@ public class DownloadHandler { 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, + ResolveInfo info = context.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)) { + if (BuildConfig.APPLICATION_ID.equals(info.activityInfo.packageName) + || MainActivity.class.getName().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); + context.startActivity(intent); return; } catch (ActivityNotFoundException ex) { // Best behavior is to fall back to a download in this @@ -81,8 +82,7 @@ public class DownloadHandler { } } } - onDownloadStartNoStream(activity, url, userAgent, contentDisposition, mimetype - ); + onDownloadStartNoStream(context, url, userAgent, contentDisposition, mimetype); } // This is to work around the fact that java.net.URI throws Exceptions @@ -119,17 +119,17 @@ public class DownloadHandler { * 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 context The context in which the download is requested. * @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, + private static void onDownloadStartNoStream(final Context context, String url, String userAgent, String contentDisposition, String mimetype) { - - String filename = URLUtil.guessFileName(url, contentDisposition, mimetype); + final Bus eventBus = BrowserApp.getAppComponent().getBus(); + final String filename = URLUtil.guessFileName(url, contentDisposition, mimetype); // Check to see if we have an SDCard String status = Environment.getExternalStorageState(); @@ -139,14 +139,14 @@ public class DownloadHandler { // 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); + msg = context.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); + msg = context.getString(R.string.download_no_sdcard_dlg_msg, filename); title = R.string.download_no_sdcard_dlg_title; } - new AlertDialog.Builder(activity).setTitle(title) + new AlertDialog.Builder(context).setTitle(title) .setIcon(android.R.drawable.ic_dialog_alert).setMessage(msg) .setPositiveButton(R.string.action_ok, null).show(); return; @@ -162,7 +162,7 @@ public class DownloadHandler { // This only happens for very bad urls, we want to catch the // exception here Log.e(TAG, "Exception while trying to parse url '" + url + '\'', e); - Utils.showSnackbar(activity, R.string.problem_download); + eventBus.post(new BrowserEvents.ShowSnackBarMessage(R.string.problem_download)); return; } @@ -172,7 +172,7 @@ public class DownloadHandler { try { request = new DownloadManager.Request(uri); } catch (IllegalArgumentException e) { - Utils.showSnackbar(activity, R.string.cannot_download); + eventBus.post(new BrowserEvents.ShowSnackBarMessage(R.string.cannot_download)); return; } request.setMimeType(mimetype); @@ -180,7 +180,7 @@ public class DownloadHandler { // or, should it be set to one of several Environment.DIRECTORY* dirs // depending on mimetype? - String location = PreferenceManager.getInstance().getDownloadDirectory(); + String location = BrowserApp.getAppComponent().getPreferenceManager().getDownloadDirectory(); Uri downloadFolder; if (location != null) { location = addNecessarySlashes(location); @@ -188,18 +188,18 @@ public class DownloadHandler { } else { location = addNecessarySlashes(DEFAULT_DOWNLOAD_PATH); downloadFolder = Uri.parse(location); - PreferenceManager.getInstance().setDownloadDirectory(location); + BrowserApp.getAppComponent().getPreferenceManager().setDownloadDirectory(location); } File dir = new File(downloadFolder.getPath()); if (!dir.isDirectory() && !dir.mkdirs()) { // Cannot make the directory - Utils.showSnackbar(activity, R.string.problem_location_download); + eventBus.post(new BrowserEvents.ShowSnackBarMessage(R.string.problem_location_download)); return; } if (!isWriteAccessAvailable(downloadFolder)) { - Utils.showSnackbar(activity, R.string.problem_location_download); + eventBus.post(new BrowserEvents.ShowSnackBarMessage(R.string.problem_location_download)); return; } request.setDestinationUri(Uri.parse(Constants.FILE + location + filename)); @@ -219,9 +219,9 @@ public class DownloadHandler { } // 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(); + new FetchUrlMimeType(context, request, addressString, cookies, userAgent).start(); } else { - final DownloadManager manager = (DownloadManager) activity + final DownloadManager manager = (DownloadManager) context .getSystemService(Context.DOWNLOAD_SERVICE); new Thread() { @Override @@ -231,15 +231,16 @@ public class DownloadHandler { } catch (IllegalArgumentException e) { // Probably got a bad URL or something e.printStackTrace(); - Utils.showSnackbar(activity, R.string.cannot_download); + eventBus.post(new BrowserEvents.ShowSnackBarMessage(R.string.cannot_download)); } catch (SecurityException e) { // TODO write a download utility that downloads files rather than rely on the system // because the system can only handle Environment.getExternal... as a path - Utils.showSnackbar(activity, R.string.problem_location_download); + eventBus.post(new BrowserEvents.ShowSnackBarMessage(R.string.problem_location_download)); } } }.start(); - Utils.showSnackbar(activity, activity.getString(R.string.download_pending) + ' ' + filename); + eventBus.post(new BrowserEvents.ShowSnackBarMessage( + context.getString(R.string.download_pending) + ' ' + filename)); } } @@ -320,12 +321,12 @@ public class DownloadHandler { } catch (IOException ignored) { return false; } - } + } public static String addNecessarySlashes(String originalPath) { if (originalPath == null || originalPath.length() == 0) { return "/"; - } + } if (originalPath.charAt(originalPath.length() - 1) != '/') { originalPath = originalPath + '/'; } 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 39da66f..80d5995 100644 --- a/app/src/main/java/acr/browser/lightning/download/FetchUrlMimeType.java +++ b/app/src/main/java/acr/browser/lightning/download/FetchUrlMimeType.java @@ -3,18 +3,21 @@ */ package acr.browser.lightning.download; -import android.app.Activity; import android.app.DownloadManager; import android.content.Context; import android.os.Environment; import android.webkit.MimeTypeMap; import android.webkit.URLUtil; +import com.squareup.otto.Bus; + import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import acr.browser.lightning.R; +import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.bus.BrowserEvents; import acr.browser.lightning.utils.Utils; /** @@ -27,7 +30,7 @@ import acr.browser.lightning.utils.Utils; */ class FetchUrlMimeType extends Thread { - private final Activity mActivity; + private final Context mContext; private final DownloadManager.Request mRequest; @@ -37,9 +40,9 @@ class FetchUrlMimeType extends Thread { private final String mUserAgent; - public FetchUrlMimeType(Activity activity, DownloadManager.Request request, String uri, + public FetchUrlMimeType(Context context, DownloadManager.Request request, String uri, String cookies, String userAgent) { - mActivity = activity; + mContext = context; mRequest = request; mUri = uri; mCookies = cookies; @@ -50,6 +53,7 @@ class FetchUrlMimeType extends Thread { public void run() { // User agent is likely to be null, though the AndroidHttpClient // seems ok with that. + final Bus evenBus = BrowserApp.getAppComponent().getBus(); String mimeType = null; String contentDisposition = null; HttpURLConnection connection = null; @@ -101,9 +105,9 @@ class FetchUrlMimeType extends Thread { } // Start the download - DownloadManager manager = (DownloadManager) mActivity + DownloadManager manager = (DownloadManager) mContext .getSystemService(Context.DOWNLOAD_SERVICE); manager.enqueue(mRequest); - Utils.showSnackbar(mActivity, mActivity.getString(R.string.download_pending) + ' ' + filename); + evenBus.post(new BrowserEvents.ShowSnackBarMessage(mContext.getString(R.string.download_pending) + ' ' + filename)); } } 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 72f3ab8..6d6b7a1 100644 --- a/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java +++ b/app/src/main/java/acr/browser/lightning/download/LightningDownloadListener.java @@ -3,7 +3,7 @@ */ package acr.browser.lightning.download; -import android.app.Activity; +import android.content.Context; import android.content.DialogInterface; import android.support.v7.app.AlertDialog; import android.util.Log; @@ -15,10 +15,10 @@ import acr.browser.lightning.constant.Constants; public class LightningDownloadListener implements DownloadListener { - private final Activity mActivity; + private final Context mContext; - public LightningDownloadListener(Activity activity) { - mActivity = activity; + public LightningDownloadListener(Context context) { + mContext = context; } @Override @@ -30,7 +30,7 @@ public class LightningDownloadListener implements DownloadListener { public void onClick(DialogInterface dialog, int which) { switch (which) { case DialogInterface.BUTTON_POSITIVE: - DownloadHandler.onDownloadStart(mActivity, url, userAgent, + DownloadHandler.onDownloadStart(mContext, url, userAgent, contentDisposition, mimetype); break; @@ -40,12 +40,12 @@ public class LightningDownloadListener implements DownloadListener { } }; - AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); // dialog + AlertDialog.Builder builder = new AlertDialog.Builder(mContext); // dialog builder.setTitle(fileName) - .setMessage(mActivity.getResources().getString(R.string.dialog_download)) - .setPositiveButton(mActivity.getResources().getString(R.string.action_download), + .setMessage(mContext.getResources().getString(R.string.dialog_download)) + .setPositiveButton(mContext.getResources().getString(R.string.action_download), dialogClickListener) - .setNegativeButton(mActivity.getResources().getString(R.string.action_cancel), + .setNegativeButton(mContext.getResources().getString(R.string.action_cancel), dialogClickListener).show(); Log.i(Constants.TAG, "Downloading" + fileName); 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 4f4b7a6..a7a4b1c 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/AdvancedSettingsFragment.java +++ b/app/src/main/java/acr/browser/lightning/fragment/AdvancedSettingsFragment.java @@ -8,7 +8,6 @@ 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 java.util.Arrays; @@ -16,9 +15,8 @@ import java.util.List; import acr.browser.lightning.R; import acr.browser.lightning.constant.Constants; -import acr.browser.lightning.preference.PreferenceManager; -public class AdvancedSettingsFragment extends PreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { +public class AdvancedSettingsFragment extends LightningPreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { private static final String SETTINGS_NEWWINDOW = "allow_new_window"; private static final String SETTINGS_ENABLECOOKIES = "allow_cookies"; @@ -29,7 +27,6 @@ public class AdvancedSettingsFragment extends PreferenceFragment implements Pref private static final String SETTINGS_TEXTENCODING = "text_encoding"; private Activity mActivity; - private PreferenceManager mPreferences; private CheckBoxPreference cbAllowPopups, cbenablecookies, cbcookiesInkognito, cbrestoreTabs; private Preference renderingmode, urlcontent, textEncoding; private CharSequence[] mUrlOptions; @@ -46,8 +43,6 @@ public class AdvancedSettingsFragment extends PreferenceFragment implements Pref } private void initPrefs() { - // mPreferences storage - mPreferences = PreferenceManager.getInstance(); renderingmode = findPreference(SETTINGS_RENDERINGMODE); textEncoding = findPreference(SETTINGS_TEXTENCODING); @@ -65,7 +60,7 @@ public class AdvancedSettingsFragment extends PreferenceFragment implements Pref cbcookiesInkognito.setOnPreferenceChangeListener(this); cbrestoreTabs.setOnPreferenceChangeListener(this); - switch (mPreferences.getRenderingMode()) { + switch (mPreferenceManager.getRenderingMode()) { case 0: renderingmode.setSummary(getString(R.string.name_normal)); break; @@ -80,16 +75,16 @@ public class AdvancedSettingsFragment extends PreferenceFragment implements Pref break; } - textEncoding.setSummary(mPreferences.getTextEncoding()); + textEncoding.setSummary(mPreferenceManager.getTextEncoding()); mUrlOptions = getResources().getStringArray(R.array.url_content_array); - int option = mPreferences.getUrlBoxContentChoice(); + int option = mPreferenceManager.getUrlBoxContentChoice(); urlcontent.setSummary(mUrlOptions[option]); - cbAllowPopups.setChecked(mPreferences.getPopupsEnabled()); - cbenablecookies.setChecked(mPreferences.getCookiesEnabled()); - cbcookiesInkognito.setChecked(mPreferences.getIncognitoCookiesEnabled()); - cbrestoreTabs.setChecked(mPreferences.getRestoreLostTabsEnabled()); + cbAllowPopups.setChecked(mPreferenceManager.getPopupsEnabled()); + cbenablecookies.setChecked(mPreferenceManager.getCookiesEnabled()); + cbcookiesInkognito.setChecked(mPreferenceManager.getIncognitoCookiesEnabled()); + cbrestoreTabs.setChecked(mPreferenceManager.getRestoreLostTabsEnabled()); } @Override @@ -114,19 +109,19 @@ public class AdvancedSettingsFragment extends PreferenceFragment implements Pref // switch preferences switch (preference.getKey()) { case SETTINGS_NEWWINDOW: - mPreferences.setPopupsEnabled((Boolean) newValue); + mPreferenceManager.setPopupsEnabled((Boolean) newValue); cbAllowPopups.setChecked((Boolean) newValue); return true; case SETTINGS_ENABLECOOKIES: - mPreferences.setCookiesEnabled((Boolean) newValue); + mPreferenceManager.setCookiesEnabled((Boolean) newValue); cbenablecookies.setChecked((Boolean) newValue); return true; case SETTINGS_COOKIESINKOGNITO: - mPreferences.setIncognitoCookiesEnabled((Boolean) newValue); + mPreferenceManager.setIncognitoCookiesEnabled((Boolean) newValue); cbcookiesInkognito.setChecked((Boolean) newValue); return true; case SETTINGS_RESTORETABS: - mPreferences.setRestoreLostTabsEnabled((Boolean) newValue); + mPreferenceManager.setRestoreLostTabsEnabled((Boolean) newValue); cbrestoreTabs.setChecked((Boolean) newValue); return true; default: @@ -142,12 +137,12 @@ public class AdvancedSettingsFragment extends PreferenceFragment implements Pref mActivity.getString(R.string.name_grayscale), mActivity.getString(R.string.name_inverted_grayscale)}; - int n = mPreferences.getRenderingMode(); + int n = mPreferenceManager.getRenderingMode(); picker.setSingleChoiceItems(chars, n, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - mPreferences.setRenderingMode(which); + mPreferenceManager.setRenderingMode(which); switch (which) { case 0: renderingmode.setSummary(getString(R.string.name_normal)); @@ -172,12 +167,12 @@ public class AdvancedSettingsFragment extends PreferenceFragment implements Pref AlertDialog.Builder picker = new AlertDialog.Builder(mActivity); picker.setTitle(getResources().getString(R.string.text_encoding)); final List textEncodingList = Arrays.asList(Constants.TEXT_ENCODINGS); - int n = textEncodingList.indexOf(mPreferences.getTextEncoding()); + int n = textEncodingList.indexOf(mPreferenceManager.getTextEncoding()); picker.setSingleChoiceItems(Constants.TEXT_ENCODINGS, n, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - mPreferences.setTextEncoding(Constants.TEXT_ENCODINGS[which]); + mPreferenceManager.setTextEncoding(Constants.TEXT_ENCODINGS[which]); textEncoding.setSummary(Constants.TEXT_ENCODINGS[which]); } }); @@ -189,12 +184,12 @@ public class AdvancedSettingsFragment extends PreferenceFragment implements Pref AlertDialog.Builder picker = new AlertDialog.Builder(mActivity); picker.setTitle(getResources().getString(R.string.url_contents)); - int n = mPreferences.getUrlBoxContentChoice(); + int n = mPreferenceManager.getUrlBoxContentChoice(); picker.setSingleChoiceItems(mUrlOptions, n, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - mPreferences.setUrlBoxContentChoice(which); + mPreferenceManager.setUrlBoxContentChoice(which); if (which < mUrlOptions.length) { urlcontent.setSummary(mUrlOptions[which]); } diff --git a/app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java b/app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java index 80aa3f9..28f9a4f 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java +++ b/app/src/main/java/acr/browser/lightning/fragment/BookmarksFragment.java @@ -1,6 +1,5 @@ package acr.browser.lightning.fragment; -import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.PorterDuff; @@ -43,7 +42,7 @@ import acr.browser.lightning.bus.BookmarkEvents; import acr.browser.lightning.bus.BrowserEvents; import acr.browser.lightning.database.BookmarkManager; import acr.browser.lightning.database.HistoryItem; -import acr.browser.lightning.dialog.BookmarksDialogBuilder; +import acr.browser.lightning.dialog.LightningDialogBuilder; import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.async.ImageDownloadTask; import acr.browser.lightning.utils.ThemeUtils; @@ -53,6 +52,10 @@ import acr.browser.lightning.utils.ThemeUtils; */ public class BookmarksFragment extends Fragment implements View.OnClickListener, View.OnLongClickListener { + private final static String TAG = BookmarksFragment.class.getSimpleName(); + + public final static String INCOGNITO_MODE = TAG + ".INCOGNITO_MODE"; + // Managers @Inject BookmarkManager mBookmarkManager; @@ -63,7 +66,10 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener, // Dialog builder @Inject - BookmarksDialogBuilder mBookmarksDialogBuilder; + LightningDialogBuilder mBookmarksDialogBuilder; + + @Inject + PreferenceManager mPreferenceManager; // Adapter private BookmarkViewAdapter mBookmarkAdapter; @@ -81,6 +87,8 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener, // Colors private int mIconColor, mScrollIndex; + private boolean mIsIncognito; + // Init asynchronously the bookmark manager private final Runnable mInitBookmarkManager = new Runnable() { @Override @@ -96,6 +104,14 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener, public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); BrowserApp.getAppComponent().inject(this); + final Bundle arguments = getArguments(); + final Context context = getContext(); + mIsIncognito = arguments.getBoolean(INCOGNITO_MODE, false); + boolean darkTheme = mPreferenceManager.getUseTheme() != 0 || mIsIncognito; + mWebpageBitmap = ThemeUtils.getThemedBitmap(context, R.drawable.ic_webpage, darkTheme); + mFolderBitmap = ThemeUtils.getThemedBitmap(context, R.drawable.ic_folder, darkTheme); + mIconColor = darkTheme ? ThemeUtils.getIconDarkThemeColor(context) : + ThemeUtils.getIconLightThemeColor(context); } // Handle bookmark click @@ -107,7 +123,7 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener, mScrollIndex = mBookmarksListView.getFirstVisiblePosition(); setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(item.getTitle(), true), true); } else { - mEventBus.post(new BookmarkEvents.Clicked(item)); + mEventBus.post(new BrowserEvents.OpenUrlInCurrentTab(item.getUrl())); } } }; @@ -135,6 +151,7 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener, mBookmarksListView.setOnItemClickListener(mItemClickListener); mBookmarksListView.setOnItemLongClickListener(mItemLongClickListener); mBookmarkTitleImage = (ImageView) view.findViewById(R.id.starIcon); + mBookmarkTitleImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); mBookmarkImage = (ImageView) view.findViewById(R.id.icon_star); final View backView = view.findViewById(R.id.bookmark_back_button); backView.setOnClickListener(new View.OnClickListener() { @@ -154,20 +171,6 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener, return view; } - @Override - public void onActivityCreated(@Nullable Bundle savedInstanceState) { - // TODO remove dependency on BrowserActivity - super.onActivityCreated(savedInstanceState); - final Activity activity = getActivity(); - final PreferenceManager preferenceManager = PreferenceManager.getInstance(); - boolean darkTheme = preferenceManager.getUseTheme() != 0 || ((BrowserActivity) activity).isIncognito(); - mWebpageBitmap = ThemeUtils.getThemedBitmap(activity, R.drawable.ic_webpage, darkTheme); - mFolderBitmap = ThemeUtils.getThemedBitmap(activity, R.drawable.ic_folder, darkTheme); - mIconColor = darkTheme ? ThemeUtils.getIconDarkThemeColor(activity) : - ThemeUtils.getIconLightThemeColor(activity); - mBookmarkTitleImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); - } - @Override public void onStart() { super.onStart(); @@ -229,8 +232,8 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener, if (event.item.isFolder()) { setBookmarkDataSet(mBookmarkManager.getBookmarksFromFolder(null, true), false); } else { - mBookmarkAdapter.notifyDataSetChanged(); - } + mBookmarkAdapter.notifyDataSetChanged(); + } } private void setBookmarkDataSet(List items, boolean animate) { @@ -295,7 +298,7 @@ public class BookmarksFragment extends Fragment implements View.OnClickListener, if (item.isFolder()) { mBookmarksDialogBuilder.showBookmarkFolderLongPressedDialog(getContext(), item); } else { - mBookmarksDialogBuilder.showLongPressedDialogForUrl(getContext(), item); + mBookmarksDialogBuilder.showLongPressLinkDialog(getContext(), item.getUrl()); } } 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 23180e9..a5ddaa1 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/DisplaySettingsFragment.java +++ b/app/src/main/java/acr/browser/lightning/fragment/DisplaySettingsFragment.java @@ -8,7 +8,6 @@ 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 android.view.Gravity; import android.view.LayoutInflater; @@ -19,9 +18,8 @@ import android.widget.SeekBar; import android.widget.TextView; import acr.browser.lightning.R; -import acr.browser.lightning.preference.PreferenceManager; -public class DisplaySettingsFragment extends PreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { +public class DisplaySettingsFragment extends LightningPreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { private static final String SETTINGS_HIDESTATUSBAR = "fullScreenOption"; private static final String SETTINGS_FULLSCREEN = "fullscreen"; @@ -38,7 +36,6 @@ public class DisplaySettingsFragment extends PreferenceFragment implements Prefe private static final float XSMALL = 10.0f; private Activity mActivity; - private PreferenceManager mPreferences; private CheckBoxPreference cbstatus, cbfullscreen, cbviewport, cboverview, cbreflow; private Preference theme; private String[] mThemeOptions; @@ -57,9 +54,8 @@ public class DisplaySettingsFragment extends PreferenceFragment implements Prefe private void initPrefs() { // mPreferences storage - mPreferences = PreferenceManager.getInstance(); mThemeOptions = this.getResources().getStringArray(R.array.themes); - mCurrentTheme = mPreferences.getUseTheme(); + mCurrentTheme = mPreferenceManager.getUseTheme(); theme = findPreference(SETTINGS_THEME); Preference textsize = findPreference(SETTINGS_TEXTSIZE); @@ -77,13 +73,13 @@ public class DisplaySettingsFragment extends PreferenceFragment implements Prefe cboverview.setOnPreferenceChangeListener(this); cbreflow.setOnPreferenceChangeListener(this); - cbstatus.setChecked(mPreferences.getHideStatusBarEnabled()); - cbfullscreen.setChecked(mPreferences.getFullScreenEnabled()); - cbviewport.setChecked(mPreferences.getUseWideViewportEnabled()); - cboverview.setChecked(mPreferences.getOverviewModeEnabled()); - cbreflow.setChecked(mPreferences.getTextReflowEnabled()); + cbstatus.setChecked(mPreferenceManager.getHideStatusBarEnabled()); + cbfullscreen.setChecked(mPreferenceManager.getFullScreenEnabled()); + cbviewport.setChecked(mPreferenceManager.getUseWideViewportEnabled()); + cboverview.setChecked(mPreferenceManager.getOverviewModeEnabled()); + cbreflow.setChecked(mPreferenceManager.getTextReflowEnabled()); - theme.setSummary(mThemeOptions[mPreferences.getUseTheme()]); + theme.setSummary(mThemeOptions[mPreferenceManager.getUseTheme()]); } @Override @@ -105,23 +101,23 @@ public class DisplaySettingsFragment extends PreferenceFragment implements Prefe // switch preferences switch (preference.getKey()) { case SETTINGS_HIDESTATUSBAR: - mPreferences.setHideStatusBarEnabled((Boolean) newValue); + mPreferenceManager.setHideStatusBarEnabled((Boolean) newValue); cbstatus.setChecked((Boolean) newValue); return true; case SETTINGS_FULLSCREEN: - mPreferences.setFullScreenEnabled((Boolean) newValue); + mPreferenceManager.setFullScreenEnabled((Boolean) newValue); cbfullscreen.setChecked((Boolean) newValue); return true; case SETTINGS_VIEWPORT: - mPreferences.setUseWideViewportEnabled((Boolean) newValue); + mPreferenceManager.setUseWideViewportEnabled((Boolean) newValue); cbviewport.setChecked((Boolean) newValue); return true; case SETTINGS_OVERVIEWMODE: - mPreferences.setOverviewModeEnabled((Boolean) newValue); + mPreferenceManager.setOverviewModeEnabled((Boolean) newValue); cboverview.setChecked((Boolean) newValue); return true; case SETTINGS_REFLOW: - mPreferences.setTextReflowEnabled((Boolean) newValue); + mPreferenceManager.setTextReflowEnabled((Boolean) newValue); cbreflow.setChecked((Boolean) newValue); return true; default: @@ -142,14 +138,14 @@ public class DisplaySettingsFragment extends PreferenceFragment implements Prefe bar.setOnSeekBarChangeListener(new TextSeekBarListener(sample)); final int MAX = 5; bar.setMax(MAX); - bar.setProgress(MAX - mPreferences.getTextSize()); + bar.setProgress(MAX - mPreferenceManager.getTextSize()); builder.setView(view); builder.setTitle(R.string.title_text_size); builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface arg0, int arg1) { - mPreferences.setTextSize(MAX - bar.getProgress()); + mPreferenceManager.setTextSize(MAX - bar.getProgress()); } }); @@ -179,12 +175,12 @@ public class DisplaySettingsFragment extends PreferenceFragment implements Prefe AlertDialog.Builder picker = new AlertDialog.Builder(mActivity); picker.setTitle(getResources().getString(R.string.theme)); - int n = mPreferences.getUseTheme(); + int n = mPreferenceManager.getUseTheme(); picker.setSingleChoiceItems(mThemeOptions, n, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - mPreferences.setUseTheme(which); + mPreferenceManager.setUseTheme(which); if (which < mThemeOptions.length) { theme.setSummary(mThemeOptions[which]); } @@ -195,7 +191,7 @@ public class DisplaySettingsFragment extends PreferenceFragment implements Prefe @Override public void onClick(DialogInterface dialog, int which) { - if (mCurrentTheme != mPreferences.getUseTheme()) { + if (mCurrentTheme != mPreferenceManager.getUseTheme()) { getActivity().onBackPressed(); } } @@ -203,7 +199,7 @@ public class DisplaySettingsFragment extends PreferenceFragment implements Prefe picker.setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { - if (mCurrentTheme != mPreferences.getUseTheme()) { + if (mCurrentTheme != mPreferenceManager.getUseTheme()) { 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 aa7080f..37d53de 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/GeneralSettingsFragment.java +++ b/app/src/main/java/acr/browser/lightning/fragment/GeneralSettingsFragment.java @@ -9,7 +9,6 @@ import android.os.Bundle; import android.os.Environment; import android.preference.CheckBoxPreference; import android.preference.Preference; -import android.preference.PreferenceFragment; import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.text.Editable; @@ -28,7 +27,7 @@ import acr.browser.lightning.utils.ProxyUtils; import acr.browser.lightning.utils.ThemeUtils; import acr.browser.lightning.utils.Utils; -public class GeneralSettingsFragment extends PreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { +public class GeneralSettingsFragment extends LightningPreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { private static final String SETTINGS_PROXY = "proxy"; private static final String SETTINGS_FLASH = "cb_flash"; @@ -45,7 +44,6 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe private Activity mActivity; private static final int API = android.os.Build.VERSION.SDK_INT; - private PreferenceManager mPreferences; private CharSequence[] mProxyChoices; private Preference proxy, useragent, downloadloc, home, searchengine; private String mDownloadLocation; @@ -65,9 +63,6 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe } private void initPrefs() { - // mPreferences storage - mPreferences = PreferenceManager.getInstance(); - proxy = findPreference(SETTINGS_PROXY); useragent = findPreference(SETTINGS_USERAGENT); downloadloc = findPreference(SETTINGS_DOWNLOAD); @@ -95,23 +90,23 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe cbgooglesuggest.setOnPreferenceChangeListener(this); cbDrawerTabs.setOnPreferenceChangeListener(this); - mAgentChoice = mPreferences.getUserAgentChoice(); - mHomepage = mPreferences.getHomepage(); - mDownloadLocation = mPreferences.getDownloadDirectory(); + mAgentChoice = mPreferenceManager.getUserAgentChoice(); + mHomepage = mPreferenceManager.getHomepage(); + mDownloadLocation = mPreferenceManager.getDownloadDirectory(); mProxyChoices = getResources().getStringArray(R.array.proxy_choices_array); - int choice = mPreferences.getProxyChoice(); + int choice = mPreferenceManager.getProxyChoice(); if (choice == Constants.PROXY_MANUAL) { - proxy.setSummary(mPreferences.getProxyHost() + ':' + mPreferences.getProxyPort()); + proxy.setSummary(mPreferenceManager.getProxyHost() + ':' + mPreferenceManager.getProxyPort()); } else { proxy.setSummary(mProxyChoices[choice]); } if (API >= 19) { - mPreferences.setFlashSupport(0); + mPreferenceManager.setFlashSupport(0); } - setSearchEngineSummary(mPreferences.getSearchChoice()); + setSearchEngineSummary(mPreferenceManager.getSearchChoice()); downloadloc.setSummary(mDownloadLocation); @@ -139,9 +134,9 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe useragent.setSummary(getResources().getString(R.string.agent_custom)); } - int flashNum = mPreferences.getFlashSupport(); - boolean imagesBool = mPreferences.getBlockImagesEnabled(); - boolean enableJSBool = mPreferences.getJavaScriptEnabled(); + int flashNum = mPreferenceManager.getFlashSupport(); + boolean imagesBool = mPreferenceManager.getBlockImagesEnabled(); + boolean enableJSBool = mPreferenceManager.getJavaScriptEnabled(); cbAds.setEnabled(Constants.FULL_VERSION); cbFlash.setEnabled(API < 19); @@ -149,17 +144,17 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe cbImages.setChecked(imagesBool); cbJsScript.setChecked(enableJSBool); cbFlash.setChecked(flashNum > 0); - cbAds.setChecked(Constants.FULL_VERSION && mPreferences.getAdBlockEnabled()); - cbColorMode.setChecked(mPreferences.getColorModeEnabled()); - cbgooglesuggest.setChecked(mPreferences.getGoogleSearchSuggestionsEnabled()); - cbDrawerTabs.setChecked(mPreferences.getShowTabsInDrawer(true)); + cbAds.setChecked(Constants.FULL_VERSION && mPreferenceManager.getAdBlockEnabled()); + cbColorMode.setChecked(mPreferenceManager.getColorModeEnabled()); + cbgooglesuggest.setChecked(mPreferenceManager.getGoogleSearchSuggestionsEnabled()); + cbDrawerTabs.setChecked(mPreferenceManager.getShowTabsInDrawer(true)); } private void searchUrlPicker() { final AlertDialog.Builder urlPicker = new AlertDialog.Builder(mActivity); urlPicker.setTitle(getResources().getString(R.string.custom_url)); final EditText getSearchUrl = new EditText(mActivity); - String mSearchUrl = mPreferences.getSearchUrl(); + String mSearchUrl = mPreferenceManager.getSearchUrl(); getSearchUrl.setText(mSearchUrl); urlPicker.setView(getSearchUrl); urlPicker.setPositiveButton(getResources().getString(R.string.action_ok), @@ -167,7 +162,7 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe @Override public void onClick(DialogInterface dialog, int which) { String text = getSearchUrl.getText().toString(); - mPreferences.setSearchUrl(text); + mPreferenceManager.setSearchUrl(text); searchengine.setSummary(getResources().getString(R.string.custom_url) + ": " + text); } @@ -184,7 +179,7 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { - mPreferences.setFlashSupport(1); + mPreferenceManager.setFlashSupport(1); } }) .setNegativeButton(getResources().getString(R.string.action_auto), @@ -192,13 +187,13 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe @Override public void onClick(DialogInterface dialog, int which) { - mPreferences.setFlashSupport(2); + mPreferenceManager.setFlashSupport(2); } }).setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { - mPreferences.setFlashSupport(0); + mPreferenceManager.setFlashSupport(0); } }); @@ -209,7 +204,7 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe private void proxyChoicePicker() { AlertDialog.Builder picker = new AlertDialog.Builder(mActivity); picker.setTitle(getResources().getString(R.string.http_proxy)); - picker.setSingleChoiceItems(mProxyChoices, mPreferences.getProxyChoice(), + picker.setSingleChoiceItems(mProxyChoices, mPreferenceManager.getProxyChoice(), new DialogInterface.OnClickListener() { @Override @@ -234,7 +229,7 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe break; } - mPreferences.setProxyChoice(choice); + mPreferenceManager.setProxyChoice(choice); if (choice < mProxyChoices.length) proxy.setSummary(mProxyChoices[choice]); } @@ -252,8 +247,8 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe filterArray[0] = new InputFilter.LengthFilter(maxCharacters - 1); eProxyPort.setFilters(filterArray); - eProxyHost.setText(mPreferences.getProxyHost()); - eProxyPort.setText(Integer.toString(mPreferences.getProxyPort())); + eProxyHost.setText(mPreferenceManager.getProxyHost()); + eProxyPort.setText(Integer.toString(mPreferenceManager.getProxyPort())); new AlertDialog.Builder(mActivity) .setTitle(R.string.manual_proxy) @@ -268,10 +263,10 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe // larger than max integer proxyPort = Integer.parseInt(eProxyPort.getText().toString()); } catch (NumberFormatException ignored) { - proxyPort = mPreferences.getProxyPort(); + proxyPort = mPreferenceManager.getProxyPort(); } - mPreferences.setProxyHost(proxyHost); - mPreferences.setProxyPort(proxyPort); + mPreferenceManager.setProxyHost(proxyHost); + mPreferenceManager.setProxyPort(proxyPort); proxy.setSummary(proxyHost + ':' + proxyPort); } }).show(); @@ -285,13 +280,13 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe "DuckDuckGo (Privacy)", "DuckDuckGo Lite (Privacy)", "Baidu (Chinese)", "Yandex (Russian)"}; - int n = mPreferences.getSearchChoice(); + int n = mPreferenceManager.getSearchChoice(); picker.setSingleChoiceItems(chars, n, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - mPreferences.setSearchChoice(which); + mPreferenceManager.setSearchChoice(which); setSearchEngineSummary(which); } }); @@ -302,7 +297,7 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe private void homepageDialog() { AlertDialog.Builder picker = new AlertDialog.Builder(mActivity); picker.setTitle(getResources().getString(R.string.home)); - mHomepage = mPreferences.getHomepage(); + mHomepage = mPreferenceManager.getHomepage(); int n; if (mHomepage.contains("about:home")) { n = 1; @@ -320,15 +315,15 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe public void onClick(DialogInterface dialog, int which) { switch (which + 1) { case 1: - mPreferences.setHomepage("about:home"); + mPreferenceManager.setHomepage("about:home"); home.setSummary(getResources().getString(R.string.action_homepage)); break; case 2: - mPreferences.setHomepage("about:blank"); + mPreferenceManager.setHomepage("about:blank"); home.setSummary(getResources().getString(R.string.action_blank)); break; case 3: - mPreferences.setHomepage("about:bookmarks"); + mPreferenceManager.setHomepage("about:bookmarks"); home.setSummary(getResources().getString(R.string.action_bookmarks)); break; case 4: @@ -345,7 +340,7 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe final AlertDialog.Builder homePicker = new AlertDialog.Builder(mActivity); homePicker.setTitle(getResources().getString(R.string.title_custom_homepage)); final EditText getHome = new EditText(mActivity); - mHomepage = mPreferences.getHomepage(); + mHomepage = mPreferenceManager.getHomepage(); if (!mHomepage.startsWith("about:")) { getHome.setText(mHomepage); } else { @@ -357,7 +352,7 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe @Override public void onClick(DialogInterface dialog, int which) { String text = getHome.getText().toString(); - mPreferences.setHomepage(text); + mPreferenceManager.setHomepage(text); home.setSummary(text); } }); @@ -367,7 +362,7 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe private void downloadLocDialog() { AlertDialog.Builder picker = new AlertDialog.Builder(mActivity); picker.setTitle(getResources().getString(R.string.title_download_location)); - mDownloadLocation = mPreferences.getDownloadDirectory(); + mDownloadLocation = mPreferenceManager.getDownloadDirectory(); int n; if (mDownloadLocation.contains(Environment.DIRECTORY_DOWNLOADS)) { n = 0; @@ -381,7 +376,7 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe public void onClick(DialogInterface dialog, int which) { switch (which) { case 0: - mPreferences.setDownloadDirectory(DownloadHandler.DEFAULT_DOWNLOAD_PATH); + mPreferenceManager.setDownloadDirectory(DownloadHandler.DEFAULT_DOWNLOAD_PATH); downloadloc.setSummary(DownloadHandler.DEFAULT_DOWNLOAD_PATH); break; case 1: @@ -397,12 +392,12 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe private void agentDialog() { AlertDialog.Builder agentPicker = new AlertDialog.Builder(mActivity); agentPicker.setTitle(getResources().getString(R.string.title_user_agent)); - mAgentChoice = mPreferences.getUserAgentChoice(); + mAgentChoice = mPreferenceManager.getUserAgentChoice(); agentPicker.setSingleChoiceItems(R.array.user_agent, mAgentChoice - 1, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - mPreferences.setUserAgentChoice(which + 1); + mPreferenceManager.setUserAgentChoice(which + 1); switch (which + 1) { case 1: useragent.setSummary(getResources().getString(R.string.agent_default)); @@ -434,7 +429,7 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe @Override public void onClick(DialogInterface dialog, int which) { String text = getAgent.getText().toString(); - mPreferences.setUserAgentString(text); + mPreferenceManager.setUserAgentString(text); useragent.setSummary(getResources().getString(R.string.agent_custom)); } }); @@ -448,12 +443,12 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe final EditText getDownload = new EditText(mActivity); getDownload.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - getDownload.setText(PreferenceManager.getInstance().getDownloadDirectory()); + getDownload.setText(mPreferenceManager.getDownloadDirectory()); final int errorColor = ContextCompat.getColor(getActivity(), R.color.error_red); final int regularColor = ThemeUtils.getTextColor(getActivity()); getDownload.setTextColor(regularColor); getDownload.addTextChangedListener(new DownloadLocationTextWatcher(getDownload, errorColor, regularColor)); - getDownload.setText(mPreferences.getDownloadDirectory()); + getDownload.setText(mPreferenceManager.getDownloadDirectory()); layout.addView(getDownload); downLocationPicker.setView(layout); @@ -463,7 +458,7 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe public void onClick(DialogInterface dialog, int which) { String text = getDownload.getText().toString(); text = DownloadHandler.addNecessarySlashes(text); - mPreferences.setDownloadDirectory(text); + mPreferenceManager.setDownloadDirectory(text); downloadloc.setSummary(text); } }); @@ -538,37 +533,37 @@ public class GeneralSettingsFragment extends PreferenceFragment implements Prefe if (cbFlash.isChecked()) { getFlashChoice(); } else { - mPreferences.setFlashSupport(0); + mPreferenceManager.setFlashSupport(0); } if (!Utils.isFlashInstalled(mActivity) && cbFlash.isChecked()) { Utils.createInformativeDialog(mActivity, R.string.title_warning, R.string.dialog_adobe_not_installed); cbFlash.setEnabled(false); - mPreferences.setFlashSupport(0); + mPreferenceManager.setFlashSupport(0); } cbFlash.setChecked((Boolean) newValue); return true; case SETTINGS_ADS: - mPreferences.setAdBlockEnabled((Boolean) newValue); + mPreferenceManager.setAdBlockEnabled((Boolean) newValue); cbAds.setChecked((Boolean) newValue); return true; case SETTINGS_IMAGES: - mPreferences.setBlockImagesEnabled((Boolean) newValue); + mPreferenceManager.setBlockImagesEnabled((Boolean) newValue); cbImages.setChecked((Boolean) newValue); return true; case SETTINGS_JAVASCRIPT: - mPreferences.setJavaScriptEnabled((Boolean) newValue); + mPreferenceManager.setJavaScriptEnabled((Boolean) newValue); cbJsScript.setChecked((Boolean) newValue); return true; case SETTINGS_COLORMODE: - mPreferences.setColorModeEnabled((Boolean) newValue); + mPreferenceManager.setColorModeEnabled((Boolean) newValue); cbColorMode.setChecked((Boolean) newValue); return true; case SETTINGS_GOOGLESUGGESTIONS: - mPreferences.setGoogleSearchSuggestionsEnabled((Boolean) newValue); + mPreferenceManager.setGoogleSearchSuggestionsEnabled((Boolean) newValue); cbgooglesuggest.setChecked((Boolean) newValue); return true; case SETTINGS_DRAWERTABS: - mPreferences.setShowTabsInDrawer((Boolean) newValue); + mPreferenceManager.setShowTabsInDrawer((Boolean) newValue); cbDrawerTabs.setChecked((Boolean) newValue); default: return false; diff --git a/app/src/main/java/acr/browser/lightning/fragment/LightningPreferenceFragment.java b/app/src/main/java/acr/browser/lightning/fragment/LightningPreferenceFragment.java new file mode 100644 index 0000000..ab7c48d --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/fragment/LightningPreferenceFragment.java @@ -0,0 +1,27 @@ +package acr.browser.lightning.fragment; + +import android.os.Bundle; +import android.preference.PreferenceFragment; + +import javax.inject.Inject; + +import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.preference.PreferenceManager; + +/** + * Simplify {@link PreferenceManager} inject in all the PreferenceFragments + * + * @author Stefano Pacifici + * @date 2015/09/16 + */ +public class LightningPreferenceFragment extends PreferenceFragment { + + @Inject + PreferenceManager mPreferenceManager; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + BrowserApp.getAppComponent().inject(this); + } +} 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 9fc225c..2ea48aa 100644 --- a/app/src/main/java/acr/browser/lightning/fragment/PrivacySettingsFragment.java +++ b/app/src/main/java/acr/browser/lightning/fragment/PrivacySettingsFragment.java @@ -11,16 +11,14 @@ import android.os.Handler; import android.os.Message; import android.preference.CheckBoxPreference; import android.preference.Preference; -import android.preference.PreferenceFragment; import android.support.v7.app.AlertDialog; import android.webkit.WebView; import acr.browser.lightning.R; -import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.utils.Utils; import acr.browser.lightning.utils.WebUtils; -public class PrivacySettingsFragment extends PreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { +public class PrivacySettingsFragment extends LightningPreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { private static final String SETTINGS_LOCATION = "location"; private static final String SETTINGS_THIRDPCOOKIES = "third_party"; @@ -35,7 +33,6 @@ public class PrivacySettingsFragment extends PreferenceFragment implements Prefe private static final String SETTINGS_WEBSTORAGEEXIT = "clear_webstorage_exit"; private Activity mActivity; - private PreferenceManager mPreferences; private CheckBoxPreference cblocation, cb3cookies, cbsavepasswords, cbcacheexit, cbhistoryexit, cbcookiesexit, cbwebstorageexit; private Handler messageHandler; @@ -52,9 +49,6 @@ public class PrivacySettingsFragment extends PreferenceFragment implements Prefe } private void initPrefs() { - // mPreferences storage - mPreferences = PreferenceManager.getInstance(); - Preference clearcache = findPreference(SETTINGS_CLEARCACHE); Preference clearhistory = findPreference(SETTINGS_CLEARHISTORY); Preference clearcookies = findPreference(SETTINGS_CLEARCOOKIES); @@ -81,13 +75,13 @@ public class PrivacySettingsFragment extends PreferenceFragment implements Prefe cbcookiesexit.setOnPreferenceChangeListener(this); cbwebstorageexit.setOnPreferenceChangeListener(this); - cblocation.setChecked(mPreferences.getLocationEnabled()); - cbsavepasswords.setChecked(mPreferences.getSavePasswordsEnabled()); - cbcacheexit.setChecked(mPreferences.getClearCacheExit()); - cbhistoryexit.setChecked(mPreferences.getClearHistoryExitEnabled()); - cbcookiesexit.setChecked(mPreferences.getClearCookiesExitEnabled()); - cb3cookies.setChecked(mPreferences.getBlockThirdPartyCookiesEnabled()); - cbwebstorageexit.setChecked(mPreferences.getClearWebStorageExitEnabled()); + cblocation.setChecked(mPreferenceManager.getLocationEnabled()); + cbsavepasswords.setChecked(mPreferenceManager.getSavePasswordsEnabled()); + cbcacheexit.setChecked(mPreferenceManager.getClearCacheExit()); + cbhistoryexit.setChecked(mPreferenceManager.getClearHistoryExitEnabled()); + cbcookiesexit.setChecked(mPreferenceManager.getClearCookiesExitEnabled()); + cb3cookies.setChecked(mPreferenceManager.getBlockThirdPartyCookiesEnabled()); + cbwebstorageexit.setChecked(mPreferenceManager.getClearWebStorageExitEnabled()); cb3cookies.setEnabled(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP); @@ -203,31 +197,31 @@ public class PrivacySettingsFragment extends PreferenceFragment implements Prefe // switch preferences switch (preference.getKey()) { case SETTINGS_LOCATION: - mPreferences.setLocationEnabled((Boolean) newValue); + mPreferenceManager.setLocationEnabled((Boolean) newValue); cblocation.setChecked((Boolean) newValue); return true; case SETTINGS_THIRDPCOOKIES: - mPreferences.setBlockThirdPartyCookiesEnabled((Boolean) newValue); + mPreferenceManager.setBlockThirdPartyCookiesEnabled((Boolean) newValue); cb3cookies.setChecked((Boolean) newValue); return true; case SETTINGS_SAVEPASSWORD: - mPreferences.setSavePasswordsEnabled((Boolean) newValue); + mPreferenceManager.setSavePasswordsEnabled((Boolean) newValue); cbsavepasswords.setChecked((Boolean) newValue); return true; case SETTINGS_CACHEEXIT: - mPreferences.setClearCacheExit((Boolean) newValue); + mPreferenceManager.setClearCacheExit((Boolean) newValue); cbcacheexit.setChecked((Boolean) newValue); return true; case SETTINGS_HISTORYEXIT: - mPreferences.setClearHistoryExitEnabled((Boolean) newValue); + mPreferenceManager.setClearHistoryExitEnabled((Boolean) newValue); cbhistoryexit.setChecked((Boolean) newValue); return true; case SETTINGS_COOKIEEXIT: - mPreferences.setClearCookiesExitEnabled((Boolean) newValue); + mPreferenceManager.setClearCookiesExitEnabled((Boolean) newValue); cbcookiesexit.setChecked((Boolean) newValue); return true; case SETTINGS_WEBSTORAGEEXIT: - mPreferences.setClearWebStorageExitEnabled((Boolean) newValue); + mPreferenceManager.setClearWebStorageExitEnabled((Boolean) newValue); cbwebstorageexit.setChecked((Boolean) newValue); return true; default: diff --git a/app/src/main/java/acr/browser/lightning/fragment/TabsFragment.java b/app/src/main/java/acr/browser/lightning/fragment/TabsFragment.java new file mode 100644 index 0000000..a0945ad --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/fragment/TabsFragment.java @@ -0,0 +1,417 @@ +package acr.browser.lightning.fragment; + +import android.animation.ArgbEvaluator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.IdRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v4.view.ViewCompat; +import android.support.v7.graphics.Palette; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.RecyclerView.LayoutManager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.squareup.otto.Bus; +import com.squareup.otto.Subscribe; + +import java.util.List; + +import javax.inject.Inject; + +import acr.browser.lightning.R; +import acr.browser.lightning.activity.TabsManager; +import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.bus.BrowserEvents; +import acr.browser.lightning.bus.NavigationEvents; +import acr.browser.lightning.bus.TabEvents; +import acr.browser.lightning.preference.PreferenceManager; +import acr.browser.lightning.utils.ThemeUtils; +import acr.browser.lightning.utils.Utils; +import acr.browser.lightning.view.LightningView; + +/** + * @author Stefano Pacifici based on Anthony C. Restaino's code + * @date 2015/09/14 + */ +public class TabsFragment extends Fragment implements View.OnClickListener, View.OnLongClickListener { + + private static final String TAG = TabsFragment.class.getSimpleName(); + + /** + * Arguments boolean to tell the fragment it is displayed in the drawner or on the tab strip + * If true, the fragment is in the left drawner in the strip otherwise. + */ + public static final String VERTICAL_MODE = TAG + ".VERTICAL_MODE"; + public static final String IS_INCOGNITO = TAG + ".IS_INCOGNITO"; + + private boolean mIsIncognito, mDarkTheme; + private int mIconColor; + private boolean mColorMode = true; + private boolean mShowInNavigationDrawer; + private int mCurrentUiColor = Color.BLACK; // TODO Only temporary + + private RecyclerView mRecyclerView; + private LightningViewAdapter mTabsAdapter; + + @Inject + TabsManager tabsManager; + + @Inject + Bus bus; + + @Inject + PreferenceManager mPreferences; + + public TabsFragment() { + BrowserApp.getAppComponent().inject(this); + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + final Bundle arguments = getArguments(); + final Context context = getContext(); + mIsIncognito = arguments.getBoolean(IS_INCOGNITO, false); + mShowInNavigationDrawer = arguments.getBoolean(VERTICAL_MODE, true); + mDarkTheme = mPreferences.getUseTheme() != 0 || mIsIncognito; + mColorMode = mPreferences.getColorModeEnabled(); + mColorMode &= !mDarkTheme; + mIconColor = mDarkTheme ? + ThemeUtils.getIconDarkThemeColor(context) : + ThemeUtils.getIconLightThemeColor(context); + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final Bundle arguments = getArguments(); + final View view; + final LayoutManager layoutManager; + if (mShowInNavigationDrawer) { + view = inflater.inflate(R.layout.tab_drawer, container, false); + layoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false); + setupFrameLayoutButton(view, R.id.new_tab_button, R.id.icon_plus); + setupFrameLayoutButton(view, R.id.action_back, R.id.icon_back); + setupFrameLayoutButton(view, R.id.action_forward, R.id.icon_forward); + setupFrameLayoutButton(view, R.id.action_home, R.id.icon_home); + } else { + view = inflater.inflate(R.layout.tab_strip, container, false); + layoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false); + } + mRecyclerView = (RecyclerView) view.findViewById(R.id.tabs_list); + mRecyclerView.setLayoutManager(layoutManager); + mTabsAdapter = new LightningViewAdapter(mShowInNavigationDrawer); + mRecyclerView.setAdapter(mTabsAdapter); + mRecyclerView.setHasFixedSize(true); + return view; + } + + private void setupFrameLayoutButton(@NonNull final View root, @IdRes final int buttonId, + @IdRes final int imageId) { + final View frameButton = root.findViewById(buttonId); + final ImageView buttonImage = (ImageView) root.findViewById(imageId); + frameButton.setOnClickListener(this); + frameButton.setOnLongClickListener(this); + buttonImage.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + mRecyclerView = null; + mTabsAdapter = null; + } + + @Override + public void onStart() { + super.onStart(); + bus.register(this); + } + + @Override + public void onResume() { + super.onResume(); + // Force adapter refresh + mTabsAdapter.notifyDataSetChanged(); + } + + @Override + public void onStop() { + super.onStop(); + bus.unregister(this); + } + + @Subscribe + public void tabsChanged(final BrowserEvents.TabsChanged event) { + if (mTabsAdapter != null) { + mTabsAdapter.notifyDataSetChanged(); + } + } + + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.new_tab_button: + bus.post(new TabEvents.NewTab()); + break; + case R.id.action_back: + bus.post(new NavigationEvents.GoBack()); + break; + case R.id.action_forward: + bus.post(new NavigationEvents.GoForward()); + break; + case R.id.action_home: + bus.post(new NavigationEvents.GoHome()); + default: + break; + } + } + + @Override + public boolean onLongClick(View v) { + switch (v.getId()) { + case R.id.action_new_tab: + bus.post(new TabEvents.NewTabLongPress()); + break; + default: + break; + } + return true; + } + + public class LightningViewAdapter extends RecyclerView.Adapter { + + private final int layoutResourceId; + private final Drawable mBackgroundTabDrawable; + private final Drawable mForegroundTabDrawable; + private final Bitmap mForegroundTabBitmap; + private ColorMatrix mColorMatrix; + private Paint mPaint; + private ColorFilter mFilter; + private static final float DESATURATED = 0.5f; + + private final boolean vertical; + + public LightningViewAdapter(final boolean vertical) { + this.layoutResourceId = vertical ? R.layout.tab_list_item : R.layout.tab_list_item_horizontal; + this.vertical = vertical; + + if (vertical) { + mBackgroundTabDrawable = null; + mForegroundTabBitmap = null; + mForegroundTabDrawable = ThemeUtils.getSelectedBackground(getContext(), mDarkTheme); + } else { + int backgroundColor = Utils.mixTwoColors(ThemeUtils.getPrimaryColor(getContext()), Color.BLACK, 0.75f); + Bitmap backgroundTabBitmap = Bitmap.createBitmap(Utils.dpToPx(175), Utils.dpToPx(30), Bitmap.Config.ARGB_8888); + Utils.drawTrapezoid(new Canvas(backgroundTabBitmap), backgroundColor, true); + mBackgroundTabDrawable = new BitmapDrawable(getResources(), backgroundTabBitmap); + + int foregroundColor = ThemeUtils.getPrimaryColor(getContext()); + mForegroundTabBitmap = Bitmap.createBitmap(Utils.dpToPx(175), Utils.dpToPx(30), Bitmap.Config.ARGB_8888); + Utils.drawTrapezoid(new Canvas(mForegroundTabBitmap), foregroundColor, false); + mForegroundTabDrawable = null; + } + } + + @Override + public LightningViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { + LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext()); + View view = inflater.inflate(layoutResourceId, viewGroup, false); + return new LightningViewHolder(view); + } + + @Override + public void onBindViewHolder(final LightningViewHolder holder, int position) { + holder.exitButton.setTag(position); + + ViewCompat.jumpDrawablesToCurrentState(holder.exitButton); + + LightningView web = tabsManager.getTabAtPosition(position); + holder.txtTitle.setText(web.getTitle()); + + final Bitmap favicon = web.getFavicon(); + if (web.isForegroundTab()) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + holder.txtTitle.setTextAppearance(R.style.boldText); + } else { + holder.txtTitle.setTextAppearance(getContext(), R.style.boldText); + } + Drawable foregroundDrawable; + if (!vertical) { + foregroundDrawable = new BitmapDrawable(getResources(), mForegroundTabBitmap); + if (!mIsIncognito && mColorMode) { + foregroundDrawable.setColorFilter(mCurrentUiColor, PorterDuff.Mode.SRC_IN); + } + } else { + foregroundDrawable = mForegroundTabDrawable; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + holder.layout.setBackground(foregroundDrawable); + } else { + holder.layout.setBackgroundDrawable(foregroundDrawable); + } + if (!mIsIncognito && mColorMode) { + changeToolbarBackground(favicon, foregroundDrawable); + } + holder.favicon.setImageBitmap(favicon); + } else { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + holder.txtTitle.setTextAppearance(R.style.normalText); + } else { + holder.txtTitle.setTextAppearance(getContext(), R.style.normalText); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + holder.layout.setBackground(mBackgroundTabDrawable); + } else { + holder.layout.setBackgroundDrawable(mBackgroundTabDrawable); + } + holder.favicon.setImageBitmap(getDesaturatedBitmap(favicon)); + } + } + + @Override + public int getItemCount() { + return tabsManager.size(); + } + + public Bitmap getDesaturatedBitmap(Bitmap favicon) { + Bitmap grayscaleBitmap = Bitmap.createBitmap(favicon.getWidth(), + favicon.getHeight(), Bitmap.Config.ARGB_8888); + + Canvas c = new Canvas(grayscaleBitmap); + if (mColorMatrix == null || mFilter == null || mPaint == null) { + mPaint = new Paint(); + mColorMatrix = new ColorMatrix(); + mColorMatrix.setSaturation(DESATURATED); + mFilter = new ColorMatrixColorFilter(mColorMatrix); + mPaint.setColorFilter(mFilter); + } + + c.drawBitmap(favicon, 0, 0, mPaint); + return grayscaleBitmap; + } + + /** + * Animates the color of the toolbar from one color to another. Optionally animates + * the color of the tab background, for use when the tabs are displayed on the top + * of the screen. + * + * @param favicon the Bitmap to extract the color from + * @param tabBackground the optional LinearLayout to color + */ + private void changeToolbarBackground(@NonNull Bitmap favicon, @Nullable final Drawable tabBackground) { + if (mShowInNavigationDrawer) { + return; + } + + final int defaultColor; + final Resources resources = getResources(); + final ColorDrawable mBackground = new ColorDrawable(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + defaultColor = resources.getColor(R.color.primary_color, null); + } else { + defaultColor = resources.getColor(R.color.primary_color); + } + if (mCurrentUiColor == Color.BLACK) { + mCurrentUiColor = defaultColor; + } + Palette.from(favicon).generate(new Palette.PaletteAsyncListener() { + @Override + public void onGenerated(Palette palette) { + + // OR with opaque black to remove transparency glitches + int color = 0xff000000 | palette.getVibrantColor(defaultColor); + + int finalColor = Utils.mixTwoColors(defaultColor, color, 0.25f); + + ValueAnimator anim = ValueAnimator.ofInt(mCurrentUiColor, finalColor); + anim.setEvaluator(new ArgbEvaluator()); + // final Window window = getWindow(); + // TODO Check this + // if (!mShowInNavigationDrawer) { + // window.setBackgroundDrawable(new ColorDrawable(Color.BLACK)); + // } + anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + final int color = (Integer) animation.getAnimatedValue(); + if (tabBackground != null) { + tabBackground.setColorFilter(color, PorterDuff.Mode.SRC_IN); + } + mCurrentUiColor = color; + } + + }); + anim.setDuration(300); + anim.start(); + } + }); + } + + public class LightningViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { + + public LightningViewHolder(View view) { + super(view); + txtTitle = (TextView) view.findViewById(R.id.textTab); + favicon = (ImageView) view.findViewById(R.id.faviconTab); + exit = (ImageView) view.findViewById(R.id.deleteButton); + layout = (LinearLayout) view.findViewById(R.id.tab_item_background); + exitButton = (FrameLayout) view.findViewById(R.id.deleteAction); + exit.setColorFilter(mIconColor, PorterDuff.Mode.SRC_IN); + + exitButton.setOnClickListener(this); + layout.setOnClickListener(this); + layout.setOnLongClickListener(this); + } + + final TextView txtTitle; + final ImageView favicon; + final ImageView exit; + final FrameLayout exitButton; + final LinearLayout layout; + + @Override + public void onClick(View v) { + if (v == exitButton) { + // Close tab + bus.post(new TabEvents.CloseTab(getAdapterPosition())); + } + if (v == layout) { + bus.post(new TabEvents.ShowTab(getAdapterPosition())); + } + } + + @Override + public boolean onLongClick(View v) { + // Show close dialog + bus.post(new TabEvents.ShowCloseDialog(getAdapterPosition())); + return true; + } + } + } +} diff --git a/app/src/main/java/acr/browser/lightning/object/ClickHandler.java b/app/src/main/java/acr/browser/lightning/object/ClickHandler.java deleted file mode 100644 index 841c03f..0000000 --- a/app/src/main/java/acr/browser/lightning/object/ClickHandler.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2014 A.C.R. Development - */ -package acr.browser.lightning.object; - -import android.content.Context; -import android.os.Handler; -import android.os.Message; - -import acr.browser.lightning.controller.BrowserController; - -public class ClickHandler extends Handler { - - private BrowserController mBrowserController; - - public ClickHandler(Context context) { - try { - mBrowserController = (BrowserController) context; - } catch (ClassCastException e) { - throw new ClassCastException(context + " must implement BrowserController"); - } - } - - @Override - public void handleMessage(Message msg) { - super.handleMessage(msg); - String url = msg.getData().getString("url"); - mBrowserController.longClickPage(url); - } -} 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 6db5ef1..5789f9e 100644 --- a/app/src/main/java/acr/browser/lightning/object/SearchAdapter.java +++ b/app/src/main/java/acr/browser/lightning/object/SearchAdapter.java @@ -56,14 +56,11 @@ public class SearchAdapter extends BaseAdapter implements Filterable { private final List mFilteredList = new ArrayList<>(5); private final List mAllBookmarks = new ArrayList<>(5); private final Object mLock = new Object(); - private HistoryDatabase mDatabaseHandler; private final Context mContext; private boolean mUseGoogle = true; private boolean mIsExecuting = false; private final boolean mDarkTheme; private final boolean mIncognito; - @Inject - BookmarkManager mBookmarkManager; private static final String CACHE_FILE_TYPE = ".sgg"; private static final String ENCODING = "ISO-8859-1"; private static final long INTERVAL_DAY = 86400000; @@ -75,11 +72,19 @@ public class SearchAdapter extends BaseAdapter implements Filterable { private final Drawable mHistoryDrawable; private final Drawable mBookmarkDrawable; + @Inject + HistoryDatabase mDatabaseHandler; + + @Inject + BookmarkManager mBookmarkManager; + + @Inject + PreferenceManager mPreferenceManager; + public SearchAdapter(Context context, boolean dark, boolean incognito) { BrowserApp.getAppComponent().inject(this); - mDatabaseHandler = HistoryDatabase.getInstance(); mAllBookmarks.addAll(mBookmarkManager.getAllBookmarks(true)); - mUseGoogle = PreferenceManager.getInstance().getGoogleSearchSuggestionsEnabled(); + mUseGoogle = mPreferenceManager.getGoogleSearchSuggestionsEnabled(); mContext = context; mSearchSubtitle = mContext.getString(R.string.suggestion); mDarkTheme = dark || incognito; @@ -114,13 +119,12 @@ public class SearchAdapter extends BaseAdapter implements Filterable { } public void refreshPreferences() { - mUseGoogle = PreferenceManager.getInstance().getGoogleSearchSuggestionsEnabled(); + mUseGoogle = mPreferenceManager.getGoogleSearchSuggestionsEnabled(); if (!mUseGoogle) { synchronized (mSuggestions) { mSuggestions.clear(); } } - mDatabaseHandler = HistoryDatabase.getInstance(); } public void refreshBookmarks() { @@ -244,13 +248,10 @@ public class SearchAdapter extends BaseAdapter implements Filterable { mBookmarks.add(mAllBookmarks.get(n)); counter++; } - } } } - if (mDatabaseHandler == null || mDatabaseHandler.isClosed()) { - mDatabaseHandler = HistoryDatabase.getInstance(); - } + List historyList = mDatabaseHandler.findItemsContaining(constraint.toString()); synchronized (mHistory) { mHistory.clear(); 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 0fda30e..f087ea5 100644 --- a/app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java +++ b/app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java @@ -1,11 +1,16 @@ package acr.browser.lightning.preference; +import android.content.Context; import android.content.SharedPreferences; +import javax.inject.Inject; +import javax.inject.Singleton; + import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.download.DownloadHandler; +@Singleton public class PreferenceManager { private static class Name { @@ -56,20 +61,13 @@ public class PreferenceManager { public static final String INITIAL_CHECK_FOR_I2P = "checkForI2P"; } - private static PreferenceManager mInstance; private final SharedPreferences mPrefs; private static final String PREFERENCES = "settings"; - public static PreferenceManager getInstance() { - if (mInstance == null) { - mInstance = new PreferenceManager(); - } - return mInstance; - } - - private PreferenceManager() { - mPrefs = BrowserApp.getAppContext().getSharedPreferences(PREFERENCES, 0); + @Inject + PreferenceManager(final Context context) { + mPrefs = context.getSharedPreferences(PREFERENCES, 0); } public boolean getAdBlockEnabled() { diff --git a/app/src/main/java/acr/browser/lightning/utils/AdBlock.java b/app/src/main/java/acr/browser/lightning/utils/AdBlock.java index 706596f..c434c33 100644 --- a/app/src/main/java/acr/browser/lightning/utils/AdBlock.java +++ b/app/src/main/java/acr/browser/lightning/utils/AdBlock.java @@ -13,6 +13,7 @@ import java.util.HashSet; import java.util.Locale; import java.util.Set; +import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.constant.Constants; import acr.browser.lightning.preference.PreferenceManager; @@ -44,11 +45,11 @@ public class AdBlock { if (mBlockedDomainsList.isEmpty() && Constants.FULL_VERSION) { loadHostsFile(context); } - mBlockAds = PreferenceManager.getInstance().getAdBlockEnabled(); + mBlockAds = BrowserApp.getAppComponent().getPreferenceManager().getAdBlockEnabled(); } public void updatePreference() { - mBlockAds = PreferenceManager.getInstance().getAdBlockEnabled(); + mBlockAds = BrowserApp.getAppComponent().getPreferenceManager().getAdBlockEnabled(); } private void loadBlockedDomainsList(final Context context) { 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 21e6791..dcb9d6b 100644 --- a/app/src/main/java/acr/browser/lightning/utils/IntentUtils.java +++ b/app/src/main/java/acr/browser/lightning/utils/IntentUtils.java @@ -2,6 +2,7 @@ package acr.browser.lightning.utils; import android.app.Activity; import android.content.ActivityNotFoundException; +import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; diff --git a/app/src/main/java/acr/browser/lightning/utils/WebUtils.java b/app/src/main/java/acr/browser/lightning/utils/WebUtils.java index a5e5fd8..f05f2ae 100644 --- a/app/src/main/java/acr/browser/lightning/utils/WebUtils.java +++ b/app/src/main/java/acr/browser/lightning/utils/WebUtils.java @@ -10,6 +10,7 @@ import android.webkit.WebStorage; import android.webkit.WebView; import android.webkit.WebViewDatabase; +import acr.browser.lightning.app.BrowserApp; import acr.browser.lightning.database.HistoryDatabase; /** @@ -34,7 +35,7 @@ public class WebUtils { } public static void clearHistory(@NonNull Context context) { - HistoryDatabase.getInstance().deleteHistory(); + BrowserApp.getAppComponent().getHistoryDatabase().deleteHistory(); WebViewDatabase m = WebViewDatabase.getInstance(context); m.clearFormData(); m.clearHttpAuthUsernamePassword(); diff --git a/app/src/main/java/acr/browser/lightning/view/IconCacheTask.java b/app/src/main/java/acr/browser/lightning/view/IconCacheTask.java new file mode 100644 index 0000000..c4845d2 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/view/IconCacheTask.java @@ -0,0 +1,44 @@ +package acr.browser.lightning.view; + +import android.graphics.Bitmap; +import android.net.Uri; +import android.util.Log; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.constant.Constants; +import acr.browser.lightning.utils.Utils; + +/** + * @author Anthony C. Restaino + * @date 2015/09/29 + */ +class IconCacheTask implements Runnable{ + private final Uri uri; + private final Bitmap icon; + + public IconCacheTask(Uri uri, Bitmap icon) { + this.uri = uri; + this.icon = icon; + } + + @Override + public void run() { + String hash = String.valueOf(uri.getHost().hashCode()); + Log.d(Constants.TAG, "Caching icon for " + uri.getHost()); + FileOutputStream fos = null; + try { + File image = new File(BrowserApp.getAppContext().getCacheDir(), hash + ".png"); + fos = new FileOutputStream(image); + icon.compress(Bitmap.CompressFormat.PNG, 100, fos); + fos.flush(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + Utils.close(fos); + } + } +} diff --git a/app/src/main/java/acr/browser/lightning/view/LightningChromeClient.java b/app/src/main/java/acr/browser/lightning/view/LightningChromeClient.java new file mode 100644 index 0000000..7167582 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/view/LightningChromeClient.java @@ -0,0 +1,250 @@ +package acr.browser.lightning.view; + +import android.Manifest; +import android.app.Activity; +import android.content.DialogInterface; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.Message; +import android.support.v7.app.AlertDialog; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.webkit.GeolocationPermissions; +import android.webkit.ValueCallback; +import android.webkit.WebChromeClient; +import android.webkit.WebView; + +import com.squareup.otto.Bus; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +import acr.browser.lightning.R; +import acr.browser.lightning.activity.BrowserActivity; +import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.bus.BrowserEvents; +import acr.browser.lightning.constant.Constants; +import acr.browser.lightning.utils.PermissionsManager; +import acr.browser.lightning.utils.Utils; + +/** + * @author Stefano Pacifici based on Anthony C. Restaino code + * @date 2015/09/21 + */ +class LightningChromeClient extends WebChromeClient { + + private static final String[] PERMISSIONS = new String[]{Manifest.permission.ACCESS_FINE_LOCATION}; + + private final BrowserActivity mActivity; + private final LightningView mLightningView; + private final Bus eventBus; + + LightningChromeClient(BrowserActivity activity, LightningView lightningView) { + mActivity = activity; + mLightningView = lightningView; + eventBus = BrowserApp.getAppComponent().getBus(); + } + + @Override + public void onProgressChanged(WebView view, int newProgress) { + if (mLightningView.isShown()) { + mActivity.updateProgress(newProgress); + } + } + + @Override + public void onReceivedIcon(WebView view, Bitmap icon) { + if (icon == null) + return; + mLightningView.mTitle.setFavicon(icon); + eventBus.post(new BrowserEvents.TabsChanged()); ; + cacheFavicon(view.getUrl(), icon); + } + + /** + * Naive caching of the favicon according to the domain name of the URL + * @param icon the icon to cache + */ + private void cacheFavicon(final String url, final Bitmap icon) { + if (icon == null) return; + final Uri uri = Uri.parse(url); + if (uri.getHost() == null) { + return; + } + new Thread(new Runnable() { + @Override + public void run() { + String hash = String.valueOf(uri.getHost().hashCode()); + Log.d(Constants.TAG, "Caching icon for " + uri.getHost()); + FileOutputStream fos = null; + try { + File image = new File(BrowserApp.getAppContext().getCacheDir(), hash + ".png"); + fos = new FileOutputStream(image); + icon.compress(Bitmap.CompressFormat.PNG, 100, fos); + fos.flush(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + Utils.close(fos); + } + } + }).start(); + } + + + @Override + public void onReceivedTitle(WebView view, String title) { + if (title != null && !title.isEmpty()) { + mLightningView.mTitle.setTitle(title); + } else { + mLightningView.mTitle.setTitle(mActivity.getString(R.string.untitled)); + } + eventBus.post(new BrowserEvents.TabsChanged()); + if (view != null) { + mActivity.updateHistory(title, view.getUrl()); + } + } + + @Override + public void onGeolocationPermissionsShowPrompt(final String origin, + final GeolocationPermissions.Callback callback) { + PermissionsManager.getInstance().requestPermissionsIfNecessary(mActivity, PERMISSIONS); + final boolean remember = true; + AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); + builder.setTitle(mActivity.getString(R.string.location)); + String org; + if (origin.length() > 50) { + org = origin.subSequence(0, 50) + "..."; + } else { + org = origin; + } + builder.setMessage(org + mActivity.getString(R.string.message_location)) + .setCancelable(true) + .setPositiveButton(mActivity.getString(R.string.action_allow), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + callback.invoke(origin, true, remember); + } + }) + .setNegativeButton(mActivity.getString(R.string.action_dont_allow), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + callback.invoke(origin, false, remember); + } + }); + AlertDialog alert = builder.create(); + alert.show(); + + } + + @Override + public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, + Message resultMsg) { + mActivity.onCreateWindow(resultMsg); + return true; + } + + @Override + public void onCloseWindow(WebView window) { + mActivity.onCloseWindow(mLightningView); + } + + public void openFileChooser(ValueCallback uploadMsg) { + mActivity.openFileChooser(uploadMsg); + } + + public void openFileChooser(ValueCallback uploadMsg, String acceptType) { + mActivity.openFileChooser(uploadMsg); + } + + public void openFileChooser(ValueCallback uploadMsg, String acceptType, String capture) { + mActivity.openFileChooser(uploadMsg); + } + + public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, + WebChromeClient.FileChooserParams fileChooserParams) { + mActivity.showFileChooser(filePathCallback); + return true; + } + + /** + * Obtain an image that is displayed as a placeholder on a video until the video has initialized + * and can begin loading. + * + * @return a Bitmap that can be used as a place holder for videos. + */ + @Override + public Bitmap getDefaultVideoPoster() { + final Resources resources = mActivity.getResources(); + return BitmapFactory.decodeResource(resources, android.R.drawable.spinner_background); + } + + /** + * Inflate a view to send to a LightningView when it needs to display a video and has to + * show a loading dialog. Inflates a progress view and returns it. + * + * @return A view that should be used to display the state + * of a video's loading progress. + */ + @Override + public View getVideoLoadingProgressView() { + LayoutInflater inflater = LayoutInflater.from(mActivity); + return inflater.inflate(R.layout.video_loading_progress, null); + } + + @Override + public void onHideCustomView() { + mActivity.onHideCustomView(); + super.onHideCustomView(); + } + + @Override + public void onShowCustomView(View view, CustomViewCallback callback) { + // While these lines might look like they work, in practice, + // Full-screen videos won't work correctly. I may test this out some + // more + // if (view instanceof FrameLayout) { + // FrameLayout frame = (FrameLayout) view; + // if (frame.getFocusedChild() instanceof VideoView) { + // VideoView video = (VideoView) frame.getFocusedChild(); + // video.stopPlayback(); + // frame.removeView(video); + // video.setVisibility(View.GONE); + // } + // } else { + mActivity.onShowCustomView(view, callback); + + // } + + super.onShowCustomView(view, callback); + } + + @Override + @Deprecated + public void onShowCustomView(View view, int requestedOrientation, + CustomViewCallback callback) { + // While these lines might look like they work, in practice, + // Full-screen videos won't work correctly. I may test this out some + // more + // if (view instanceof FrameLayout) { + // FrameLayout frame = (FrameLayout) view; + // if (frame.getFocusedChild() instanceof VideoView) { + // VideoView video = (VideoView) frame.getFocusedChild(); + // video.stopPlayback(); + // frame.removeView(video); + // video.setVisibility(View.GONE); + // } + // } else { + mActivity.onShowCustomView(view, callback); + + // } + + super.onShowCustomView(view, requestedOrientation, callback); + } +} 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 a14f65d..964e897 100644 --- a/app/src/main/java/acr/browser/lightning/view/LightningView.java +++ b/app/src/main/java/acr/browser/lightning/view/LightningView.java @@ -6,26 +6,18 @@ package acr.browser.lightning.view; import android.Manifest; import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.ActivityNotFoundException; import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; -import android.net.MailTo; import android.net.Uri; -import android.net.http.SslError; import android.os.Build; +import android.os.Handler; 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; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; @@ -34,60 +26,48 @@ 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; -import android.webkit.SslErrorHandler; -import android.webkit.ValueCallback; -import android.webkit.WebChromeClient; -import android.webkit.WebResourceRequest; -import android.webkit.WebResourceResponse; import android.webkit.WebSettings; import android.webkit.WebSettings.LayoutAlgorithm; import android.webkit.WebSettings.PluginState; import android.webkit.WebView; -import android.webkit.WebViewClient; -import android.widget.EditText; -import android.widget.LinearLayout; -import java.io.ByteArrayInputStream; +import com.squareup.otto.Bus; + import java.io.File; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.net.URISyntaxException; + +import javax.inject.Inject; import java.util.ArrayList; import java.util.List; import acr.browser.lightning.R; +import acr.browser.lightning.activity.BrowserActivity; import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.bus.BrowserEvents; import acr.browser.lightning.constant.Constants; +import acr.browser.lightning.constant.HistoryPage; import acr.browser.lightning.constant.StartPage; -import acr.browser.lightning.controller.BrowserController; +import acr.browser.lightning.dialog.LightningDialogBuilder; import acr.browser.lightning.download.LightningDownloadListener; import acr.browser.lightning.preference.PreferenceManager; -import acr.browser.lightning.utils.AdBlock; -import acr.browser.lightning.utils.IntentUtils; import acr.browser.lightning.utils.PermissionsManager; +import acr.browser.lightning.utils.ProxyUtils; import acr.browser.lightning.utils.ThemeUtils; import acr.browser.lightning.utils.Utils; public class LightningView { - private final Title mTitle; + final LightningViewTitle mTitle; private WebView mWebView; - private final boolean mIsIncognitoTab; - private final BrowserController mBrowserController; + final boolean mIsIncognitoTab; private final GestureDetector mGestureDetector; - private final Activity mActivity; + private final BrowserActivity mActivity; private static String mHomepage; private static String mDefaultUserAgent; - // TODO fix so that mWebpageBitmap can be static - static changes the icon when switching from light to dark and then back to light - private final Bitmap mWebpageBitmap; - private static PreferenceManager mPreferences; - private final AdBlock mAdBlock; - private final IntentUtils mIntentUtils; private final Paint mPaint = new Paint(); private boolean isForegroundTab; - private boolean mTextReflow = false; private boolean mInvertPage = false; private boolean mToggleDesktop = false; private static float mMaxFling; @@ -101,24 +81,28 @@ public class LightningView { }; private final PermissionsManager mPermissionsManager; private static final String[] PERMISSIONS = new String[]{Manifest.permission.ACCESS_FINE_LOCATION}; + private final WebViewHandler webViewHandler = new WebViewHandler(); + + @Inject + Bus eventBus; + + @Inject + PreferenceManager mPreferences; + + @Inject + LightningDialogBuilder bookmarksDialogBuilder; @SuppressLint("NewApi") - public LightningView(Activity activity, String url, boolean darkTheme, boolean isIncognito, BrowserController controller) { - + public LightningView(BrowserActivity activity, String url, boolean darkTheme, boolean isIncognito) { + BrowserApp.getAppComponent().inject(this); mActivity = activity; mWebView = new WebView(activity); mIsIncognitoTab = isIncognito; - mTitle = new Title(activity, darkTheme); - mAdBlock = AdBlock.getInstance(activity.getApplicationContext()); + mTitle = new LightningViewTitle(activity, darkTheme); mPermissionsManager = PermissionsManager.getInstance(); - mWebpageBitmap = mTitle.mDefaultIcon; - mMaxFling = ViewConfiguration.get(activity).getScaledMaximumFlingVelocity(); - mBrowserController = controller; - - mIntentUtils = new IntentUtils(mActivity); mWebView.setDrawingCacheBackgroundColor(Color.WHITE); mWebView.setFocusableInTouchMode(true); mWebView.setFocusable(true); @@ -135,8 +119,8 @@ public class LightningView { mWebView.setScrollbarFadingEnabled(true); mWebView.setSaveEnabled(true); mWebView.setNetworkAvailable(true); - mWebView.setWebChromeClient(new LightningChromeClient(activity)); - mWebView.setWebViewClient(new LightningWebClient(activity)); + mWebView.setWebChromeClient(new LightningChromeClient(activity, this)); + mWebView.setWebViewClient(new LightningWebClient(activity, this)); mWebView.setDownloadListener(new LightningDownloadListener(activity)); mGestureDetector = new GestureDetector(activity, new CustomGestureListener()); mWebView.setOnTouchListener(new TouchListener()); @@ -162,12 +146,37 @@ public class LightningView { if (mHomepage.startsWith("about:home")) { mWebView.loadUrl(StartPage.getHomepage(mActivity)); } else if (mHomepage.startsWith("about:bookmarks")) { - mBrowserController.openBookmarkPage(mWebView); + loadBookmarkpage(); } else { mWebView.loadUrl(mHomepage); } } + /** + * Load the HTML bookmarks page in this view + */ + public void loadBookmarkpage() { + if (mWebView == null) + return; + Bitmap folderIcon = ThemeUtils.getThemedBitmap(mActivity, R.drawable.ic_folder, false); + FileOutputStream outputStream = null; + File image = new File(mActivity.getCacheDir(), "folder.png"); + try { + outputStream = new FileOutputStream(image); + folderIcon.compress(Bitmap.CompressFormat.PNG, 100, outputStream); + folderIcon.recycle(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } finally { + Utils.close(outputStream); + } + File bookmarkWebPage = new File(mActivity.getFilesDir(), Constants.BOOKMARKS_FILENAME); + + BrowserApp.getAppComponent().getBookmarkPage().buildBookmarkPage(null); + mWebView.loadUrl(Constants.FILE + bookmarkWebPage); + + } + /** * Initialize the preference driven settings of the WebView * @@ -182,12 +191,9 @@ public class LightningView { } else if (settings == null) { settings = mWebView.getSettings(); } - mPreferences = PreferenceManager.getInstance(); settings.setDefaultTextEncodingName(mPreferences.getTextEncoding()); mHomepage = mPreferences.getHomepage(); - mAdBlock.updatePreference(); - setColorMode(mPreferences.getRenderingMode()); if (!mIsIncognitoTab) { @@ -242,7 +248,6 @@ public class LightningView { } if (mPreferences.getTextReflowEnabled()) { - mTextReflow = true; settings.setLayoutAlgorithm(LayoutAlgorithm.NARROW_COLUMNS); if (API >= android.os.Build.VERSION_CODES.KITKAT) { try { @@ -254,7 +259,6 @@ public class LightningView { } } } else { - mTextReflow = false; settings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL); } @@ -408,7 +412,7 @@ public class LightningView { public void setForegroundTab(boolean isForeground) { isForegroundTab = isForeground; - mBrowserController.updateTabs(); + eventBus.post(new BrowserEvents.TabsChanged()); } public boolean isForegroundTab() { @@ -511,7 +515,7 @@ public class LightningView { public synchronized void reload() { // Check if configured proxy is available - if (mBrowserController.proxyIsNotReady()) { + if (!ProxyUtils.getInstance().isProxyReady()) { // User has been notified return; } @@ -579,6 +583,60 @@ public class LightningView { } } + /** + * Used by {@link LightningWebClient} + * + * @return true if the page is in inverted mode, false otherwise + */ + public boolean getInvertePage() { + return mInvertPage; + } + /** + * handles a long click on the page, parameter String url + * 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 + */ + private void longClickPage(final String url) { + final WebView.HitTestResult result = mWebView.getHitTestResult(); + String currentUrl = mWebView.getUrl(); + if (currentUrl != null && currentUrl.startsWith(Constants.FILE)) { + if (currentUrl.endsWith(HistoryPage.FILENAME)) { + if (url != null) { + bookmarksDialogBuilder.showLongPressedHistoryLinkDialog(mActivity, url); + } else if (result != null && result.getExtra() != null) { + final String newUrl = result.getExtra(); + bookmarksDialogBuilder.showLongPressedHistoryLinkDialog(mActivity, newUrl); + } + } else if (currentUrl.endsWith(Constants.BOOKMARKS_FILENAME)) { + if (url != null) { + bookmarksDialogBuilder.showLongPressLinkDialog(mActivity, url); + } else if (result != null && result.getExtra() != null) { + final String newUrl = result.getExtra(); + bookmarksDialogBuilder.showLongPressLinkDialog(mActivity, newUrl); + } + } + } else { + if (url != null) { + if (result != null) { + if (result.getType() == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE || result.getType() == WebView.HitTestResult.IMAGE_TYPE) { + bookmarksDialogBuilder.showLongPressImageDialog(mActivity, url, getUserAgent()); + } else { + bookmarksDialogBuilder.showLongPressLinkDialog(mActivity, url); + } + } else { + bookmarksDialogBuilder.showLongPressLinkDialog(mActivity, url); + } + } else if (result != null && result.getExtra() != null) { + final String newUrl = result.getExtra(); + if (result.getType() == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE || result.getType() == WebView.HitTestResult.IMAGE_TYPE) { + bookmarksDialogBuilder.showLongPressImageDialog(mActivity, newUrl, getUserAgent()); + } else { + bookmarksDialogBuilder.showLongPressLinkDialog(mActivity, newUrl); + } + } + } + } + public boolean canGoBack() { return mWebView != null && mWebView.canGoBack(); } @@ -598,8 +656,7 @@ public class LightningView { public synchronized void loadUrl(String url) { // Check if configured proxy is available - if (mBrowserController.proxyIsNotReady()) { - // User has been notified + if (!ProxyUtils.getInstance().isProxyReady()) { return; } @@ -621,500 +678,6 @@ public class LightningView { } } - private static class IconCacheTask implements Runnable { - private final Uri uri; - private final Bitmap icon; - - public IconCacheTask(Uri uri, Bitmap icon) { - this.uri = uri; - this.icon = icon; - } - - @Override - public void run() { - String hash = String.valueOf(uri.getHost().hashCode()); - Log.d(Constants.TAG, "Caching icon for " + uri.getHost()); - FileOutputStream fos = null; - try { - File image = new File(BrowserApp.getAppContext().getCacheDir(), hash + ".png"); - fos = new FileOutputStream(image); - icon.compress(Bitmap.CompressFormat.PNG, 100, fos); - fos.flush(); - } catch (IOException e) { - e.printStackTrace(); - } finally { - Utils.close(fos); - } - } - } - - public class LightningWebClient extends WebViewClient { - - final Activity mActivity; - - LightningWebClient(Activity activity) { - mActivity = activity; - } - - @Override - public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - if (mAdBlock.isAd(request.getUrl().toString())) { - ByteArrayInputStream EMPTY = new ByteArrayInputStream("".getBytes()); - return new WebResourceResponse("text/plain", "utf-8", EMPTY); - } - } - return super.shouldInterceptRequest(view, request); - } - - @SuppressWarnings("deprecation") - @Override - public WebResourceResponse shouldInterceptRequest(WebView view, String url) { - if (mAdBlock.isAd(url)) { - ByteArrayInputStream EMPTY = new ByteArrayInputStream("".getBytes()); - return new WebResourceResponse("text/plain", "utf-8", EMPTY); - } - return null; - } - - @SuppressLint("NewApi") - @Override - public void onPageFinished(WebView view, String url) { - if (view.isShown()) { - mBrowserController.updateUrl(url, true); - view.postInvalidate(); - } - if (view.getTitle() == null || view.getTitle().isEmpty()) { - mTitle.setTitle(mActivity.getString(R.string.untitled)); - } else { - mTitle.setTitle(view.getTitle()); - } - if (API >= android.os.Build.VERSION_CODES.KITKAT && mInvertPage) { - view.evaluateJavascript(Constants.JAVASCRIPT_INVERT_PAGE, null); - } - mBrowserController.updateTabs(); - } - - @Override - public void onPageStarted(WebView view, String url, Bitmap favicon) { - if (isShown()) { - mBrowserController.updateUrl(url, false); - mBrowserController.showActionBar(); - } - mTitle.setFavicon(mWebpageBitmap); - mBrowserController.updateTabs(); - } - - @Override - public void onReceivedHttpAuthRequest(final WebView view, @NonNull final HttpAuthHandler handler, - final String host, final String realm) { - - AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); - final EditText name = new EditText(mActivity); - final EditText password = new EditText(mActivity); - LinearLayout passLayout = new LinearLayout(mActivity); - passLayout.setOrientation(LinearLayout.VERTICAL); - - passLayout.addView(name); - passLayout.addView(password); - - name.setHint(mActivity.getString(R.string.hint_username)); - name.setSingleLine(); - password.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD); - password.setSingleLine(); - password.setTransformationMethod(new PasswordTransformationMethod()); - password.setHint(mActivity.getString(R.string.hint_password)); - builder.setTitle(mActivity.getString(R.string.title_sign_in)); - builder.setView(passLayout); - builder.setCancelable(true) - .setPositiveButton(mActivity.getString(R.string.title_sign_in), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - String user = name.getText().toString(); - String pass = password.getText().toString(); - handler.proceed(user.trim(), pass.trim()); - Log.d(Constants.TAG, "Request Login"); - - } - }) - .setNegativeButton(mActivity.getString(R.string.action_cancel), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - handler.cancel(); - - } - }); - AlertDialog alert = builder.create(); - alert.show(); - - } - - private boolean mIsRunning = false; - private float mZoomScale = 0.0f; - - @SuppressLint("NewApi") - @Override - public void onScaleChanged(final WebView view, final float oldScale, final float newScale) { - if (view.isShown() && mTextReflow && API >= android.os.Build.VERSION_CODES.KITKAT) { - if (mIsRunning) - return; - if (Math.abs(mZoomScale - newScale) > 0.01f) { - mIsRunning = view.postDelayed(new Runnable() { - - @Override - public void run() { - mZoomScale = newScale; - view.evaluateJavascript(Constants.JAVASCRIPT_TEXT_REFLOW, null); - mIsRunning = false; - } - - }, 100); - } - - } - } - - private List getAllSslErrorMessageCodes(SslError error) { - - List errorCodeMessageCodes = new ArrayList<>(); - - if (error.hasError(SslError.SSL_DATE_INVALID)) { - errorCodeMessageCodes.add(R.string.message_certificate_date_invalid); - } - if (error.hasError(SslError.SSL_EXPIRED)) { - errorCodeMessageCodes.add(R.string.message_certificate_expired); - } - if (error.hasError(SslError.SSL_IDMISMATCH)) { - errorCodeMessageCodes.add(R.string.message_certificate_domain_mismatch); - } - if (error.hasError(SslError.SSL_NOTYETVALID)) { - errorCodeMessageCodes.add(R.string.message_certificate_not_yet_valid); - } - if (error.hasError(SslError.SSL_UNTRUSTED)) { - errorCodeMessageCodes.add(R.string.message_certificate_untrusted); - } - if (error.hasError(SslError.SSL_INVALID)) { - errorCodeMessageCodes.add(R.string.message_certificate_invalid); - } - - return errorCodeMessageCodes; - } - - @Override - public void onReceivedSslError(WebView view, @NonNull final SslErrorHandler handler, SslError error) { - - List errorCodeMessageCodes = getAllSslErrorMessageCodes(error); - - StringBuilder stringBuilder = new StringBuilder(); - for (Integer messageCode : errorCodeMessageCodes) { - stringBuilder.append(" - ").append(mActivity.getString(messageCode)).append('\n'); - } - String alertMessage = - mActivity.getString(R.string.message_insecure_connection, stringBuilder.toString()); - - AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); - builder.setTitle(mActivity.getString(R.string.title_warning)); - builder.setMessage(alertMessage) - .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(); - } - }); - builder.create().show(); - } - - @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.proxyIsNotReady()) { - // User has been notified - return true; - } - - if (mIsIncognitoTab) { - return super.shouldOverrideUrlLoading(view, url); - } - if (url.startsWith("about:")) { - return super.shouldOverrideUrlLoading(view, url); - } - if (url.contains("mailto:")) { - MailTo mailTo = MailTo.parse(url); - Intent i = Utils.newEmailIntent(mailTo.getTo(), mailTo.getSubject(), - mailTo.getBody(), mailTo.getCc()); - mActivity.startActivity(i); - view.reload(); - return true; - } else if (url.startsWith("intent://")) { - Intent intent; - try { - intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); - } catch (URISyntaxException ex) { - return false; - } - if (intent != null) { - try { - mActivity.startActivity(intent); - } catch (ActivityNotFoundException e) { - Log.e(Constants.TAG, "ActivityNotFoundException"); - } - return true; - } - } - return mIntentUtils.startActivityForUrl(mWebView, url); - } - } - - public class LightningChromeClient extends WebChromeClient { - - final Activity mActivity; - - LightningChromeClient(Activity activity) { - mActivity = activity; - } - - @Override - public void onProgressChanged(WebView view, int newProgress) { - if (isShown()) { - mBrowserController.updateProgress(newProgress); - } - } - - @Override - public void onReceivedIcon(WebView view, Bitmap icon) { - if (icon == null) - return; - mTitle.setFavicon(icon); - mBrowserController.updateTabs(); - 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.updateTabs(); - if (view != null) - mBrowserController.updateHistory(title, view.getUrl()); - } - - @Override - public void onGeolocationPermissionsShowPrompt(final String origin, - final GeolocationPermissions.Callback callback) { - mPermissionsManager.requestPermissionsIfNecessary(mActivity, PERMISSIONS); - final boolean remember = true; - AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); - builder.setTitle(mActivity.getString(R.string.location)); - String org; - if (origin.length() > 50) { - org = origin.subSequence(0, 50) + "..."; - } else { - org = origin; - } - builder.setMessage(org + mActivity.getString(R.string.message_location)) - .setCancelable(true) - .setPositiveButton(mActivity.getString(R.string.action_allow), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - callback.invoke(origin, true, remember); - } - }) - .setNegativeButton(mActivity.getString(R.string.action_dont_allow), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - callback.invoke(origin, false, remember); - } - }); - AlertDialog alert = builder.create(); - alert.show(); - - } - - @Override - public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, - Message resultMsg) { - mBrowserController.onCreateWindow(resultMsg); - return true; - } - - @Override - public void onCloseWindow(WebView window) { - mBrowserController.onCloseWindow(LightningView.this); - } - - @SuppressWarnings("unused") - public void openFileChooser(ValueCallback uploadMsg) { - mBrowserController.openFileChooser(uploadMsg); - } - - @SuppressWarnings("unused") - public void openFileChooser(ValueCallback uploadMsg, String acceptType) { - mBrowserController.openFileChooser(uploadMsg); - } - - @SuppressWarnings("unused") - 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 { - mBrowserController.onShowCustomView(view, callback); - - // } - - super.onShowCustomView(view, callback); - } - - @SuppressWarnings("deprecation") - @Override - @Deprecated - public void onShowCustomView(View view, int requestedOrientation, - CustomViewCallback callback) { - // While these lines might look like they work, in practice, - // Full-screen videos won't work correctly. I may test this out some - // more - // if (view instanceof FrameLayout) { - // FrameLayout frame = (FrameLayout) view; - // if (frame.getFocusedChild() instanceof VideoView) { - // VideoView video = (VideoView) frame.getFocusedChild(); - // video.stopPlayback(); - // frame.removeView(video); - // video.setVisibility(View.GONE); - // } - // } else { - mBrowserController.onShowCustomView(view, callback); - - // } - - super.onShowCustomView(view, requestedOrientation, callback); - } - } - - public class Title { - - private Bitmap mFavicon; - private String mTitle; - private final Bitmap mDefaultIcon; - - public Title(Context context, boolean darkTheme) { - mDefaultIcon = ThemeUtils.getThemedBitmap(context, R.drawable.ic_webpage, darkTheme); - mFavicon = mDefaultIcon; - mTitle = mActivity.getString(R.string.action_new_tab); - } - - public void setFavicon(Bitmap favicon) { - if (favicon == null) { - mFavicon = mDefaultIcon; - } else { - mFavicon = Utils.padFavicon(favicon); - } - } - - public void setTitle(String title) { - if (title == null) { - mTitle = ""; - } else { - mTitle = title; - } - } - - public void setTitleAndFavicon(String title, Bitmap favicon) { - mTitle = title; - - if (favicon == null) { - mFavicon = mDefaultIcon; - } else { - mFavicon = Utils.padFavicon(favicon); - } - } - - public String getTitle() { - return mTitle; - } - - public Bitmap getFavicon() { - return mFavicon; - } - - } - private class TouchListener implements OnTouchListener { float mLocation; @@ -1137,9 +700,9 @@ public class LightningView { } else if (mAction == MotionEvent.ACTION_UP) { final float distance = (mY - mLocation); if (distance > SCROLL_UP_THRESHOLD && view.getScrollY() < SCROLL_UP_THRESHOLD) { - mBrowserController.showActionBar(); + mActivity.showActionBar(); } else if (distance < -SCROLL_UP_THRESHOLD) { - mBrowserController.hideActionBar(); + mActivity.hideActionBar(); } mLocation = 0; } @@ -1154,9 +717,9 @@ public class LightningView { public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { int power = (int) (velocityY * 100 / mMaxFling); if (power < -10) { - mBrowserController.hideActionBar(); + mActivity.hideActionBar(); } else if (power > 15) { - mBrowserController.showActionBar(); + mActivity.showActionBar(); } return super.onFling(e1, e2, velocityX, velocityY); } @@ -1172,8 +735,13 @@ public class LightningView { @Override public void onLongPress(MotionEvent e) { - if (mCanTriggerLongPress) - mBrowserController.onLongPress(); + if (mCanTriggerLongPress) { + Message msg = webViewHandler.obtainMessage(); + if (msg != null) { + msg.setTarget(webViewHandler); + mWebView.requestFocusNodeHref(msg); + } + } } /** @@ -1195,4 +763,13 @@ public class LightningView { mCanTriggerLongPress = true; } } + + private class WebViewHandler extends Handler { + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); + final String url = msg.getData().getString("url"); + longClickPage(url); + } + } } diff --git a/app/src/main/java/acr/browser/lightning/view/LightningViewTitle.java b/app/src/main/java/acr/browser/lightning/view/LightningViewTitle.java new file mode 100644 index 0000000..2452d85 --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/view/LightningViewTitle.java @@ -0,0 +1,66 @@ +package acr.browser.lightning.view; + +import android.content.Context; +import android.graphics.Bitmap; + +import acr.browser.lightning.R; +import acr.browser.lightning.utils.ThemeUtils; +import acr.browser.lightning.utils.Utils; + +/** + * @author Stefano Pacifici base on Anthony C. Restaino's code + * @date 2015/09/21 + */ +class LightningViewTitle { + + private static Bitmap DEFAULT_ICON = null; + + private Bitmap mFavicon; + private String mTitle; + + public LightningViewTitle(Context context, boolean darkTheme) { + if (DEFAULT_ICON == null) { + DEFAULT_ICON = ThemeUtils.getThemedBitmap(context, R.drawable.ic_webpage, darkTheme); + } + mFavicon = DEFAULT_ICON; + mTitle = context.getString(R.string.action_new_tab); + } + + public void setFavicon(Bitmap favicon) { + if (favicon == null) { + mFavicon = DEFAULT_ICON; + } else { + mFavicon = Utils.padFavicon(favicon); + } + } + + public void setTitle(String title) { + if (title == null) { + mTitle = ""; + } else { + mTitle = title; + } + } + + public void setTitleAndFavicon(String title, Bitmap favicon) { + mTitle = title; + + if (favicon == null) { + mFavicon = DEFAULT_ICON; + } else { + mFavicon = Utils.padFavicon(favicon); + } + } + + public String getTitle() { + return mTitle; + } + + public Bitmap getFavicon() { + return mFavicon; + } + + public Bitmap getDefaultIcon() { + return DEFAULT_ICON; + } +} diff --git a/app/src/main/java/acr/browser/lightning/view/LightningWebClient.java b/app/src/main/java/acr/browser/lightning/view/LightningWebClient.java new file mode 100644 index 0000000..b57556e --- /dev/null +++ b/app/src/main/java/acr/browser/lightning/view/LightningWebClient.java @@ -0,0 +1,276 @@ +package acr.browser.lightning.view; + +import android.annotation.SuppressLint; +import android.content.ActivityNotFoundException; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.MailTo; +import android.net.http.SslError; +import android.os.Build; +import android.os.Message; +import android.support.annotation.NonNull; +import android.support.v7.app.AlertDialog; +import android.text.InputType; +import android.text.method.PasswordTransformationMethod; +import android.util.Log; +import android.webkit.HttpAuthHandler; +import android.webkit.SslErrorHandler; +import android.webkit.WebResourceRequest; +import android.webkit.WebResourceResponse; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.EditText; +import android.widget.LinearLayout; + +import com.squareup.otto.Bus; + +import java.io.ByteArrayInputStream; +import java.net.URISyntaxException; + +import acr.browser.lightning.R; +import acr.browser.lightning.activity.BrowserActivity; +import acr.browser.lightning.app.BrowserApp; +import acr.browser.lightning.bus.BrowserEvents; +import acr.browser.lightning.constant.Constants; +import acr.browser.lightning.utils.AdBlock; +import acr.browser.lightning.utils.IntentUtils; +import acr.browser.lightning.utils.ProxyUtils; +import acr.browser.lightning.utils.Utils; + +/** + * @author Stefano Pacifici based on Anthony C. Restaino's code + * @date 2015/09/22 + */ +public class LightningWebClient extends WebViewClient { + + private final BrowserActivity mActivity; + private final LightningView mLightningView; + private final AdBlock mAdBlock; + private final Bus mEventBus; + private final IntentUtils mIntentUtils; + + LightningWebClient(BrowserActivity activity, LightningView lightningView) { + mActivity = activity; + mLightningView = lightningView; + mAdBlock = AdBlock.getInstance(activity); + mAdBlock.updatePreference(); + mEventBus = BrowserApp.getAppComponent().getBus(); + mIntentUtils = new IntentUtils(activity); + } + + @Override + public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + if (mAdBlock.isAd(request.getUrl().toString())) { + ByteArrayInputStream EMPTY = new ByteArrayInputStream("".getBytes()); + return new WebResourceResponse("text/plain", "utf-8", EMPTY); + } + } + return super.shouldInterceptRequest(view, request); + } + + @Override + public WebResourceResponse shouldInterceptRequest(WebView view, String url) { + if (mAdBlock.isAd(url)) { + ByteArrayInputStream EMPTY = new ByteArrayInputStream("".getBytes()); + return new WebResourceResponse("text/plain", "utf-8", EMPTY); + } + return null; + } + + @SuppressLint("NewApi") + @Override + public void onPageFinished(WebView view, String url) { + if (view.isShown()) { + mActivity.updateUrl(url, true); + view.postInvalidate(); + } + if (view.getTitle() == null || view.getTitle().isEmpty()) { + mLightningView.mTitle.setTitle(mActivity.getString(R.string.untitled)); + } else { + mLightningView.mTitle.setTitle(view.getTitle()); + } + if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT && + mLightningView.getInvertePage()) { + view.evaluateJavascript(Constants.JAVASCRIPT_INVERT_PAGE, null); + } + mEventBus.post(new BrowserEvents.TabsChanged()); + } + + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + if (mLightningView.isShown()) { + mActivity.updateUrl(url, false); + mActivity.showActionBar(); + } + mEventBus.post(new BrowserEvents.TabsChanged()); + } + + @Override + public void onReceivedHttpAuthRequest(final WebView view, @NonNull final HttpAuthHandler handler, + final String host, final String realm) { + + AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); + final EditText name = new EditText(mActivity); + final EditText password = new EditText(mActivity); + LinearLayout passLayout = new LinearLayout(mActivity); + passLayout.setOrientation(LinearLayout.VERTICAL); + + passLayout.addView(name); + passLayout.addView(password); + + name.setHint(mActivity.getString(R.string.hint_username)); + name.setSingleLine(); + password.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD); + password.setSingleLine(); + password.setTransformationMethod(new PasswordTransformationMethod()); + password.setHint(mActivity.getString(R.string.hint_password)); + builder.setTitle(mActivity.getString(R.string.title_sign_in)); + builder.setView(passLayout); + builder.setCancelable(true) + .setPositiveButton(mActivity.getString(R.string.title_sign_in), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + String user = name.getText().toString(); + String pass = password.getText().toString(); + handler.proceed(user.trim(), pass.trim()); + Log.d(Constants.TAG, "Request Login"); + + } + }) + .setNegativeButton(mActivity.getString(R.string.action_cancel), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + handler.cancel(); + + } + }); + AlertDialog alert = builder.create(); + alert.show(); + + } + + private boolean mIsRunning = false; + private float mZoomScale = 0.0f; + + @SuppressLint("NewApi") + @Override + public void onScaleChanged(final WebView view, final float oldScale, final float newScale) { + if (view.isShown() && mLightningView.mPreferences.getTextReflowEnabled() && + Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { + if (mIsRunning) + return; + if (Math.abs(mZoomScale - newScale) > 0.01f) { + mIsRunning = view.postDelayed(new Runnable() { + + @Override + public void run() { + mZoomScale = newScale; + view.evaluateJavascript(Constants.JAVASCRIPT_TEXT_REFLOW, null); + mIsRunning = false; + } + + }, 100); + } + + } + } + + @Override + public void onReceivedSslError(WebView view, @NonNull final SslErrorHandler handler, SslError error) { + AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); + builder.setTitle(mActivity.getString(R.string.title_warning)); + builder.setMessage(mActivity.getString(R.string.message_untrusted_certificate)) + .setCancelable(true) + .setPositiveButton(mActivity.getString(R.string.action_yes), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + handler.proceed(); + } + }) + .setNegativeButton(mActivity.getString(R.string.action_no), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + handler.cancel(); + } + }); + AlertDialog alert = builder.create(); + if (error.getPrimaryError() == SslError.SSL_UNTRUSTED) { + alert.show(); + } else { + handler.proceed(); + } + + } + + @Override + public void onFormResubmission(WebView view, @NonNull final Message dontResend, final Message resend) { + AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); + builder.setTitle(mActivity.getString(R.string.title_form_resubmission)); + builder.setMessage(mActivity.getString(R.string.message_form_resubmission)) + .setCancelable(true) + .setPositiveButton(mActivity.getString(R.string.action_yes), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + + resend.sendToTarget(); + } + }) + .setNegativeButton(mActivity.getString(R.string.action_no), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + + dontResend.sendToTarget(); + } + }); + AlertDialog alert = builder.create(); + alert.show(); + } + + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + // Check if configured proxy is available + if (!ProxyUtils.getInstance().isProxyReady()) { + // User has been notified + return true; + } + + if (mLightningView.mIsIncognitoTab) { + return super.shouldOverrideUrlLoading(view, url); + } + if (url.startsWith("about:")) { + return super.shouldOverrideUrlLoading(view, url); + } + if (url.contains("mailto:")) { + MailTo mailTo = MailTo.parse(url); + Intent i = Utils.newEmailIntent(mailTo.getTo(), mailTo.getSubject(), + mailTo.getBody(), mailTo.getCc()); + mActivity.startActivity(i); + view.reload(); + return true; + } else if (url.startsWith("intent://")) { + Intent intent; + try { + intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); + } catch (URISyntaxException ex) { + return false; + } + if (intent != null) { + try { + mActivity.startActivity(intent); + } catch (ActivityNotFoundException e) { + Log.e(Constants.TAG, "ActivityNotFoundException"); + } + return true; + } + } + return mIntentUtils.startActivityForUrl(view, url); + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 31777df..eae84d4 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -25,7 +25,14 @@ - + - - - + android:layout_height="match_parent" /> + \ No newline at end of file diff --git a/app/src/main/res/layout/tab_drawer.xml b/app/src/main/res/layout/tab_drawer.xml index dbed4bd..bbf334e 100644 --- a/app/src/main/res/layout/tab_drawer.xml +++ b/app/src/main/res/layout/tab_drawer.xml @@ -1,12 +1,9 @@ + android:dividerHeight="0dp" + android:overScrollMode="ifContentScrolls" /> + + diff --git a/app/src/main/res/layout/toolbar.xml b/app/src/main/res/layout/toolbar.xml index 9b144f3..cc0fbcb 100644 --- a/app/src/main/res/layout/toolbar.xml +++ b/app/src/main/res/layout/toolbar.xml @@ -8,13 +8,10 @@ android:elevation="2dp" android:orientation="vertical"> - + android:id="@+id/tabs_toolbar_container"/>