/* * Copyright (c) 2015 Zhang Hai <Dreaming.in.Code.ZH@Gmail.com> * All Rights Reserved. */ package me.zhanghai.android.douya.ui; import android.accounts.Account; import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.webkit.JsPromptResult; import android.webkit.JsResult; import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.ProgressBar; import android.widget.TextView; import java.util.HashMap; import java.util.Map; import java.util.regex.Pattern; import butterknife.BindDimen; import butterknife.BindView; import butterknife.ButterKnife; import me.zhanghai.android.douya.R; import me.zhanghai.android.douya.account.info.AccountContract; import me.zhanghai.android.douya.account.util.AccountUtils; import me.zhanghai.android.douya.link.DoubanUriHandler; import me.zhanghai.android.douya.link.FrodoBridge; import me.zhanghai.android.douya.network.Http; import me.zhanghai.android.douya.network.api.credential.ApiCredential; import me.zhanghai.android.douya.settings.info.Settings; import me.zhanghai.android.douya.util.ClipboardUtils; import me.zhanghai.android.douya.util.StringUtils; import me.zhanghai.android.douya.util.ToastUtils; import me.zhanghai.android.douya.util.UrlUtils; import me.zhanghai.android.douya.util.ViewUtils; public class WebViewActivity extends AppCompatActivity { private static final Pattern DOUBAN_HOST_PATTERN = Pattern.compile(".*\\.douban\\.(com|fm)"); private static final String DOUBAN_OAUTH2_REDIRECT_URL_FORMAT = "https://www.douban.com/accounts/auth2_redir?url=%1$s&apikey=%2$s"; @BindDimen(R.dimen.toolbar_height) int mToolbarHeight; @BindView(R.id.appBarWrapper) AppBarWrapperLayout mAppbarWrapperLayout; @BindView(R.id.toolbar) Toolbar mToolbar; @BindView(R.id.toolbar_progress) ProgressBar mProgress; @BindView(R.id.web) WebView mWebView; @BindView(R.id.error) TextView mErrorText; private MenuItem mGoForwardMenuItem; private MenuItem mOpenWithNativeMenuItem; private boolean mProgressVisible; public static Intent makeIntent(Uri uri, Context context) { return new Intent(context, WebViewActivity.class) .setData(uri); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.webview_activity); ButterKnife.bind(this); setupToolbar(); setupWebView(); } @Override protected void onDestroy() { super.onDestroy(); ((ViewGroup) mWebView.getParent()).removeView(mWebView); mWebView.destroy(); mWebView = null; } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); Toolbar newToolbar = (Toolbar) LayoutInflater.from(mToolbar.getContext()) .inflate(R.layout.webview_acitivity_toolbar, mAppbarWrapperLayout, false); ViewUtils.replaceChild(mAppbarWrapperLayout, mToolbar, newToolbar); ButterKnife.bind(this); setupToolbar(); ViewUtils.setVisibleOrGone(mProgress, mProgressVisible); ((ViewGroup.MarginLayoutParams) mWebView.getLayoutParams()).topMargin = mToolbarHeight; mWebView.requestLayout(); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.webview, menu); mGoForwardMenuItem = menu.findItem(R.id.action_go_forward); updateGoForward(); mOpenWithNativeMenuItem = menu.findItem(R.id.action_open_with_native); updateOpenWithNative(); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: finish(); return true; case R.id.action_go_forward: goForward(); return true; case R.id.action_reload: reloadWebView(); return true; case R.id.action_copy_url: copyUrl(); return true; case R.id.action_open_with_native: toggleOpenWithNative(); return true; case R.id.action_open_in_browser: openInBrowser(); return true; default: return super.onOptionsItemSelected(item); } } @Override public void onBackPressed() { if (mWebView.canGoBack()) { mWebView.goBack(); } else { super.onBackPressed(); } } protected void onLoadUri(WebView webView) { String url = getIntent().getData().toString(); setTitle(url); Map<String, String> headers = null; if (isDoubanUrl(url)) { Account account = AccountUtils.getActiveAccount(); if (account != null) { String authToken = AccountUtils.peekAuthToken(account, AccountContract.AUTH_TOKEN_TYPE_FRODO); if (!TextUtils.isEmpty(authToken)) { url = StringUtils.formatUs(DOUBAN_OAUTH2_REDIRECT_URL_FORMAT, Uri.encode(url), Uri.encode(ApiCredential.ApiV2.KEY)); headers = new HashMap<>(); headers.put(Http.Headers.AUTHORIZATION, Http.Headers.makeBearerAuthorization(authToken)); } } } webView.loadUrl(url, headers); } private boolean isDoubanUrl(String url) { return DOUBAN_HOST_PATTERN.matcher(Uri.parse(url).getHost()).matches(); } protected void onPageStared(WebView webView, String url, Bitmap favicon) { updateGoForward(); } protected void onPageFinished(WebView webView, String url) {} protected boolean shouldOverrideUrlLoading(WebView webView, String url) { Uri uri = Uri.parse(url); return (Settings.OPEN_WITH_NATIVE_IN_WEBVIEW.getValue() && DoubanUriHandler.open(uri, this)) || FrodoBridge.openFrodoUri(uri, this) || (Settings.PROGRESSIVE_THIRD_PARTY_APP.getValue() && FrodoBridge.openUri(uri, this)); } protected void reloadWebView() { hideError(); mWebView.reload(); } private void setupToolbar() { setSupportActionBar(mToolbar); } private void setProgressVisible(boolean visible) { if (mProgressVisible != visible) { mProgressVisible = visible; ViewUtils.setVisibleOrGone(mProgress, visible); } } @SuppressLint("SetJavaScriptEnabled") private void setupWebView() { WebSettings webSettings = mWebView.getSettings(); webSettings.setBuiltInZoomControls(true); webSettings.setDisplayZoomControls(false); webSettings.setLoadWithOverviewMode(true); webSettings.setJavaScriptEnabled(true); if (Settings.REQUEST_DESKTOP_SITE_IN_WEBVIEW.getValue()) { String desktopUserAgent = webSettings.getUserAgentString() .replaceFirst("(Linux;.*?)", "(X11; Linux x86_64)") .replace("Mobile Safari/", "Safari/"); webSettings.setUserAgentString(desktopUserAgent); } // NOTE: This gives double tap zooming. webSettings.setUseWideViewPort(true); mWebView.setWebChromeClient(new ChromeClient()); mWebView.setWebViewClient(new ViewClient()); onLoadUri(mWebView); } private void showError(String error) { mWebView.setVisibility(View.INVISIBLE); mErrorText.setText(error); mErrorText.setVisibility(View.VISIBLE); } private void hideError() { mErrorText.setVisibility(View.INVISIBLE); mErrorText.setText(null); mWebView.setVisibility(View.VISIBLE); } private void goForward() { mWebView.goForward(); // Handled in onPageStared(). //updateGoForward(); } private void updateGoForward() { if (mGoForwardMenuItem == null) { return; } mGoForwardMenuItem.setEnabled(mWebView.canGoForward()); } private void copyUrl() { String url = mWebView.getUrl(); if (!TextUtils.isEmpty(url)) { ClipboardUtils.copyText(mWebView.getTitle(), url, this); } else { ToastUtils.show(R.string.webview_copy_url_empty, this); } } private void toggleOpenWithNative() { Settings.OPEN_WITH_NATIVE_IN_WEBVIEW.putValue( !Settings.OPEN_WITH_NATIVE_IN_WEBVIEW.getValue()); updateOpenWithNative(); } private void updateOpenWithNative() { if (mOpenWithNativeMenuItem == null) { return; } mOpenWithNativeMenuItem.setChecked(Settings.OPEN_WITH_NATIVE_IN_WEBVIEW.getValue()); } private void openInBrowser() { String url = mWebView.getUrl(); if (!TextUtils.isEmpty(url)) { UrlUtils.openWithIntent(url, this); } else { ToastUtils.show(R.string.webview_copy_url_empty, this); } } private class ChromeClient extends WebChromeClient { // NOTE: WebView can be trying to show an AlertDialog after the activity is finished, which // will result in a WindowManager$BadTokenException. @Override public boolean onJsAlert(WebView view, String url, String message, JsResult result) { return WebViewActivity.this.isFinishing() || super.onJsAlert(view, url, message, result); } @Override public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { return WebViewActivity.this.isFinishing() || super.onJsConfirm(view, url, message, result); } @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { return WebViewActivity.this.isFinishing() || super.onJsPrompt(view, url, message, defaultValue, result); } @Override public boolean onJsBeforeUnload(WebView view, String url, String message, JsResult result) { return WebViewActivity.this.isFinishing() || super.onJsBeforeUnload(view, url, message, result); } @Override public void onProgressChanged(WebView view, int newProgress) { setProgressVisible(newProgress != 100); } @Override public void onReceivedTitle(WebView view, String title) { setTitle(title); } } private class ViewClient extends WebViewClient { @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { WebViewActivity.this.onPageStared(view, url, favicon); } @Override public void onPageFinished(WebView view, String url) { WebViewActivity.this.onPageFinished(view, url); } @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { showError(description); } @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { return WebViewActivity.this.shouldOverrideUrlLoading(view, url); } } }