PageRenderTime 96ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/app/src/main/java/acr/browser/lightning/view/LightningView.java

https://gitlab.com/jbwhips883/Lightning-Browser
Java | 1145 lines | 932 code | 133 blank | 80 comment | 154 complexity | 273a687730ee63b3d0f302593c870881 MD5 | raw file
Possible License(s): MPL-2.0
  1. /*
  2. * Copyright 2014 A.C.R. Development
  3. */
  4. package acr.browser.lightning.view;
  5. import android.annotation.SuppressLint;
  6. import android.app.Activity;
  7. import android.app.AlertDialog;
  8. import android.content.ActivityNotFoundException;
  9. import android.content.Context;
  10. import android.content.DialogInterface;
  11. import android.content.Intent;
  12. import android.graphics.Bitmap;
  13. import android.graphics.ColorMatrix;
  14. import android.graphics.ColorMatrixColorFilter;
  15. import android.graphics.Paint;
  16. import android.net.MailTo;
  17. import android.net.Uri;
  18. import android.net.http.SslError;
  19. import android.os.Build;
  20. import android.os.Message;
  21. import android.support.annotation.NonNull;
  22. import android.text.InputType;
  23. import android.text.method.PasswordTransformationMethod;
  24. import android.util.Log;
  25. import android.view.GestureDetector;
  26. import android.view.GestureDetector.SimpleOnGestureListener;
  27. import android.view.MotionEvent;
  28. import android.view.View;
  29. import android.view.View.OnTouchListener;
  30. import android.webkit.CookieManager;
  31. import android.webkit.GeolocationPermissions;
  32. import android.webkit.HttpAuthHandler;
  33. import android.webkit.SslErrorHandler;
  34. import android.webkit.ValueCallback;
  35. import android.webkit.WebChromeClient;
  36. import android.webkit.WebResourceRequest;
  37. import android.webkit.WebResourceResponse;
  38. import android.webkit.WebSettings;
  39. import android.webkit.WebSettings.LayoutAlgorithm;
  40. import android.webkit.WebSettings.PluginState;
  41. import android.webkit.WebView;
  42. import android.webkit.WebViewClient;
  43. import android.widget.EditText;
  44. import android.widget.LinearLayout;
  45. import java.io.ByteArrayInputStream;
  46. import java.io.File;
  47. import java.io.FileOutputStream;
  48. import java.io.FileWriter;
  49. import java.io.IOException;
  50. import java.net.URISyntaxException;
  51. import acr.browser.lightning.R;
  52. import acr.browser.lightning.constant.Constants;
  53. import acr.browser.lightning.constant.StartPage;
  54. import acr.browser.lightning.controller.BrowserController;
  55. import acr.browser.lightning.download.LightningDownloadListener;
  56. import acr.browser.lightning.preference.PreferenceManager;
  57. import acr.browser.lightning.utils.AdBlock;
  58. import acr.browser.lightning.utils.IntentUtils;
  59. import acr.browser.lightning.utils.Utils;
  60. public class LightningView {
  61. private final Title mTitle;
  62. private WebView mWebView;
  63. private BrowserController mBrowserController;
  64. private GestureDetector mGestureDetector;
  65. private final Activity mActivity;
  66. private WebSettings mSettings;
  67. private static String mHomepage;
  68. private static String mDefaultUserAgent;
  69. private static Bitmap mWebpageBitmap;
  70. private static PreferenceManager mPreferences;
  71. private final AdBlock mAdBlock;
  72. private IntentUtils mIntentUtils;
  73. private final Paint mPaint = new Paint();
  74. private boolean isForegroundTab;
  75. private boolean mTextReflow = false;
  76. private boolean mInvertPage = false;
  77. private static final int API = android.os.Build.VERSION.SDK_INT;
  78. private static final int SCROLL_UP_THRESHOLD = Utils.convertDpToPixels(10);
  79. private static final int SCROLL_DOWN_THRESHOLD = Utils.convertDpToPixels(100);
  80. private static final float[] mNegativeColorArray = { -1.0f, 0, 0, 0, 255, // red
  81. 0, -1.0f, 0, 0, 255, // green
  82. 0, 0, -1.0f, 0, 255, // blue
  83. 0, 0, 0, 1.0f, 0 // alpha
  84. };
  85. @SuppressWarnings("deprecation")
  86. @SuppressLint("NewApi")
  87. public LightningView(Activity activity, String url, boolean darkTheme) {
  88. mActivity = activity;
  89. mWebView = new WebView(activity);
  90. mTitle = new Title(activity, darkTheme);
  91. mAdBlock = AdBlock.getInstance(activity.getApplicationContext());
  92. mWebpageBitmap = Utils.getWebpageBitmap(activity.getResources(), darkTheme);
  93. try {
  94. mBrowserController = (BrowserController) activity;
  95. } catch (ClassCastException e) {
  96. throw new ClassCastException(activity + " must implement BrowserController");
  97. }
  98. mIntentUtils = new IntentUtils(mBrowserController);
  99. mWebView.setDrawingCacheBackgroundColor(0x00000000);
  100. mWebView.setFocusableInTouchMode(true);
  101. mWebView.setFocusable(true);
  102. mWebView.setAnimationCacheEnabled(false);
  103. mWebView.setDrawingCacheEnabled(false);
  104. mWebView.setWillNotCacheDrawing(true);
  105. mWebView.setAlwaysDrawnWithCacheEnabled(false);
  106. mWebView.setBackgroundColor(activity.getResources().getColor(android.R.color.white));
  107. if (API > 15) {
  108. mWebView.setBackground(null);
  109. mWebView.getRootView().setBackground(null);
  110. } else if (mWebView.getRootView() != null) {
  111. mWebView.getRootView().setBackgroundDrawable(null);
  112. }
  113. mWebView.setScrollbarFadingEnabled(true);
  114. mWebView.setSaveEnabled(true);
  115. mWebView.setWebChromeClient(new LightningChromeClient(activity));
  116. mWebView.setWebViewClient(new LightningWebClient(activity));
  117. mWebView.setDownloadListener(new LightningDownloadListener(activity));
  118. mGestureDetector = new GestureDetector(activity, new CustomGestureListener());
  119. mWebView.setOnTouchListener(new TouchListener());
  120. mDefaultUserAgent = mWebView.getSettings().getUserAgentString();
  121. mSettings = mWebView.getSettings();
  122. initializeSettings(mWebView.getSettings(), activity);
  123. initializePreferences(activity);
  124. if (url != null) {
  125. if (!url.trim().isEmpty()) {
  126. mWebView.loadUrl(url);
  127. } else {
  128. // don't load anything, the user is looking for a blank tab
  129. }
  130. } else {
  131. if (mHomepage.startsWith("about:home")) {
  132. mWebView.loadUrl(getHomepage());
  133. } else if (mHomepage.startsWith("about:bookmarks")) {
  134. mBrowserController.openBookmarkPage(mWebView);
  135. } else {
  136. mWebView.loadUrl(mHomepage);
  137. }
  138. }
  139. }
  140. public String getHomepage() {
  141. StringBuilder homepageBuilder = new StringBuilder();
  142. homepageBuilder.append(StartPage.HEAD);
  143. String icon;
  144. String searchUrl;
  145. switch (mPreferences.getSearchChoice()) {
  146. case 0:
  147. // CUSTOM SEARCH
  148. icon = "file:///android_asset/lightning.png";
  149. searchUrl = mPreferences.getSearchUrl();
  150. break;
  151. case 1:
  152. // GOOGLE_SEARCH;
  153. icon = "file:///android_asset/google.png";
  154. // "https://www.google.com/images/srpr/logo11w.png";
  155. searchUrl = Constants.GOOGLE_SEARCH;
  156. break;
  157. case 2:
  158. // ANDROID SEARCH;
  159. icon = "file:///android_asset/ask.png";
  160. searchUrl = Constants.ASK_SEARCH;
  161. break;
  162. case 3:
  163. // BING_SEARCH;
  164. icon = "file:///android_asset/bing.png";
  165. // "http://upload.wikimedia.org/wikipedia/commons/thumb/b/b1/Bing_logo_%282013%29.svg/500px-Bing_logo_%282013%29.svg.png";
  166. searchUrl = Constants.BING_SEARCH;
  167. break;
  168. case 4:
  169. // YAHOO_SEARCH;
  170. icon = "file:///android_asset/yahoo.png";
  171. // "http://upload.wikimedia.org/wikipedia/commons/thumb/2/24/Yahoo%21_logo.svg/799px-Yahoo%21_logo.svg.png";
  172. searchUrl = Constants.YAHOO_SEARCH;
  173. break;
  174. case 5:
  175. // STARTPAGE_SEARCH;
  176. icon = "file:///android_asset/startpage.png";
  177. // "https://startpage.com/graphics/startp_logo.gif";
  178. searchUrl = Constants.STARTPAGE_SEARCH;
  179. break;
  180. case 6:
  181. // STARTPAGE_MOBILE
  182. icon = "file:///android_asset/startpage.png";
  183. // "https://startpage.com/graphics/startp_logo.gif";
  184. searchUrl = Constants.STARTPAGE_MOBILE_SEARCH;
  185. break;
  186. case 7:
  187. // DUCK_SEARCH;
  188. icon = "file:///android_asset/duckduckgo.png";
  189. // "https://duckduckgo.com/assets/logo_homepage.normal.v101.png";
  190. searchUrl = Constants.DUCK_SEARCH;
  191. break;
  192. case 8:
  193. // DUCK_LITE_SEARCH;
  194. icon = "file:///android_asset/duckduckgo.png";
  195. // "https://duckduckgo.com/assets/logo_homepage.normal.v101.png";
  196. searchUrl = Constants.DUCK_LITE_SEARCH;
  197. break;
  198. case 9:
  199. // BAIDU_SEARCH;
  200. icon = "file:///android_asset/baidu.png";
  201. // "http://www.baidu.com/img/bdlogo.gif";
  202. searchUrl = Constants.BAIDU_SEARCH;
  203. break;
  204. case 10:
  205. // YANDEX_SEARCH;
  206. icon = "file:///android_asset/yandex.png";
  207. // "http://upload.wikimedia.org/wikipedia/commons/thumb/9/91/Yandex.svg/600px-Yandex.svg.png";
  208. searchUrl = Constants.YANDEX_SEARCH;
  209. break;
  210. default:
  211. // DEFAULT GOOGLE_SEARCH;
  212. icon = "file:///android_asset/google.png";
  213. searchUrl = Constants.GOOGLE_SEARCH;
  214. break;
  215. }
  216. homepageBuilder.append(icon);
  217. homepageBuilder.append(StartPage.MIDDLE);
  218. homepageBuilder.append(searchUrl);
  219. homepageBuilder.append(StartPage.END);
  220. File homepage = new File(mActivity.getFilesDir(), "homepage.html");
  221. try {
  222. FileWriter hWriter = new FileWriter(homepage, false);
  223. hWriter.write(homepageBuilder.toString());
  224. hWriter.close();
  225. } catch (IOException e) {
  226. e.printStackTrace();
  227. }
  228. return Constants.FILE + homepage;
  229. }
  230. @SuppressWarnings("deprecation")
  231. @SuppressLint({ "NewApi", "SetJavaScriptEnabled" })
  232. public synchronized void initializePreferences(Context context) {
  233. mPreferences = PreferenceManager.getInstance();
  234. mHomepage = mPreferences.getHomepage();
  235. mAdBlock.updatePreference();
  236. if (mSettings == null && mWebView != null) {
  237. mSettings = mWebView.getSettings();
  238. } else if (mSettings == null) {
  239. return;
  240. }
  241. setColorMode(mPreferences.getRenderingMode());
  242. if (!mBrowserController.isIncognito()) {
  243. mSettings.setGeolocationEnabled(mPreferences.getLocationEnabled());
  244. } else {
  245. mSettings.setGeolocationEnabled(false);
  246. }
  247. if (API < 19) {
  248. switch (mPreferences.getFlashSupport()) {
  249. case 0:
  250. mSettings.setPluginState(PluginState.OFF);
  251. break;
  252. case 1:
  253. mSettings.setPluginState(PluginState.ON_DEMAND);
  254. break;
  255. case 2:
  256. mSettings.setPluginState(PluginState.ON);
  257. break;
  258. default:
  259. break;
  260. }
  261. }
  262. switch (mPreferences.getUserAgentChoice()) {
  263. case 1:
  264. if (API > 16) {
  265. mSettings.setUserAgentString(WebSettings.getDefaultUserAgent(context));
  266. } else {
  267. mSettings.setUserAgentString(mDefaultUserAgent);
  268. }
  269. break;
  270. case 2:
  271. mSettings.setUserAgentString(Constants.DESKTOP_USER_AGENT);
  272. break;
  273. case 3:
  274. mSettings.setUserAgentString(Constants.MOBILE_USER_AGENT);
  275. break;
  276. case 4:
  277. mSettings.setUserAgentString(mPreferences.getUserAgentString(mDefaultUserAgent));
  278. break;
  279. }
  280. if (mPreferences.getSavePasswordsEnabled() && !mBrowserController.isIncognito()) {
  281. if (API < 18) {
  282. mSettings.setSavePassword(true);
  283. }
  284. mSettings.setSaveFormData(true);
  285. } else {
  286. if (API < 18) {
  287. mSettings.setSavePassword(false);
  288. }
  289. mSettings.setSaveFormData(false);
  290. }
  291. if (mPreferences.getJavaScriptEnabled()) {
  292. mSettings.setJavaScriptEnabled(true);
  293. mSettings.setJavaScriptCanOpenWindowsAutomatically(true);
  294. }
  295. if (mPreferences.getTextReflowEnabled()) {
  296. mTextReflow = true;
  297. mSettings.setLayoutAlgorithm(LayoutAlgorithm.NARROW_COLUMNS);
  298. if (API >= android.os.Build.VERSION_CODES.KITKAT) {
  299. try {
  300. mSettings.setLayoutAlgorithm(LayoutAlgorithm.TEXT_AUTOSIZING);
  301. } catch (Exception e) {
  302. // This shouldn't be necessary, but there are a number
  303. // of KitKat devices that crash trying to set this
  304. Log.e(Constants.TAG, "Problem setting LayoutAlgorithm to TEXT_AUTOSIZING");
  305. }
  306. }
  307. } else {
  308. mTextReflow = false;
  309. mSettings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL);
  310. }
  311. mSettings.setBlockNetworkImage(mPreferences.getBlockImagesEnabled());
  312. mSettings.setSupportMultipleWindows(mPreferences.getPopupsEnabled());
  313. mSettings.setUseWideViewPort(mPreferences.getUseWideViewportEnabled());
  314. mSettings.setLoadWithOverviewMode(mPreferences.getOverviewModeEnabled());
  315. switch (mPreferences.getTextSize()) {
  316. case 1:
  317. mSettings.setTextZoom(200);
  318. break;
  319. case 2:
  320. mSettings.setTextZoom(150);
  321. break;
  322. case 3:
  323. mSettings.setTextZoom(100);
  324. break;
  325. case 4:
  326. mSettings.setTextZoom(75);
  327. break;
  328. case 5:
  329. mSettings.setTextZoom(50);
  330. break;
  331. }
  332. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  333. CookieManager.getInstance().setAcceptThirdPartyCookies(mWebView,
  334. !mPreferences.getBlockThirdPartyCookiesEnabled());
  335. }
  336. }
  337. @SuppressWarnings("deprecation")
  338. @SuppressLint({ "SetJavaScriptEnabled", "NewApi" })
  339. public void initializeSettings(WebSettings settings, Context context) {
  340. if (API < 18) {
  341. settings.setAppCacheMaxSize(Long.MAX_VALUE);
  342. }
  343. if (API < 17) {
  344. settings.setEnableSmoothTransition(true);
  345. }
  346. if (API > 16) {
  347. settings.setMediaPlaybackRequiresUserGesture(true);
  348. }
  349. if (API >= Build.VERSION_CODES.LOLLIPOP && !mBrowserController.isIncognito()) {
  350. settings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
  351. } else if (API >= Build.VERSION_CODES.LOLLIPOP) {
  352. // We're in Incognito mode, reject
  353. settings.setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW);
  354. }
  355. settings.setDomStorageEnabled(true);
  356. settings.setAppCacheEnabled(true);
  357. settings.setCacheMode(WebSettings.LOAD_DEFAULT);
  358. settings.setDatabaseEnabled(true);
  359. settings.setSupportZoom(true);
  360. settings.setBuiltInZoomControls(true);
  361. settings.setDisplayZoomControls(false);
  362. settings.setAllowContentAccess(true);
  363. settings.setAllowFileAccess(true);
  364. settings.setDefaultTextEncodingName("utf-8");
  365. if (API > 16) {
  366. settings.setAllowFileAccessFromFileURLs(false);
  367. settings.setAllowUniversalAccessFromFileURLs(false);
  368. }
  369. settings.setAppCachePath(context.getDir("appcache", 0).getPath());
  370. settings.setGeolocationDatabasePath(context.getDir("geolocation", 0).getPath());
  371. if (API < Build.VERSION_CODES.KITKAT) {
  372. settings.setDatabasePath(context.getDir("databases", 0).getPath());
  373. }
  374. }
  375. public boolean isShown() {
  376. return mWebView != null && mWebView.isShown();
  377. }
  378. public synchronized void onPause() {
  379. if (mWebView != null) {
  380. mWebView.onPause();
  381. }
  382. }
  383. public synchronized void onResume() {
  384. if (mWebView != null) {
  385. mWebView.onResume();
  386. }
  387. }
  388. public void setForegroundTab(boolean isForeground) {
  389. isForegroundTab = isForeground;
  390. mBrowserController.update();
  391. }
  392. public boolean isForegroundTab() {
  393. return isForegroundTab;
  394. }
  395. public int getProgress() {
  396. if (mWebView != null) {
  397. return mWebView.getProgress();
  398. } else {
  399. return 100;
  400. }
  401. }
  402. public synchronized void stopLoading() {
  403. if (mWebView != null) {
  404. mWebView.stopLoading();
  405. }
  406. }
  407. public void setHardwareRendering() {
  408. mWebView.setLayerType(View.LAYER_TYPE_HARDWARE, mPaint);
  409. }
  410. public void setNormalRendering() {
  411. mWebView.setLayerType(View.LAYER_TYPE_NONE, null);
  412. }
  413. public void setSoftwareRendering() {
  414. mWebView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
  415. }
  416. public void setColorMode(int mode) {
  417. mInvertPage = false;
  418. switch (mode) {
  419. case 0:
  420. mPaint.setColorFilter(null);
  421. // setSoftwareRendering(); // Some devices get segfaults
  422. // in the WebView with Hardware Acceleration enabled,
  423. // the only fix is to disable hardware rendering
  424. setNormalRendering();
  425. mInvertPage = false;
  426. break;
  427. case 1:
  428. ColorMatrixColorFilter filterInvert = new ColorMatrixColorFilter(
  429. mNegativeColorArray);
  430. mPaint.setColorFilter(filterInvert);
  431. setHardwareRendering();
  432. mInvertPage = true;
  433. break;
  434. case 2:
  435. ColorMatrix cm = new ColorMatrix();
  436. cm.setSaturation(0);
  437. ColorMatrixColorFilter filterGray = new ColorMatrixColorFilter(cm);
  438. mPaint.setColorFilter(filterGray);
  439. setHardwareRendering();
  440. break;
  441. case 3:
  442. ColorMatrix matrix = new ColorMatrix();
  443. matrix.set(mNegativeColorArray);
  444. ColorMatrix matrixGray = new ColorMatrix();
  445. matrixGray.setSaturation(0);
  446. ColorMatrix concat = new ColorMatrix();
  447. concat.setConcat(matrix, matrixGray);
  448. ColorMatrixColorFilter filterInvertGray = new ColorMatrixColorFilter(concat);
  449. mPaint.setColorFilter(filterInvertGray);
  450. setHardwareRendering();
  451. mInvertPage = true;
  452. break;
  453. }
  454. }
  455. public synchronized void pauseTimers() {
  456. if (mWebView != null) {
  457. mWebView.pauseTimers();
  458. }
  459. }
  460. public synchronized void resumeTimers() {
  461. if (mWebView != null) {
  462. mWebView.resumeTimers();
  463. }
  464. }
  465. public void requestFocus() {
  466. if (mWebView != null && !mWebView.hasFocus()) {
  467. mWebView.requestFocus();
  468. }
  469. }
  470. public void setVisibility(int visible) {
  471. if (mWebView != null) {
  472. mWebView.setVisibility(visible);
  473. }
  474. }
  475. public void clearCache(boolean disk) {
  476. if (mWebView != null) {
  477. mWebView.clearCache(disk);
  478. }
  479. }
  480. public synchronized void reload() {
  481. // Check if configured proxy is available
  482. if (!mBrowserController.isProxyReady()) {
  483. // User has been notified
  484. return;
  485. }
  486. if (mWebView != null) {
  487. mWebView.reload();
  488. }
  489. }
  490. private void cacheFavicon(Bitmap icon) {
  491. String hash = String.valueOf(Utils.getDomainName(getUrl()).hashCode());
  492. Log.d(Constants.TAG, "Caching icon for " + Utils.getDomainName(getUrl()));
  493. File image = new File(mActivity.getCacheDir(), hash + ".png");
  494. try {
  495. FileOutputStream fos = new FileOutputStream(image);
  496. icon.compress(Bitmap.CompressFormat.PNG, 100, fos);
  497. fos.flush();
  498. fos.close();
  499. } catch (IOException e) {
  500. e.printStackTrace();
  501. }
  502. }
  503. @SuppressWarnings("deprecation")
  504. @SuppressLint("NewApi")
  505. public synchronized void find(String text) {
  506. if (mWebView != null) {
  507. if (API > 16) {
  508. mWebView.findAllAsync(text);
  509. } else {
  510. mWebView.findAll(text);
  511. }
  512. }
  513. }
  514. public Activity getActivity() {
  515. return mActivity;
  516. }
  517. public synchronized void onDestroy() {
  518. if (mWebView != null) {
  519. mWebView.stopLoading();
  520. mWebView.onPause();
  521. mWebView.clearHistory();
  522. mWebView.setVisibility(View.GONE);
  523. mWebView.removeAllViews();
  524. mWebView.destroyDrawingCache();
  525. // mWebView.destroy(); //this is causing the segfault
  526. mWebView = null;
  527. }
  528. }
  529. public synchronized void goBack() {
  530. if (mWebView != null) {
  531. mWebView.goBack();
  532. }
  533. }
  534. public String getUserAgent() {
  535. if (mWebView != null) {
  536. return mWebView.getSettings().getUserAgentString();
  537. } else {
  538. return "";
  539. }
  540. }
  541. public synchronized void goForward() {
  542. if (mWebView != null) {
  543. mWebView.goForward();
  544. }
  545. }
  546. public boolean canGoBack() {
  547. return mWebView != null && mWebView.canGoBack();
  548. }
  549. public boolean canGoForward() {
  550. return mWebView != null && mWebView.canGoForward();
  551. }
  552. public WebView getWebView() {
  553. return mWebView;
  554. }
  555. public Bitmap getFavicon() {
  556. return mTitle.getFavicon();
  557. }
  558. public synchronized void loadUrl(String url) {
  559. // Check if configured proxy is available
  560. if (!mBrowserController.isProxyReady()) {
  561. // User has been notified
  562. return;
  563. }
  564. if (mWebView != null) {
  565. mWebView.loadUrl(url);
  566. }
  567. }
  568. public synchronized void invalidate() {
  569. if (mWebView != null) {
  570. mWebView.invalidate();
  571. }
  572. }
  573. public String getTitle() {
  574. return mTitle.getTitle();
  575. }
  576. public String getUrl() {
  577. if (mWebView != null) {
  578. return mWebView.getUrl();
  579. } else {
  580. return "";
  581. }
  582. }
  583. public class LightningWebClient extends WebViewClient {
  584. final Context mActivity;
  585. LightningWebClient(Context context) {
  586. mActivity = context;
  587. }
  588. @Override
  589. public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
  590. if (mAdBlock.isAd(request.getUrl().getHost())) {
  591. ByteArrayInputStream EMPTY = new ByteArrayInputStream("".getBytes());
  592. return new WebResourceResponse("text/plain", "utf-8", EMPTY);
  593. }
  594. return super.shouldInterceptRequest(view, request);
  595. }
  596. @Override
  597. public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
  598. if (mAdBlock.isAd(url)) {
  599. ByteArrayInputStream EMPTY = new ByteArrayInputStream("".getBytes());
  600. return new WebResourceResponse("text/plain", "utf-8", EMPTY);
  601. }
  602. return null;
  603. }
  604. @Override
  605. public void onPageFinished(WebView view, String url) {
  606. if (view.isShown()) {
  607. mBrowserController.updateUrl(url, true);
  608. view.postInvalidate();
  609. }
  610. if (view.getTitle() == null || view.getTitle().isEmpty()) {
  611. mTitle.setTitle(mActivity.getString(R.string.untitled));
  612. } else {
  613. mTitle.setTitle(view.getTitle());
  614. }
  615. if (API >= android.os.Build.VERSION_CODES.KITKAT && mInvertPage) {
  616. view.evaluateJavascript(Constants.JAVASCRIPT_INVERT_PAGE, null);
  617. }
  618. mBrowserController.update();
  619. }
  620. @Override
  621. public void onPageStarted(WebView view, String url, Bitmap favicon) {
  622. if (isShown()) {
  623. mBrowserController.updateUrl(url, false);
  624. mBrowserController.showActionBar();
  625. }
  626. mTitle.setFavicon(mWebpageBitmap);
  627. mBrowserController.update();
  628. }
  629. @Override
  630. public void onReceivedHttpAuthRequest(final WebView view, @NonNull final HttpAuthHandler handler,
  631. final String host, final String realm) {
  632. AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
  633. final EditText name = new EditText(mActivity);
  634. final EditText password = new EditText(mActivity);
  635. LinearLayout passLayout = new LinearLayout(mActivity);
  636. passLayout.setOrientation(LinearLayout.VERTICAL);
  637. passLayout.addView(name);
  638. passLayout.addView(password);
  639. name.setHint(mActivity.getString(R.string.hint_username));
  640. name.setSingleLine();
  641. password.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
  642. password.setSingleLine();
  643. password.setTransformationMethod(new PasswordTransformationMethod());
  644. password.setHint(mActivity.getString(R.string.hint_password));
  645. builder.setTitle(mActivity.getString(R.string.title_sign_in));
  646. builder.setView(passLayout);
  647. builder.setCancelable(true)
  648. .setPositiveButton(mActivity.getString(R.string.title_sign_in),
  649. new DialogInterface.OnClickListener() {
  650. @Override
  651. public void onClick(DialogInterface dialog, int id) {
  652. String user = name.getText().toString();
  653. String pass = password.getText().toString();
  654. handler.proceed(user.trim(), pass.trim());
  655. Log.d(Constants.TAG, "Request Login");
  656. }
  657. })
  658. .setNegativeButton(mActivity.getString(R.string.action_cancel),
  659. new DialogInterface.OnClickListener() {
  660. @Override
  661. public void onClick(DialogInterface dialog, int id) {
  662. handler.cancel();
  663. }
  664. });
  665. AlertDialog alert = builder.create();
  666. alert.show();
  667. }
  668. private boolean mIsRunning = false;
  669. private float mZoomScale = 0.0f;
  670. @Override
  671. public void onScaleChanged(final WebView view, final float oldScale, final float newScale) {
  672. if (view.isShown() && mTextReflow && API >= android.os.Build.VERSION_CODES.KITKAT) {
  673. if (mIsRunning)
  674. return;
  675. if (Math.abs(mZoomScale - newScale) > 0.01f) {
  676. mIsRunning = view.postDelayed(new Runnable() {
  677. @Override
  678. public void run() {
  679. mZoomScale = newScale;
  680. view.evaluateJavascript(Constants.JAVASCRIPT_TEXT_REFLOW, null);
  681. mIsRunning = false;
  682. }
  683. }, 100);
  684. }
  685. }
  686. }
  687. @Override
  688. public void onReceivedSslError(WebView view, @NonNull final SslErrorHandler handler, SslError error) {
  689. AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
  690. builder.setTitle(mActivity.getString(R.string.title_warning));
  691. builder.setMessage(mActivity.getString(R.string.message_untrusted_certificate))
  692. .setCancelable(true)
  693. .setPositiveButton(mActivity.getString(R.string.action_yes),
  694. new DialogInterface.OnClickListener() {
  695. @Override
  696. public void onClick(DialogInterface dialog, int id) {
  697. handler.proceed();
  698. }
  699. })
  700. .setNegativeButton(mActivity.getString(R.string.action_no),
  701. new DialogInterface.OnClickListener() {
  702. @Override
  703. public void onClick(DialogInterface dialog, int id) {
  704. handler.cancel();
  705. }
  706. });
  707. AlertDialog alert = builder.create();
  708. if (error.getPrimaryError() == SslError.SSL_UNTRUSTED) {
  709. alert.show();
  710. } else {
  711. handler.proceed();
  712. }
  713. }
  714. @Override
  715. public void onFormResubmission(WebView view, @NonNull final Message dontResend, final Message resend) {
  716. AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
  717. builder.setTitle(mActivity.getString(R.string.title_form_resubmission));
  718. builder.setMessage(mActivity.getString(R.string.message_form_resubmission))
  719. .setCancelable(true)
  720. .setPositiveButton(mActivity.getString(R.string.action_yes),
  721. new DialogInterface.OnClickListener() {
  722. @Override
  723. public void onClick(DialogInterface dialog, int id) {
  724. resend.sendToTarget();
  725. }
  726. })
  727. .setNegativeButton(mActivity.getString(R.string.action_no),
  728. new DialogInterface.OnClickListener() {
  729. @Override
  730. public void onClick(DialogInterface dialog, int id) {
  731. dontResend.sendToTarget();
  732. }
  733. });
  734. AlertDialog alert = builder.create();
  735. alert.show();
  736. }
  737. @Override
  738. public boolean shouldOverrideUrlLoading(WebView view, String url) {
  739. // Check if configured proxy is available
  740. if (!mBrowserController.isProxyReady()) {
  741. // User has been notified
  742. return true;
  743. }
  744. if (mBrowserController.isIncognito()) {
  745. return super.shouldOverrideUrlLoading(view, url);
  746. }
  747. if (url.startsWith("about:")) {
  748. return super.shouldOverrideUrlLoading(view, url);
  749. }
  750. if (url.contains("mailto:")) {
  751. MailTo mailTo = MailTo.parse(url);
  752. Intent i = Utils.newEmailIntent(mActivity, mailTo.getTo(), mailTo.getSubject(),
  753. mailTo.getBody(), mailTo.getCc());
  754. mActivity.startActivity(i);
  755. view.reload();
  756. return true;
  757. } else if (url.startsWith("intent://")) {
  758. Intent intent;
  759. try {
  760. intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
  761. } catch (URISyntaxException ex) {
  762. return false;
  763. }
  764. if (intent != null) {
  765. try {
  766. mActivity.startActivity(intent);
  767. } catch (ActivityNotFoundException e) {
  768. Log.e(Constants.TAG, "ActivityNotFoundException");
  769. }
  770. return true;
  771. }
  772. }
  773. return mIntentUtils.startActivityForUrl(mWebView, url);
  774. }
  775. }
  776. public class LightningChromeClient extends WebChromeClient {
  777. final Context mActivity;
  778. LightningChromeClient(Context context) {
  779. mActivity = context;
  780. }
  781. @Override
  782. public void onProgressChanged(WebView view, int newProgress) {
  783. if (isShown()) {
  784. mBrowserController.updateProgress(newProgress);
  785. }
  786. }
  787. @Override
  788. public void onReceivedIcon(WebView view, Bitmap icon) {
  789. mTitle.setFavicon(icon);
  790. mBrowserController.update();
  791. cacheFavicon(icon);
  792. }
  793. @Override
  794. public void onReceivedTitle(WebView view, String title) {
  795. if (!title.isEmpty()) {
  796. mTitle.setTitle(title);
  797. } else {
  798. mTitle.setTitle(mActivity.getString(R.string.untitled));
  799. }
  800. mBrowserController.update();
  801. mBrowserController.updateHistory(title, view.getUrl());
  802. }
  803. @Override
  804. public void onGeolocationPermissionsShowPrompt(final String origin,
  805. final GeolocationPermissions.Callback callback) {
  806. final boolean remember = true;
  807. AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
  808. builder.setTitle(mActivity.getString(R.string.location));
  809. String org;
  810. if (origin.length() > 50) {
  811. org = origin.subSequence(0, 50) + "...";
  812. } else {
  813. org = origin;
  814. }
  815. builder.setMessage(org + mActivity.getString(R.string.message_location))
  816. .setCancelable(true)
  817. .setPositiveButton(mActivity.getString(R.string.action_allow),
  818. new DialogInterface.OnClickListener() {
  819. @Override
  820. public void onClick(DialogInterface dialog, int id) {
  821. callback.invoke(origin, true, remember);
  822. }
  823. })
  824. .setNegativeButton(mActivity.getString(R.string.action_dont_allow),
  825. new DialogInterface.OnClickListener() {
  826. @Override
  827. public void onClick(DialogInterface dialog, int id) {
  828. callback.invoke(origin, false, remember);
  829. }
  830. });
  831. AlertDialog alert = builder.create();
  832. alert.show();
  833. }
  834. @Override
  835. public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture,
  836. Message resultMsg) {
  837. mBrowserController.onCreateWindow(isUserGesture, resultMsg);
  838. return true;
  839. }
  840. @Override
  841. public void onCloseWindow(WebView window) {
  842. // TODO Auto-generated method stub
  843. super.onCloseWindow(window);
  844. }
  845. public void openFileChooser(ValueCallback<Uri> uploadMsg) {
  846. mBrowserController.openFileChooser(uploadMsg);
  847. }
  848. public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
  849. mBrowserController.openFileChooser(uploadMsg);
  850. }
  851. public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
  852. mBrowserController.openFileChooser(uploadMsg);
  853. }
  854. public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
  855. WebChromeClient.FileChooserParams fileChooserParams) {
  856. mBrowserController.showFileChooser(filePathCallback);
  857. return true;
  858. }
  859. @Override
  860. public Bitmap getDefaultVideoPoster() {
  861. return mBrowserController.getDefaultVideoPoster();
  862. }
  863. @Override
  864. public View getVideoLoadingProgressView() {
  865. return mBrowserController.getVideoLoadingProgressView();
  866. }
  867. @Override
  868. public void onHideCustomView() {
  869. mBrowserController.onHideCustomView();
  870. super.onHideCustomView();
  871. }
  872. @Override
  873. public void onShowCustomView(View view, CustomViewCallback callback) {
  874. // While these lines might look like they work, in practice,
  875. // Full-screen videos won't work correctly. I may test this out some
  876. // more
  877. // if (view instanceof FrameLayout) {
  878. // FrameLayout frame = (FrameLayout) view;
  879. // if (frame.getFocusedChild() instanceof VideoView) {
  880. // VideoView video = (VideoView) frame.getFocusedChild();
  881. // video.stopPlayback();
  882. // frame.removeView(video);
  883. // video.setVisibility(View.GONE);
  884. // }
  885. // } else {
  886. Activity activity = mBrowserController.getActivity();
  887. mBrowserController.onShowCustomView(view, activity.getRequestedOrientation(), callback);
  888. // }
  889. super.onShowCustomView(view, callback);
  890. }
  891. @Override
  892. @Deprecated
  893. public void onShowCustomView(View view, int requestedOrientation,
  894. CustomViewCallback callback) {
  895. // While these lines might look like they work, in practice,
  896. // Full-screen videos won't work correctly. I may test this out some
  897. // more
  898. // if (view instanceof FrameLayout) {
  899. // FrameLayout frame = (FrameLayout) view;
  900. // if (frame.getFocusedChild() instanceof VideoView) {
  901. // VideoView video = (VideoView) frame.getFocusedChild();
  902. // video.stopPlayback();
  903. // frame.removeView(video);
  904. // video.setVisibility(View.GONE);
  905. // }
  906. // } else {
  907. mBrowserController.onShowCustomView(view, requestedOrientation, callback);
  908. // }
  909. super.onShowCustomView(view, requestedOrientation, callback);
  910. }
  911. }
  912. public class Title {
  913. private Bitmap mFavicon;
  914. private String mTitle;
  915. private final Bitmap mDefaultIcon;
  916. public Title(Context context, boolean darkTheme) {
  917. mDefaultIcon = Utils.getWebpageBitmap(context.getResources(), darkTheme);
  918. mFavicon = mDefaultIcon;
  919. mTitle = mActivity.getString(R.string.action_new_tab);
  920. }
  921. public void setFavicon(Bitmap favicon) {
  922. if (favicon == null) {
  923. mFavicon = mDefaultIcon;
  924. } else {
  925. mFavicon = Utils.padFavicon(favicon);
  926. }
  927. }
  928. public void setTitle(String title) {
  929. if (title == null) {
  930. mTitle = "";
  931. } else {
  932. mTitle = title;
  933. }
  934. }
  935. public void setTitleAndFavicon(String title, Bitmap favicon) {
  936. mTitle = title;
  937. if (favicon == null) {
  938. mFavicon = mDefaultIcon;
  939. } else {
  940. mFavicon = Utils.padFavicon(favicon);
  941. }
  942. }
  943. public String getTitle() {
  944. return mTitle;
  945. }
  946. public Bitmap getFavicon() {
  947. return mFavicon;
  948. }
  949. }
  950. private class TouchListener implements OnTouchListener {
  951. float mLocation;
  952. float mY;
  953. int mAction;
  954. @SuppressLint("ClickableViewAccessibility")
  955. @Override
  956. public boolean onTouch(View view, MotionEvent arg1) {
  957. if (view != null && !view.hasFocus()) {
  958. view.requestFocus();
  959. }
  960. mAction = arg1.getAction();
  961. mY = arg1.getY();
  962. if (mAction == MotionEvent.ACTION_DOWN) {
  963. mLocation = mY;
  964. } else if (mAction == MotionEvent.ACTION_UP) {
  965. if ((mY - mLocation) > SCROLL_DOWN_THRESHOLD) {
  966. if (mWebView.getScrollY() != 0) {
  967. mBrowserController.showActionBar();
  968. } else {
  969. mBrowserController.toggleActionBar();
  970. }
  971. } else if ((mY - mLocation) < -SCROLL_UP_THRESHOLD) {
  972. mBrowserController.hideActionBar();
  973. }
  974. mLocation = 0;
  975. }
  976. mGestureDetector.onTouchEvent(arg1);
  977. return false;
  978. }
  979. }
  980. private class CustomGestureListener extends SimpleOnGestureListener {
  981. /**
  982. * Without this, onLongPress is not called when user is zooming using
  983. * two fingers, but is when using only one.
  984. *
  985. * The required behaviour is to not trigger this when the user is
  986. * zooming, it shouldn't matter how much fingers the user's using.
  987. */
  988. private boolean mCanTriggerLongPress = true;
  989. @Override
  990. public void onLongPress(MotionEvent e) {
  991. if (mCanTriggerLongPress)
  992. mBrowserController.onLongPress();
  993. }
  994. /**
  995. * Is called when the user is swiping after the doubletap, which in our
  996. * case means that he is zooming.
  997. */
  998. @Override
  999. public boolean onDoubleTapEvent(MotionEvent e) {
  1000. mCanTriggerLongPress = false;
  1001. return false;
  1002. }
  1003. /**
  1004. * Is called when something is starting being pressed, always before
  1005. * onLongPress.
  1006. */
  1007. @Override
  1008. public void onShowPress(MotionEvent e) {
  1009. mCanTriggerLongPress = true;
  1010. }
  1011. }
  1012. }