@Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    restored = false;

    isFullscreen = getFullscreenPreferences();
    UIHelper.ToggleFullscreen(this, isFullscreen);
    UIHelper.SetTheme(this, R.layout.activity_display_light_novel_content);
    UIHelper.SetActionBarDisplayHomeAsUp(this, true);

    // UI Helper
    uih = new DisplayNovelContentUIHelper(this);
    uih.prepareCompatSearchBox(webView);
    uih.prepareTopDownButton();

    // custom link handler
    client = new BakaTsukiWebViewClient(this);
    webView = (NonLeakingWebView) findViewById(R.id.webViewContent);
    webView.setWebViewClient(client);
    webView.setWebChromeClient(new BakaTsukiWebChromeClient(this));

    loadingText = (TextView) findViewById(R.id.emptyList);
    loadingBar = (ProgressBar) findViewById(R.id.loadProgress);

    tts = new DisplayNovelContentTTSHelper(this);

    Log.d(TAG, "OnCreate Completed.");
  }
  @Override
  public boolean onKeyDown(int keyCode, KeyEvent event) {
    boolean useVolumeRocker =
        PreferenceManager.getDefaultSharedPreferences(this)
            .getBoolean(Constants.PREF_USE_VOLUME_FOR_SCROLL, false);
    if (useVolumeRocker) {
      int scrollSize = UIHelper.getIntFromPreferences(Constants.PREF_SCROLL_SIZE, 5) * 100;

      boolean invertScroll =
          PreferenceManager.getDefaultSharedPreferences(this)
              .getBoolean(Constants.PREF_INVERT_SCROLL, false);
      if (invertScroll) scrollSize = scrollSize * -1;

      if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
        webView.flingScroll(0, -scrollSize);
        Log.d("Volume", "Up Pressed");
        return true;
      } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
        webView.flingScroll(0, +scrollSize);
        Log.d("Volume", "Down Pressed");
        return true;
      } else return super.onKeyDown(keyCode, event);
    }
    return super.onKeyDown(keyCode, event);
  }
  @Override
  protected void onDestroy() {
    if (webView != null) {
      RelativeLayout rootView = (RelativeLayout) findViewById(R.id.rootView);
      rootView.removeView(webView);
      webView.removeAllViews();
      webView.destroy();
    }
    tts.unbindTtsService();

    getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    super.onDestroy();
  }
 public void toggleProgressBar(boolean show) {
   if (webView == null || loadingBar == null || loadingText == null) return;
   synchronized (this) {
     if (show) {
       loadingText.setVisibility(TextView.VISIBLE);
       loadingBar.setVisibility(ProgressBar.VISIBLE);
       webView.setVisibility(ListView.GONE);
     } else {
       loadingText.setVisibility(TextView.GONE);
       loadingBar.setVisibility(ProgressBar.GONE);
       webView.setVisibility(ListView.VISIBLE);
     }
   }
 }
 // region Search box
 @SuppressWarnings("deprecation")
 private void showSearchBox() {
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) webView.showFindDialog("", true);
   else {
     RelativeLayout searchBox = (RelativeLayout) findViewById(R.id.searchBox);
     searchBox.setVisibility(View.VISIBLE);
   }
 }
 /**
  * Load chapter from DB
  *
  * @param pageModel
  * @param refresh
  */
 @SuppressLint("NewApi")
 private void executeTask(PageModel pageModel, boolean refresh) {
   NonLeakingWebView webView = (NonLeakingWebView) findViewById(R.id.webViewContent);
   if (pageModel.isExternal()) {
     loadExternalUrl(pageModel, refresh);
   } else {
     isPageLoaded = false;
     task = new LoadNovelContentTask(pageModel, refresh, this);
     String key = TAG + ":" + pageModel.getPage();
     boolean isAdded = LNReaderApplication.getInstance().addTask(key, task);
     if (isAdded) {
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
         task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
       else task.execute();
     } else {
       if (UIHelper.getColorPreferences(this))
         webView.loadData(
             "<p style='background: black; color: white;'>"
                 + getResources().getString(R.string.background_task_load)
                 + "</p>",
             "text/html",
             "utf-8");
       else
         webView.loadData(
             "<p style='background: white; color: black;'>"
                 + getResources().getString(R.string.background_task_load)
                 + "</p>",
             "text/html",
             "utf-8");
       LoadNovelContentTask tempTask =
           (LoadNovelContentTask) LNReaderApplication.getInstance().getTask(key);
       if (tempTask != null) {
         task = tempTask;
         task.owner = this;
       }
       toggleProgressBar(true);
     }
   }
   setPrevNextButtonState(pageModel);
 }
  /** Setup webView */
  @SuppressLint({"NewApi", "SetJavaScriptEnabled"})
  private void setWebViewSettings() {
    NonLeakingWebView wv = (NonLeakingWebView) findViewById(R.id.webViewContent);

    wv.getSettings().setAllowFileAccess(true);

    wv.getSettings().setSupportZoom(UIHelper.getZoomPreferences(this));
    wv.getSettings().setBuiltInZoomControls(UIHelper.getZoomPreferences(this));

    wv.setDisplayZoomControl(UIHelper.getZoomControlPreferences(this));

    wv.getSettings().setLoadWithOverviewMode(true);
    // wv.getSettings().setUseWideViewPort(true);
    wv.getSettings().setLoadsImagesAutomatically(getShowImagesPreferences());
    if (UIHelper.getColorPreferences(this)) wv.setBackgroundColor(0);
    wv.getSettings().setJavaScriptEnabled(true);

    if (isPageLoaded)
      wv.loadUrl("javascript:toogleEnableBookmark(" + getBookmarkPreferences() + ")");
  }
  /**
   * Load chapter for external url (not hosted in Baka-Tsuki). Used local cache if available
   * (wac/mht).
   *
   * @param pageModel
   * @param refresh
   */
  public void loadExternalUrl(PageModel pageModel, boolean refresh) {
    try {
      // check if .wac available
      String url = pageModel.getPage();
      String wacName = Util.getSavedWacName(url);
      final NonLeakingWebView wv = (NonLeakingWebView) findViewById(R.id.webViewContent);
      if (!Util.isStringNullOrEmpty(wacName) && !refresh) {
        client.setExternalNeedSave(false);
        String[] urlParts = url.split("#", 2);
        if (urlParts.length == 2) {
          executeLoadWacTask(wacName, urlParts[1], url);
        } else executeLoadWacTask(wacName, "", url);
      } else {
        if (refresh) {
          Toast.makeText(this, "Refreshing WAC: " + wacName, Toast.LENGTH_SHORT).show();
          Log.i(TAG, "Refreshing WAC: " + wacName);
        } else {
          Log.w(TAG, "WAC not available: " + wacName);
        }

        client.setExternalNeedSave(true);

        setWebViewSettings();
        wv.loadUrl(url);

        Intent currIntent = this.getIntent();
        currIntent.putExtra(Constants.EXTRA_PAGE, url);
        currIntent.putExtra(Constants.EXTRA_PAGE_IS_EXTERNAL, true);
      }
      setChapterTitle(pageModel);
      buildTOCMenu(pageModel);
      content = null;
    } catch (Exception ex) {
      Log.e(TAG, "Cannot load external content: " + pageModel.getPage(), ex);
    }
  }
  /**
   * Used to move to the last read position upon receiving load complete event from webView client
   */
  public void notifyLoadComplete() {
    isPageLoaded = true;
    if (webView != null && content != null) {

      // move to last read paragraph, delay after webView load the pages.
      webView.postDelayed(
          new Runnable() {
            @Override
            public void run() {
              int y = getIntent().getIntExtra(Constants.EXTRA_P_INDEX, content.getLastYScroll());
              Log.d(TAG, "notifyLoadComplete(): Move to the saved pos: " + y);
              webView.loadUrl("javascript:goToParagraph(" + y + ")");
            }
          },
          UIHelper.getIntFromPreferences(Constants.PREF_KITKAT_WEBVIEW_FIX_DELAY, 500) + 100);
    }
  }
  @Override
  public boolean onPrepareOptionsMenu(Menu menu) {
    super.onPrepareOptionsMenu(menu);
    _menu = menu;

    try {
      if (content != null) {
        setPrevNextButtonState(content.getPageModel());
        _menu.findItem(R.id.menu_save_external).setVisible(false);
        _menu.findItem(R.id.menu_browser_back).setVisible(false);
      } else {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
          _menu.findItem(R.id.menu_save_external).setVisible(true);
        _menu.findItem(R.id.menu_browser_back).setVisible(true);
        if (webView != null) _menu.findItem(R.id.menu_browser_back).setEnabled(webView.canGoBack());
      }
    } catch (Exception e) {
      Log.w(TAG, "Cannot get current page model");
    }

    return true;
  }
  /**
   * Setup the chapter from DB, Internal page only Including JS for Bookmark highlighting, last read
   * position, and CSS
   *
   * @param loadedContent
   */
  @SuppressLint("NewApi")
  public void setContent(NovelContentModel loadedContent) {
    this.content = loadedContent;
    try {
      PageModel pageModel = content.getPageModel();

      if (content.getLastUpdate().getTime() < pageModel.getLastUpdate().getTime())
        Toast.makeText(
                this,
                getResources()
                    .getString(
                        R.string.content_may_updated,
                        content.getLastUpdate().toString(),
                        pageModel.getLastUpdate().toString()),
                Toast.LENGTH_LONG)
            .show();

      // load the contents here
      final NonLeakingWebView wv = (NonLeakingWebView) findViewById(R.id.webViewContent);
      setWebViewSettings();

      int lastPos = content.getLastYScroll();
      int pIndex = getIntent().getIntExtra(Constants.EXTRA_P_INDEX, -1);
      if (pIndex > 0) lastPos = pIndex;

      if (content.getLastZoom() > 0) {
        wv.setInitialScale((int) (content.getLastZoom() * 100));
      } else {
        wv.setInitialScale(100);
      }

      StringBuilder html = new StringBuilder();
      html.append("<html><head>");
      html.append(DisplayNovelContentHtmlHelper.getCSSSheet());
      html.append(DisplayNovelContentHtmlHelper.getViewPortMeta());
      html.append(
          DisplayNovelContentHtmlHelper.prepareJavaScript(
              lastPos, content.getBookmarks(), getBookmarkPreferences()));
      html.append("</head><body onclick='toogleHighlight(this, event);' onload='setup();'>");
      html.append(content.getContent());
      html.append("</body></html>");

      wv.loadDataWithBaseURL(
          UIHelper.getBaseUrl(this),
          html.toString(),
          "text/html",
          "utf-8",
          NonLeakingWebView.PREFIX_PAGEMODEL + content.getPage());
      setChapterTitle(pageModel);
      Log.d(
          TAG,
          "Load Content: "
              + content.getLastXScroll()
              + " "
              + content.getLastYScroll()
              + " "
              + content.getLastZoom());

      buildTOCMenu(pageModel);
      buildBookmarkMenu();

      invalidateOptionsMenu();

      Log.d(TAG, "Loaded: " + content.getPage());

      Intent currIntent = this.getIntent();
      currIntent.putExtra(Constants.EXTRA_PAGE, content.getPage());
      currIntent.putExtra(Constants.EXTRA_PAGE_IS_EXTERNAL, false);

    } catch (Exception e) {
      Log.e(TAG, "Cannot load content.", e);
    }
  }
 public void searchNext(View view) {
   webView.findNext(true);
 }
 public void searchPrev(View view) {
   webView.findNext(false);
 }
 public void goTop(View view) {
   webView.pageUp(true);
 }
  /** Update last read chapter and the position. Run async */
  @SuppressWarnings("deprecation")
  public void setLastReadState() {
    NonLeakingWebView wv = (NonLeakingWebView) findViewById(R.id.webViewContent);
    final float currentScale = wv.getScale();
    final int lastY = wv.getScrollY() + wv.getBottom();
    final int contentHeight = wv.getContentHeight();
    new Thread(
            new Runnable() {

              @Override
              public void run() {
                if (content != null) {
                  // save zoom level, position is updated from updateLastLine()
                  content.setLastZoom(currentScale);
                  try {
                    content = NovelsDao.getInstance().updateNovelContent(content, false);
                  } catch (Exception ex) {
                    Log.e(TAG, "Error when saving state: " + ex.getMessage(), ex);
                  }

                  // check if complete read.
                  if (contentHeight <= lastY) {
                    if (content != null) {
                      try {
                        PageModel page = content.getPageModel();
                        if (!page.getPage().endsWith("&action=edit&redlink=1")) {
                          page.setFinishedRead(true);
                          NovelsDao.getInstance().updatePageModel(page);
                        }
                      } catch (Exception ex) {
                        Log.e(
                            TAG, "Error updating PageModel for Content: " + content.getPage(), ex);
                      }
                    }
                  }
                  Log.d(
                      TAG,
                      "Update Content:X="
                          + content.getLastXScroll()
                          + ":Y="
                          + content.getLastYScroll()
                          + ":Z="
                          + content.getLastZoom());
                }

                // save for jump to last read.
                SharedPreferences sharedPrefs =
                    PreferenceManager.getDefaultSharedPreferences(
                        LNReaderApplication.getInstance());
                SharedPreferences.Editor editor = sharedPrefs.edit();
                String lastPage;
                if (content != null) {
                  lastPage = content.getPage();
                } else {
                  lastPage = getIntent().getStringExtra(Constants.EXTRA_PAGE);
                }
                editor.putString(Constants.PREF_LAST_READ, lastPage);
                editor.commit();
                Log.i(TAG, "Last Read State Update complete: " + lastPage);
              }
            })
        .start();
  }
 public void goBottom(View view) {
   webView.pageDown(true);
 }
  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
      case R.id.menu_settings:
        Intent launchNewIntent = new Intent(this, DisplaySettingsActivity.class);
        startActivity(launchNewIntent);
        return true;
      case R.id.menu_refresh_chapter_content:

        /*
         * Implement code to refresh chapter content
         */
        PageModel page = null;
        if (content != null) {
          try {
            page = content.getPageModel();
          } catch (Exception e) {
            Log.e(TAG, "Cannot get current chapter.", e);
          }
        } else {
          String pageStr = getIntent().getStringExtra(Constants.EXTRA_PAGE);
          try {
            page = NovelsDao.getInstance().getExistingPageModel(new PageModel(pageStr), null);
            if (page == null) {
              // no page model, just url
              page = new PageModel(pageStr);
              page.setExternal(true);
            }
          } catch (Exception e) {
            Log.e(TAG, "Cannot get current chapter.", e);
          }
        }

        if (page != null) {
          executeTask(page, true);
        }
        return true;
      case R.id.invert_colors:

        /*
         * Implement code to invert colors
         */
        UIHelper.ToggleColorPref(this);
        UIHelper.Recreate(this);
        return true;
      case R.id.menu_chapter_previous:

        /*
         * Implement code to move to previous chapter
         */
        String currentPage = getIntent().getStringExtra(Constants.EXTRA_PAGE);
        try {
          if (novelDetails == null)
            novelDetails =
                NovelsDao.getInstance().getNovelDetails(content.getPageModel(), null, false);
          PageModel prev =
              novelDetails.getPrev(
                  currentPage, UIHelper.getShowMissing(this), UIHelper.getShowRedlink(this));
          if (prev != null) {
            jumpTo(prev);
          } else {
            Toast.makeText(
                    this,
                    getResources().getString(R.string.first_available_chapter),
                    Toast.LENGTH_SHORT)
                .show();
          }
        } catch (Exception e) {
          Log.e(TAG, "Cannot get previous chapter.", e);
        }
        return true;
      case R.id.menu_chapter_next:

        /*
         * Implement code to move to next chapter
         */
        String currentPage2 = getIntent().getStringExtra(Constants.EXTRA_PAGE);
        try {
          if (novelDetails == null)
            novelDetails =
                NovelsDao.getInstance().getNovelDetails(content.getPageModel(), null, false);

          PageModel next =
              novelDetails.getNext(
                  currentPage2, UIHelper.getShowMissing(this), UIHelper.getShowRedlink(this));
          if (next != null) {
            jumpTo(next);
          } else {
            Toast.makeText(
                    this,
                    getResources().getString(R.string.last_available_chapter),
                    Toast.LENGTH_SHORT)
                .show();
          }
        } catch (Exception e) {
          Log.e(TAG, "Cannot get next chapter.", e);
        }
        return true;
      case R.id.menu_chapter_toc:
        if (tocMenu != null) tocMenu.show();
        return true;
      case R.id.menu_search:
        showSearchBox();
        return true;
      case R.id.menu_bookmarks_here:
        if (bookmarkMenu != null) bookmarkMenu.show();
        return true;
      case R.id.menu_bookmarks:
        Intent bookmarkIntent = new Intent(this, DisplayBookmarkActivity.class);
        startActivity(bookmarkIntent);
        return true;
      case R.id.menu_downloads_list:
        Intent downloadsItent = new Intent(this, DownloadListActivity.class);
        startActivity(downloadsItent);
        return true;
      case R.id.menu_speak:
        tts.start(webView, content.getLastYScroll());
        return true;
      case R.id.menu_pause_tts:
        tts.pause();
        return true;
      case R.id.menu_save_external:
        // save based on current intent page name.
        String url = getIntent().getStringExtra(Constants.EXTRA_PAGE);
        NonLeakingWebView wv = (NonLeakingWebView) findViewById(R.id.webViewContent);
        if (!url.startsWith("http")) {
          url = getTitle().toString();
          Log.w(
              TAG,
              "Current page is not started with http, resolve from current webView url: " + url);
        }

        if (wv != null && !Util.isStringNullOrEmpty(url)) wv.saveMyWebArchive(url);
        return true;
      case R.id.menu_browser_back:
        if (webView != null && webView.canGoBack()) {
          // only good for android 4.4++
          webView.goBack();
        }
        return true;
      case android.R.id.home:
        finish();
        return true;
      default:
        return super.onOptionsItemSelected(item);
    }
  }