package com.erakk.lnreader.helper; import android.content.Intent; import android.net.Uri; import android.preference.PreferenceManager; import android.util.Log; import android.webkit.WebView; import android.webkit.WebViewClient; import com.erakk.lnreader.Constants; import com.erakk.lnreader.LNReaderApplication; import com.erakk.lnreader.R; import com.erakk.lnreader.UI.activity.DisplayImageActivity; import com.erakk.lnreader.UI.activity.DisplayLightNovelContentActivity; import com.erakk.lnreader.UIHelper; import com.erakk.lnreader.dao.NovelsDao; import com.erakk.lnreader.model.PageModel; import java.lang.ref.WeakReference; public class BakaTsukiWebViewClient extends WebViewClient { private static final String TAG = BakaTsukiWebViewClient.class.toString(); protected WeakReference<DisplayLightNovelContentActivity> activityRef; private boolean hasError = false; private boolean scaleChangedRunnablePending = false; private boolean isExternalNeedSave = true; public BakaTsukiWebViewClient(DisplayLightNovelContentActivity caller) { super(); this.activityRef = new WeakReference<DisplayLightNovelContentActivity>(caller); } public void setExternalNeedSave(boolean value) { synchronized (this) { isExternalNeedSave = value; } } @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { Log.d(TAG, "Try handling: " + url); hasError = false; final DisplayLightNovelContentActivity caller = activityRef.get(); if (caller == null) return false; if (view.canGoBack()) caller.setLastReadState(); Log.i(TAG, "Handling: " + url); if (url.contains("title=File:")) { handleImageLinkActivity(url, caller); } else if (url.startsWith("wyciwyg://") || url.startsWith("cid:frame-")) { // ignore wyciwyg:// protocol // ignore cid:frame-, refer to issue #246 return true; } else { boolean isHandled = handleInternalPage(view, url, caller); if (!isHandled) { boolean useInternalWebView = PreferenceManager.getDefaultSharedPreferences(caller).getBoolean(Constants.PREF_USE_INTERNAL_WEBVIEW, false); if (useInternalWebView) { // set intent to external page caller.getIntent().removeExtra(Constants.EXTRA_PAGE); caller.getIntent().putExtra(Constants.EXTRA_PAGE, Util.SanitizeBaseUrl(url, false)); caller.getIntent().removeExtra(Constants.EXTRA_PAGE_IS_EXTERNAL); caller.getIntent().putExtra(Constants.EXTRA_PAGE_IS_EXTERNAL, true); // check if the same page and trying to load anchor link. // need to handle if page use urlParams to do navigation. if (url.contains("#")) { String currUrl = Util.SanitizeBaseUrl(view.getUrl(), false); String[] urlParts = url.split("#", 2); String[] urlQuery = urlParts[0].split("\\?", 2); if (!Util.isStringNullOrEmpty(currUrl) && currUrl.startsWith(urlQuery[0])) { if (urlParts.length == 2) view.loadUrl("javascript:window.location.hash=" + urlParts[1] + ";"); } } else { // don't sanitize the url due to redirect issue PageModel pageModel = new PageModel(); pageModel.setPage(url); pageModel.setExternal(true); PageModel temp = pageModel; try { temp = NovelsDao.getInstance().getExistingPageModel(pageModel, null); } catch (Exception e) { Log.e(TAG, "Failed to get pageModel: " + url, e); } if (temp != null) pageModel = temp; Log.i(TAG, "Loading external url: " + pageModel.getPage()); caller.loadExternalUrl(pageModel, false); } } else { // set the intent page to the current page caller.getIntent().removeExtra(Constants.EXTRA_PAGE); if (caller.content != null) caller.getIntent().putExtra(Constants.EXTRA_PAGE, caller.content.getPage()); else caller.getIntent().putExtra(Constants.EXTRA_PAGE, caller.getIntent().getStringExtra(Constants.EXTRA_PAGE)); caller.getIntent().removeExtra(Constants.EXTRA_PAGE_IS_EXTERNAL); caller.getIntent().putExtra(Constants.EXTRA_PAGE_IS_EXTERNAL, false); // use default handler. caller.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); } } } return true; } /** * check if internal and load it * * @param view * @param url * @param caller * @return true if internal page is loaded successfully. */ private boolean handleInternalPage(WebView view, String url, DisplayLightNovelContentActivity caller) { if (url.contains("/project/index.php?title=")) { try { String titles[] = url.split("title=", 2); if (titles.length == 2 && !(titles[1].length() == 0)) { // split anchor text String[] titles2 = titles[1].split("#", 2); // check if load different page. synchronized (caller.content) { String currentPage = caller.content.getPage(); if (!currentPage.equalsIgnoreCase(titles2[0])) { Log.d(TAG, "Got different page name: " + titles2[0]); PageModel tempPage = new PageModel(); tempPage.setPage(titles2[0]); PageModel pageModel = NovelsDao.getInstance().getExistingPageModel(tempPage, null); if (pageModel != null) { caller.jumpTo(pageModel); Log.d(TAG, "Loading : " + pageModel.getPage()); } else { Log.w(TAG, "PageModel not downloaded yet, most likely not listed in chapter list: " + titles2[0]); tempPage.setTitle(titles2[0]); tempPage.setParent(currentPage); tempPage.setType(PageModel.TYPE_CONTENT); NovelsDao.getInstance().updatePageModel(tempPage); caller.jumpTo(tempPage); } } else Log.d(TAG, "Already loaded: " + currentPage); // navigate to the anchor if exist. if (titles2.length == 2) { view.loadUrl("#" + titles2[1]); } } return true; } else Log.w(TAG, "Unknown format for internal url: " + url); } catch (Exception e) { Log.e(TAG, "Failed to load: " + url, e); } } return false; } private void handleImageLinkActivity(String url, DisplayLightNovelContentActivity caller) { Intent intent = new Intent(caller, DisplayImageActivity.class); intent.putExtra(Constants.EXTRA_IMAGE_URL, url); intent.putExtra("image_list", caller.content.getBigImages()); intent.putExtra(Constants.EXTRA_PAGE, caller.content.getPage()); caller.startActivity(intent); } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); final DisplayLightNovelContentActivity caller = activityRef.get(); if (caller != null && !hasError) { NonLeakingWebView wv = (NonLeakingWebView) caller.findViewById(R.id.webViewContent); String page = caller.getIntent().getStringExtra(Constants.EXTRA_PAGE); // assumption all external page is start with http // and ignore for internal pages which loaded with base url to bakatsuki.org if (url.startsWith("http") && !url.startsWith(UIHelper.getBaseUrl(view.getContext()))) { if (!isExternalNeedSave || !getAllowSaveExternal()) { Log.d(TAG, "Skip auto save for: " + page + " " + !isExternalNeedSave + " " + !getAllowSaveExternal()); return; } wv.saveMyWebArchive(url); } } } private boolean getAllowSaveExternal() { return PreferenceManager.getDefaultSharedPreferences(LNReaderApplication.getInstance()).getBoolean(Constants.PREF_SAVE_EXTERNAL_URL, true); } @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { super.onReceivedError(view, errorCode, description, failingUrl); hasError = true; Log.w(TAG, String.format("Error detected: [%s] %s => %s", errorCode, description, failingUrl)); } /** * KitKat chromium text zoom handler, see http://stackoverflow.com/a/20000193 * * @param webView * @param oldScale * @param newScale */ @Override public void onScaleChanged(final WebView webView, float oldScale, float newScale) { if (UIHelper.getKitKatWebViewFix(webView.getContext())) { if (scaleChangedRunnablePending) { Log.d(TAG, "OnScaleChange KitKat handler already running"); return; } synchronized (webView) { scaleChangedRunnablePending = true; webView.postDelayed(new Runnable() { @Override public void run() { webView.loadUrl("javascript:recalcWidth();", null); scaleChangedRunnablePending = false; } }, UIHelper.getIntFromPreferences(Constants.PREF_KITKAT_WEBVIEW_FIX_DELAY, 500)); } } } }