package io.github.mthli.Ninja.View; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; import android.net.MailTo; import android.os.Build; import android.os.Handler; import android.os.Message; import android.preference.PreferenceManager; import android.view.*; import android.webkit.WebSettings; import android.webkit.WebView; import io.github.mthli.Ninja.Browser.*; import io.github.mthli.Ninja.Database.Record; import io.github.mthli.Ninja.Database.RecordAction; import io.github.mthli.Ninja.R; import io.github.mthli.Ninja.Unit.BrowserUnit; import io.github.mthli.Ninja.Unit.IntentUnit; import io.github.mthli.Ninja.Unit.ViewUnit; import java.net.URISyntaxException; public class NinjaWebView extends WebView implements AlbumController { private static final float[] NEGATIVE_COLOR = { -1.0f, 0, 0, 0, 255, // Red 0, -1.0f, 0, 0, 255, // Green 0, 0, -1.0f, 0, 255, // Blue 0, 0, 0, 1.0f, 0 // Alpha }; private Context context; private int flag = BrowserUnit.FLAG_NINJA; private int dimen144dp; private int dimen108dp; private int animTime; private Album album; private NinjaWebViewClient webViewClient; private NinjaWebChromeClient webChromeClient; private NinjaDownloadListener downloadListener; private NinjaClickHandler clickHandler; private GestureDetector gestureDetector; private AdBlock adBlock; public AdBlock getAdBlock() { return adBlock; } private boolean foreground; public boolean isForeground() { return foreground; } private String userAgentOriginal; public String getUserAgentOriginal() { return userAgentOriginal; } private BrowserController browserController = null; public BrowserController getBrowserController() { return browserController; } public void setBrowserController(BrowserController browserController) { this.browserController = browserController; this.album.setBrowserController(browserController); } public NinjaWebView(Context context) { super(context); // Cannot create a dialog, the WebView context is not an Activity this.context = context; this.dimen144dp = getResources().getDimensionPixelSize(R.dimen.layout_width_144dp); this.dimen108dp = getResources().getDimensionPixelSize(R.dimen.layout_height_108dp); this.animTime = getResources().getInteger(android.R.integer.config_shortAnimTime); this.foreground = false; this.adBlock = new AdBlock(this.context); this.album = new Album(this.context, this, this.browserController); this.webViewClient = new NinjaWebViewClient(this); this.webChromeClient = new NinjaWebChromeClient(this); this.downloadListener = new NinjaDownloadListener(this.context); this.clickHandler = new NinjaClickHandler(this); this.gestureDetector = new GestureDetector(context, new NinjaGestureListener(this)); initWebView(); initWebSettings(); initPreferences(); initAlbum(); } private synchronized void initWebView() { setAlwaysDrawnWithCacheEnabled(true); setAnimationCacheEnabled(true); setDrawingCacheBackgroundColor(0x00000000); setDrawingCacheEnabled(true); setWillNotCacheDrawing(false); setSaveEnabled(true); setBackground(null); getRootView().setBackground(null); setBackgroundColor(context.getResources().getColor(R.color.white)); setFocusable(true); setFocusableInTouchMode(true); setHorizontalScrollBarEnabled(false); setVerticalScrollBarEnabled(false); setScrollbarFadingEnabled(true); setWebViewClient(webViewClient); setWebChromeClient(webChromeClient); setDownloadListener(downloadListener); setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { gestureDetector.onTouchEvent(motionEvent); return false; } }); } private synchronized void initWebSettings() { WebSettings webSettings = getSettings(); userAgentOriginal = webSettings.getUserAgentString(); webSettings.setAllowContentAccess(true); webSettings.setAllowFileAccess(true); webSettings.setAllowFileAccessFromFileURLs(true); webSettings.setAllowUniversalAccessFromFileURLs(true); webSettings.setAppCacheEnabled(true); webSettings.setAppCachePath(context.getCacheDir().toString()); webSettings.setCacheMode(WebSettings.LOAD_DEFAULT); webSettings.setDatabaseEnabled(true); webSettings.setDomStorageEnabled(true); webSettings.setGeolocationDatabasePath(context.getFilesDir().toString()); webSettings.setSupportZoom(true); webSettings.setBuiltInZoomControls(true); webSettings.setDisplayZoomControls(false); webSettings.setDefaultTextEncodingName(BrowserUnit.URL_ENCODING); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { webSettings.setLoadsImagesAutomatically(true); } else { webSettings.setLoadsImagesAutomatically(false); } } public synchronized void initPreferences() { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); WebSettings webSettings = getSettings(); webSettings.setLoadWithOverviewMode(true); webSettings.setTextZoom(100); webSettings.setUseWideViewPort(true); webSettings.setBlockNetworkImage(!sp.getBoolean(context.getString(R.string.sp_images), true)); webSettings.setJavaScriptEnabled(sp.getBoolean(context.getString(R.string.sp_javascript), true)); webSettings.setJavaScriptCanOpenWindowsAutomatically(sp.getBoolean(context.getString(R.string.sp_javascript), true)); webSettings.setGeolocationEnabled(sp.getBoolean(context.getString(R.string.sp_location), true)); webSettings.setSupportMultipleWindows(sp.getBoolean(context.getString(R.string.sp_multiple_windows), false)); webSettings.setSaveFormData(sp.getBoolean(context.getString(R.string.sp_passwords), true)); boolean textReflow = sp.getBoolean(context.getString(R.string.sp_text_reflow), true); if (textReflow) { webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { try { webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING); } catch (Exception e) {} } } else { webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL); } int userAgent = Integer.valueOf(sp.getString(context.getString(R.string.sp_user_agent), "0")); if (userAgent == 1) { webSettings.setUserAgentString(BrowserUnit.UA_DESKTOP); } else if (userAgent == 2) { webSettings.setUserAgentString(sp.getString(context.getString(R.string.sp_user_agent_custom), userAgentOriginal)); } else { webSettings.setUserAgentString(userAgentOriginal); } int mode = Integer.valueOf(sp.getString(context.getString(R.string.sp_rendering), "0")); initRendering(mode); webViewClient.enableAdBlock(sp.getBoolean(context.getString(R.string.sp_ad_block), true)); } private synchronized void initAlbum() { album.setAlbumCover(null); album.setAlbumTitle(context.getString(R.string.album_untitled)); album.setBrowserController(browserController); } private void initRendering(int mode) { Paint paint = new Paint(); switch (mode) { case 0: { // Default paint.setColorFilter(null); break; } case 1: { // Grayscale ColorMatrix matrix = new ColorMatrix(); matrix.setSaturation(0); ColorMatrixColorFilter filter = new ColorMatrixColorFilter(matrix); paint.setColorFilter(filter); break; } case 2: { // Inverted ColorMatrixColorFilter filter = new ColorMatrixColorFilter(NEGATIVE_COLOR); paint.setColorFilter(filter); break; } case 3: { // Inverted grayscale ColorMatrix matrix = new ColorMatrix(); matrix.set(NEGATIVE_COLOR); ColorMatrix gcm = new ColorMatrix(); gcm.setSaturation(0); ColorMatrix concat = new ColorMatrix(); concat.setConcat(matrix, gcm); ColorMatrixColorFilter filter = new ColorMatrixColorFilter(concat); paint.setColorFilter(filter); break; } default: { paint.setColorFilter(null); break; } } // maybe sometime LAYER_TYPE_NONE would better? setLayerType(View.LAYER_TYPE_HARDWARE, paint); } @Override public synchronized void loadUrl(String url) { if (url == null || url.trim().isEmpty()) { NinjaToast.show(context, R.string.toast_load_error); return; } url = BrowserUnit.queryWrapper(context, url.trim()); if (url.startsWith(BrowserUnit.URL_SCHEME_MAIL_TO)) { Intent intent = IntentUnit.getEmailIntent(MailTo.parse(url)); context.startActivity(intent); reload(); return; } else if (url.startsWith(BrowserUnit.URL_SCHEME_INTENT)) { Intent intent; try { intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); context.startActivity(intent); } catch (URISyntaxException u) {} return; } webViewClient.updateWhite(adBlock.isWhite(url)); super.loadUrl(url); if (browserController != null && foreground) { browserController.updateBookmarks(); } } @Override public void reload() { webViewClient.updateWhite(adBlock.isWhite(getUrl())); super.reload(); } @Override public int getFlag() { return flag; } @Override public void setFlag(int flag) { this.flag = flag; } @Override public View getAlbumView() { return album.getAlbumView(); } @Override public void setAlbumCover(Bitmap bitmap) { album.setAlbumCover(bitmap); } @Override public String getAlbumTitle() { return album.getAlbumTitle(); } @Override public void setAlbumTitle(String title) { album.setAlbumTitle(title); } @Override public synchronized void activate() { requestFocus(); foreground = true; album.activate(); } @Override public synchronized void deactivate() { clearFocus(); foreground = false; album.deactivate(); } public synchronized void update(int progress) { if (foreground) { browserController.updateProgress(progress); } setAlbumCover(ViewUnit.capture(this, dimen144dp, dimen108dp, false, Bitmap.Config.RGB_565)); if (isLoadFinish()) { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); if (sp.getBoolean(context.getString(R.string.sp_scroll_bar), true)) { setHorizontalScrollBarEnabled(true); setVerticalScrollBarEnabled(true); } else { setHorizontalScrollBarEnabled(false); setVerticalScrollBarEnabled(false); } setScrollbarFadingEnabled(true); new Handler().postDelayed(new Runnable() { @Override public void run() { setAlbumCover(ViewUnit.capture(NinjaWebView.this, dimen144dp, dimen108dp, false, Bitmap.Config.RGB_565)); } }, animTime); if (prepareRecord()) { RecordAction action = new RecordAction(context); action.open(true); action.addHistory(new Record(getTitle(), getUrl(), System.currentTimeMillis())); action.close(); browserController.updateAutoComplete(); } } } public synchronized void update(String title, String url) { album.setAlbumTitle(title); if (foreground) { browserController.updateBookmarks(); browserController.updateInputBox(url); } } public synchronized void pause() { onPause(); pauseTimers(); } public synchronized void resume() { onResume(); resumeTimers(); } @Override public synchronized void destroy() { stopLoading(); onPause(); clearHistory(); setVisibility(GONE); removeAllViews(); destroyDrawingCache(); super.destroy(); } public boolean isLoadFinish() { return getProgress() >= BrowserUnit.PROGRESS_MAX; } public void onLongPress() { Message click = clickHandler.obtainMessage(); if (click != null) { click.setTarget(clickHandler); } requestFocusNodeHref(click); } private boolean prepareRecord() { String title = getTitle(); String url = getUrl(); if (title == null || title.isEmpty() || url == null || url.isEmpty() || url.startsWith(BrowserUnit.URL_SCHEME_ABOUT) || url.startsWith(BrowserUnit.URL_SCHEME_MAIL_TO) || url.startsWith(BrowserUnit.URL_SCHEME_INTENT)) { return false; } return true; } }