/* * Tint Browser for Android * * Copyright (C) 2012 - to infinity and beyond J. Devauchelle and contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 3 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ package com.seal.ui.managers; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import java.util.UUID; import android.annotation.SuppressLint; import android.app.ActionBar; import android.app.DownloadManager; import android.app.FragmentManager; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences.Editor; import android.graphics.Bitmap; import android.graphics.Picture; import android.graphics.drawable.BitmapDrawable; import android.net.Uri; import android.os.Handler; import android.os.Message; import android.preference.PreferenceManager; import android.text.TextUtils; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.webkit.GeolocationPermissions.Callback; import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebChromeClient.CustomViewCallback; import android.webkit.WebViewDatabase; import android.widget.FrameLayout; import android.widget.Toast; import com.mogoweb.chrome.WebView; import com.mogoweb.chrome.WebView.HitTestResult; import com.seal.R; import com.seal.controllers.Controller; import com.seal.model.DownloadItem; import com.seal.providers.BookmarksWrapper; import com.seal.tasks.ThumbnailSaver; import com.seal.ui.activities.BookmarksActivity; import com.seal.ui.activities.EditBookmarkActivity; import com.seal.ui.activities.SealBrowserActivity; import com.seal.ui.components.CustomWebView; import com.seal.ui.dialogs.GeolocationPermissionsDialog; import com.seal.ui.fragments.BaseWebViewFragment; import com.seal.ui.fragments.StartPageFragment; import com.seal.utils.ApplicationUtils; import com.seal.utils.Constants; @SuppressLint("HandlerLeak") public abstract class BaseUIManager implements UIManager {// , WebViewFragmentListener { protected static final FrameLayout.LayoutParams COVER_SCREEN_PARAMS = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); private static final int FOCUS_NODE_HREF = 102; private FrameLayout mFullscreenContainer; private View mCustomView; private WebChromeClient.CustomViewCallback mCustomViewCallback; private int mOriginalOrientation; private GeolocationPermissionsDialog mGeolocationPermissionsDialog; protected SealBrowserActivity mActivity; protected ActionBar mActionBar; protected FragmentManager mFragmentManager; protected boolean mHomePageLoading = false; protected boolean mMenuVisible = false; private ValueCallback<Uri> mUploadMessage = null; protected StartPageFragment mStartPageFragment = null; private Handler mHandler; public BaseUIManager(SealBrowserActivity activity) { mActivity = activity; mActionBar = mActivity.getActionBar(); mFragmentManager = mActivity.getFragmentManager(); mGeolocationPermissionsDialog = null; setupUI(); startHandler(); } protected abstract String getCurrentUrl(); protected abstract int getTabCount(); protected abstract BaseWebViewFragment getWebViewFragmentByUUID(UUID fragmentId); protected abstract void showStartPage(BaseWebViewFragment webViewFragment); protected abstract void hideStartPage(BaseWebViewFragment webViewFragment); protected abstract void resetUI(); protected void setApplicationButtonImage(Bitmap icon) { BitmapDrawable image = ApplicationUtils.getApplicationButtonImage(mActivity, icon); if (image != null) { mActionBar.setIcon(image); } else { mActionBar.setIcon(R.drawable.ic_launcher); } } protected void setupUI() { setFullScreenFromPreferences(); } @Override public SealBrowserActivity getMainActivity() { return mActivity; } @Override public void addTab(boolean loadHomePage, boolean privateBrowsing, boolean openByParent) { if (loadHomePage) { addTab(PreferenceManager.getDefaultSharedPreferences(mActivity).getString(Constants.PREFERENCE_HOME_PAGE, Constants.URL_ABOUT_START), false, privateBrowsing, openByParent); } else { addTab(null, false, privateBrowsing, openByParent); } } @Override public void togglePrivateBrowsing() { BaseWebViewFragment fragment = getCurrentWebViewFragment(); if (fragment != null) { CustomWebView webView = fragment.getWebView(); String currentUrl = webView.getUrl(); fragment.setPrivateBrowsing(!fragment.isPrivateBrowsingEnabled()); fragment.resetWebView(); resetUI(); loadUrl(currentUrl); } } @Override public void loadUrl(String url) { loadUrl(getCurrentWebViewFragment(), url); } @Override public void loadUrl(UUID tabId, String url, boolean loadInCurrentTabIfNotFound) { BaseWebViewFragment fragment = getWebViewFragmentByUUID(tabId); if (fragment != null) { loadUrl(fragment, url); } else { if (loadInCurrentTabIfNotFound) { loadUrl(url); } } } @Override public void loadRawUrl(UUID tabId, String url, boolean loadInCurrentTabIfNotFound) { BaseWebViewFragment fragment = getWebViewFragmentByUUID(tabId); if (fragment != null) { fragment.getWebView().loadRawUrl(url); } else { if (loadInCurrentTabIfNotFound) { getCurrentWebView().loadRawUrl(url); } } } @Override public void loadHomePage() { mHomePageLoading = true; loadUrl(PreferenceManager.getDefaultSharedPreferences(mActivity).getString(Constants.PREFERENCE_HOME_PAGE, Constants.URL_ABOUT_START)); } @Override public void loadHomePage(UUID tabId, boolean loadInCurrentTabIfNotFound) { mHomePageLoading = true; loadUrl(tabId, PreferenceManager.getDefaultSharedPreferences(mActivity).getString(Constants.PREFERENCE_HOME_PAGE, Constants.URL_ABOUT_START), loadInCurrentTabIfNotFound); } @Override public void loadCurrentUrl() { loadUrl(getCurrentUrl()); } @Override public void openBookmarksActivityForResult() { Intent i = new Intent(mActivity, BookmarksActivity.class); mActivity.startActivityForResult(i, SealBrowserActivity.ACTIVITY_BOOKMARKS); } @Override public void addBookmarkFromCurrentPage() { Intent i = new Intent(mActivity, EditBookmarkActivity.class); i.putExtra(Constants.EXTRA_ID, (long) -1); i.putExtra(Constants.EXTRA_LABEL, getCurrentWebView().getTitle()); i.putExtra(Constants.EXTRA_URL, getCurrentWebView().getUrl()); mActivity.startActivity(i); } @Override public void shareCurrentPage() { WebView webView = getCurrentWebView(); if (webView != null) { ApplicationUtils.sharePage(mActivity, webView.getTitle(), webView.getUrl()); } } @Override public void startSearch() { WebView webView = getCurrentWebView(); if (webView != null) { // webView.showFindDialog(null, true); } } @Override public void clearFormData() { WebViewDatabase.getInstance(mActivity).clearFormData(); getCurrentWebView().clearFormData(); } @Override public void clearCache() { getCurrentWebView().clearCache(true); } @Override public void setHttpAuthUsernamePassword(String host, String realm, String username, String password) { getCurrentWebView().setHttpAuthUsernamePassword(host, realm, username, password); } @Override public CustomWebView getWebViewByTabId(UUID tabId) { BaseWebViewFragment fragment = getWebViewFragmentByUUID(tabId); if (fragment != null) { return fragment.getWebView(); } else { return null; } } @Override public void setUploadMessage(ValueCallback<Uri> uploadMsg) { mUploadMessage = uploadMsg; } @Override public ValueCallback<Uri> getUploadMessage() { return mUploadMessage; } @Override public void onNewIntent(Intent intent) { if (intent != null) { if (Intent.ACTION_VIEW.equals(intent.getAction()) || Intent.ACTION_MAIN.equals(intent.getAction())) { // ACTION_VIEW and ACTION_MAIN can specify an url to load. String url = intent.getDataString(); if (!TextUtils.isEmpty(url)) { if (!isCurrentTabReusable()) { addTab(url, false, PreferenceManager.getDefaultSharedPreferences(this.getMainActivity()).getBoolean(Constants.PREFERENCE_INCOGNITO_BY_DEFAULT, false), false); } else { loadUrl(url); } } else { // We do not have an url. Open a new tab if there is no tab currently opened, // else do nothing. if (getTabCount() <= 0) { addTab(true, PreferenceManager.getDefaultSharedPreferences(this.getMainActivity()).getBoolean(Constants.PREFERENCE_INCOGNITO_BY_DEFAULT, false), false); } } } else if (Constants.ACTION_BROWSER_CONTEXT_MENU.equals(intent.getAction())) { if (intent.hasExtra(Constants.EXTRA_ACTION_ID)) { int actionId = intent.getIntExtra(Constants.EXTRA_ACTION_ID, -1); switch (actionId) { case SealBrowserActivity.CONTEXT_MENU_OPEN: if (HitTestResult.SRC_IMAGE_ANCHOR_TYPE == intent.getIntExtra(Constants.EXTRA_HIT_TEST_RESULT, -1)) { requestHrefNode(SealBrowserActivity.CONTEXT_MENU_OPEN); } else { loadUrl(intent.getStringExtra(Constants.EXTRA_URL)); } break; case SealBrowserActivity.CONTEXT_MENU_OPEN_IN_NEW_TAB: if (HitTestResult.SRC_IMAGE_ANCHOR_TYPE == intent.getIntExtra(Constants.EXTRA_HIT_TEST_RESULT, -1)) { requestHrefNode(SealBrowserActivity.CONTEXT_MENU_OPEN_IN_NEW_TAB, intent.getBooleanExtra(Constants.EXTRA_INCOGNITO, false)); } else { addTab(intent.getStringExtra(Constants.EXTRA_URL), false, intent.getBooleanExtra(Constants.EXTRA_INCOGNITO, false), false); } break; case SealBrowserActivity.CONTEXT_MENU_OPEN_IN_BACKGROUND: if (HitTestResult.SRC_IMAGE_ANCHOR_TYPE == intent.getIntExtra(Constants.EXTRA_HIT_TEST_RESULT, -1)) { requestHrefNode(SealBrowserActivity.CONTEXT_MENU_OPEN_IN_BACKGROUND, intent.getBooleanExtra(Constants.EXTRA_INCOGNITO, false)); } else { addTab(intent.getStringExtra(Constants.EXTRA_URL), true, intent.getBooleanExtra(Constants.EXTRA_INCOGNITO, false), false); } break; case SealBrowserActivity.CONTEXT_MENU_COPY: if (HitTestResult.SRC_IMAGE_ANCHOR_TYPE == intent.getIntExtra(Constants.EXTRA_HIT_TEST_RESULT, -1)) { requestHrefNode(SealBrowserActivity.CONTEXT_MENU_COPY); } else { ApplicationUtils.copyTextToClipboard(mActivity, intent.getStringExtra(Constants.EXTRA_URL), mActivity.getResources().getString(R.string.UrlCopyToastMessage)); } break; case SealBrowserActivity.CONTEXT_MENU_DOWNLOAD: if (HitTestResult.SRC_IMAGE_ANCHOR_TYPE == intent.getIntExtra(Constants.EXTRA_HIT_TEST_RESULT, -1)) { requestHrefNode(SealBrowserActivity.CONTEXT_MENU_DOWNLOAD); } else { try { DownloadItem item = new DownloadItem(intent.getStringExtra(Constants.EXTRA_URL)); long id = ((DownloadManager) mActivity.getSystemService(Context.DOWNLOAD_SERVICE)).enqueue(item); item.setId(id); Controller.getInstance().getDownloadsList().add(item); Toast.makeText(mActivity, String.format(mActivity.getString(R.string.DownloadStart), item.getFileName()), Toast.LENGTH_SHORT) .show(); } catch (IllegalArgumentException e) { Toast.makeText(mActivity, mActivity.getString(R.string.DownloadErrorNotSupportedScheme), Toast.LENGTH_SHORT).show(); } catch (NullPointerException e) { Toast.makeText(mActivity, mActivity.getString(R.string.DownloadErrorInvalidUrl), Toast.LENGTH_SHORT).show(); } } break; case SealBrowserActivity.CONTEXT_MENU_SHARE: if (HitTestResult.SRC_IMAGE_ANCHOR_TYPE == intent.getIntExtra(Constants.EXTRA_HIT_TEST_RESULT, -1)) { requestHrefNode(SealBrowserActivity.CONTEXT_MENU_SHARE); } else { ApplicationUtils.sharePage(mActivity, null, intent.getStringExtra(Constants.EXTRA_URL)); } break; default: if (HitTestResult.SRC_IMAGE_ANCHOR_TYPE == intent.getIntExtra(Constants.EXTRA_HIT_TEST_RESULT, -1)) { requestHrefNode(actionId); } else { Controller .getInstance() .getAddonManager() .onContributedContextLinkMenuItemSelected(mActivity, actionId, intent.getIntExtra(Constants.EXTRA_HIT_TEST_RESULT, -1), intent.getStringExtra(Constants.EXTRA_URL), getCurrentWebView()); } break; } } } } else { addTab(true, false, false); } } @Override public boolean onKeyBack() { if (mCustomView != null) { onHideCustomView(); return true; } return false; } @Override public void onPageFinished(final WebView view, final String url) { if (mHomePageLoading) { mHomePageLoading = false; if (PreferenceManager.getDefaultSharedPreferences(mActivity).getBoolean(Constants.TECHNICAL_PREFERENCE_HOMEPAGE_URL_UPDATE_NEEDED, false)) { Editor editor = PreferenceManager.getDefaultSharedPreferences(mActivity).edit(); editor.putBoolean(Constants.TECHNICAL_PREFERENCE_HOMEPAGE_URL_UPDATE_NEEDED, false); editor.putString(Constants.PREFERENCE_HOME_PAGE, url); editor.commit(); } } view.postDelayed(new Runnable() { @Override public void run() { if (BookmarksWrapper.urlHasBookmark(mActivity.getContentResolver(), url, view.getOriginalUrl())) { Picture p = view.capturePicture(); new ThumbnailSaver(mActivity.getContentResolver(), url, view.getOriginalUrl(), p, ApplicationUtils .getBookmarksThumbnailsDimensions(mActivity)).execute(); } } }, 2000); } @Override public void onClientPageFinished(CustomWebView view, String url) { } @Override public void onReceivedIcon(WebView view, Bitmap icon) { if (view == getCurrentWebView()) { setApplicationButtonImage(icon); } } @Override public void onMainActivityPause() { CustomWebView webView = getCurrentWebView(); if (webView != null) { webView.pauseTimers(); } } @Override public void onMainActivityResume() { CustomWebView webView = getCurrentWebView(); if (webView != null) { webView.resumeTimers(); } } @Override public void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback) { if (mCustomView != null) { callback.onCustomViewHidden(); return; } if (requestedOrientation == -1) { requestedOrientation = mActivity.getRequestedOrientation(); } mOriginalOrientation = mActivity.getRequestedOrientation(); FrameLayout decor = (FrameLayout) mActivity.getWindow().getDecorView(); mFullscreenContainer = new FullscreenHolder(mActivity); mFullscreenContainer.addView(view, COVER_SCREEN_PARAMS); decor.addView(mFullscreenContainer, COVER_SCREEN_PARAMS); mCustomView = view; mCustomViewCallback = callback; mActivity.setRequestedOrientation(requestedOrientation); } @Override public void onHideCustomView() { if (mCustomView == null) return; FrameLayout decor = (FrameLayout) mActivity.getWindow().getDecorView(); decor.removeView(mFullscreenContainer); mFullscreenContainer = null; mCustomView = null; mCustomViewCallback.onCustomViewHidden(); // Show the content view. mActivity.setRequestedOrientation(mOriginalOrientation); } @Override public void onGeolocationPermissionsShowPrompt(String origin, Callback callback) { if (mGeolocationPermissionsDialog == null) { mGeolocationPermissionsDialog = new GeolocationPermissionsDialog(mActivity); } mGeolocationPermissionsDialog.initialize(origin, callback); mGeolocationPermissionsDialog.show(); } @Override public void onGeolocationPermissionsHidePrompt() { if (mGeolocationPermissionsDialog != null) { mGeolocationPermissionsDialog.hide(); } } @Override public void loadUrl(BaseWebViewFragment webViewFragment, String url) { CustomWebView webView = webViewFragment.getWebView(); if (Constants.URL_ABOUT_START.equals(url)) { showStartPage(webViewFragment); // Check if there is no pb with this. // This recreate a new WebView, because i cannot found a way // to reset completely (history and display) a WebView. webViewFragment.resetWebView(); } else { hideStartPage(webViewFragment); webView.loadUrl(url); } webView.requestFocus(); } @Override public boolean isFullScreen() { return PreferenceManager.getDefaultSharedPreferences(mActivity).getBoolean(Constants.PREFERENCE_FULL_SCREEN, false); } @Override public void toggleFullScreen() { boolean newValue = !isFullScreen(); Editor editor = PreferenceManager.getDefaultSharedPreferences(mActivity).edit(); editor.putBoolean(Constants.PREFERENCE_FULL_SCREEN, newValue); editor.commit(); setFullScreenFromPreferences(); } @Override public void saveTabs() { String userStartPage = PreferenceManager.getDefaultSharedPreferences(mActivity).getString(Constants.PREFERENCE_HOME_PAGE, Constants.URL_ABOUT_START); Set<String> tabs = new HashSet<String>(); for (BaseWebViewFragment f : getTabsFragments()) { if (!f.isStartPageShown() && !f.isWebViewOnUrl(userStartPage) && !f.isPrivateBrowsingEnabled()) { tabs.add(f.getWebView().getUrl()); } } Editor editor = PreferenceManager.getDefaultSharedPreferences(mActivity).edit(); editor.putStringSet(Constants.TECHNICAL_PREFERENCE_SAVED_TABS, tabs); editor.commit(); } protected abstract Collection<BaseWebViewFragment> getTabsFragments(); protected abstract void setFullScreenFromPreferences(); protected boolean isStartPageShownOnCurrentTab() { BaseWebViewFragment currentWebViewFragment = getCurrentWebViewFragment(); return currentWebViewFragment != null && currentWebViewFragment.isStartPageShown(); } protected boolean isHomePageStartPage() { return Constants.URL_ABOUT_START.equals(PreferenceManager.getDefaultSharedPreferences(mActivity).getString(Constants.PREFERENCE_HOME_PAGE, Constants.URL_ABOUT_START)); } private void startHandler() { mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case FOCUS_NODE_HREF: String url = (String) msg.getData().get("url"); String src = (String) msg.getData().get("src"); if (url == "") { url = src; } if (TextUtils.isEmpty(url)) { break; } switch (msg.arg1) { case SealBrowserActivity.CONTEXT_MENU_OPEN: loadUrl(url); break; case SealBrowserActivity.CONTEXT_MENU_OPEN_IN_NEW_TAB: addTab(url, false, msg.arg2 > 0 ? true : false, false); break; case SealBrowserActivity.CONTEXT_MENU_OPEN_IN_BACKGROUND: addTab(url, true, msg.arg2 > 0 ? true : false, false); break; case SealBrowserActivity.CONTEXT_MENU_COPY: ApplicationUtils.copyTextToClipboard(mActivity, url, mActivity.getResources().getString(R.string.UrlCopyToastMessage)); break; case SealBrowserActivity.CONTEXT_MENU_DOWNLOAD: /*DownloadItem item = new DownloadItem(url); long id = ((DownloadManager) mActivity.getSystemService(Context.DOWNLOAD_SERVICE)).enqueue(item); item.setId(id); Controller.getInstance().getDownloadsList().add(item); Toast.makeText(mActivity, String.format(mActivity.getString(R.string.DownloadStart), item.getFileName()), Toast.LENGTH_SHORT).show(); */ break; case SealBrowserActivity.CONTEXT_MENU_SHARE: ApplicationUtils.sharePage(mActivity, null, url); break; default: Controller.getInstance().getAddonManager() .onContributedContextLinkMenuItemSelected(mActivity, msg.arg1, HitTestResult.SRC_IMAGE_ANCHOR_TYPE, url, getCurrentWebView()); break; } break; default: super.handleMessage(msg); } } }; } private void requestHrefNode(int action) { requestHrefNode(action, false); } private void requestHrefNode(int action, boolean incognito) { WebView webView = getCurrentWebView(); if (webView != null) { final HashMap<String, WebView> hrefMap = new HashMap<String, WebView>(); hrefMap.put("webview", webView); final Message msg = mHandler.obtainMessage(FOCUS_NODE_HREF, action, incognito ? 1 : 0, hrefMap); webView.requestFocusNodeHref(msg); } } /** * Check if the current tab can be reused to display an intent request. A tab is reusable if it is on the user-defined start page. * * @return True if the current tab can be reused. */ private boolean isCurrentTabReusable() { String homePageUrl = PreferenceManager.getDefaultSharedPreferences(mActivity).getString(Constants.PREFERENCE_HOME_PAGE, Constants.URL_ABOUT_START); BaseWebViewFragment currentWebViewFragment = getCurrentWebViewFragment(); CustomWebView currentWebView = getCurrentWebView(); return (currentWebViewFragment != null && currentWebViewFragment.isStartPageShown()) || (currentWebView != null && homePageUrl != null && homePageUrl.equals(currentWebView.getUrl())); } static class FullscreenHolder extends FrameLayout { public FullscreenHolder(Context ctx) { super(ctx); setBackgroundColor(ctx.getResources().getColor(android.R.color.black)); } @Override public boolean onTouchEvent(MotionEvent evt) { return true; } } }